]> git.proxmox.com Git - mirror_smartmontools-debian.git/commitdiff
Imported smartmontools-5.27.cvs20061002
authorGuido Guenther <agx@sigxcpu.org>
Mon, 2 Oct 2006 14:38:42 +0000 (16:38 +0200)
committerGuido Guenther <agx@bogon.sigxcpu.org>
Mon, 2 Oct 2006 14:38:42 +0000 (16:38 +0200)
into Git repository

95 files changed:
CHANGELOG
INSTALL
Makefile.am
Makefile.in [deleted file]
NEWS
README
TODO
WARNINGS
aclocal.m4 [deleted file]
atacmdnames.c [deleted file]
atacmdnames.cpp [new file with mode: 0644]
atacmds.c [deleted file]
atacmds.cpp [new file with mode: 0644]
atacmds.h
ataprint.c [deleted file]
ataprint.cpp [new file with mode: 0644]
ataprint.h
config.guess [deleted file]
config.h.in [deleted file]
config.sub [deleted file]
configure [deleted file]
configure.in
depcomp [deleted file]
do_release [new file with mode: 0755]
examplescripts/Makefile.in [deleted file]
extern.h
install-sh [deleted file]
knowndrives.c [deleted file]
knowndrives.cpp [new file with mode: 0644]
knowndrives.h
missing [deleted file]
os_darwin.c [deleted file]
os_darwin.cpp [new file with mode: 0644]
os_freebsd.c [deleted file]
os_freebsd.cpp [new file with mode: 0644]
os_generic.c [deleted file]
os_generic.cpp [new file with mode: 0644]
os_linux.c [deleted file]
os_linux.cpp [new file with mode: 0644]
os_linux.h
os_netbsd.c [deleted file]
os_netbsd.cpp [new file with mode: 0644]
os_openbsd.c [deleted file]
os_openbsd.cpp [new file with mode: 0644]
os_openbsd.h
os_os2.cpp [new file with mode: 0644]
os_os2.h [new file with mode: 0644]
os_os2/configure.os2 [new file with mode: 0644]
os_os2/hdreg.h [new file with mode: 0644]
os_solaris.c [deleted file]
os_solaris.cpp [new file with mode: 0644]
os_solaris.h
os_win32.c [deleted file]
os_win32.cpp [new file with mode: 0644]
os_win32/daemon_win32.c [deleted file]
os_win32/daemon_win32.cpp [new file with mode: 0644]
os_win32/hostname_win32.c [deleted file]
os_win32/hostname_win32.cpp [new file with mode: 0644]
os_win32/hostname_win32.h
os_win32/installer.nsi [new file with mode: 0644]
os_win32/smartctl_vc6.dsp [new file with mode: 0644]
os_win32/smartd_vc6.dsp [new file with mode: 0644]
os_win32/smartmontools_vc6.dsw [new file with mode: 0644]
os_win32/syslog.h
os_win32/syslog_win32.c [deleted file]
os_win32/syslog_win32.cpp [new file with mode: 0644]
os_win32/syslogevt.c [new file with mode: 0644]
os_win32/syslogevt.mc [new file with mode: 0644]
os_win32/syslogevt_vc6.dsp [new file with mode: 0644]
posix/getopt.c [new file with mode: 0644]
posix/getopt.h [new file with mode: 0644]
posix/getopt1.c [new file with mode: 0644]
posix/regex.h
scsiata.cpp [new file with mode: 0644]
scsiata.h [new file with mode: 0644]
scsicmds.c [deleted file]
scsicmds.cpp [new file with mode: 0644]
scsicmds.h
scsiprint.c [deleted file]
scsiprint.cpp [new file with mode: 0644]
smartctl.8.in
smartctl.8.in.orig [new file with mode: 0644]
smartctl.c [deleted file]
smartctl.cpp [new file with mode: 0644]
smartctl.h
smartd.8.in
smartd.c [deleted file]
smartd.conf
smartd.conf.5.in
smartd.cpp [new file with mode: 0644]
smartd.h
smartmontools.spec
utility.c [deleted file]
utility.cpp [new file with mode: 0644]
utility.h

index 7857883b6e5657f1338150f5e7303acc5e21f3a1..faa571a237a6488a86a75865d0c96b63e5244279 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,9 +1,9 @@
 CHANGELOG for smartmontools
 
-$Id: CHANGELOG,v 1.537 2006/04/12 16:11:44 ballen4705 Exp $
+$Id: CHANGELOG,v 1.566 2006/09/27 21:42:03 chrfranke Exp $
 
 The most recent version of this file is:
-http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/CHANGELOG?sortby=date&view=markup
+http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/CHANGELOG?view=markup
 
 Maintainers / Developers Key:
 [BA] Bruce Allen
@@ -27,11 +27,110 @@ Maintainers / Developers Key:
 [PW] Phil Williams
 [LW] Leon Woestenberg
 [RZ] Richard Zybert
+[SZ] Sf Zhou
 
 NOTES FOR FUTURE RELEASES: see TODO file.
 
 <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
 
+  [CF] Windows: Added support for 3ware 9000 controllers using extended
+       SMART functionality in new 3ware driver. This includes DEVICESCAN
+       support for at most 2 controllers. Thanks to Greg de Valois from
+       AMCC/3ware for new driver features, development support and
+       hardware for testing.
+
+  [SZ] smartd: Support HighPoint RocketRAID controller under GNU/linux
+
+  [DG] [SCSI] First cut for '-l background' to show background scan
+       results log
+
+  [SZ] smartctl: Support HighPoint RocketRAID controller under GNU/linux
+
+  [KS] C++ compile fixes for Solaris with a few cleanups.
+
+  [BA] C++ compile fixes for Darwin (thanks to CF)
+
+  [CF] Removed old *.c files (now in CVS Attic).
+
+  [CF] Added changes for C++ to platform independent and Windows
+       related files.
+
+  [BA] Tagged last .c Version with PRE_MOVE_TO_CPP. Copied *.c,v
+       to *.cpp,v in CVS repository to preserve history of source
+       files. Removed sm5_Darwin repository.
+
+  [CF] smartctl: Added -n option to skip checks when disk is in
+       low-power mode.
+
+  [CF] Windows: Added alternate system call for power state check
+       because the PASS THROUGH calls may spin up the disk.
+
+  [CF] smartd: Modified power state logging to report state changes
+       instead of standby condition.
+
+  [CF] smartd: Ignore -n directive on scheduled self tests.
+
+  [DG] [SCSI] Make start stop cycle counter log page decoding
+       more robust
+
+  [DG] Modify smartctl (but not smartd) to detect probable ATA
+       devices behind a SAT layer. In the absence of an explicit
+       device type, change to device type 'sat'.
+
+  [DG] Add indication that controller (device) type has been
+       explicitly set. Preparation for automatic detection of
+       'sat' device type unless user specifies a device type.
+
+  [SS] NetBSD: Deliver strings from ata_identify_device properly 
+       on little- and big-endian platforms.
+
+  [BA] Added published ANSI ATA-7 spec to list of recognized ATA
+       versions.
+
+  [BA] Code janitor: added missing header strings for '-V' option.
+
+  [DG] [SATA] Extend 'sat' device type to allow either 12 or 16 byte
+       variant of the SAT ATA PASS THROUGH SCSI command. Syntax is
+       '-d sat,<n>' where <n> can be 0, 12 or 16 . The ',<n>' part
+       is optional. Currently defaults to 16 byte variant but that
+       could be made platform or even device dependent.
+
+  [DG] [SATA] Add new 'sat' device type for SATA disks behind a
+       SCSI to ATA Translation (SAT) Layer (SATL). Uses the ATA
+       PASS THROUGH (16) SCSI command thence the generic SCSI
+       passthrough for each platform.
+
+  [CF] Windows: Added script and make targets to create installer
+       with NSIS (http://nsis.sourceforge.net/)
+
+  [CF] Updated hostname and links for new SourceForge CVS service.
+
+  [CF] smartd: Added '-W' directive to track temperature changes
+       and warn if temperature limits are reached.
+
+  [CF] Windows: Added IOCTL_ATA_PASS_THROUGH (Win2003, XP SP2)
+       for commands unsupported by SMART_IOCTL. Added device
+       specific options to select subset and ordering of the ATA
+       IOCTLs actually used. These options are specified as
+       modifiers of the device name (/dev/hd[a-j]:[saic]+)
+
+  [CF] Windows: Added support for drives 4-7 (/dev/hd[e-h]) via
+       SMARTVSE.VXD on Win9x/ME. Thanks to Dariusz Rzonca for
+       patch and testing.
+
+  [DG] [SCSI/SATA linux] from lk 2.6.17 SATA disk identification in
+       libata will change. Expand LibAta detection to see old
+       identifier and new variant (VPD page 0x83).
+
+  [BA] Identified Attribute 190 for Western Digital disks.  This
+       stores temperature in Celsius, just like Attribute 194.
+       But it has a failure threshold set to correspond to the
+       maximum design operating temperature of the disk, which
+       is 55 Celsius on the WD800JD drives that I studied.
+       So if this Attribute has 'failed
+       in the past' this means that the maximum disk operating
+       temperature has been exceeded.
+
 smartmontools 5.36 Stable Release
 
   [BA] Linux: smartd/smartctl issue syntax hints to user if 3ware
diff --git a/INSTALL b/INSTALL
index c1ca8ca0b77e3a700649d5701874e8026f91a6e9..523adbe79c2df2e48ee14c21f11f193a1631e16f 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,7 +1,7 @@
 Smartmontools installation instructions
 =======================================
 
-$Id: INSTALL,v 1.62 2005/10/22 17:11:39 chrfranke Exp $
+$Id: INSTALL,v 1.68 2006/09/27 21:42:03 chrfranke Exp $
 
 Please also see the smartmontools home page:
 http://smartmontools.sourceforge.net/
@@ -123,15 +123,24 @@ Table of contents:
     to get loaded at Windows startup. The default location in a new
     installation of some versions of Windows is the WINDOWS\SYSTEM folder.
     In this case, move SMARTVSD.VXD to WINDOWS\SYSTEM\IOSUBSYS and reboot
-    (http://support.microsoft.com/default.aspx?scid=kb;en-us;265854).
+    (http://support.microsoft.com/kb/265854/en-us).
+    SMARTVSD.VXD may also be missing in a new installation
+    (http://support.microsoft.com/kb/199886/en-us).
 
     SMARTVSD.VXD relies on the standard IDE port driver ESDI_506.PDR.
     If the system uses a vendor specific driver, access of SMART data
     is not possible on 9x/ME. This is the case if e.g. the optional
     "IDE miniport driver" is installed on a system with VIA chipset.
 
-    On NT4/2000/XP, also other ATA or SATA devices are supported if
-    the device driver implements the SMART IOCTL.
+    Some ATA controllers (e.g. Promise) provided a custom SMARTVSD.VXD
+    for their Win9x/ME driver. To access SMART data from both the legacy
+    (/dev/h[a-d]) and this additional (/dev/hd[e-h]) controller, rename
+    this file to SMARTVSE.VXD. Open the file with a hex editor and replace
+    all occurrences of the string "SMARTVSD" with "SMARTVSE". Then reinstall
+    the original Windows SMARTVSD.VXD.
+
+    On NT4/2000/XP/2003, ATA or SATA devices are supported if the device
+    driver implements the SMART IOCTL.
 
     The IDE/ATA read log command (smartctl -l, --log, -a, --all) is
     not supported by the SMART IOCTL of NT4/2000/XP. Undocumented
@@ -141,18 +150,28 @@ Table of contents:
     SCSI devices are supported on all versions of Windows. An installed
     ASPI interface (WNASPI32.DLL) is required to access SCSI devices.
     The code was tested with Adaptec Windows ASPI drivers 4.71.2.
-    (http://www.adaptec.com/worldwide/support/drivers_by_product.jsp?cat=/Product/ASPI-4.70)
+    (http://www.adaptec.com/en-US/support/scsi_soft/ASPI/ASPI-4.70/)
+    Links to other ASPI drivers can be found at http://www.nu2.nu/aspi/.
+
+    3ware 9000 RAID controllers are supported using new features added
+    to the 3ware 9000 Windows driver. These features are not implemented
+    in the latest 'Released' driver (9.3.0.6) available at the time of
+    this writing. But an 'in Engineering Phase' driver v3.00.02.061 or
+    later can be used to access SMART functionality of each individual
+    drive. Older drivers provide SMART access only to the first drive
+    (port) of each unit. The commands READ LOG and ABORT SELFTEST are
+    still unsupported due to the limitations of SMART IOCTL (see above).
 
     G) MacOS/Darwin
 
     The code was tested on MacOS 10.3.4.  It should work from 10.3
     forwards.  It doesn't support 10.2.
 
-    There's an important limitation (see WARNINGS); due to bugs in
-    the libraries used, you cannot run a short test or switch SMART
-    support off on a drive; if you try, you will just run an extended
-    test or switch SMART support on.  So don't panic when your "short"
-    test seems to be taking hours.
+    It's important to know that on 10.3.x, some things don't work
+    (see WARNINGS): due to bugs in the libraries used, you cannot run
+    a short test or switch SMART support off on a drive; if you try,
+    you will just run an extended test or switch SMART support on.  So
+    don't panic when your "short" test seems to be taking hours.
 
     It's also not possible at present to control when the offline
     routine runs. If your drive doesn't have it running automatically by
@@ -174,17 +193,16 @@ Table of contents:
      - checking the power mode [-n Directive of smartd] (this is not
        completely impossible, but not by using a documented API)
 
-   * Are implemented, but don't work due to OS bugs:
+   * Work on 10.4 and later, but not on 10.3:
      - switching off SMART (switching *on* works fine)
      - switching off auto-save (but why would you want to?)
      - running the short test (that leaves you with only the extended test)
 
-    The last set have been filed in Apple's bug tracking system and
-    hopefully will be fixed in the next major version of Mac OS.
-
     However, some things do work well.  For ATA devices, all the
     informational output is available, unless you want something that only
-    an offline test updates.
+    an offline test updates.  On many newer Mac OS systems, the
+    hard drive comes with the offline test switched on by default, so
+    even that works.
 
     H) OS/2, eComStation
 
@@ -197,8 +215,8 @@ Table of contents:
 [2] Installing from CVS
 =======================
     Get the sources from the CVS repository:
-    cvs -d :pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools login
-    cvs -d :pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools co sm5
+    cvs -d :pserver:anonymous@smartmontools.cvs.sourceforge.net:/cvsroot/smartmontools login
+    cvs -d :pserver:anonymous@smartmontools.cvs.sourceforge.net:/cvsroot/smartmontools co sm5
     (when prompted for a password, just press Enter)
 
     Then type:
@@ -331,6 +349,12 @@ SuSE:
 =========================
   ./configure --with-initscriptdir=/Library/StartupItems
 
+  If you'd like to build the i386 version on a powerpc machine, you can
+  use
+
+  CC='gcc -isysroot /Developer/SDKs/MacOSX10.4u.sdk -arch i386' \
+    ./configure --host=i386-apple-darwin \
+               --with-initscriptdir=/Library/StartupItems
 
 [7] Guidelines for NetBSD/OpenBSD
 =================================
@@ -418,7 +442,7 @@ To compile the Windows release with MinGW, use the following on Cygwin:
 To build the Windows binary distribution, use:
 
   make dist-win32
-  
+
   This builds the distribution in directory
 
   ./smartmontools-VERSION.win32/
@@ -427,6 +451,23 @@ To build the Windows binary distribution, use:
 
   ./smartmontools-VERSION.win32.zip
 
+To create a Windows installer, use:
+
+  make installer-win32
+
+  This builds the distribution directory and packs it into the
+  self-extracting install program
+
+  ./smartmontools-VERSION.win32-setup.exe
+
+  The installer is build using the command "makensis" from the NSIS
+  package. See http://nsis.sourceforge.net/ for documentation and
+  download location. The install script was tested with NSIS 2.17.
+
+To both create and run the (interactive) installer, use:
+
+  make install-win32
+
   Additional make targets are distdir-win32 to build the directory
   only and cleandist-win32 for cleanup.
 
index 9bc740d66c7c95fc2890d67e0c1bddef25de1908..7f79e9dfbeee58811effb1fadd5da8e5294e3cf7 100644 (file)
@@ -1,65 +1,72 @@
 ## Process this file with automake to produce Makefile.in
 #
-# $Id: Makefile.am,v 1.74 2005/11/29 18:22:51 chrfranke Exp $
+# $Id: Makefile.am,v 1.79 2006/08/09 20:40:19 chrfranke Exp $
 #
 
 @SET_MAKE@
 
+# Make sure .cpp takes precedence to avoid compiling old .c file
+SUFFIXES = .cpp .c .s .o
+
+
 AM_CPPFLAGS = -DSMARTMONTOOLS_SYSCONFDIR=\"$(sysconfdir)\"
 
 sbin_PROGRAMS = smartd         \
                smartctl
 
-smartd_SOURCES =  smartd.c      \
-                  smartd.h      \
-                  atacmdnames.c        \
-                  atacmdnames.h        \
-                  atacmds.c    \
-                  atacmds.h    \
-                  ataprint.c   \
-                  ataprint.h   \
-                  extern.h      \
-                  int64.h       \
-                  knowndrives.c        \
-                  knowndrives.h        \
-                  scsicmds.c   \
-                  scsicmds.h   \
-                  scsiprint.c  \
-                  scsiprint.h  \
-                  utility.c    \
+smartd_SOURCES =  smartd.cpp      \
+                  smartd.h        \
+                  atacmdnames.cpp \
+                  atacmdnames.h   \
+                  atacmds.cpp     \
+                  atacmds.h       \
+                  ataprint.cpp    \
+                  ataprint.h      \
+                  extern.h        \
+                  int64.h         \
+                  knowndrives.cpp \
+                  knowndrives.h   \
+                  scsicmds.cpp    \
+                  scsicmds.h      \
+                  scsiata.cpp     \
+                  scsiata.h       \
+                  scsiprint.cpp   \
+                  scsiprint.h     \
+                  utility.cpp     \
                   utility.h
 
 smartd_LDADD = @os_deps@ @os_libs@
 smartd_DEPENDENCIES = @os_deps@
-EXTRA_smartd_SOURCES = os_darwin.c      \
-                      os_darwin.h      \
-                      os_linux.c       \
+
+EXTRA_smartd_SOURCES = os_darwin.cpp    \
+                       os_darwin.h      \
+                       os_linux.cpp     \
                        os_linux.h       \
-                      os_freebsd.c     \
-                      os_freebsd.h     \
-                      os_netbsd.c      \
-                      os_netbsd.h      \
-                      os_openbsd.c     \
-                      os_openbsd.h     \
-                      os_solaris.c     \
-                      os_solaris.h     \
-                      os_solaris_ata.s \
-                      os_win32.c       \
-                      os_generic.c     \
-                      os_generic.h
+                       os_freebsd.cpp   \
+                       os_freebsd.h     \
+                       os_netbsd.cpp    \
+                       os_netbsd.h      \
+                       os_openbsd.cpp   \
+                       os_openbsd.h     \
+                       os_solaris.cpp   \
+                       os_solaris.h     \
+                       os_solaris_ata.s \
+                       os_win32.cpp     \
+                       os_generic.cpp   \
+                       os_generic.h
 
 
 if OS_WIN32_MINGW
 
-smartd_SOURCES +=                         \
-                posix/regex.h             \
-                posix/regex.c             \
-                os_win32/daemon_win32.h   \
-                os_win32/daemon_win32.c   \
-                os_win32/hostname_win32.h \
-                os_win32/hostname_win32.c \
-                os_win32/syslog.h         \
-                os_win32/syslog_win32.c
+smartd_SOURCES +=                           \
+                posix/regex.h               \
+                posix/regex.c               \
+                os_win32/daemon_win32.h     \
+                os_win32/daemon_win32.cpp   \
+                os_win32/hostname_win32.h   \
+                os_win32/hostname_win32.cpp \
+                os_win32/syslog.h           \
+                os_win32/syslog_win32.cpp
 
 # Included by regex.c:
 EXTRA_smartd_SOURCES +=                   \
@@ -70,40 +77,43 @@ EXTRA_smartd_SOURCES +=                   \
 
 endif
 
-smartctl_SOURCES= smartctl.c    \
-                  smartctl.h    \
-                  atacmdnames.c        \
-                  atacmdnames.h        \
-                  atacmds.c    \
-                  atacmds.h    \
-                  ataprint.c   \
-                  ataprint.h   \
-                  extern.h      \
-                  int64.h       \
-                  knowndrives.c        \
-                  knowndrives.h        \
-                  scsicmds.c   \
-                  scsicmds.h   \
-                  scsiprint.c  \
-                  scsiprint.h  \
-                  utility.c    \
+smartctl_SOURCES= smartctl.cpp    \
+                  smartctl.h      \
+                  atacmdnames.cpp \
+                  atacmdnames.h   \
+                  atacmds.cpp     \
+                  atacmds.h       \
+                  ataprint.cpp    \
+                  ataprint.h      \
+                  extern.h        \
+                  int64.h         \
+                  knowndrives.cpp \
+                  knowndrives.h   \
+                  scsicmds.cpp    \
+                  scsicmds.h      \
+                  scsiata.cpp     \
+                  scsiata.h       \
+                  scsiprint.cpp   \
+                  scsiprint.h     \
+                  utility.cpp     \
                   utility.h
 
 smartctl_LDADD = @os_deps@ @os_libs@
 smartctl_DEPENDENCIES = @os_deps@
-EXTRA_smartctl_SOURCES = os_linux.c \
-                       os_linux.h   \
-                      os_freebsd.c \
-                      os_freebsd.h \
-                      os_netbsd.c  \
-                      os_netbsd.h  \
-                      os_openbsd.c \
-                      os_openbsd.h \
-                      os_solaris.c \
-                      os_solaris.h \
-                      os_win32.c   \
-                      os_generic.c \
-                      os_generic.h
+
+EXTRA_smartctl_SOURCES = os_linux.cpp \
+                       os_linux.h     \
+                       os_freebsd.cpp \
+                       os_freebsd.h   \
+                       os_netbsd.cpp  \
+                       os_netbsd.h    \
+                       os_openbsd.cpp \
+                       os_openbsd.h   \
+                       os_solaris.cpp \
+                       os_solaris.h   \
+                       os_win32.cpp   \
+                       os_generic.cpp \
+                       os_generic.h
 
 if OS_WIN32_MINGW
 
@@ -207,6 +217,7 @@ EXTRA_DIST = smartmontools.spec                    \
              os_darwin/SMART.in                    \
              os_darwin/StartupParameters.plist     \
              os_darwin/English_Localizable.strings \
+             os_win32/installer.nsi                \
              $(docs_DATA)
 
 CLEANFILES = smartd.conf.5      \
@@ -322,8 +333,9 @@ MAN2HTML = man2html
 #MAN2HTML = groff -man -Thtml
 MAN2TXT = groff -man -Tascii -P'-bcou'
 
-# Fix links in man2html output
-FIXHTML = sed 's,<A HREF="http://[-a-z/]*/man2html?\([1-8]\)+\(smart[cd][.a-z]*\)">,<A HREF="\2.\1.html">,g' \
+# Remove HTTP header and fix links in man2html output
+FIXHTML = sed '1s,^Content-type.*,,' \
+        | sed 's,<A HREF="http://[-a-z/]*/man2html?\([1-8]\)+\(smart[cd][.a-z]*\)">,<A HREF="\2.\1.html">,g' \
         | sed 's,<A HREF="http://[-a-z/]*/man2html">,<A HREF=".">,g' \
         | sed 's,<A HREF="http://[-a-z/]*/man2html?[^"]*">\([^<]*\)</A>,\1,g' \
         | sed 's,<A HREF="mailto:[^s][^m][^a][^"]*">\([^<]*\)</A>,\1,g'
@@ -334,11 +346,21 @@ htmlman: smartctl.8.html smartd.8.html smartd.conf.5.html
 
 txtman:  smartctl.8.txt smartd.8.txt smartd.conf.5.txt
 
+if OS_WIN32_MINGW
+
+%.5.html: %.5
+       $(DOS2UNIX) < $< | $(MAN2HTML) | $(FIXHTML) > $@
+
+%.8.html: %.8
+       $(DOS2UNIX) < $< | $(MAN2HTML) | $(FIXHTML) > $@
+else
+
 %.5.html: %.5
        $(MAN2HTML) $< | $(FIXHTML) > $@
 
 %.8.html: %.8
        $(MAN2HTML) $< | $(FIXHTML) > $@
+endif
 
 %.5.txt: %.5
        $(MAN2TXT) $< > $@
@@ -353,6 +375,7 @@ if OS_WIN32_MINGW
 
 distdir_win32 = $(PACKAGE)-$(VERSION).win32
 distzip_win32 = $(PACKAGE)-$(VERSION).win32.zip
+distinst_win32= $(PACKAGE)-$(VERSION).win32-setup.exe
 
 exedir_win32 = $(distdir_win32)/bin
 docdir_win32 = $(distdir_win32)/doc
@@ -379,17 +402,38 @@ CLEANFILES += $(FILES_WIN32) $(exedir_win32)/syslogevt.exe distdir.mkdir sysloge
 
 # Textfile converter from cygutils
 UNIX2DOS = unix2dos -D
+DOS2UNIX = dos2unix -U
 
 # Build Windows distribution
 
 dist-win32: $(distzip_win32)
 
+install-win32: $(distinst_win32)
+       ./$(distinst_win32)
+
+installer-win32: $(distinst_win32)
+
 distdir-win32: distdir.mkdir $(FILES_WIN32) syslogevt.check
 
 $(distzip_win32): distdir.mkdir $(FILES_WIN32) syslogevt.check
        @rm -fv $(distzip_win32)
        cd $(distdir_win32) && zip -9Dr ../$(distzip_win32) .
 
+# Build NSIS installer, try to locate makensis in default location first
+$(distinst_win32): $(srcdir)/os_win32/installer.nsi distdir.mkdir $(FILES_WIN32) syslogevt.check
+       @makensis="$(MAKENSIS)"; if [ -z "$$makensis" ]; then \
+         if [ ! -z "$$PROGRAMFILES" ] && "$$PROGRAMFILES/NSIS/makensis" /VERSION >/dev/null 2>&1; then \
+           makensis="$$PROGRAMFILES/NSIS/makensis"; \
+         elif makensis /VERSION >/dev/null 2>&1; then \
+           makensis=makensis; \
+         else \
+           echo 'makensis: command not found. Please download and install NSIS' 1>&2; \
+           echo 'from http://nsis.sourceforge.net/Download' 1>&2; exit 1; \
+         fi; \
+       fi; \
+       echo "$$makensis /V2 /NOCD /DINPDIR=$(distdir_win32) /DOUTFILE=$(distinst_win32) $(srcdir)/os_win32/installer.nsi"; \
+       "$$makensis" /V2 /NOCD /DINPDIR="$(distdir_win32)" /DOUTFILE="$(distinst_win32)" "$(srcdir)/os_win32/installer.nsi"
+
 cleandist-win32:
        rm -rf $(distdir_win32) distdir.mkdir syslogevt.check
 
@@ -428,7 +472,7 @@ config-vc6: $(srcdir)/os_win32/config_vc6.h
 
 $(srcdir)/os_win32/config_vc6.h: config.h
        sed '1i/* config_vc6.h.  Generated by Makefile.  */' $< | \
-       sed 's,^#define HAVE_\(ATTR_PACKED\|INTTYPES_H\|STDINT_H\|STRTOULL\|U*INT64_T\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */,' | \
+       sed 's,^#define HAVE_\(ATTR_PACKED\|INTTYPES_H\|STDINT_H\|STRINGS_H\|STRTOULL\|U*INT64_T\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */,' | \
        sed 's,i.86-pc-mingw32,i686-pc-win32vc6,' > $@
 
 endif
diff --git a/Makefile.in b/Makefile.in
deleted file mode 100644 (file)
index 5d65d9f..0000000
+++ /dev/null
@@ -1,1311 +0,0 @@
-# Makefile.in generated by automake 1.9.3 from Makefile.am.
-# @configure_input@
-
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004  Free Software Foundation, Inc.
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-@SET_MAKE@
-
-#
-# $Id: Makefile.am,v 1.74 2005/11/29 18:22:51 chrfranke Exp $
-#
-
-
-SOURCES = $(smartctl_SOURCES) $(EXTRA_smartctl_SOURCES) $(smartd_SOURCES) $(EXTRA_smartd_SOURCES)
-
-srcdir = @srcdir@
-top_srcdir = @top_srcdir@
-VPATH = @srcdir@
-pkgdatadir = $(datadir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-top_builddir = .
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-INSTALL = @INSTALL@
-install_sh_DATA = $(install_sh) -c -m 644
-install_sh_PROGRAM = $(install_sh) -c
-install_sh_SCRIPT = $(install_sh) -c
-INSTALL_HEADER = $(INSTALL_DATA)
-transform = $(program_transform_name)
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_triplet = @build@
-host_triplet = @host@
-sbin_PROGRAMS = smartd$(EXEEXT) smartctl$(EXEEXT)
-@OS_WIN32_MINGW_TRUE@am__append_1 = \
-@OS_WIN32_MINGW_TRUE@                posix/regex.h             \
-@OS_WIN32_MINGW_TRUE@                posix/regex.c             \
-@OS_WIN32_MINGW_TRUE@                os_win32/daemon_win32.h   \
-@OS_WIN32_MINGW_TRUE@                os_win32/daemon_win32.c   \
-@OS_WIN32_MINGW_TRUE@                os_win32/hostname_win32.h \
-@OS_WIN32_MINGW_TRUE@                os_win32/hostname_win32.c \
-@OS_WIN32_MINGW_TRUE@                os_win32/syslog.h         \
-@OS_WIN32_MINGW_TRUE@                os_win32/syslog_win32.c
-
-
-# Included by regex.c:
-@OS_WIN32_MINGW_TRUE@am__append_2 = \
-@OS_WIN32_MINGW_TRUE@                posix/regcomp.c           \
-@OS_WIN32_MINGW_TRUE@                posix/regexec.c           \
-@OS_WIN32_MINGW_TRUE@                posix/regex_internal.c    \
-@OS_WIN32_MINGW_TRUE@                posix/regex_internal.h
-
-@OS_WIN32_MINGW_TRUE@am__append_3 = \
-@OS_WIN32_MINGW_TRUE@                posix/regex.h          \
-@OS_WIN32_MINGW_TRUE@                posix/regex.c          \
-@OS_WIN32_MINGW_TRUE@                os_win32/syslog.h
-
-
-# Included by regex.c:
-@OS_WIN32_MINGW_TRUE@am__append_4 = \
-@OS_WIN32_MINGW_TRUE@                posix/regcomp.c        \
-@OS_WIN32_MINGW_TRUE@                posix/regexec.c        \
-@OS_WIN32_MINGW_TRUE@                posix/regex_internal.c \
-@OS_WIN32_MINGW_TRUE@                posix/regex_internal.h
-
-@SMARTD_SUFFIX_TRUE@am__append_5 = smartd.conf$(smartd_suffix)
-@OS_WIN32_MINGW_TRUE@am__append_6 = $(FILES_WIN32) $(exedir_win32)/syslogevt.exe distdir.mkdir syslogevt.check
-DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
-       $(srcdir)/Makefile.in $(srcdir)/config.h.in \
-       $(top_srcdir)/configure AUTHORS COPYING INSTALL NEWS TODO \
-       config.guess config.sub depcomp install-sh missing
-subdir = .
-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/configure.in
-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
-       $(ACLOCAL_M4)
-am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
- configure.lineno configure.status.lineno
-mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = config.h
-CONFIG_CLEAN_FILES =
-am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" \
-       "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docsdir)" \
-       "$(DESTDIR)$(initddir)" "$(DESTDIR)$(sysconfdir)"
-sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
-PROGRAMS = $(sbin_PROGRAMS)
-am__smartctl_SOURCES_DIST = smartctl.c smartctl.h atacmdnames.c \
-       atacmdnames.h atacmds.c atacmds.h ataprint.c ataprint.h \
-       extern.h int64.h knowndrives.c knowndrives.h scsicmds.c \
-       scsicmds.h scsiprint.c scsiprint.h utility.c utility.h \
-       posix/regex.h posix/regex.c os_win32/syslog.h
-@OS_WIN32_MINGW_TRUE@am__objects_1 = regex.$(OBJEXT)
-am_smartctl_OBJECTS = smartctl.$(OBJEXT) atacmdnames.$(OBJEXT) \
-       atacmds.$(OBJEXT) ataprint.$(OBJEXT) knowndrives.$(OBJEXT) \
-       scsicmds.$(OBJEXT) scsiprint.$(OBJEXT) utility.$(OBJEXT) \
-       $(am__objects_1)
-am__EXTRA_smartctl_SOURCES_DIST = os_linux.c os_linux.h os_freebsd.c \
-       os_freebsd.h os_netbsd.c os_netbsd.h os_openbsd.c os_openbsd.h \
-       os_solaris.c os_solaris.h os_win32.c os_generic.c os_generic.h \
-       posix/regcomp.c posix/regexec.c posix/regex_internal.c \
-       posix/regex_internal.h
-smartctl_OBJECTS = $(am_smartctl_OBJECTS)
-am__smartd_SOURCES_DIST = smartd.c smartd.h atacmdnames.c \
-       atacmdnames.h atacmds.c atacmds.h ataprint.c ataprint.h \
-       extern.h int64.h knowndrives.c knowndrives.h scsicmds.c \
-       scsicmds.h scsiprint.c scsiprint.h utility.c utility.h \
-       posix/regex.h posix/regex.c os_win32/daemon_win32.h \
-       os_win32/daemon_win32.c os_win32/hostname_win32.h \
-       os_win32/hostname_win32.c os_win32/syslog.h \
-       os_win32/syslog_win32.c
-@OS_WIN32_MINGW_TRUE@am__objects_3 = regex.$(OBJEXT) \
-@OS_WIN32_MINGW_TRUE@  daemon_win32.$(OBJEXT) \
-@OS_WIN32_MINGW_TRUE@  hostname_win32.$(OBJEXT) \
-@OS_WIN32_MINGW_TRUE@  syslog_win32.$(OBJEXT)
-am_smartd_OBJECTS = smartd.$(OBJEXT) atacmdnames.$(OBJEXT) \
-       atacmds.$(OBJEXT) ataprint.$(OBJEXT) knowndrives.$(OBJEXT) \
-       scsicmds.$(OBJEXT) scsiprint.$(OBJEXT) utility.$(OBJEXT) \
-       $(am__objects_3)
-am__EXTRA_smartd_SOURCES_DIST = os_darwin.c os_darwin.h os_linux.c \
-       os_linux.h os_freebsd.c os_freebsd.h os_netbsd.c os_netbsd.h \
-       os_openbsd.c os_openbsd.h os_solaris.c os_solaris.h \
-       os_solaris_ata.s os_win32.c os_generic.c os_generic.h \
-       posix/regcomp.c posix/regexec.c posix/regex_internal.c \
-       posix/regex_internal.h
-smartd_OBJECTS = $(am_smartd_OBJECTS)
-DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
-depcomp = $(SHELL) $(top_srcdir)/depcomp
-am__depfiles_maybe = depfiles
-COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
-       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-CCLD = $(CC)
-LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
-CCASCOMPILE = $(CCAS) $(AM_CCASFLAGS) $(CCASFLAGS)
-SOURCES = $(smartctl_SOURCES) $(EXTRA_smartctl_SOURCES) \
-       $(smartd_SOURCES) $(EXTRA_smartd_SOURCES)
-DIST_SOURCES = $(am__smartctl_SOURCES_DIST) \
-       $(am__EXTRA_smartctl_SOURCES_DIST) $(am__smartd_SOURCES_DIST) \
-       $(am__EXTRA_smartd_SOURCES_DIST)
-RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
-       html-recursive info-recursive install-data-recursive \
-       install-exec-recursive install-info-recursive \
-       install-recursive installcheck-recursive installdirs-recursive \
-       pdf-recursive ps-recursive uninstall-info-recursive \
-       uninstall-recursive
-man5dir = $(mandir)/man5
-man8dir = $(mandir)/man8
-NROFF = nroff
-MANS = $(man_MANS)
-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
-am__vpath_adj = case $$p in \
-    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
-    *) f=$$p;; \
-  esac;
-am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
-docsDATA_INSTALL = $(INSTALL_DATA)
-initdDATA_INSTALL = $(INSTALL_DATA)
-sysconfDATA_INSTALL = $(INSTALL_DATA)
-DATA = $(docs_DATA) $(initd_DATA) $(sysconf_DATA)
-ETAGS = etags
-CTAGS = ctags
-DIST_SUBDIRS = $(SUBDIRS)
-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
-distdir = $(PACKAGE)-$(VERSION)
-top_distdir = $(distdir)
-am__remove_distdir = \
-  { test ! -d $(distdir) \
-    || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
-         && rm -fr $(distdir); }; }
-DIST_ARCHIVES = $(distdir).tar.gz
-GZIP_ENV = --best
-distuninstallcheck_listfiles = find . -type f -print
-distcleancheck_listfiles = find . -type f -print
-ACLOCAL = @ACLOCAL@
-AMDEP_FALSE = @AMDEP_FALSE@
-AMDEP_TRUE = @AMDEP_TRUE@
-AMTAR = @AMTAR@
-ASFLAGS = @ASFLAGS@
-AUTOCONF = @AUTOCONF@
-AUTOHEADER = @AUTOHEADER@
-AUTOMAKE = @AUTOMAKE@
-AWK = @AWK@
-CC = @CC@
-CCAS = @CCAS@
-CCASFLAGS = @CCASFLAGS@
-CCDEPMODE = @CCDEPMODE@
-CFLAGS = @CFLAGS@
-CPP = @CPP@
-CPPFLAGS = @CPPFLAGS@
-CYGPATH_W = @CYGPATH_W@
-DEFS = @DEFS@
-DEPDIR = @DEPDIR@
-ECHO_C = @ECHO_C@
-ECHO_N = @ECHO_N@
-ECHO_T = @ECHO_T@
-EGREP = @EGREP@
-EXEEXT = @EXEEXT@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
-LDFLAGS = @LDFLAGS@
-LIBOBJS = @LIBOBJS@
-LIBS = @LIBS@
-LTLIBOBJS = @LTLIBOBJS@
-MAINT = @MAINT@
-MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
-MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
-MAKEINFO = @MAKEINFO@
-OBJEXT = @OBJEXT@
-OS_DARWIN_FALSE = @OS_DARWIN_FALSE@
-OS_DARWIN_TRUE = @OS_DARWIN_TRUE@
-OS_SOLARIS_FALSE = @OS_SOLARIS_FALSE@
-OS_SOLARIS_TRUE = @OS_SOLARIS_TRUE@
-OS_WIN32_MINGW_FALSE = @OS_WIN32_MINGW_FALSE@
-OS_WIN32_MINGW_TRUE = @OS_WIN32_MINGW_TRUE@
-PACKAGE = @PACKAGE@
-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
-PACKAGE_NAME = @PACKAGE_NAME@
-PACKAGE_STRING = @PACKAGE_STRING@
-PACKAGE_TARNAME = @PACKAGE_TARNAME@
-PACKAGE_VERSION = @PACKAGE_VERSION@
-PATH_SEPARATOR = @PATH_SEPARATOR@
-SET_MAKE = @SET_MAKE@
-SHELL = @SHELL@
-SMARTD_SUFFIX_FALSE = @SMARTD_SUFFIX_FALSE@
-SMARTD_SUFFIX_TRUE = @SMARTD_SUFFIX_TRUE@
-STRIP = @STRIP@
-VERSION = @VERSION@
-ac_ct_CC = @ac_ct_CC@
-ac_ct_STRIP = @ac_ct_STRIP@
-am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
-am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
-am__include = @am__include@
-am__leading_dot = @am__leading_dot@
-am__quote = @am__quote@
-am__tar = @am__tar@
-am__untar = @am__untar@
-bindir = @bindir@
-build = @build@
-build_alias = @build_alias@
-build_cpu = @build_cpu@
-build_os = @build_os@
-build_vendor = @build_vendor@
-datadir = @datadir@
-docdir = @docdir@
-exampledir = @exampledir@
-exec_prefix = @exec_prefix@
-gcc_have_attr_packed = @gcc_have_attr_packed@
-host = @host@
-host_alias = @host_alias@
-host_cpu = @host_cpu@
-host_os = @host_os@
-host_vendor = @host_vendor@
-includedir = @includedir@
-infodir = @infodir@
-initddir = @initddir@
-install_sh = @install_sh@
-libc_have_working_snprintf = @libc_have_working_snprintf@
-libdir = @libdir@
-libexecdir = @libexecdir@
-localstatedir = @localstatedir@
-mandir = @mandir@
-mkdir_p = @mkdir_p@
-oldincludedir = @oldincludedir@
-os_deps = @os_deps@
-os_libs = @os_libs@
-prefix = @prefix@
-program_transform_name = @program_transform_name@
-releaseversion = @releaseversion@
-sbindir = @sbindir@
-sharedstatedir = @sharedstatedir@
-smartd_suffix = @smartd_suffix@
-smartmontools_release_date = @smartmontools_release_date@
-smartmontools_release_time = @smartmontools_release_time@
-sysconfdir = @sysconfdir@
-target_alias = @target_alias@
-AM_CPPFLAGS = -DSMARTMONTOOLS_SYSCONFDIR=\"$(sysconfdir)\"
-smartd_SOURCES = smartd.c smartd.h atacmdnames.c atacmdnames.h \
-       atacmds.c atacmds.h ataprint.c ataprint.h extern.h int64.h \
-       knowndrives.c knowndrives.h scsicmds.c scsicmds.h scsiprint.c \
-       scsiprint.h utility.c utility.h $(am__append_1)
-smartd_LDADD = @os_deps@ @os_libs@
-smartd_DEPENDENCIES = @os_deps@
-EXTRA_smartd_SOURCES = os_darwin.c os_darwin.h os_linux.c os_linux.h \
-       os_freebsd.c os_freebsd.h os_netbsd.c os_netbsd.h os_openbsd.c \
-       os_openbsd.h os_solaris.c os_solaris.h os_solaris_ata.s \
-       os_win32.c os_generic.c os_generic.h $(am__append_2)
-smartctl_SOURCES = smartctl.c smartctl.h atacmdnames.c atacmdnames.h \
-       atacmds.c atacmds.h ataprint.c ataprint.h extern.h int64.h \
-       knowndrives.c knowndrives.h scsicmds.c scsicmds.h scsiprint.c \
-       scsiprint.h utility.c utility.h $(am__append_3)
-smartctl_LDADD = @os_deps@ @os_libs@
-smartctl_DEPENDENCIES = @os_deps@
-EXTRA_smartctl_SOURCES = os_linux.c os_linux.h os_freebsd.c \
-       os_freebsd.h os_netbsd.c os_netbsd.h os_openbsd.c os_openbsd.h \
-       os_solaris.c os_solaris.h os_win32.c os_generic.c os_generic.h \
-       $(am__append_4)
-
-# This block is required because Solaris uses manual page section 1m
-# for administrative command (linux/freebsd use section 8) and Solaris
-# uses manual page section 4 for file formats (linux/freebsd use
-# section 5).  Automake can deal cleanly with man page sections 1-8
-# and n, but NOT with sections of the form 1m.
-@OS_SOLARIS_TRUE@extra_MANS = smartd.conf.4 \
-@OS_SOLARIS_TRUE@                  smartctl.1m   \
-@OS_SOLARIS_TRUE@                  smartd.1m
-
-# For systems that adopts traditional manner
-@OS_SOLARIS_FALSE@man_MANS = smartd.conf.5 \
-@OS_SOLARIS_FALSE@                  smartctl.8    \
-@OS_SOLARIS_FALSE@                  smartd.8
-
-docsdir = $(docdir)
-docs_DATA = AUTHORS     \
-            CHANGELOG   \
-            COPYING     \
-            INSTALL     \
-            NEWS        \
-            README      \
-            TODO        \
-            WARNINGS    \
-            smartd.conf
-
-sysconf_DATA = smartd.conf$(smartd_suffix)
-EXTRA_DIST = smartmontools.spec                    \
-             smartd.initd.in                       \
-             smartd.8.in                           \
-             smartctl.8.in                         \
-             smartd.conf.5.in                      \
-             smartd.conf                           \
-             autogen.sh                            \
-             os_darwin/SMART.in                    \
-             os_darwin/StartupParameters.plist     \
-             os_darwin/English_Localizable.strings \
-             $(docs_DATA)
-
-CLEANFILES = smartd.conf.5 smartd.conf.4 smartd.8 smartd.1m \
-       smartd.8.html smartd.8.txt smartctl.8 smartctl.1m \
-       smartctl.8.html smartctl.8.txt smartd.conf.5.html \
-       smartd.conf.5.txt smartd.initd SMART $(am__append_5) \
-       $(am__append_6)
-@OS_DARWIN_FALSE@initd_DATA = smartd.initd
-@OS_DARWIN_TRUE@initd_DATA = SMART                            \
-@OS_DARWIN_TRUE@       os_darwin/StartupParameters.plist     \
-@OS_DARWIN_TRUE@       os_darwin/English_Localizable.strings
-
-@OS_DARWIN_FALSE@initd_install_name = smartd$(smartd_suffix)
-@OS_DARWIN_TRUE@initd_install_name = SMART
-@OS_DARWIN_FALSE@initd_DATA_install = install-initdDATA-generic
-@OS_DARWIN_TRUE@initd_DATA_install = install-initdDATA-darwin
-
-# Commands to convert man pages into .html and .txt
-# TODO: configure
-MAN2HTML = man2html
-#MAN2HTML = groff -man -Thtml
-MAN2TXT = groff -man -Tascii -P'-bcou'
-
-# Fix links in man2html output
-FIXHTML = sed 's,<A HREF="http://[-a-z/]*/man2html?\([1-8]\)+\(smart[cd][.a-z]*\)">,<A HREF="\2.\1.html">,g' \
-        | sed 's,<A HREF="http://[-a-z/]*/man2html">,<A HREF=".">,g' \
-        | sed 's,<A HREF="http://[-a-z/]*/man2html?[^"]*">\([^<]*\)</A>,\1,g' \
-        | sed 's,<A HREF="mailto:[^s][^m][^a][^"]*">\([^<]*\)</A>,\1,g'
-
-
-# Definitions for Windows distribution
-@OS_WIN32_MINGW_TRUE@distdir_win32 = $(PACKAGE)-$(VERSION).win32
-@OS_WIN32_MINGW_TRUE@distzip_win32 = $(PACKAGE)-$(VERSION).win32.zip
-@OS_WIN32_MINGW_TRUE@exedir_win32 = $(distdir_win32)/bin
-@OS_WIN32_MINGW_TRUE@docdir_win32 = $(distdir_win32)/doc
-@OS_WIN32_MINGW_TRUE@FILES_WIN32 = $(exedir_win32)/smartctl.exe \
-@OS_WIN32_MINGW_TRUE@              $(exedir_win32)/smartd.exe \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/AUTHORS.txt \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/CHANGELOG.txt \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/COPYING.txt \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/INSTALL.txt \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/NEWS.txt \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/README.txt \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/TODO.txt \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/WARNINGS.txt \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartd.conf \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartctl.8.html \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartctl.8.txt \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartd.8.html \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartd.8.txt \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartd.conf.5.html \
-@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartd.conf.5.txt
-
-
-# Textfile converter from cygutils
-@OS_WIN32_MINGW_TRUE@UNIX2DOS = unix2dos -D
-SUBDIRS = . examplescripts
-all: config.h
-       $(MAKE) $(AM_MAKEFLAGS) all-recursive
-
-.SUFFIXES:
-.SUFFIXES: .c .o .obj .s
-am--refresh:
-       @:
-$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
-       @for dep in $?; do \
-         case '$(am__configure_deps)' in \
-           *$$dep*) \
-             echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \
-             cd $(srcdir) && $(AUTOMAKE) --foreign  \
-               && exit 0; \
-             exit 1;; \
-         esac; \
-       done; \
-       echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign  Makefile'; \
-       cd $(top_srcdir) && \
-         $(AUTOMAKE) --foreign  Makefile
-.PRECIOUS: Makefile
-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
-       @case '$?' in \
-         *config.status*) \
-           echo ' $(SHELL) ./config.status'; \
-           $(SHELL) ./config.status;; \
-         *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
-       esac;
-
-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
-       $(SHELL) ./config.status --recheck
-
-$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
-       cd $(srcdir) && $(AUTOCONF)
-$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
-       cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
-
-config.h: stamp-h1
-       @if test ! -f $@; then \
-         rm -f stamp-h1; \
-         $(MAKE) stamp-h1; \
-       else :; fi
-
-stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
-       @rm -f stamp-h1
-       cd $(top_builddir) && $(SHELL) ./config.status config.h
-$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) 
-       cd $(top_srcdir) && $(AUTOHEADER)
-       rm -f stamp-h1
-       touch $@
-
-distclean-hdr:
-       -rm -f config.h stamp-h1
-install-sbinPROGRAMS: $(sbin_PROGRAMS)
-       @$(NORMAL_INSTALL)
-       test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)"
-       @list='$(sbin_PROGRAMS)'; for p in $$list; do \
-         p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
-         if test -f $$p \
-         ; then \
-           f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
-          echo " $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
-          $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \
-         else :; fi; \
-       done
-
-uninstall-sbinPROGRAMS:
-       @$(NORMAL_UNINSTALL)
-       @list='$(sbin_PROGRAMS)'; for p in $$list; do \
-         f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
-         echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
-         rm -f "$(DESTDIR)$(sbindir)/$$f"; \
-       done
-
-clean-sbinPROGRAMS:
-       -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
-smartctl$(EXEEXT): $(smartctl_OBJECTS) $(smartctl_DEPENDENCIES) 
-       @rm -f smartctl$(EXEEXT)
-       $(LINK) $(smartctl_LDFLAGS) $(smartctl_OBJECTS) $(smartctl_LDADD) $(LIBS)
-smartd$(EXEEXT): $(smartd_OBJECTS) $(smartd_DEPENDENCIES) 
-       @rm -f smartd$(EXEEXT)
-       $(LINK) $(smartd_LDFLAGS) $(smartd_OBJECTS) $(smartd_LDADD) $(LIBS)
-
-mostlyclean-compile:
-       -rm -f *.$(OBJEXT)
-
-distclean-compile:
-       -rm -f *.tab.c
-
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atacmdnames.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atacmds.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ataprint.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon_win32.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostname_win32.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/knowndrives.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_darwin.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_freebsd.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_generic.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_linux.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_netbsd.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_openbsd.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_solaris.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_win32.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regcomp.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex_internal.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regexec.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scsicmds.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scsiprint.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smartctl.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smartd.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syslog_win32.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utility.Po@am__quote@
-
-.c.o:
-@am__fastdepCC_TRUE@   if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(COMPILE) -c $<
-
-.c.obj:
-@am__fastdepCC_TRUE@   if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(COMPILE) -c `$(CYGPATH_W) '$<'`
-
-regex.o: posix/regex.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex.o -MD -MP -MF "$(DEPDIR)/regex.Tpo" -c -o regex.o `test -f 'posix/regex.c' || echo '$(srcdir)/'`posix/regex.c; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regex.Tpo" "$(DEPDIR)/regex.Po"; else rm -f "$(DEPDIR)/regex.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regex.c' object='regex.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex.o `test -f 'posix/regex.c' || echo '$(srcdir)/'`posix/regex.c
-
-regex.obj: posix/regex.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex.obj -MD -MP -MF "$(DEPDIR)/regex.Tpo" -c -o regex.obj `if test -f 'posix/regex.c'; then $(CYGPATH_W) 'posix/regex.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex.c'; fi`; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regex.Tpo" "$(DEPDIR)/regex.Po"; else rm -f "$(DEPDIR)/regex.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regex.c' object='regex.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex.obj `if test -f 'posix/regex.c'; then $(CYGPATH_W) 'posix/regex.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex.c'; fi`
-
-regcomp.o: posix/regcomp.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regcomp.o -MD -MP -MF "$(DEPDIR)/regcomp.Tpo" -c -o regcomp.o `test -f 'posix/regcomp.c' || echo '$(srcdir)/'`posix/regcomp.c; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regcomp.Tpo" "$(DEPDIR)/regcomp.Po"; else rm -f "$(DEPDIR)/regcomp.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regcomp.c' object='regcomp.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regcomp.o `test -f 'posix/regcomp.c' || echo '$(srcdir)/'`posix/regcomp.c
-
-regcomp.obj: posix/regcomp.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regcomp.obj -MD -MP -MF "$(DEPDIR)/regcomp.Tpo" -c -o regcomp.obj `if test -f 'posix/regcomp.c'; then $(CYGPATH_W) 'posix/regcomp.c'; else $(CYGPATH_W) '$(srcdir)/posix/regcomp.c'; fi`; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regcomp.Tpo" "$(DEPDIR)/regcomp.Po"; else rm -f "$(DEPDIR)/regcomp.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regcomp.c' object='regcomp.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regcomp.obj `if test -f 'posix/regcomp.c'; then $(CYGPATH_W) 'posix/regcomp.c'; else $(CYGPATH_W) '$(srcdir)/posix/regcomp.c'; fi`
-
-regexec.o: posix/regexec.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regexec.o -MD -MP -MF "$(DEPDIR)/regexec.Tpo" -c -o regexec.o `test -f 'posix/regexec.c' || echo '$(srcdir)/'`posix/regexec.c; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regexec.Tpo" "$(DEPDIR)/regexec.Po"; else rm -f "$(DEPDIR)/regexec.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regexec.c' object='regexec.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regexec.o `test -f 'posix/regexec.c' || echo '$(srcdir)/'`posix/regexec.c
-
-regexec.obj: posix/regexec.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regexec.obj -MD -MP -MF "$(DEPDIR)/regexec.Tpo" -c -o regexec.obj `if test -f 'posix/regexec.c'; then $(CYGPATH_W) 'posix/regexec.c'; else $(CYGPATH_W) '$(srcdir)/posix/regexec.c'; fi`; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regexec.Tpo" "$(DEPDIR)/regexec.Po"; else rm -f "$(DEPDIR)/regexec.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regexec.c' object='regexec.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regexec.obj `if test -f 'posix/regexec.c'; then $(CYGPATH_W) 'posix/regexec.c'; else $(CYGPATH_W) '$(srcdir)/posix/regexec.c'; fi`
-
-regex_internal.o: posix/regex_internal.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex_internal.o -MD -MP -MF "$(DEPDIR)/regex_internal.Tpo" -c -o regex_internal.o `test -f 'posix/regex_internal.c' || echo '$(srcdir)/'`posix/regex_internal.c; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regex_internal.Tpo" "$(DEPDIR)/regex_internal.Po"; else rm -f "$(DEPDIR)/regex_internal.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regex_internal.c' object='regex_internal.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex_internal.o `test -f 'posix/regex_internal.c' || echo '$(srcdir)/'`posix/regex_internal.c
-
-regex_internal.obj: posix/regex_internal.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex_internal.obj -MD -MP -MF "$(DEPDIR)/regex_internal.Tpo" -c -o regex_internal.obj `if test -f 'posix/regex_internal.c'; then $(CYGPATH_W) 'posix/regex_internal.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex_internal.c'; fi`; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regex_internal.Tpo" "$(DEPDIR)/regex_internal.Po"; else rm -f "$(DEPDIR)/regex_internal.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regex_internal.c' object='regex_internal.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex_internal.obj `if test -f 'posix/regex_internal.c'; then $(CYGPATH_W) 'posix/regex_internal.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex_internal.c'; fi`
-
-daemon_win32.o: os_win32/daemon_win32.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT daemon_win32.o -MD -MP -MF "$(DEPDIR)/daemon_win32.Tpo" -c -o daemon_win32.o `test -f 'os_win32/daemon_win32.c' || echo '$(srcdir)/'`os_win32/daemon_win32.c; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/daemon_win32.Tpo" "$(DEPDIR)/daemon_win32.Po"; else rm -f "$(DEPDIR)/daemon_win32.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/daemon_win32.c' object='daemon_win32.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o daemon_win32.o `test -f 'os_win32/daemon_win32.c' || echo '$(srcdir)/'`os_win32/daemon_win32.c
-
-daemon_win32.obj: os_win32/daemon_win32.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT daemon_win32.obj -MD -MP -MF "$(DEPDIR)/daemon_win32.Tpo" -c -o daemon_win32.obj `if test -f 'os_win32/daemon_win32.c'; then $(CYGPATH_W) 'os_win32/daemon_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/daemon_win32.c'; fi`; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/daemon_win32.Tpo" "$(DEPDIR)/daemon_win32.Po"; else rm -f "$(DEPDIR)/daemon_win32.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/daemon_win32.c' object='daemon_win32.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o daemon_win32.obj `if test -f 'os_win32/daemon_win32.c'; then $(CYGPATH_W) 'os_win32/daemon_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/daemon_win32.c'; fi`
-
-hostname_win32.o: os_win32/hostname_win32.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hostname_win32.o -MD -MP -MF "$(DEPDIR)/hostname_win32.Tpo" -c -o hostname_win32.o `test -f 'os_win32/hostname_win32.c' || echo '$(srcdir)/'`os_win32/hostname_win32.c; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/hostname_win32.Tpo" "$(DEPDIR)/hostname_win32.Po"; else rm -f "$(DEPDIR)/hostname_win32.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/hostname_win32.c' object='hostname_win32.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hostname_win32.o `test -f 'os_win32/hostname_win32.c' || echo '$(srcdir)/'`os_win32/hostname_win32.c
-
-hostname_win32.obj: os_win32/hostname_win32.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hostname_win32.obj -MD -MP -MF "$(DEPDIR)/hostname_win32.Tpo" -c -o hostname_win32.obj `if test -f 'os_win32/hostname_win32.c'; then $(CYGPATH_W) 'os_win32/hostname_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/hostname_win32.c'; fi`; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/hostname_win32.Tpo" "$(DEPDIR)/hostname_win32.Po"; else rm -f "$(DEPDIR)/hostname_win32.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/hostname_win32.c' object='hostname_win32.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hostname_win32.obj `if test -f 'os_win32/hostname_win32.c'; then $(CYGPATH_W) 'os_win32/hostname_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/hostname_win32.c'; fi`
-
-syslog_win32.o: os_win32/syslog_win32.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT syslog_win32.o -MD -MP -MF "$(DEPDIR)/syslog_win32.Tpo" -c -o syslog_win32.o `test -f 'os_win32/syslog_win32.c' || echo '$(srcdir)/'`os_win32/syslog_win32.c; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/syslog_win32.Tpo" "$(DEPDIR)/syslog_win32.Po"; else rm -f "$(DEPDIR)/syslog_win32.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/syslog_win32.c' object='syslog_win32.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o syslog_win32.o `test -f 'os_win32/syslog_win32.c' || echo '$(srcdir)/'`os_win32/syslog_win32.c
-
-syslog_win32.obj: os_win32/syslog_win32.c
-@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT syslog_win32.obj -MD -MP -MF "$(DEPDIR)/syslog_win32.Tpo" -c -o syslog_win32.obj `if test -f 'os_win32/syslog_win32.c'; then $(CYGPATH_W) 'os_win32/syslog_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/syslog_win32.c'; fi`; \
-@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/syslog_win32.Tpo" "$(DEPDIR)/syslog_win32.Po"; else rm -f "$(DEPDIR)/syslog_win32.Tpo"; exit 1; fi
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/syslog_win32.c' object='syslog_win32.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o syslog_win32.obj `if test -f 'os_win32/syslog_win32.c'; then $(CYGPATH_W) 'os_win32/syslog_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/syslog_win32.c'; fi`
-
-.s.o:
-       $(CCASCOMPILE) -c $<
-
-.s.obj:
-       $(CCASCOMPILE) -c `$(CYGPATH_W) '$<'`
-uninstall-info-am:
-install-man5: $(man5_MANS) $(man_MANS)
-       @$(NORMAL_INSTALL)
-       test -z "$(man5dir)" || $(mkdir_p) "$(DESTDIR)$(man5dir)"
-       @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
-       l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
-       for i in $$l2; do \
-         case "$$i" in \
-           *.5*) list="$$list $$i" ;; \
-         esac; \
-       done; \
-       for i in $$list; do \
-         if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
-         else file=$$i; fi; \
-         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
-         case "$$ext" in \
-           5*) ;; \
-           *) ext='5' ;; \
-         esac; \
-         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
-         inst=`echo $$inst | sed -e 's/^.*\///'`; \
-         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
-         echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
-         $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst"; \
-       done
-uninstall-man5:
-       @$(NORMAL_UNINSTALL)
-       @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
-       l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
-       for i in $$l2; do \
-         case "$$i" in \
-           *.5*) list="$$list $$i" ;; \
-         esac; \
-       done; \
-       for i in $$list; do \
-         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
-         case "$$ext" in \
-           5*) ;; \
-           *) ext='5' ;; \
-         esac; \
-         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
-         inst=`echo $$inst | sed -e 's/^.*\///'`; \
-         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
-         echo " rm -f '$(DESTDIR)$(man5dir)/$$inst'"; \
-         rm -f "$(DESTDIR)$(man5dir)/$$inst"; \
-       done
-install-man8: $(man8_MANS) $(man_MANS)
-       @$(NORMAL_INSTALL)
-       test -z "$(man8dir)" || $(mkdir_p) "$(DESTDIR)$(man8dir)"
-       @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
-       l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
-       for i in $$l2; do \
-         case "$$i" in \
-           *.8*) list="$$list $$i" ;; \
-         esac; \
-       done; \
-       for i in $$list; do \
-         if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
-         else file=$$i; fi; \
-         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
-         case "$$ext" in \
-           8*) ;; \
-           *) ext='8' ;; \
-         esac; \
-         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
-         inst=`echo $$inst | sed -e 's/^.*\///'`; \
-         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
-         echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
-         $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \
-       done
-uninstall-man8:
-       @$(NORMAL_UNINSTALL)
-       @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
-       l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
-       for i in $$l2; do \
-         case "$$i" in \
-           *.8*) list="$$list $$i" ;; \
-         esac; \
-       done; \
-       for i in $$list; do \
-         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
-         case "$$ext" in \
-           8*) ;; \
-           *) ext='8' ;; \
-         esac; \
-         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
-         inst=`echo $$inst | sed -e 's/^.*\///'`; \
-         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
-         echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \
-         rm -f "$(DESTDIR)$(man8dir)/$$inst"; \
-       done
-install-docsDATA: $(docs_DATA)
-       @$(NORMAL_INSTALL)
-       test -z "$(docsdir)" || $(mkdir_p) "$(DESTDIR)$(docsdir)"
-       @list='$(docs_DATA)'; for p in $$list; do \
-         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
-         f=$(am__strip_dir) \
-         echo " $(docsDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(docsdir)/$$f'"; \
-         $(docsDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(docsdir)/$$f"; \
-       done
-install-sysconfDATA: $(sysconf_DATA)
-       @$(NORMAL_INSTALL)
-       test -z "$(sysconfdir)" || $(mkdir_p) "$(DESTDIR)$(sysconfdir)"
-       @list='$(sysconf_DATA)'; for p in $$list; do \
-         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
-         f=$(am__strip_dir) \
-         echo " $(sysconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(sysconfdir)/$$f'"; \
-         $(sysconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(sysconfdir)/$$f"; \
-       done
-
-uninstall-sysconfDATA:
-       @$(NORMAL_UNINSTALL)
-       @list='$(sysconf_DATA)'; for p in $$list; do \
-         f=$(am__strip_dir) \
-         echo " rm -f '$(DESTDIR)$(sysconfdir)/$$f'"; \
-         rm -f "$(DESTDIR)$(sysconfdir)/$$f"; \
-       done
-
-# This directory's subdirectories are mostly independent; you can cd
-# into them and run `make' without going through this Makefile.
-# To change the values of `make' variables: instead of editing Makefiles,
-# (1) if the variable is set in `config.status', edit `config.status'
-#     (which will cause the Makefiles to be regenerated when you run `make');
-# (2) otherwise, pass the desired values on the `make' command line.
-$(RECURSIVE_TARGETS):
-       @set fnord $$MAKEFLAGS; amf=$$2; \
-       dot_seen=no; \
-       target=`echo $@ | sed s/-recursive//`; \
-       list='$(SUBDIRS)'; for subdir in $$list; do \
-         echo "Making $$target in $$subdir"; \
-         if test "$$subdir" = "."; then \
-           dot_seen=yes; \
-           local_target="$$target-am"; \
-         else \
-           local_target="$$target"; \
-         fi; \
-         (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
-          || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
-       done; \
-       if test "$$dot_seen" = "no"; then \
-         $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
-       fi; test -z "$$fail"
-
-mostlyclean-recursive clean-recursive distclean-recursive \
-maintainer-clean-recursive:
-       @set fnord $$MAKEFLAGS; amf=$$2; \
-       dot_seen=no; \
-       case "$@" in \
-         distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
-         *) list='$(SUBDIRS)' ;; \
-       esac; \
-       rev=''; for subdir in $$list; do \
-         if test "$$subdir" = "."; then :; else \
-           rev="$$subdir $$rev"; \
-         fi; \
-       done; \
-       rev="$$rev ."; \
-       target=`echo $@ | sed s/-recursive//`; \
-       for subdir in $$rev; do \
-         echo "Making $$target in $$subdir"; \
-         if test "$$subdir" = "."; then \
-           local_target="$$target-am"; \
-         else \
-           local_target="$$target"; \
-         fi; \
-         (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
-          || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
-       done && test -z "$$fail"
-tags-recursive:
-       list='$(SUBDIRS)'; for subdir in $$list; do \
-         test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
-       done
-ctags-recursive:
-       list='$(SUBDIRS)'; for subdir in $$list; do \
-         test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
-       done
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
-       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
-       unique=`for i in $$list; do \
-           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-         done | \
-         $(AWK) '    { files[$$0] = 1; } \
-              END { for (i in files) print i; }'`; \
-       mkid -fID $$unique
-tags: TAGS
-
-TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
-               $(TAGS_FILES) $(LISP)
-       tags=; \
-       here=`pwd`; \
-       if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
-         include_option=--etags-include; \
-         empty_fix=.; \
-       else \
-         include_option=--include; \
-         empty_fix=; \
-       fi; \
-       list='$(SUBDIRS)'; for subdir in $$list; do \
-         if test "$$subdir" = .; then :; else \
-           test ! -f $$subdir/TAGS || \
-             tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
-         fi; \
-       done; \
-       list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
-       unique=`for i in $$list; do \
-           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-         done | \
-         $(AWK) '    { files[$$0] = 1; } \
-              END { for (i in files) print i; }'`; \
-       if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
-         test -n "$$unique" || unique=$$empty_fix; \
-         $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
-           $$tags $$unique; \
-       fi
-ctags: CTAGS
-CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
-               $(TAGS_FILES) $(LISP)
-       tags=; \
-       here=`pwd`; \
-       list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
-       unique=`for i in $$list; do \
-           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
-         done | \
-         $(AWK) '    { files[$$0] = 1; } \
-              END { for (i in files) print i; }'`; \
-       test -z "$(CTAGS_ARGS)$$tags$$unique" \
-         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
-            $$tags $$unique
-
-GTAGS:
-       here=`$(am__cd) $(top_builddir) && pwd` \
-         && cd $(top_srcdir) \
-         && gtags -i $(GTAGS_ARGS) $$here
-
-distclean-tags:
-       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-
-distdir: $(DISTFILES)
-       $(am__remove_distdir)
-       mkdir $(distdir)
-       $(mkdir_p) $(distdir)/os_darwin
-       @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
-       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
-       list='$(DISTFILES)'; for file in $$list; do \
-         case $$file in \
-           $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
-           $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
-         esac; \
-         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
-         dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
-         if test "$$dir" != "$$file" && test "$$dir" != "."; then \
-           dir="/$$dir"; \
-           $(mkdir_p) "$(distdir)$$dir"; \
-         else \
-           dir=''; \
-         fi; \
-         if test -d $$d/$$file; then \
-           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
-             cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
-           fi; \
-           cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
-         else \
-           test -f $(distdir)/$$file \
-           || cp -p $$d/$$file $(distdir)/$$file \
-           || exit 1; \
-         fi; \
-       done
-       list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
-         if test "$$subdir" = .; then :; else \
-           test -d "$(distdir)/$$subdir" \
-           || $(mkdir_p) "$(distdir)/$$subdir" \
-           || exit 1; \
-           distdir=`$(am__cd) $(distdir) && pwd`; \
-           top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
-           (cd $$subdir && \
-             $(MAKE) $(AM_MAKEFLAGS) \
-               top_distdir="$$top_distdir" \
-               distdir="$$distdir/$$subdir" \
-               distdir) \
-             || exit 1; \
-         fi; \
-       done
-       -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
-         ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
-         ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
-         ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
-       || chmod -R a+r $(distdir)
-dist-gzip: distdir
-       tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
-       $(am__remove_distdir)
-
-dist-bzip2: distdir
-       tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
-       $(am__remove_distdir)
-
-dist-tarZ: distdir
-       tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
-       $(am__remove_distdir)
-
-dist-shar: distdir
-       shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
-       $(am__remove_distdir)
-
-dist-zip: distdir
-       -rm -f $(distdir).zip
-       zip -rq $(distdir).zip $(distdir)
-       $(am__remove_distdir)
-
-dist dist-all: distdir
-       tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
-       $(am__remove_distdir)
-
-# This target untars the dist file and tries a VPATH configuration.  Then
-# it guarantees that the distribution is self-contained by making another
-# tarfile.
-distcheck: dist
-       case '$(DIST_ARCHIVES)' in \
-       *.tar.gz*) \
-         GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\
-       *.tar.bz2*) \
-         bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
-       *.tar.Z*) \
-         uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
-       *.shar.gz*) \
-         GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\
-       *.zip*) \
-         unzip $(distdir).zip ;;\
-       esac
-       chmod -R a-w $(distdir); chmod a+w $(distdir)
-       mkdir $(distdir)/_build
-       mkdir $(distdir)/_inst
-       chmod a-w $(distdir)
-       dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
-         && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
-         && cd $(distdir)/_build \
-         && ../configure --srcdir=.. --prefix="$$dc_install_base" \
-           $(DISTCHECK_CONFIGURE_FLAGS) \
-         && $(MAKE) $(AM_MAKEFLAGS) \
-         && $(MAKE) $(AM_MAKEFLAGS) dvi \
-         && $(MAKE) $(AM_MAKEFLAGS) check \
-         && $(MAKE) $(AM_MAKEFLAGS) install \
-         && $(MAKE) $(AM_MAKEFLAGS) installcheck \
-         && $(MAKE) $(AM_MAKEFLAGS) uninstall \
-         && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
-               distuninstallcheck \
-         && chmod -R a-w "$$dc_install_base" \
-         && ({ \
-              (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
-              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
-              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
-              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
-                   distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
-             } || { rm -rf "$$dc_destdir"; exit 1; }) \
-         && rm -rf "$$dc_destdir" \
-         && $(MAKE) $(AM_MAKEFLAGS) dist \
-         && rm -rf $(DIST_ARCHIVES) \
-         && $(MAKE) $(AM_MAKEFLAGS) distcleancheck
-       $(am__remove_distdir)
-       @(echo "$(distdir) archives ready for distribution: "; \
-         list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
-         sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
-distuninstallcheck:
-       @cd $(distuninstallcheck_dir) \
-       && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
-          || { echo "ERROR: files left after uninstall:" ; \
-               if test -n "$(DESTDIR)"; then \
-                 echo "  (check DESTDIR support)"; \
-               fi ; \
-               $(distuninstallcheck_listfiles) ; \
-               exit 1; } >&2
-distcleancheck: distclean
-       @if test '$(srcdir)' = . ; then \
-         echo "ERROR: distcleancheck can only run from a VPATH build" ; \
-         exit 1 ; \
-       fi
-       @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
-         || { echo "ERROR: files left in build directory after distclean:" ; \
-              $(distcleancheck_listfiles) ; \
-              exit 1; } >&2
-check-am: all-am
-check: check-recursive
-all-am: Makefile $(PROGRAMS) $(MANS) $(DATA) config.h
-installdirs: installdirs-recursive
-installdirs-am:
-       for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docsdir)" "$(DESTDIR)$(initddir)" "$(DESTDIR)$(sysconfdir)"; do \
-         test -z "$$dir" || $(mkdir_p) "$$dir"; \
-       done
-install: install-recursive
-install-exec: install-exec-recursive
-install-data: install-data-recursive
-uninstall: uninstall-recursive
-
-install-am: all-am
-       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-
-installcheck: installcheck-recursive
-install-strip:
-       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
-         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
-         `test -z '$(STRIP)' || \
-           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
-mostlyclean-generic:
-
-clean-generic:
-       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
-
-distclean-generic:
-       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-
-maintainer-clean-generic:
-       @echo "This command is intended for maintainers to use"
-       @echo "it deletes files that may require special tools to rebuild."
-clean: clean-recursive
-
-clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
-
-distclean: distclean-recursive
-       -rm -f $(am__CONFIG_DISTCLEAN_FILES)
-       -rm -rf ./$(DEPDIR)
-       -rm -f Makefile
-distclean-am: clean-am distclean-compile distclean-generic \
-       distclean-hdr distclean-tags
-
-dvi: dvi-recursive
-
-dvi-am:
-
-html: html-recursive
-
-info: info-recursive
-
-info-am:
-
-install-data-am: install-docsDATA install-initdDATA install-man
-
-install-exec-am: install-sbinPROGRAMS install-sysconfDATA
-
-install-info: install-info-recursive
-
-@OS_SOLARIS_FALSE@install-man: install-man5 install-man8
-
-installcheck-am:
-
-maintainer-clean: maintainer-clean-recursive
-       -rm -f $(am__CONFIG_DISTCLEAN_FILES)
-       -rm -rf $(top_srcdir)/autom4te.cache
-       -rm -rf ./$(DEPDIR)
-       -rm -f Makefile
-maintainer-clean-am: distclean-am maintainer-clean-generic
-
-mostlyclean: mostlyclean-recursive
-
-mostlyclean-am: mostlyclean-compile mostlyclean-generic
-
-pdf: pdf-recursive
-
-pdf-am:
-
-ps: ps-recursive
-
-ps-am:
-
-uninstall-am: uninstall-docsDATA uninstall-info-am uninstall-initdDATA \
-       uninstall-man uninstall-sbinPROGRAMS uninstall-sysconfDATA
-
-uninstall-info: uninstall-info-recursive
-
-@OS_SOLARIS_FALSE@uninstall-man: uninstall-man5 uninstall-man8
-
-.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \
-       check-am clean clean-generic clean-recursive \
-       clean-sbinPROGRAMS ctags ctags-recursive dist dist-all \
-       dist-bzip2 dist-gzip dist-shar dist-tarZ dist-zip distcheck \
-       distclean distclean-compile distclean-generic distclean-hdr \
-       distclean-recursive distclean-tags distcleancheck distdir \
-       distuninstallcheck dvi dvi-am html html-am info info-am \
-       install install-am install-data install-data-am \
-       install-docsDATA install-exec install-exec-am install-info \
-       install-info-am install-initdDATA install-man install-man5 \
-       install-man8 install-sbinPROGRAMS install-strip \
-       install-sysconfDATA installcheck installcheck-am installdirs \
-       installdirs-am maintainer-clean maintainer-clean-generic \
-       maintainer-clean-recursive mostlyclean mostlyclean-compile \
-       mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \
-       tags tags-recursive uninstall uninstall-am uninstall-docsDATA \
-       uninstall-info-am uninstall-initdDATA uninstall-man \
-       uninstall-man5 uninstall-man8 uninstall-sbinPROGRAMS \
-       uninstall-sysconfDATA
-
-
-@SET_MAKE@
-@OS_SOLARIS_TRUE@install-man: $(extra_MANS)
-@OS_SOLARIS_TRUE@      @$(NORMAL_INSTALL)
-@OS_SOLARIS_TRUE@      $(mkinstalldirs) $(DESTDIR)$(mandir)/man4
-@OS_SOLARIS_TRUE@      $(mkinstalldirs) $(DESTDIR)$(mandir)/man1m
-@OS_SOLARIS_TRUE@      for i in $(extra_MANS); do \
-@OS_SOLARIS_TRUE@        if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
-@OS_SOLARIS_TRUE@        else file=$$i; fi; \
-@OS_SOLARIS_TRUE@        ext=`echo $$i | sed -e 's/^.*\\.//'`; \
-@OS_SOLARIS_TRUE@        inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
-@OS_SOLARIS_TRUE@        inst=`echo $$inst | sed -e 's/^.*\///'`; \
-@OS_SOLARIS_TRUE@        inst=`echo $$inst | sed '$(transform)'`.$$ext; \
-@OS_SOLARIS_TRUE@        echo " $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$$ext/$$inst"; \
-@OS_SOLARIS_TRUE@        $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$$ext/$$inst; \
-@OS_SOLARIS_TRUE@      done
-@OS_SOLARIS_TRUE@uninstall-man:
-@OS_SOLARIS_TRUE@      @$(NORMAL_UNINSTALL)
-@OS_SOLARIS_TRUE@      for i in $(extra_MANS); do \
-@OS_SOLARIS_TRUE@        if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
-@OS_SOLARIS_TRUE@        else file=$$i; fi; \
-@OS_SOLARIS_TRUE@        ext=`echo $$i | sed -e 's/^.*\\.//'`; \
-@OS_SOLARIS_TRUE@        inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
-@OS_SOLARIS_TRUE@        inst=`echo $$inst | sed -e 's/^.*\///'`; \
-@OS_SOLARIS_TRUE@        inst=`echo $$inst | sed '$(transform)'`.$$ext; \
-@OS_SOLARIS_TRUE@        echo " rm -f $(DESTDIR)$(mandir)/man$$ext/$$inst"; \
-@OS_SOLARIS_TRUE@        rm -f $(DESTDIR)$(mandir)/man$$ext/$$inst; \
-@OS_SOLARIS_TRUE@      done
-@OS_SOLARIS_TRUE@%.1m: %.8
-@OS_SOLARIS_TRUE@      awk '/^.TH/ {$$3="1m"} {print}' < $< | \
-@OS_SOLARIS_TRUE@      sed -e 's/smartd\.conf\(.*\)(5)/smartd.conf\1(4)/g' \
-@OS_SOLARIS_TRUE@            -e 's/syslog\.conf\(.*\)(5)/syslog.conf\1(4)/g' \
-@OS_SOLARIS_TRUE@          -e 's/smartctl\(.*\)(8)/smartctl\1(1m)/g' \
-@OS_SOLARIS_TRUE@          -e 's/syslogd\(.*\)(8)/syslogd\1(1m)/g' \
-@OS_SOLARIS_TRUE@            -e 's|/var/log/messages|/var/adm/messages|g' \
-@OS_SOLARIS_TRUE@          -e 's/smartd\(.*\)(8)/smartd\1(1m)/g' > $@
-@OS_SOLARIS_TRUE@%.4: %.5
-@OS_SOLARIS_TRUE@      awk '/^.TH/ {$$3="4"}  {print}' < $< | \
-@OS_SOLARIS_TRUE@      sed -e 's/smartd\.conf\(.*\)(5)/smartd.conf\1(4)/g' \
-@OS_SOLARIS_TRUE@            -e 's/syslog\.conf\(.*\)(5)/syslog.conf\1(4)/g' \
-@OS_SOLARIS_TRUE@          -e 's/smartctl\(.*\)(8)/smartdctl\1(1m)/g' \
-@OS_SOLARIS_TRUE@          -e 's/syslogd\(.*\)(8)/syslogd\1(1m)/g' \
-@OS_SOLARIS_TRUE@            -e 's|/var/log/messages|/var/adm/messages|g' \
-@OS_SOLARIS_TRUE@          -e 's/smartd\(.*\)(8)/smartd\1(1m)/g' > $@
-
-@SMARTD_SUFFIX_TRUE@smartd.conf$(smartd_suffix): smartd.conf
-@SMARTD_SUFFIX_TRUE@   cp ${srcdir}/smartd.conf smartd.conf$(smartd_suffix)
-
-smartd.conf.5.in: smartd.8.in
-       sed '1,/STARTINCLUDE/ D;/ENDINCLUDE/,$$D' < $(srcdir)/smartd.8.in > $(top_builddir)/tmp.directives
-       sed '/STARTINCLUDE/,$$D'  < $(srcdir)/smartd.conf.5.in > $(top_builddir)/tmp.head
-       sed '1,/ENDINCLUDE/D'   < $(srcdir)/smartd.conf.5.in > $(top_builddir)/tmp.tail
-       cat $(top_builddir)/tmp.head > $(srcdir)/smartd.conf.5.in
-       echo '.\" STARTINCLUDE' >> $(srcdir)/smartd.conf.5.in
-       cat $(top_builddir)/tmp.directives >> $(srcdir)/smartd.conf.5.in
-       echo '.\" ENDINCLUDE'   >> $(srcdir)/smartd.conf.5.in
-       cat $(top_builddir)/tmp.tail >> $(srcdir)/smartd.conf.5.in
-       rm -f $(top_builddir)/tmp.head $(top_builddir)/tmp.tail $(top_builddir)/tmp.directives
-
-@OS_DARWIN_TRUE@SMART : os_darwin/SMART.in
-@OS_DARWIN_TRUE@       sed "s|/usr/sbin/|$(sbindir)/|" $< > $@
-
-@OS_DARWIN_TRUE@install-initdDATA-darwin: $(initd_DATA)
-@OS_DARWIN_TRUE@       $(mkinstalldirs) $(DESTDIR)$(initddir)
-@OS_DARWIN_TRUE@       $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART
-@OS_DARWIN_TRUE@       $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART/Resources
-@OS_DARWIN_TRUE@       $(INSTALL_SCRIPT) $(top_builddir)/SMART $(DESTDIR)$(initddir)/SMART
-@OS_DARWIN_TRUE@       $(INSTALL_DATA) $(srcdir)/os_darwin/StartupParameters.plist \
-@OS_DARWIN_TRUE@           $(DESTDIR)$(initddir)/SMART/StartupParameters.plist
-@OS_DARWIN_TRUE@       for i in English ; do \
-@OS_DARWIN_TRUE@         RDIR=$(DESTDIR)$(initddir)/SMART/Resources/$${i}.lproj ; \
-@OS_DARWIN_TRUE@         $(mkinstalldirs) $$RDIR ;\
-@OS_DARWIN_TRUE@         $(INSTALL_DATA) $(srcdir)/os_darwin/$${i}_Localizable.strings \
-@OS_DARWIN_TRUE@           $$RDIR/Localizable.strings ; \
-@OS_DARWIN_TRUE@       done
-@OS_DARWIN_TRUE@       @echo -e "\n\n####################################################################\n#"
-@OS_DARWIN_TRUE@       @echo -e "#                       PLEASE READ THIS BOX!\n#"
-@OS_DARWIN_TRUE@       @echo -e "#   To manually start the smartd daemon, run:\n#   ${initddir}/SMART/SMART start\n#"
-@OS_DARWIN_TRUE@       @echo -e "#   To automatically start smartd on bootup, add the line:\n#   SMARTd=-YES-\n#   to /etc/hostconfig\n#"
-@OS_DARWIN_TRUE@       @echo -e "#   smartd can now use a configuration file ${sysconfdir}/smartd.conf. Do:\n#   man smartd"
-@OS_DARWIN_TRUE@       @echo -e "#   to learn about it. A sample configuration file can be found in:\n#   ${docdir}\n#"
-@OS_DARWIN_TRUE@       @echo -e "####################################################################\n\n"
-
-@OS_DARWIN_FALSE@smartd.initd: $(srcdir)/smartd.initd.in Makefile
-@OS_DARWIN_FALSE@      sed "s|/usr/local/sbin/|$(sbindir)/|g" $(srcdir)/smartd.initd.in > $@
-
-@OS_DARWIN_FALSE@install-initdDATA-generic: $(initd_DATA)
-@OS_DARWIN_FALSE@      $(mkinstalldirs) $(DESTDIR)$(initddir)
-@OS_DARWIN_FALSE@      $(INSTALL_SCRIPT) $(top_builddir)/smartd.initd $(DESTDIR)$(initddir)/smartd$(smartd_suffix)
-@OS_DARWIN_FALSE@      @echo -e "\n\n####################################################################\n#"
-@OS_DARWIN_FALSE@      @echo -e "#                       PLEASE READ THIS BOX!\n#"
-@OS_DARWIN_FALSE@      @echo -e "#   To manually start the smartd daemon, run:\n#   ${initddir}/smartd start\n#"
-@OS_DARWIN_FALSE@      @echo -e "#   To automatically start smartd on bootup, run:\n#   /sbin/chkconfig --add smartd\n#"
-@OS_DARWIN_FALSE@      @echo -e "#   smartd can now use a configuration file ${sysconfdir}/smartd.conf. Do:\n#   man smartd"
-@OS_DARWIN_FALSE@      @echo -e "#   to learn about it. A sample configuration file can be found in:\n#   ${docdir}\n#"
-@OS_DARWIN_FALSE@      @echo -e "####################################################################\n\n"
-
-install-initdDATA : $(initd_DATA_install)
-
-uninstall-initdDATA:
-       rm -rf $(DESTDIR)$(initddir)/$(initd_install_name)
-
-uninstall-docsDATA:
-       rm -rf $(DESTDIR)$(docsdir)
-
-smart%: $(srcdir)/smart%.in Makefile
-       sed "s|CURRENT_CVS_VERSION|$(releaseversion)|g" $< | \
-       sed "s|CURRENT_CVS_DATE|$(smartmontools_release_date)|g" | \
-       sed "s|CURRENT_CVS_TIME|$(smartmontools_release_time)|g" | \
-       sed "s|/usr/local/share/man/|$(mandir)/|g" | \
-       sed "s|/usr/local/sbin/|$(sbindir)/|g" | \
-       sed "s|/usr/local/etc/rc\\.d/init.d/|$(initddir)/|g" | \
-       sed "s|/usr/local/share/doc/smartmontools-5.1/|$(docsdir)/|g" | \
-       sed "s|/usr/local/etc/smartd\\.conf|$(sysconfdir)/smartd.conf|g" > $@
-
-# Convert man pages into .html and .txt
-
-htmlman: smartctl.8.html smartd.8.html smartd.conf.5.html
-
-txtman:  smartctl.8.txt smartd.8.txt smartd.conf.5.txt
-
-%.5.html: %.5
-       $(MAN2HTML) $< | $(FIXHTML) > $@
-
-%.8.html: %.8
-       $(MAN2HTML) $< | $(FIXHTML) > $@
-
-%.5.txt: %.5
-       $(MAN2TXT) $< > $@
-
-%.8.txt: %.8
-       $(MAN2TXT) $< > $@
-
-# Build Windows distribution
-
-@OS_WIN32_MINGW_TRUE@dist-win32: $(distzip_win32)
-
-@OS_WIN32_MINGW_TRUE@distdir-win32: distdir.mkdir $(FILES_WIN32) syslogevt.check
-
-@OS_WIN32_MINGW_TRUE@$(distzip_win32): distdir.mkdir $(FILES_WIN32) syslogevt.check
-@OS_WIN32_MINGW_TRUE@  @rm -fv $(distzip_win32)
-@OS_WIN32_MINGW_TRUE@  cd $(distdir_win32) && zip -9Dr ../$(distzip_win32) .
-
-@OS_WIN32_MINGW_TRUE@cleandist-win32:
-@OS_WIN32_MINGW_TRUE@  rm -rf $(distdir_win32) distdir.mkdir syslogevt.check
-
-@OS_WIN32_MINGW_TRUE@distdir.mkdir:
-@OS_WIN32_MINGW_TRUE@  @test -d $(exedir_win32) || mkdir -pv $(exedir_win32)
-@OS_WIN32_MINGW_TRUE@  @test -d $(docdir_win32) || mkdir -pv $(docdir_win32)
-@OS_WIN32_MINGW_TRUE@  touch $@
-
-@OS_WIN32_MINGW_TRUE@syslogevt.check:
-@OS_WIN32_MINGW_TRUE@  @if [ -f $(srcdir)/os_win32/syslogevt.exe ]; then \
-@OS_WIN32_MINGW_TRUE@    cp -pv $(srcdir)/os_win32/syslogevt.exe $(exedir_win32)/syslogevt.exe; \
-@OS_WIN32_MINGW_TRUE@   else echo "Warning: $(srcdir)/os_win32/syslogevt.exe missing."; fi
-@OS_WIN32_MINGW_TRUE@  touch $@
-
-@OS_WIN32_MINGW_TRUE@$(exedir_win32)/%.exe: %.exe
-@OS_WIN32_MINGW_TRUE@  cp -p $< $@
-@OS_WIN32_MINGW_TRUE@  strip -s $@
-@OS_WIN32_MINGW_TRUE@  touch -r $< $@
-
-@OS_WIN32_MINGW_TRUE@$(docdir_win32)/%: %
-@OS_WIN32_MINGW_TRUE@  $(UNIX2DOS) < $< > $@
-@OS_WIN32_MINGW_TRUE@  touch -r $< $@
-
-@OS_WIN32_MINGW_TRUE@$(docdir_win32)/%.txt: $(srcdir)/%
-@OS_WIN32_MINGW_TRUE@  $(UNIX2DOS) < $< > $@
-@OS_WIN32_MINGW_TRUE@  touch -r $< $@
-
-@OS_WIN32_MINGW_TRUE@$(docdir_win32)/%.conf: $(srcdir)/%.conf
-@OS_WIN32_MINGW_TRUE@  $(UNIX2DOS) < $< > $@
-@OS_WIN32_MINGW_TRUE@  touch -r $< $@
-
-# Build config_vc6.h for MSVC 6 from MinGW config.h
-
-@OS_WIN32_MINGW_TRUE@config-vc6: $(srcdir)/os_win32/config_vc6.h
-
-@OS_WIN32_MINGW_TRUE@$(srcdir)/os_win32/config_vc6.h: config.h
-@OS_WIN32_MINGW_TRUE@  sed '1i/* config_vc6.h.  Generated by Makefile.  */' $< | \
-@OS_WIN32_MINGW_TRUE@  sed 's,^#define HAVE_\(ATTR_PACKED\|INTTYPES_H\|STDINT_H\|STRTOULL\|U*INT64_T\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */,' | \
-@OS_WIN32_MINGW_TRUE@  sed 's,i.86-pc-mingw32,i686-pc-win32vc6,' > $@
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
diff --git a/NEWS b/NEWS
index 21aaf9e14b2db12d0909001bcabc84761e0e6b24..2d6fbfa85752da64dbe43df8dec2def15b5e891a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,9 +1,9 @@
 smartmontools NEWS
 ------------------
-CVS ID: $Id: NEWS,v 1.29 2006/04/12 15:46:30 ballen4705 Exp $
+CVS ID: $Id: NEWS,v 1.30 2006/05/19 16:33:33 chrfranke Exp $
 
 The most up-to-date version of this file is:
-http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/NEWS?sortby=date&view=markup
+http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/NEWS?view=markup
 
 Date 2006-04-12
 Summary: smartmontools release 5.36 (STABLE)
@@ -72,7 +72,7 @@ This is an stable release of smartmontools.
 Note added 2004/7/7: users building a Solaris/Intel version of the code should 
 modify the 'configure' file, changing "pc-*-solaris*" on line 106
 to read "*-pc-solaris*".  Reference:
-http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/configure.in?r1=1.83&r2=1.84
+http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/configure.in?r1=1.83&r2=1.84
 
 
 Date: 2004-5-4
diff --git a/README b/README
index 6d6e1c821fb5d6dd70d8c3c83da1381c8d774848..15cd0bc1fdab3870d50ac800a4547bec17b7d88a 100644 (file)
--- a/README
+++ b/README
@@ -3,7 +3,7 @@ smartmontools - S.M.A.R.T. utility toolset for Darwin/Mac
 OSX, FreeBSD, Linux, NetBSD, OpenBSD, Solaris, and Windows.
 ==========================================================
 
-$Id: README,v 1.55 2006/04/12 14:54:28 ballen4705 Exp $
+$Id: README,v 1.56 2006/05/19 16:33:33 chrfranke Exp $
 
 == HOME ==
 The home for smartmontools is located at:
@@ -74,14 +74,14 @@ http://sourceforge.net/project/showfiles.php?group_id=64297
 CVS
 ---
 
-cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools login (when prompted for a password, just press Enter)
-cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools co sm5
+cvs -d:pserver:anonymous@smartmontools.cvs.sourceforge.net:/cvsroot/smartmontools login (when prompted for a password, just press Enter)
+cvs -d:pserver:anonymous@smartmontools.cvs.sourceforge.net:/cvsroot/smartmontools co sm5
 
 This will create a subdirectory called sm5/ containing the code.
 
 To instead get the 5.1-16 release:
 
-cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools co -r RELEASE_5_1_16 sm5
+cvs -d:pserver:anonymous@smartmontools.cvs.sourceforge.net:/cvsroot/smartmontools co -r RELEASE_5_1_16 sm5
 
 To update your sources to the 5.1-18 release:
 
@@ -94,7 +94,7 @@ cd sm5
 cvs up -A
 
 You can see what the different tags are by looking at
-http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/smartmontools/sm5/ .
+http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/ .
 You'll see the tag names in the little scroll window where it says "Show
 only files with tag".
 
diff --git a/TODO b/TODO
index 1a5945d5804bdc01ca469e3ce4a170603bdec5f5..5d37a4a783bee2dba41031e76550b9b7e2589524 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,6 +1,6 @@
 TODO list for smartmontools:
 
-$Id: TODO,v 1.59 2005/12/11 18:40:35 ballen4705 Exp $
+$Id: TODO,v 1.60 2006/06/05 17:36:03 chrfranke Exp $
 
 SATA devices under Linux
 ------------------------
@@ -131,10 +131,6 @@ Add IDE/ATA selective self test.
 Access SCSI devices via IOCTL_SCSI_PASS_THROUGH on 2000/XP
 to support systems with missing ASPI driver.
 
-Windows
--------
-Provide some installer.
-
 Packaging
 ---------
 Under freebsd and solaris, the following are wrong:
index 21e23d85fcc57845d0717bb29aa536bce3edd664..2abfe7d0d81a9ea2434672587d0b2ab732383d68 100644 (file)
--- a/WARNINGS
+++ b/WARNINGS
@@ -1,7 +1,7 @@
-$Id: WARNINGS,v 1.32 2005/04/20 19:17:33 geoffk1 Exp $
+$Id: WARNINGS,v 1.33 2006/05/19 16:33:33 chrfranke Exp $
 
 The most recent version of this file can be found here:
-http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/WARNINGS?view=markup
+http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/WARNINGS?view=markup
 
 The following are reports of serious problems (eg system lockup) which
 were due to smartmontools.  There are DARWIN, LINUX, FREEBSD, SOLARIS
@@ -107,7 +107,7 @@ NOTE:     The IOCTL call SMART_RCV_DRIVE_DATA does not support
           ATA_SMART_READ_LOG_SECTOR on NT4/2000/XP. The Win32 
           implementation of smartctl/smartd uses undocumented
           and possibly buggy system calls for this purpose:
-          NT4: IOCTL_SCSI_PASS_THROUGH.with undocumented pseudo SCSI
+          NT4: IOCTL_SCSI_PASS_THROUGH with undocumented pseudo SCSI
           command SCSIOP_ATA_PASSTHROUGH (0xCC).
           2000/XP: Undocumented IOCTL_IDE_PASS_THROUGH.
 
diff --git a/aclocal.m4 b/aclocal.m4
deleted file mode 100644 (file)
index cf94d1b..0000000
+++ /dev/null
@@ -1,1116 +0,0 @@
-# generated automatically by aclocal 1.9.3 -*- Autoconf -*-
-
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
-# Free Software Foundation, Inc.
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-#                                                        -*- Autoconf -*-
-# Copyright (C) 2002, 2003  Free Software Foundation, Inc.
-# Generated from amversion.in; do not edit by hand.
-
-# 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, 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
-
-# AM_AUTOMAKE_VERSION(VERSION)
-# ----------------------------
-# Automake X.Y traces this macro to ensure aclocal.m4 has been
-# generated from the m4 files accompanying Automake X.Y.
-AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
-
-# AM_SET_CURRENT_AUTOMAKE_VERSION
-# -------------------------------
-# Call AM_AUTOMAKE_VERSION so it can be traced.
-# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
-AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-        [AM_AUTOMAKE_VERSION([1.9.3])])
-
-# Figure out how to run the assembler.             -*- Autoconf -*-
-
-# serial 3
-
-# Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
-
-# 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, 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.
-
-# AM_PROG_AS
-# ----------
-AC_DEFUN([AM_PROG_AS],
-[# By default we simply use the C compiler to build assembly code.
-AC_REQUIRE([AC_PROG_CC])
-test "${CCAS+set}" = set || CCAS=$CC
-test "${CCASFLAGS+set}" = set || CCASFLAGS=$CFLAGS
-AC_ARG_VAR([CCAS],      [assembler compiler command (defaults to CC)])
-AC_ARG_VAR([CCASFLAGS], [assembler compiler flags (defaults to CFLAGS)])
-])
-
-# AM_AUX_DIR_EXPAND
-
-# Copyright (C) 2001, 2003 Free Software Foundation, Inc.
-
-# 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, 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.
-
-# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
-# $ac_aux_dir to `$srcdir/foo'.  In other projects, it is set to
-# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
-#
-# Of course, Automake must honor this variable whenever it calls a
-# tool from the auxiliary directory.  The problem is that $srcdir (and
-# therefore $ac_aux_dir as well) can be either absolute or relative,
-# depending on how configure is run.  This is pretty annoying, since
-# it makes $ac_aux_dir quite unusable in subdirectories: in the top
-# source directory, any form will work fine, but in subdirectories a
-# relative path needs to be adjusted first.
-#
-# $ac_aux_dir/missing
-#    fails when called from a subdirectory if $ac_aux_dir is relative
-# $top_srcdir/$ac_aux_dir/missing
-#    fails if $ac_aux_dir is absolute,
-#    fails when called from a subdirectory in a VPATH build with
-#          a relative $ac_aux_dir
-#
-# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
-# are both prefixed by $srcdir.  In an in-source build this is usually
-# harmless because $srcdir is `.', but things will broke when you
-# start a VPATH build or use an absolute $srcdir.
-#
-# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
-# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
-#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
-# and then we would define $MISSING as
-#   MISSING="\${SHELL} $am_aux_dir/missing"
-# This will work as long as MISSING is not called from configure, because
-# unfortunately $(top_srcdir) has no meaning in configure.
-# However there are other variables, like CC, which are often used in
-# configure, and could therefore not use this "fixed" $ac_aux_dir.
-#
-# Another solution, used here, is to always expand $ac_aux_dir to an
-# absolute PATH.  The drawback is that using absolute paths prevent a
-# configured tree to be moved without reconfiguration.
-
-AC_DEFUN([AM_AUX_DIR_EXPAND],
-[dnl Rely on autoconf to set up CDPATH properly.
-AC_PREREQ([2.50])dnl
-# expand $ac_aux_dir to an absolute path
-am_aux_dir=`cd $ac_aux_dir && pwd`
-])
-
-# AM_CONDITIONAL                                              -*- Autoconf -*-
-
-# Copyright (C) 1997, 2000, 2001, 2003, 2004 Free Software Foundation, Inc.
-
-# 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, 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.
-
-# serial 6
-
-# AM_CONDITIONAL(NAME, SHELL-CONDITION)
-# -------------------------------------
-# Define a conditional.
-AC_DEFUN([AM_CONDITIONAL],
-[AC_PREREQ(2.52)dnl
- ifelse([$1], [TRUE],  [AC_FATAL([$0: invalid condition: $1])],
-       [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
-AC_SUBST([$1_TRUE])
-AC_SUBST([$1_FALSE])
-if $2; then
-  $1_TRUE=
-  $1_FALSE='#'
-else
-  $1_TRUE='#'
-  $1_FALSE=
-fi
-AC_CONFIG_COMMANDS_PRE(
-[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
-  AC_MSG_ERROR([[conditional "$1" was never defined.
-Usually this means the macro was only invoked conditionally.]])
-fi])])
-
-# serial 7                                             -*- Autoconf -*-
-
-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
-# Free Software Foundation, Inc.
-
-# 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, 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.
-
-
-# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
-# written in clear, in which case automake, when reading aclocal.m4,
-# will think it sees a *use*, and therefore will trigger all it's
-# C support machinery.  Also note that it means that autoscan, seeing
-# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
-
-
-
-# _AM_DEPENDENCIES(NAME)
-# ----------------------
-# See how the compiler implements dependency checking.
-# NAME is "CC", "CXX", "GCJ", or "OBJC".
-# We try a few techniques and use that to set a single cache variable.
-#
-# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
-# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
-# dependency, and given that the user is not expected to run this macro,
-# just rely on AC_PROG_CC.
-AC_DEFUN([_AM_DEPENDENCIES],
-[AC_REQUIRE([AM_SET_DEPDIR])dnl
-AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
-AC_REQUIRE([AM_MAKE_INCLUDE])dnl
-AC_REQUIRE([AM_DEP_TRACK])dnl
-
-ifelse([$1], CC,   [depcc="$CC"   am_compiler_list=],
-       [$1], CXX,  [depcc="$CXX"  am_compiler_list=],
-       [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
-       [$1], GCJ,  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
-                   [depcc="$$1"   am_compiler_list=])
-
-AC_CACHE_CHECK([dependency style of $depcc],
-               [am_cv_$1_dependencies_compiler_type],
-[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
-  # We make a subdir and do the tests there.  Otherwise we can end up
-  # making bogus files that we don't know about and never remove.  For
-  # instance it was reported that on HP-UX the gcc test will end up
-  # making a dummy file named `D' -- because `-MD' means `put the output
-  # in D'.
-  mkdir conftest.dir
-  # Copy depcomp to subdir because otherwise we won't find it if we're
-  # using a relative directory.
-  cp "$am_depcomp" conftest.dir
-  cd conftest.dir
-  # We will build objects and dependencies in a subdirectory because
-  # it helps to detect inapplicable dependency modes.  For instance
-  # both Tru64's cc and ICC support -MD to output dependencies as a
-  # side effect of compilation, but ICC will put the dependencies in
-  # the current directory while Tru64 will put them in the object
-  # directory.
-  mkdir sub
-
-  am_cv_$1_dependencies_compiler_type=none
-  if test "$am_compiler_list" = ""; then
-     am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
-  fi
-  for depmode in $am_compiler_list; do
-    # Setup a source with many dependencies, because some compilers
-    # like to wrap large dependency lists on column 80 (with \), and
-    # we should not choose a depcomp mode which is confused by this.
-    #
-    # We need to recreate these files for each test, as the compiler may
-    # overwrite some of them when testing with obscure command lines.
-    # This happens at least with the AIX C compiler.
-    : > sub/conftest.c
-    for i in 1 2 3 4 5 6; do
-      echo '#include "conftst'$i'.h"' >> sub/conftest.c
-      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
-      # Solaris 8's {/usr,}/bin/sh.
-      touch sub/conftst$i.h
-    done
-    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
-
-    case $depmode in
-    nosideeffect)
-      # after this tag, mechanisms are not by side-effect, so they'll
-      # only be used when explicitly requested
-      if test "x$enable_dependency_tracking" = xyes; then
-       continue
-      else
-       break
-      fi
-      ;;
-    none) break ;;
-    esac
-    # We check with `-c' and `-o' for the sake of the "dashmstdout"
-    # mode.  It turns out that the SunPro C++ compiler does not properly
-    # handle `-M -o', and we need to detect this.
-    if depmode=$depmode \
-       source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
-       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
-       $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
-         >/dev/null 2>conftest.err &&
-       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
-       grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
-       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
-      # icc doesn't choke on unknown options, it will just issue warnings
-      # or remarks (even with -Werror).  So we grep stderr for any message
-      # that says an option was ignored or not supported.
-      # When given -MP, icc 7.0 and 7.1 complain thusly:
-      #   icc: Command line warning: ignoring option '-M'; no argument required
-      # The diagnosis changed in icc 8.0:
-      #   icc: Command line remark: option '-MP' not supported
-      if (grep 'ignoring option' conftest.err ||
-          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
-        am_cv_$1_dependencies_compiler_type=$depmode
-        break
-      fi
-    fi
-  done
-
-  cd ..
-  rm -rf conftest.dir
-else
-  am_cv_$1_dependencies_compiler_type=none
-fi
-])
-AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
-AM_CONDITIONAL([am__fastdep$1], [
-  test "x$enable_dependency_tracking" != xno \
-  && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
-])
-
-
-# AM_SET_DEPDIR
-# -------------
-# Choose a directory name for dependency files.
-# This macro is AC_REQUIREd in _AM_DEPENDENCIES
-AC_DEFUN([AM_SET_DEPDIR],
-[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
-AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
-])
-
-
-# AM_DEP_TRACK
-# ------------
-AC_DEFUN([AM_DEP_TRACK],
-[AC_ARG_ENABLE(dependency-tracking,
-[  --disable-dependency-tracking  speeds up one-time build
-  --enable-dependency-tracking   do not reject slow dependency extractors])
-if test "x$enable_dependency_tracking" != xno; then
-  am_depcomp="$ac_aux_dir/depcomp"
-  AMDEPBACKSLASH='\'
-fi
-AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
-AC_SUBST([AMDEPBACKSLASH])
-])
-
-# Generate code to set up dependency tracking.   -*- Autoconf -*-
-
-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
-#   Free Software Foundation, Inc.
-
-# 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, 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.
-
-#serial 2
-
-# _AM_OUTPUT_DEPENDENCY_COMMANDS
-# ------------------------------
-AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
-[for mf in $CONFIG_FILES; do
-  # Strip MF so we end up with the name of the file.
-  mf=`echo "$mf" | sed -e 's/:.*$//'`
-  # Check whether this is an Automake generated Makefile or not.
-  # We used to match only the files named `Makefile.in', but
-  # some people rename them; so instead we look at the file content.
-  # Grep'ing the first line is not enough: some people post-process
-  # each Makefile.in and add a new line on top of each file to say so.
-  # So let's grep whole file.
-  if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
-    dirpart=`AS_DIRNAME("$mf")`
-  else
-    continue
-  fi
-  # Extract the definition of DEPDIR, am__include, and am__quote
-  # from the Makefile without running `make'.
-  DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
-  test -z "$DEPDIR" && continue
-  am__include=`sed -n 's/^am__include = //p' < "$mf"`
-  test -z "am__include" && continue
-  am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
-  # When using ansi2knr, U may be empty or an underscore; expand it
-  U=`sed -n 's/^U = //p' < "$mf"`
-  # Find all dependency output files, they are included files with
-  # $(DEPDIR) in their names.  We invoke sed twice because it is the
-  # simplest approach to changing $(DEPDIR) to its actual value in the
-  # expansion.
-  for file in `sed -n "
-    s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
-       sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
-    # Make sure the directory exists.
-    test -f "$dirpart/$file" && continue
-    fdir=`AS_DIRNAME(["$file"])`
-    AS_MKDIR_P([$dirpart/$fdir])
-    # echo "creating $dirpart/$file"
-    echo '# dummy' > "$dirpart/$file"
-  done
-done
-])# _AM_OUTPUT_DEPENDENCY_COMMANDS
-
-
-# AM_OUTPUT_DEPENDENCY_COMMANDS
-# -----------------------------
-# This macro should only be invoked once -- use via AC_REQUIRE.
-#
-# This code is only required when automatic dependency tracking
-# is enabled.  FIXME.  This creates each `.P' file that we will
-# need in order to bootstrap the dependency handling code.
-AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
-[AC_CONFIG_COMMANDS([depfiles],
-     [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
-     [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
-])
-
-# Like AC_CONFIG_HEADER, but automatically create stamp file. -*- Autoconf -*-
-
-# Copyright (C) 1996, 1997, 2000, 2001, 2003 Free Software Foundation, Inc.
-
-# 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, 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.
-
-# serial 7
-
-# AM_CONFIG_HEADER is obsolete.  It has been replaced by AC_CONFIG_HEADERS.
-AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)])
-
-# Do all the work for Automake.                            -*- Autoconf -*-
-
-# This macro actually does too much some checks are only needed if
-# your package does certain things.  But this isn't really a big deal.
-
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
-# Free Software Foundation, Inc.
-
-# 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, 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.
-
-# serial 11
-
-# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
-# AM_INIT_AUTOMAKE([OPTIONS])
-# -----------------------------------------------
-# The call with PACKAGE and VERSION arguments is the old style
-# call (pre autoconf-2.50), which is being phased out.  PACKAGE
-# and VERSION should now be passed to AC_INIT and removed from
-# the call to AM_INIT_AUTOMAKE.
-# We support both call styles for the transition.  After
-# the next Automake release, Autoconf can make the AC_INIT
-# arguments mandatory, and then we can depend on a new Autoconf
-# release and drop the old call support.
-AC_DEFUN([AM_INIT_AUTOMAKE],
-[AC_PREREQ([2.58])dnl
-dnl Autoconf wants to disallow AM_ names.  We explicitly allow
-dnl the ones we care about.
-m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
-AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
-AC_REQUIRE([AC_PROG_INSTALL])dnl
-# test to see if srcdir already configured
-if test "`cd $srcdir && pwd`" != "`pwd`" &&
-   test -f $srcdir/config.status; then
-  AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
-fi
-
-# test whether we have cygpath
-if test -z "$CYGPATH_W"; then
-  if (cygpath --version) >/dev/null 2>/dev/null; then
-    CYGPATH_W='cygpath -w'
-  else
-    CYGPATH_W=echo
-  fi
-fi
-AC_SUBST([CYGPATH_W])
-
-# Define the identity of the package.
-dnl Distinguish between old-style and new-style calls.
-m4_ifval([$2],
-[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
- AC_SUBST([PACKAGE], [$1])dnl
- AC_SUBST([VERSION], [$2])],
-[_AM_SET_OPTIONS([$1])dnl
- AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
- AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
-
-_AM_IF_OPTION([no-define],,
-[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
- AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
-
-# Some tools Automake needs.
-AC_REQUIRE([AM_SANITY_CHECK])dnl
-AC_REQUIRE([AC_ARG_PROGRAM])dnl
-AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
-AM_MISSING_PROG(AUTOCONF, autoconf)
-AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
-AM_MISSING_PROG(AUTOHEADER, autoheader)
-AM_MISSING_PROG(MAKEINFO, makeinfo)
-AM_PROG_INSTALL_SH
-AM_PROG_INSTALL_STRIP
-AC_REQUIRE([AM_PROG_MKDIR_P])dnl
-# We need awk for the "check" target.  The system "awk" is bad on
-# some platforms.
-AC_REQUIRE([AC_PROG_AWK])dnl
-AC_REQUIRE([AC_PROG_MAKE_SET])dnl
-AC_REQUIRE([AM_SET_LEADING_DOT])dnl
-_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
-              [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
-                            [_AM_PROG_TAR([v7])])])
-_AM_IF_OPTION([no-dependencies],,
-[AC_PROVIDE_IFELSE([AC_PROG_CC],
-                  [_AM_DEPENDENCIES(CC)],
-                  [define([AC_PROG_CC],
-                          defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
-AC_PROVIDE_IFELSE([AC_PROG_CXX],
-                  [_AM_DEPENDENCIES(CXX)],
-                  [define([AC_PROG_CXX],
-                          defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
-])
-])
-
-
-# When config.status generates a header, we must update the stamp-h file.
-# This file resides in the same directory as the config header
-# that is generated.  The stamp files are numbered to have different names.
-
-# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
-# loop where config.status creates the headers, so we can generate
-# our stamp files there.
-AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
-[# Compute $1's index in $config_headers.
-_am_stamp_count=1
-for _am_header in $config_headers :; do
-  case $_am_header in
-    $1 | $1:* )
-      break ;;
-    * )
-      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
-  esac
-done
-echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count])
-
-# AM_PROG_INSTALL_SH
-# ------------------
-# Define $install_sh.
-
-# Copyright (C) 2001, 2003 Free Software Foundation, Inc.
-
-# 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, 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.
-
-AC_DEFUN([AM_PROG_INSTALL_SH],
-[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
-install_sh=${install_sh-"$am_aux_dir/install-sh"}
-AC_SUBST(install_sh)])
-
-#                                                          -*- Autoconf -*-
-# Copyright (C) 2003  Free Software Foundation, Inc.
-
-# 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, 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.
-
-# serial 1
-
-# Check whether the underlying file-system supports filenames
-# with a leading dot.  For instance MS-DOS doesn't.
-AC_DEFUN([AM_SET_LEADING_DOT],
-[rm -rf .tst 2>/dev/null
-mkdir .tst 2>/dev/null
-if test -d .tst; then
-  am__leading_dot=.
-else
-  am__leading_dot=_
-fi
-rmdir .tst 2>/dev/null
-AC_SUBST([am__leading_dot])])
-
-# Add --enable-maintainer-mode option to configure.
-# From Jim Meyering
-
-# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004
-# Free Software Foundation, Inc.
-
-# 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, 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.
-
-# serial 3
-
-AC_DEFUN([AM_MAINTAINER_MODE],
-[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
-  dnl maintainer-mode is disabled by default
-  AC_ARG_ENABLE(maintainer-mode,
-[  --enable-maintainer-mode  enable make rules and dependencies not useful
-                         (and sometimes confusing) to the casual installer],
-      USE_MAINTAINER_MODE=$enableval,
-      USE_MAINTAINER_MODE=no)
-  AC_MSG_RESULT([$USE_MAINTAINER_MODE])
-  AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes])
-  MAINT=$MAINTAINER_MODE_TRUE
-  AC_SUBST(MAINT)dnl
-]
-)
-
-AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE])
-
-# Check to see how 'make' treats includes.     -*- Autoconf -*-
-
-# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
-
-# 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, 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.
-
-# serial 2
-
-# AM_MAKE_INCLUDE()
-# -----------------
-# Check to see how make treats includes.
-AC_DEFUN([AM_MAKE_INCLUDE],
-[am_make=${MAKE-make}
-cat > confinc << 'END'
-am__doit:
-       @echo done
-.PHONY: am__doit
-END
-# If we don't find an include directive, just comment out the code.
-AC_MSG_CHECKING([for style of include used by $am_make])
-am__include="#"
-am__quote=
-_am_result=none
-# First try GNU make style include.
-echo "include confinc" > confmf
-# We grep out `Entering directory' and `Leaving directory'
-# messages which can occur if `w' ends up in MAKEFLAGS.
-# In particular we don't look at `^make:' because GNU make might
-# be invoked under some other name (usually "gmake"), in which
-# case it prints its new name instead of `make'.
-if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
-   am__include=include
-   am__quote=
-   _am_result=GNU
-fi
-# Now try BSD make style include.
-if test "$am__include" = "#"; then
-   echo '.include "confinc"' > confmf
-   if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
-      am__include=.include
-      am__quote="\""
-      _am_result=BSD
-   fi
-fi
-AC_SUBST([am__include])
-AC_SUBST([am__quote])
-AC_MSG_RESULT([$_am_result])
-rm -f confinc confmf
-])
-
-#  -*- Autoconf -*-
-
-
-# Copyright (C) 1997, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
-
-# 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, 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.
-
-# serial 3
-
-# AM_MISSING_PROG(NAME, PROGRAM)
-# ------------------------------
-AC_DEFUN([AM_MISSING_PROG],
-[AC_REQUIRE([AM_MISSING_HAS_RUN])
-$1=${$1-"${am_missing_run}$2"}
-AC_SUBST($1)])
-
-
-# AM_MISSING_HAS_RUN
-# ------------------
-# Define MISSING if not defined so far and test if it supports --run.
-# If it does, set am_missing_run to use it, otherwise, to nothing.
-AC_DEFUN([AM_MISSING_HAS_RUN],
-[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
-test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
-# Use eval to expand $SHELL
-if eval "$MISSING --run true"; then
-  am_missing_run="$MISSING --run "
-else
-  am_missing_run=
-  AC_MSG_WARN([`missing' script is too old or missing])
-fi
-])
-
-# AM_PROG_MKDIR_P
-# ---------------
-# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise.
-
-# Copyright (C) 2003, 2004 Free Software Foundation, Inc.
-
-# 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, 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.
-
-# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories
-# created by `make install' are always world readable, even if the
-# installer happens to have an overly restrictive umask (e.g. 077).
-# This was a mistake.  There are at least two reasons why we must not
-# use `-m 0755':
-#   - it causes special bits like SGID to be ignored,
-#   - it may be too restrictive (some setups expect 775 directories).
-#
-# Do not use -m 0755 and let people choose whatever they expect by
-# setting umask.
-#
-# We cannot accept any implementation of `mkdir' that recognizes `-p'.
-# Some implementations (such as Solaris 8's) are not thread-safe: if a
-# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c'
-# concurrently, both version can detect that a/ is missing, but only
-# one can create it and the other will error out.  Consequently we
-# restrict ourselves to GNU make (using the --version option ensures
-# this.)
-AC_DEFUN([AM_PROG_MKDIR_P],
-[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
-  # We used to keeping the `.' as first argument, in order to
-  # allow $(mkdir_p) to be used without argument.  As in
-  #   $(mkdir_p) $(somedir)
-  # where $(somedir) is conditionally defined.  However this is wrong
-  # for two reasons:
-  #  1. if the package is installed by a user who cannot write `.'
-  #     make install will fail,
-  #  2. the above comment should most certainly read
-  #     $(mkdir_p) $(DESTDIR)$(somedir)
-  #     so it does not work when $(somedir) is undefined and
-  #     $(DESTDIR) is not.
-  #  To support the latter case, we have to write
-  #     test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
-  #  so the `.' trick is pointless.
-  mkdir_p='mkdir -p --'
-else
-  # On NextStep and OpenStep, the `mkdir' command does not
-  # recognize any option.  It will interpret all options as
-  # directories to create, and then abort because `.' already
-  # exists.
-  for d in ./-p ./--version;
-  do
-    test -d $d && rmdir $d
-  done
-  # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
-  if test -f "$ac_aux_dir/mkinstalldirs"; then
-    mkdir_p='$(mkinstalldirs)'
-  else
-    mkdir_p='$(install_sh) -d'
-  fi
-fi
-AC_SUBST([mkdir_p])])
-
-# Helper functions for option handling.                    -*- Autoconf -*-
-
-# Copyright (C) 2001, 2002, 2003  Free Software Foundation, Inc.
-
-# 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, 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.
-
-# serial 2
-
-# _AM_MANGLE_OPTION(NAME)
-# -----------------------
-AC_DEFUN([_AM_MANGLE_OPTION],
-[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
-
-# _AM_SET_OPTION(NAME)
-# ------------------------------
-# Set option NAME.  Presently that only means defining a flag for this option.
-AC_DEFUN([_AM_SET_OPTION],
-[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
-
-# _AM_SET_OPTIONS(OPTIONS)
-# ----------------------------------
-# OPTIONS is a space-separated list of Automake options.
-AC_DEFUN([_AM_SET_OPTIONS],
-[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
-
-# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
-# -------------------------------------------
-# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
-AC_DEFUN([_AM_IF_OPTION],
-[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
-
-#
-# Check to make sure that the build environment is sane.
-#
-
-# Copyright (C) 1996, 1997, 2000, 2001, 2003 Free Software Foundation, Inc.
-
-# 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, 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.
-
-# serial 3
-
-# AM_SANITY_CHECK
-# ---------------
-AC_DEFUN([AM_SANITY_CHECK],
-[AC_MSG_CHECKING([whether build environment is sane])
-# Just in case
-sleep 1
-echo timestamp > conftest.file
-# Do `set' in a subshell so we don't clobber the current shell's
-# arguments.  Must try -L first in case configure is actually a
-# symlink; some systems play weird games with the mod time of symlinks
-# (eg FreeBSD returns the mod time of the symlink's containing
-# directory).
-if (
-   set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
-   if test "$[*]" = "X"; then
-      # -L didn't work.
-      set X `ls -t $srcdir/configure conftest.file`
-   fi
-   rm -f conftest.file
-   if test "$[*]" != "X $srcdir/configure conftest.file" \
-      && test "$[*]" != "X conftest.file $srcdir/configure"; then
-
-      # If neither matched, then we have a broken ls.  This can happen
-      # if, for instance, CONFIG_SHELL is bash and it inherits a
-      # broken ls alias from the environment.  This has actually
-      # happened.  Such a system could not be considered "sane".
-      AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
-alias in your environment])
-   fi
-
-   test "$[2]" = conftest.file
-   )
-then
-   # Ok.
-   :
-else
-   AC_MSG_ERROR([newly created file is older than distributed files!
-Check your system clock])
-fi
-AC_MSG_RESULT(yes)])
-
-# AM_PROG_INSTALL_STRIP
-
-# Copyright (C) 2001, 2003 Free Software Foundation, Inc.
-
-# 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, 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.
-
-# One issue with vendor `install' (even GNU) is that you can't
-# specify the program used to strip binaries.  This is especially
-# annoying in cross-compiling environments, where the build's strip
-# is unlikely to handle the host's binaries.
-# Fortunately install-sh will honor a STRIPPROG variable, so we
-# always use install-sh in `make install-strip', and initialize
-# STRIPPROG with the value of the STRIP variable (set by the user).
-AC_DEFUN([AM_PROG_INSTALL_STRIP],
-[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
-# Installed binaries are usually stripped using `strip' when the user
-# run `make install-strip'.  However `strip' might not be the right
-# tool to use in cross-compilation environments, therefore Automake
-# will honor the `STRIP' environment variable to overrule this program.
-dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
-if test "$cross_compiling" != no; then
-  AC_CHECK_TOOL([STRIP], [strip], :)
-fi
-INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
-AC_SUBST([INSTALL_STRIP_PROGRAM])])
-
-# Check how to create a tarball.                            -*- Autoconf -*-
-
-# Copyright (C) 2004  Free Software Foundation, Inc.
-
-# 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, 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.
-
-# serial 1
-
-
-# _AM_PROG_TAR(FORMAT)
-# --------------------
-# Check how to create a tarball in format FORMAT.
-# FORMAT should be one of `v7', `ustar', or `pax'.
-#
-# Substitute a variable $(am__tar) that is a command
-# writing to stdout a FORMAT-tarball containing the directory
-# $tardir.
-#     tardir=directory && $(am__tar) > result.tar
-#
-# Substitute a variable $(am__untar) that extract such
-# a tarball read from stdin.
-#     $(am__untar) < result.tar
-AC_DEFUN([_AM_PROG_TAR],
-[# Always define AMTAR for backward compatibility.
-AM_MISSING_PROG([AMTAR], [tar])
-m4_if([$1], [v7],
-     [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
-     [m4_case([$1], [ustar],, [pax],,
-              [m4_fatal([Unknown tar format])])
-AC_MSG_CHECKING([how to create a $1 tar archive])
-# Loop over all known methods to create a tar archive until one works.
-_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
-_am_tools=${am_cv_prog_tar_$1-$_am_tools}
-# Do not fold the above two line into one, because Tru64 sh and
-# Solaris sh will not grok spaces in the rhs of `-'.
-for _am_tool in $_am_tools
-do
-  case $_am_tool in
-  gnutar)
-    for _am_tar in tar gnutar gtar;
-    do
-      AM_RUN_LOG([$_am_tar --version]) && break
-    done
-    am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
-    am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
-    am__untar="$_am_tar -xf -"
-    ;;
-  plaintar)
-    # Must skip GNU tar: if it does not support --format= it doesn't create
-    # ustar tarball either.
-    (tar --version) >/dev/null 2>&1 && continue
-    am__tar='tar chf - "$$tardir"'
-    am__tar_='tar chf - "$tardir"'
-    am__untar='tar xf -'
-    ;;
-  pax)
-    am__tar='pax -L -x $1 -w "$$tardir"'
-    am__tar_='pax -L -x $1 -w "$tardir"'
-    am__untar='pax -r'
-    ;;
-  cpio)
-    am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
-    am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
-    am__untar='cpio -i -H $1 -d'
-    ;;
-  none)
-    am__tar=false
-    am__tar_=false
-    am__untar=false
-    ;;
-  esac
-
-  # If the value was cached, stop now.  We just wanted to have am__tar
-  # and am__untar set.
-  test -n "${am_cv_prog_tar_$1}" && break
-
-  # tar/untar a dummy directory, and stop if the command works
-  rm -rf conftest.dir
-  mkdir conftest.dir
-  echo GrepMe > conftest.dir/file
-  AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
-  rm -rf conftest.dir
-  if test -s conftest.tar; then
-    AM_RUN_LOG([$am__untar <conftest.tar])
-    grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
-  fi
-done
-rm -rf conftest.dir
-
-AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
-AC_MSG_RESULT([$am_cv_prog_tar_$1])])
-AC_SUBST([am__tar])
-AC_SUBST([am__untar])
-]) # _AM_PROG_TAR
-
diff --git a/atacmdnames.c b/atacmdnames.c
deleted file mode 100644 (file)
index 52b68f1..0000000
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
- * atacmdnames.c
- *
- * This module is based on the T13/1532D Volume 1 Revision 3 (ATA/ATAPI-7)
- * specification, which is available from http://www.t13.org/#FTP_site
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- * Address of support mailing list: smartmontools-support@lists.sourceforge.net
- *
- * Copyright (C) 2003-6 Philip Williams
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include "atacmdnames.h"
-#include <stdlib.h>
-#include <stdio.h>
-
-#define COMMAND_TABLE_SIZE 256
-
-const char *atacmdnames_c_cvsid="$Id: atacmdnames.c,v 1.13 2006/04/12 14:54:28 ballen4705 Exp $" ATACMDNAMES_H_CVSID;
-
-const char cmd_reserved[]        = "[RESERVED]";
-const char cmd_vendor_specific[] = "[VENDOR SPECIFIC]";
-const char cmd_reserved_sa[]     = "[RESERVED FOR SERIAL ATA]";
-const char cmd_reserved_cf[]     = "[RESERVED FOR COMPACTFLASH ASSOCIATION]";
-const char cmd_reserved_mcpt[]   = "[RESERVED FOR MEDIA CARD PASS THROUGH]";
-const char cmd_recalibrate_ret4[]= "RECALIBRATE [RET-4]";
-const char cmd_seek_ret4[]       = "SEEK [RET-4]";
-
-const char *command_table[COMMAND_TABLE_SIZE] = {
-/*-------------------------------------------------- 00h-0Fh -----*/
-  "NOP",
-  cmd_reserved,
-  cmd_reserved,
-  "CFA REQUEST EXTENDED ERROR CODE",
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  "DEVICE RESET",
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-/*-------------------------------------------------- 10h-1Fh -----*/
-  "RECALIBRATE [OBS-4]",
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-  cmd_recalibrate_ret4,
-/*-------------------------------------------------- 20h-2Fh -----*/
-  "READ SECTOR(S)",
-  "READ SECTOR(S) [OBS-5]",
-  "READ LONG (w/ retry) [OBS-4]",
-  "READ LONG (w/o retry) [OBS-4]",
-  "READ SECTOR(S) EXT",
-  "READ DMA EXT",
-  "READ DMA QUEUED EXT",
-  "READ NATIVE MAX ADDRESS EXT",
-  cmd_reserved,
-  "READ MULTIPLE EXT",
-  "READ STREAM DMA",
-  "READ STREAM PIO",
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  "READ LOG EXT",
-/*-------------------------------------------------- 30h-3Fh -----*/
-  "WRITE SECTOR(S)",
-  "WRITE SECTOR(S) [OBS-5]",
-  "WRITE LONG(w/ retry) [OBS-4]",
-  "WRITE LONG(w/o retry) [OBS-4]",
-  "WRITE SECTORS(S) EXT",
-  "WRITE DMA EXT",
-  "WRITE DMA QUEUED EXT",
-  "SET MAX ADDRESS EXT",
-  "CFA WRITE SECTORS WITHOUT ERASE",
-  "WRITE MULTIPLE EXT",
-  "WRITE STREAM DMA",
-  "WRITE STREAM PIO",
-  "WRITE VERIFY [OBS-4]",
-  "WRITE DMA FUA EXT",
-  "WRITE DMA QUEUED FUA EXT",
-  "WRITE LOG EXT",
-/*-------------------------------------------------- 40h-4Fh -----*/
-  "READ VERIFY SECTOR(S)",
-  "READ VERIFY SECTOR(S) [OBS-5]",
-  "READ VERIFY SECTOR(S) EXT",
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-/*-------------------------------------------------- 50h-5Fh -----*/
-  "FORMAT TRACK [OBS-4]",
-  "CONFIGURE STREAM",
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-/*-------------------------------------------------- 60h-6Fh -----*/
-  cmd_reserved_sa,
-  cmd_reserved_sa,
-  cmd_reserved_sa,
-  cmd_reserved_sa,
-  cmd_reserved_sa,
-  cmd_reserved_sa,
-  cmd_reserved_sa,
-  cmd_reserved_sa,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-/*-------------------------------------------------- 70h-7Fh -----*/
-  "SEEK [OBS-7]",
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-  cmd_seek_ret4,
-/*-------------------------------------------------- 80h-8Fh -----*/
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  "CFA TRANSLATE SECTOR [VS IF NO CFA]",
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-/*-------------------------------------------------- 90h-9Fh -----*/
-  "EXECUTE DEVICE DIAGNOSTIC",
-  "INITIALIZE DEVICE PARAMETERS [OBS-6]",
-  "DOWNLOAD MICROCODE",
-  cmd_reserved,
-  "STANDBY IMMEDIATE [RET-4]",
-  "IDLE IMMEDIATE [RET-4]",
-  "STANDBY [RET-4]",
-  "IDLE [RET-4]",
-  "CHECK POWER MODE [RET-4]",
-  "SLEEP [RET-4]",
-  cmd_vendor_specific,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-/*-------------------------------------------------- A0h-AFh -----*/
-  "PACKET",
-  "IDENTIFY PACKET DEVICE",
-  "SERVICE",
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-/*-------------------------------------------------- B0h-BFh -----*/
-  "SMART",
-  "DEVICE CONFIGURATION",
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved_cf,
-  cmd_reserved_cf,
-  cmd_reserved_cf,
-  cmd_reserved_cf,
-  cmd_reserved_cf,
-  cmd_reserved_cf,
-  cmd_reserved_cf,
-  cmd_reserved_cf,
-/*-------------------------------------------------- C0h-CFh -----*/
-  "CFA ERASE SECTORS [VS IF NO CFA]",
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  "READ MULTIPLE",
-  "WRITE MULTIPLE",
-  "SET MULTIPLE MODE",
-  "READ DMA QUEUED",
-  "READ DMA",
-  "READ DMA [OBS-5]",
-  "WRITE DMA",
-  "WRITE DMA [OBS-5]",
-  "WRITE DMA QUEUED",
-  "CFA WRITE MULTIPLE WITHOUT ERASE",
-  "WRITE MULTIPLE FUA EXT",
-  cmd_reserved,
-/*-------------------------------------------------- D0h-DFh -----*/
-  cmd_reserved,
-  "CHECK MEDIA CARD TYPE",
-  cmd_reserved_mcpt,
-  cmd_reserved_mcpt,
-  cmd_reserved_mcpt,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  cmd_reserved,
-  "GET MEDIA STATUS",
-  "ACKNOWLEDGE MEDIA CHANGE [RET-4]",
-  "BOOT POST-BOOT [RET-4]",
-  "BOOT PRE-BOOT [RET-4]",
-  "MEDIA LOCK",
-  "MEDIA UNLOCK",
-/*-------------------------------------------------- E0h-EFh -----*/
-  "STANDBY IMMEDIATE",
-  "IDLE IMMEDIATE",
-  "STANDBY",
-  "IDLE",
-  "READ BUFFER",
-  "CHECK POWER MODE",
-  "SLEEP",
-  "FLUSH CACHE",
-  "WRITE BUFFER",
-  "WRITE SAME [RET-4]",  /* Warning!  This command is retired but the value of
-                            f_reg is used in look_up_ata_command().  If this
-                            command code is reclaimed in a future standard then
-                            be sure to update look_up_ata_command(). */
-  "FLUSH CACHE EXIT",
-  cmd_reserved,
-  "IDENTIFY DEVICE",
-  "MEDIA EJECT",
-  "IDENTIFY DEVICE DMA [OBS-4]",
-  "SET FEATURES",
-/*-------------------------------------------------- F0h-FFh -----*/
-  cmd_vendor_specific,
-  "SECURITY SET PASSWORD",
-  "SECURITY UNLOCK",
-  "SECURITY ERASE PREPARE",
-  "SECURITY ERASE UNIT",
-  "SECURITY FREEZE LOCK",
-  "SECURITY DISABLE PASSWORD",
-  cmd_vendor_specific,
-  "READ NATIVE MAX ADDRESS",
-  "SET MAX",
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific,
-  cmd_vendor_specific
-};
-
-/* Returns the name of the command (and possibly sub-command) with the given
-   command code and feature register values.   For most command codes this
-   simply returns the corresponding entry in the command_table array, but for
-   others the value of the feature register specifies a subcommand or
-   distinguishes commands. */
-const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg) {
-
-  // check that command table not messed up.  The compiler will issue
-  // warnings if there are too many array elements, but won't issue
-  // warnings if there are not enough of them.
-  if (sizeof(command_table) != sizeof(char *)*COMMAND_TABLE_SIZE){
-    fprintf(stderr, 
-            "Problem in atacmdnames.c.  Command Table command_table[] does\n"
-            "not have %d entries!  It has %d entries. Please fix it.\n",
-            COMMAND_TABLE_SIZE, (int)(sizeof(command_table)/sizeof(char *)));
-    abort();
-  }
-
-  switch (c_code) {
-  case 0x00:  /* NOP */
-    switch (f_reg) {
-    case 0x00:
-      return "NOP [Abort queued commands]";
-    case 0x01:
-      return "NOP [Don't abort queued commands]";
-    default:
-      return "NOP [Reserved subcommand]";
-    }
-  case 0x92:  /* DOWNLOAD MICROCODE */
-    switch (f_reg) {
-    case 0x01:
-      return "DOWNLOAD MICROCODE [Temporary]";
-    case 0x07:
-      return "DOWNLOAD MICROCODE [Save]";
-    default:
-      return "DOWNLOAD MICROCODE [Reserved subcommand]";
-    }
-  case 0xB0:  /* SMART */
-    switch (f_reg) {
-    case 0xD0:
-      return "SMART READ DATA";
-    case 0xD1:
-      return "SMART READ ATTRIBUTE THRESHOLDS [OBS-4]";
-    case 0xD2:
-      return "SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE";
-    case 0xD3:
-      return "SMART SAVE ATTRIBUTE VALUES [OBS-6]";
-    case 0xD4:
-      return "SMART EXECUTE OFF-LINE IMMEDIATE";
-    case 0xD5:
-      return "SMART READ LOG";
-    case 0xD6:
-      return "SMART WRITE LOG";
-    case 0xD7:
-      return "SMART WRITE ATTRIBUTE THRESHOLDS [NS, OBS-4]";
-    case 0xD8:
-      return "SMART ENABLE OPERATIONS";
-    case 0xD9:
-      return "SMART DISABLE OPERATIONS";
-    case 0xDA:
-      return "SMART RETURN STATUS";
-    case 0xDB:
-      return "SMART EN/DISABLE AUTO OFFLINE [NS (SFF-8035i)]";
-    default:
-        if (f_reg >= 0xE0)
-          return "[Vendor specific SMART command]";
-        else
-          return "[Reserved SMART command]";
-    }
-  case 0xB1:  /* DEVICE CONFIGURATION */
-    switch (f_reg) {
-    case 0xC0:
-      return "DEVICE CONFIGURATION RESTORE";
-    case 0xC1:
-      return "DEVICE CONFIGURATION FREEZE LOCK";
-    case 0xC2:
-      return "DEVICE CONFIGURATION IDENTIFY";
-    case 0xC3:
-      return "DEVICE CONFIGURATION SET";
-    default:
-      return "DEVICE CONFIGURATION [Reserved command]";
-    }
-  case 0xE9:  /* WRITE SAME */
-    switch (f_reg) {
-    case 0x22:
-      return "WRITE SAME [Start specified] [RET-4]";
-    case 0xDD:
-      return "WRITE SAME [Start unspecified] [RET-4]";
-    default:
-      return "WRITE SAME [Invalid subcommand] [RET-4]";
-    } 
-  case 0xEF:  /* SET FEATURES */
-    switch (f_reg) {
-    case 0x01:
-      return "SET FEATURES [Enable 8-bit PIO]";
-    case 0x02:
-      return "SET FEATURES [Enable write cache]";
-    case 0x03:
-      return "SET FEATURES [Set transfer mode]";
-    case 0x04:
-      return "SET FEATURES [Enable auto DR] [OBS-4]";
-    case 0x05:
-      return "SET FEATURES [Enable APM]";
-    case 0x06:
-      return "SET FEATURES [Enable Pwr-Up In Standby]";
-    case 0x07:
-      return "SET FEATURES [Set device spin-up]";
-    case 0x09:
-      return "SET FEATURES [Reserved (address offset)]";
-    case 0x0A:
-      return "SET FEATURES [Enable CFA power mode 1]";
-    case 0x10:
-      return "SET FEATURES [Reserved for Serial ATA]";
-    case 0x20:
-      return "SET FEATURES [Set Time-ltd R/W WCT]";
-    case 0x21:
-      return "SET FEATURES [Set Time-ltd R/W EH]";
-    case 0x31:
-      return "SET FEATURES [Disable Media Status Notf]";
-    case 0x33:
-      return "SET FEATURES [Disable retry] [OBS-4]";
-    case 0x42:
-      return "SET FEATURES [Enable AAM]";
-    case 0x43:
-      return "SET FEATURES [Set Max Host I/F S Times]";
-    case 0x44:
-      return "SET FEATURES [Length of VS data] [OBS-4]";
-    case 0x54:
-      return "SET FEATURES [Set cache segs] [OBS-4]";
-    case 0x55:
-      return "SET FEATURES [Disable read look-ahead]";
-    case 0x5D:
-      return "SET FEATURES [Enable release interrupt]";
-    case 0x5E:
-      return "SET FEATURES [Enable SERVICE interrupt]";
-    case 0x66:
-      return "SET FEATURES [Disable revert defaults]";
-    case 0x77:
-      return "SET FEATURES [Disable ECC] [OBS-4]";
-    case 0x81:
-      return "SET FEATURES [Disable 8-bit PIO]";
-    case 0x82:
-      return "SET FEATURES [Disable write cache]";
-    case 0x84:
-      return "SET FEATURES [Disable auto DR] [OBS-4]";
-    case 0x85:
-      return "SET FEATURES [Disable APM]";
-    case 0x86:
-      return "SET FEATURES [Disable Pwr-Up In Standby]";
-    case 0x88:
-      return "SET FEATURES [Disable ECC] [OBS-4]";
-    case 0x89:
-      return "SET FEATURES [Reserved (address offset)]";
-    case 0x8A:
-      return "SET FEATURES [Disable CFA power mode 1]";
-    case 0x90:
-      return "SET FEATURES [Reserved for Serial ATA]";
-    case 0x95:
-      return "SET FEATURES [Enable Media Status Notf]";
-    case 0x99:
-      return "SET FEATURES [Enable retries] [OBS-4]";
-    case 0x9A:
-      return "SET FEATURES [Set max avg curr] [OBS-4]";
-    case 0xAA:
-      return "SET FEATURES [Enable read look-ahead]";
-    case 0xAB:
-      return "SET FEATURES [Set max prefetch] [OBS-4]";
-    case 0xBB:
-      return "SET FEATURES [4 bytes VS data] [OBS-4]";
-    case 0xC2:
-      return "SET FEATURES [Disable AAM]";
-    case 0xCC:
-      return "SET FEATURES [Enable revert to defaults]";
-    case 0xDD:
-      return "SET FEATURES [Disable release interrupt]";
-    case 0xDE:
-      return "SET FEATURES [Disable SERVICE interrupt]";
-    case 0xE0:
-      return "SET FEATURES [Obsolete subcommand]";
-    default:
-      if (f_reg >= 0xF0)
-        return "SET FEATURES [Reserved for CFA]";
-      else
-        return "SET FEATURES [Reserved subcommand]";
-    }
-  case 0xF9:  /* SET MAX */
-    switch (f_reg) {
-    case 0x00:
-      return "SET MAX ADDRESS [OBS-6]";
-    case 0x01:
-      return "SET MAX SET PASSWORD";
-    case 0x02:
-      return "SET MAX LOCK";
-    case 0x03:
-      return "SET MAX UNLOCK";
-    case 0x04:
-      return "SET MAX FREEZE LOCK";
-    default:
-      return "[Reserved SET MAX command]";
-    }
-  default:
-    return command_table[c_code];
-  }
-}
diff --git a/atacmdnames.cpp b/atacmdnames.cpp
new file mode 100644 (file)
index 0000000..0504e34
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * atacmdnames.cpp
+ *
+ * This module is based on the T13/1532D Volume 1 Revision 3 (ATA/ATAPI-7)
+ * specification, which is available from http://www.t13.org/#FTP_site
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ * Address of support mailing list: smartmontools-support@lists.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Philip Williams
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "atacmdnames.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#define COMMAND_TABLE_SIZE 256
+
+const char *atacmdnames_c_cvsid="$Id: atacmdnames.cpp,v 1.14 2006/08/09 20:40:19 chrfranke Exp $" ATACMDNAMES_H_CVSID;
+
+const char cmd_reserved[]        = "[RESERVED]";
+const char cmd_vendor_specific[] = "[VENDOR SPECIFIC]";
+const char cmd_reserved_sa[]     = "[RESERVED FOR SERIAL ATA]";
+const char cmd_reserved_cf[]     = "[RESERVED FOR COMPACTFLASH ASSOCIATION]";
+const char cmd_reserved_mcpt[]   = "[RESERVED FOR MEDIA CARD PASS THROUGH]";
+const char cmd_recalibrate_ret4[]= "RECALIBRATE [RET-4]";
+const char cmd_seek_ret4[]       = "SEEK [RET-4]";
+
+const char *command_table[COMMAND_TABLE_SIZE] = {
+/*-------------------------------------------------- 00h-0Fh -----*/
+  "NOP",
+  cmd_reserved,
+  cmd_reserved,
+  "CFA REQUEST EXTENDED ERROR CODE",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  "DEVICE RESET",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- 10h-1Fh -----*/
+  "RECALIBRATE [OBS-4]",
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+/*-------------------------------------------------- 20h-2Fh -----*/
+  "READ SECTOR(S)",
+  "READ SECTOR(S) [OBS-5]",
+  "READ LONG (w/ retry) [OBS-4]",
+  "READ LONG (w/o retry) [OBS-4]",
+  "READ SECTOR(S) EXT",
+  "READ DMA EXT",
+  "READ DMA QUEUED EXT",
+  "READ NATIVE MAX ADDRESS EXT",
+  cmd_reserved,
+  "READ MULTIPLE EXT",
+  "READ STREAM DMA",
+  "READ STREAM PIO",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  "READ LOG EXT",
+/*-------------------------------------------------- 30h-3Fh -----*/
+  "WRITE SECTOR(S)",
+  "WRITE SECTOR(S) [OBS-5]",
+  "WRITE LONG(w/ retry) [OBS-4]",
+  "WRITE LONG(w/o retry) [OBS-4]",
+  "WRITE SECTORS(S) EXT",
+  "WRITE DMA EXT",
+  "WRITE DMA QUEUED EXT",
+  "SET MAX ADDRESS EXT",
+  "CFA WRITE SECTORS WITHOUT ERASE",
+  "WRITE MULTIPLE EXT",
+  "WRITE STREAM DMA",
+  "WRITE STREAM PIO",
+  "WRITE VERIFY [OBS-4]",
+  "WRITE DMA FUA EXT",
+  "WRITE DMA QUEUED FUA EXT",
+  "WRITE LOG EXT",
+/*-------------------------------------------------- 40h-4Fh -----*/
+  "READ VERIFY SECTOR(S)",
+  "READ VERIFY SECTOR(S) [OBS-5]",
+  "READ VERIFY SECTOR(S) EXT",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- 50h-5Fh -----*/
+  "FORMAT TRACK [OBS-4]",
+  "CONFIGURE STREAM",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- 60h-6Fh -----*/
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- 70h-7Fh -----*/
+  "SEEK [OBS-7]",
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+/*-------------------------------------------------- 80h-8Fh -----*/
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  "CFA TRANSLATE SECTOR [VS IF NO CFA]",
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+/*-------------------------------------------------- 90h-9Fh -----*/
+  "EXECUTE DEVICE DIAGNOSTIC",
+  "INITIALIZE DEVICE PARAMETERS [OBS-6]",
+  "DOWNLOAD MICROCODE",
+  cmd_reserved,
+  "STANDBY IMMEDIATE [RET-4]",
+  "IDLE IMMEDIATE [RET-4]",
+  "STANDBY [RET-4]",
+  "IDLE [RET-4]",
+  "CHECK POWER MODE [RET-4]",
+  "SLEEP [RET-4]",
+  cmd_vendor_specific,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- A0h-AFh -----*/
+  "PACKET",
+  "IDENTIFY PACKET DEVICE",
+  "SERVICE",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- B0h-BFh -----*/
+  "SMART",
+  "DEVICE CONFIGURATION",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+/*-------------------------------------------------- C0h-CFh -----*/
+  "CFA ERASE SECTORS [VS IF NO CFA]",
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  "READ MULTIPLE",
+  "WRITE MULTIPLE",
+  "SET MULTIPLE MODE",
+  "READ DMA QUEUED",
+  "READ DMA",
+  "READ DMA [OBS-5]",
+  "WRITE DMA",
+  "WRITE DMA [OBS-5]",
+  "WRITE DMA QUEUED",
+  "CFA WRITE MULTIPLE WITHOUT ERASE",
+  "WRITE MULTIPLE FUA EXT",
+  cmd_reserved,
+/*-------------------------------------------------- D0h-DFh -----*/
+  cmd_reserved,
+  "CHECK MEDIA CARD TYPE",
+  cmd_reserved_mcpt,
+  cmd_reserved_mcpt,
+  cmd_reserved_mcpt,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  "GET MEDIA STATUS",
+  "ACKNOWLEDGE MEDIA CHANGE [RET-4]",
+  "BOOT POST-BOOT [RET-4]",
+  "BOOT PRE-BOOT [RET-4]",
+  "MEDIA LOCK",
+  "MEDIA UNLOCK",
+/*-------------------------------------------------- E0h-EFh -----*/
+  "STANDBY IMMEDIATE",
+  "IDLE IMMEDIATE",
+  "STANDBY",
+  "IDLE",
+  "READ BUFFER",
+  "CHECK POWER MODE",
+  "SLEEP",
+  "FLUSH CACHE",
+  "WRITE BUFFER",
+  "WRITE SAME [RET-4]",  /* Warning!  This command is retired but the value of
+                            f_reg is used in look_up_ata_command().  If this
+                            command code is reclaimed in a future standard then
+                            be sure to update look_up_ata_command(). */
+  "FLUSH CACHE EXIT",
+  cmd_reserved,
+  "IDENTIFY DEVICE",
+  "MEDIA EJECT",
+  "IDENTIFY DEVICE DMA [OBS-4]",
+  "SET FEATURES",
+/*-------------------------------------------------- F0h-FFh -----*/
+  cmd_vendor_specific,
+  "SECURITY SET PASSWORD",
+  "SECURITY UNLOCK",
+  "SECURITY ERASE PREPARE",
+  "SECURITY ERASE UNIT",
+  "SECURITY FREEZE LOCK",
+  "SECURITY DISABLE PASSWORD",
+  cmd_vendor_specific,
+  "READ NATIVE MAX ADDRESS",
+  "SET MAX",
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific
+};
+
+/* Returns the name of the command (and possibly sub-command) with the given
+   command code and feature register values.   For most command codes this
+   simply returns the corresponding entry in the command_table array, but for
+   others the value of the feature register specifies a subcommand or
+   distinguishes commands. */
+const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg) {
+
+  // check that command table not messed up.  The compiler will issue
+  // warnings if there are too many array elements, but won't issue
+  // warnings if there are not enough of them.
+  if (sizeof(command_table) != sizeof(char *)*COMMAND_TABLE_SIZE){
+    fprintf(stderr, 
+            "Problem in atacmdnames.c.  Command Table command_table[] does\n"
+            "not have %d entries!  It has %d entries. Please fix it.\n",
+            COMMAND_TABLE_SIZE, (int)(sizeof(command_table)/sizeof(char *)));
+    abort();
+  }
+
+  switch (c_code) {
+  case 0x00:  /* NOP */
+    switch (f_reg) {
+    case 0x00:
+      return "NOP [Abort queued commands]";
+    case 0x01:
+      return "NOP [Don't abort queued commands]";
+    default:
+      return "NOP [Reserved subcommand]";
+    }
+  case 0x92:  /* DOWNLOAD MICROCODE */
+    switch (f_reg) {
+    case 0x01:
+      return "DOWNLOAD MICROCODE [Temporary]";
+    case 0x07:
+      return "DOWNLOAD MICROCODE [Save]";
+    default:
+      return "DOWNLOAD MICROCODE [Reserved subcommand]";
+    }
+  case 0xB0:  /* SMART */
+    switch (f_reg) {
+    case 0xD0:
+      return "SMART READ DATA";
+    case 0xD1:
+      return "SMART READ ATTRIBUTE THRESHOLDS [OBS-4]";
+    case 0xD2:
+      return "SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE";
+    case 0xD3:
+      return "SMART SAVE ATTRIBUTE VALUES [OBS-6]";
+    case 0xD4:
+      return "SMART EXECUTE OFF-LINE IMMEDIATE";
+    case 0xD5:
+      return "SMART READ LOG";
+    case 0xD6:
+      return "SMART WRITE LOG";
+    case 0xD7:
+      return "SMART WRITE ATTRIBUTE THRESHOLDS [NS, OBS-4]";
+    case 0xD8:
+      return "SMART ENABLE OPERATIONS";
+    case 0xD9:
+      return "SMART DISABLE OPERATIONS";
+    case 0xDA:
+      return "SMART RETURN STATUS";
+    case 0xDB:
+      return "SMART EN/DISABLE AUTO OFFLINE [NS (SFF-8035i)]";
+    default:
+        if (f_reg >= 0xE0)
+          return "[Vendor specific SMART command]";
+        else
+          return "[Reserved SMART command]";
+    }
+  case 0xB1:  /* DEVICE CONFIGURATION */
+    switch (f_reg) {
+    case 0xC0:
+      return "DEVICE CONFIGURATION RESTORE";
+    case 0xC1:
+      return "DEVICE CONFIGURATION FREEZE LOCK";
+    case 0xC2:
+      return "DEVICE CONFIGURATION IDENTIFY";
+    case 0xC3:
+      return "DEVICE CONFIGURATION SET";
+    default:
+      return "DEVICE CONFIGURATION [Reserved command]";
+    }
+  case 0xE9:  /* WRITE SAME */
+    switch (f_reg) {
+    case 0x22:
+      return "WRITE SAME [Start specified] [RET-4]";
+    case 0xDD:
+      return "WRITE SAME [Start unspecified] [RET-4]";
+    default:
+      return "WRITE SAME [Invalid subcommand] [RET-4]";
+    } 
+  case 0xEF:  /* SET FEATURES */
+    switch (f_reg) {
+    case 0x01:
+      return "SET FEATURES [Enable 8-bit PIO]";
+    case 0x02:
+      return "SET FEATURES [Enable write cache]";
+    case 0x03:
+      return "SET FEATURES [Set transfer mode]";
+    case 0x04:
+      return "SET FEATURES [Enable auto DR] [OBS-4]";
+    case 0x05:
+      return "SET FEATURES [Enable APM]";
+    case 0x06:
+      return "SET FEATURES [Enable Pwr-Up In Standby]";
+    case 0x07:
+      return "SET FEATURES [Set device spin-up]";
+    case 0x09:
+      return "SET FEATURES [Reserved (address offset)]";
+    case 0x0A:
+      return "SET FEATURES [Enable CFA power mode 1]";
+    case 0x10:
+      return "SET FEATURES [Reserved for Serial ATA]";
+    case 0x20:
+      return "SET FEATURES [Set Time-ltd R/W WCT]";
+    case 0x21:
+      return "SET FEATURES [Set Time-ltd R/W EH]";
+    case 0x31:
+      return "SET FEATURES [Disable Media Status Notf]";
+    case 0x33:
+      return "SET FEATURES [Disable retry] [OBS-4]";
+    case 0x42:
+      return "SET FEATURES [Enable AAM]";
+    case 0x43:
+      return "SET FEATURES [Set Max Host I/F S Times]";
+    case 0x44:
+      return "SET FEATURES [Length of VS data] [OBS-4]";
+    case 0x54:
+      return "SET FEATURES [Set cache segs] [OBS-4]";
+    case 0x55:
+      return "SET FEATURES [Disable read look-ahead]";
+    case 0x5D:
+      return "SET FEATURES [Enable release interrupt]";
+    case 0x5E:
+      return "SET FEATURES [Enable SERVICE interrupt]";
+    case 0x66:
+      return "SET FEATURES [Disable revert defaults]";
+    case 0x77:
+      return "SET FEATURES [Disable ECC] [OBS-4]";
+    case 0x81:
+      return "SET FEATURES [Disable 8-bit PIO]";
+    case 0x82:
+      return "SET FEATURES [Disable write cache]";
+    case 0x84:
+      return "SET FEATURES [Disable auto DR] [OBS-4]";
+    case 0x85:
+      return "SET FEATURES [Disable APM]";
+    case 0x86:
+      return "SET FEATURES [Disable Pwr-Up In Standby]";
+    case 0x88:
+      return "SET FEATURES [Disable ECC] [OBS-4]";
+    case 0x89:
+      return "SET FEATURES [Reserved (address offset)]";
+    case 0x8A:
+      return "SET FEATURES [Disable CFA power mode 1]";
+    case 0x90:
+      return "SET FEATURES [Reserved for Serial ATA]";
+    case 0x95:
+      return "SET FEATURES [Enable Media Status Notf]";
+    case 0x99:
+      return "SET FEATURES [Enable retries] [OBS-4]";
+    case 0x9A:
+      return "SET FEATURES [Set max avg curr] [OBS-4]";
+    case 0xAA:
+      return "SET FEATURES [Enable read look-ahead]";
+    case 0xAB:
+      return "SET FEATURES [Set max prefetch] [OBS-4]";
+    case 0xBB:
+      return "SET FEATURES [4 bytes VS data] [OBS-4]";
+    case 0xC2:
+      return "SET FEATURES [Disable AAM]";
+    case 0xCC:
+      return "SET FEATURES [Enable revert to defaults]";
+    case 0xDD:
+      return "SET FEATURES [Disable release interrupt]";
+    case 0xDE:
+      return "SET FEATURES [Disable SERVICE interrupt]";
+    case 0xE0:
+      return "SET FEATURES [Obsolete subcommand]";
+    default:
+      if (f_reg >= 0xF0)
+        return "SET FEATURES [Reserved for CFA]";
+      else
+        return "SET FEATURES [Reserved subcommand]";
+    }
+  case 0xF9:  /* SET MAX */
+    switch (f_reg) {
+    case 0x00:
+      return "SET MAX ADDRESS [OBS-6]";
+    case 0x01:
+      return "SET MAX SET PASSWORD";
+    case 0x02:
+      return "SET MAX LOCK";
+    case 0x03:
+      return "SET MAX UNLOCK";
+    case 0x04:
+      return "SET MAX FREEZE LOCK";
+    default:
+      return "[Reserved SET MAX command]";
+    }
+  default:
+    return command_table[c_code];
+  }
+}
diff --git a/atacmds.c b/atacmds.c
deleted file mode 100644 (file)
index 4a74f40..0000000
--- a/atacmds.c
+++ /dev/null
@@ -1,1887 +0,0 @@
-/*
- * atacmds.c
- * 
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
- * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- * 
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <ctype.h>
-
-#include "config.h"
-#include "int64.h"
-#include "atacmds.h"
-#include "extern.h"
-#include "utility.h"
-
-const char *atacmds_c_cvsid="$Id: atacmds.c,v 1.168 2006/04/12 17:01:46 ballen4705 Exp $"
-ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID UTILITY_H_CVSID;
-
-// to hold onto exit code for atexit routine
-extern int exitstatus;
-
-// for passing global control variables
-extern smartmonctrl *con;
-
-// These Drive Identity tables are taken from hdparm 5.2, and are also
-// given in the ATA/ATAPI specs for the IDENTIFY DEVICE command.  Note
-// that SMART was first added into the ATA/ATAPI-3 Standard with
-// Revision 3 of the document, July 25, 1995.  Look at the "Document
-// Status" revision commands at the beginning of
-// http://www.t13.org/project/d2008r6.pdf to see this.
-#define NOVAL_0                 0x0000
-#define NOVAL_1                 0xffff
-/* word 81: minor version number */
-#define MINOR_MAX 0x22
-const char *minor_str[] = {                     /* word 81 value: */
-  "Device does not report version",             /* 0x0000       */
-  "ATA-1 X3T9.2 781D prior to revision 4",      /* 0x0001       */
-  "ATA-1 published, ANSI X3.221-1994",          /* 0x0002       */
-  "ATA-1 X3T9.2 781D revision 4",               /* 0x0003       */
-  "ATA-2 published, ANSI X3.279-1996",          /* 0x0004       */
-  "ATA-2 X3T10 948D prior to revision 2k",      /* 0x0005       */
-  "ATA-3 X3T10 2008D revision 1",               /* 0x0006       */ /* SMART NOT INCLUDED */
-  "ATA-2 X3T10 948D revision 2k",               /* 0x0007       */
-  "ATA-3 X3T10 2008D revision 0",               /* 0x0008       */ 
-  "ATA-2 X3T10 948D revision 3",                /* 0x0009       */
-  "ATA-3 published, ANSI X3.298-199x",          /* 0x000a       */
-  "ATA-3 X3T10 2008D revision 6",               /* 0x000b       */ /* 1st VERSION WITH SMART */
-  "ATA-3 X3T13 2008D revision 7 and 7a",        /* 0x000c       */
-  "ATA/ATAPI-4 X3T13 1153D revision 6",         /* 0x000d       */
-  "ATA/ATAPI-4 T13 1153D revision 13",          /* 0x000e       */
-  "ATA/ATAPI-4 X3T13 1153D revision 7",         /* 0x000f       */
-  "ATA/ATAPI-4 T13 1153D revision 18",          /* 0x0010       */
-  "ATA/ATAPI-4 T13 1153D revision 15",          /* 0x0011       */
-  "ATA/ATAPI-4 published, ANSI NCITS 317-1998", /* 0x0012       */
-  "ATA/ATAPI-5 T13 1321D revision 3",           /* 0x0013       */
-  "ATA/ATAPI-4 T13 1153D revision 14",          /* 0x0014       */
-  "ATA/ATAPI-5 T13 1321D revision 1",           /* 0x0015       */
-  "ATA/ATAPI-5 published, ANSI NCITS 340-2000", /* 0x0016       */
-  "ATA/ATAPI-4 T13 1153D revision 17",          /* 0x0017       */
-  "ATA/ATAPI-6 T13 1410D revision 0",           /* 0x0018       */
-  "ATA/ATAPI-6 T13 1410D revision 3a",          /* 0x0019       */
-  "ATA/ATAPI-7 T13 1532D revision 1",           /* 0x001a       */
-  "ATA/ATAPI-6 T13 1410D revision 2",           /* 0x001b       */
-  "ATA/ATAPI-6 T13 1410D revision 1",           /* 0x001c       */
-  "reserved",                                   /* 0x001d       */
-  "ATA/ATAPI-7 T13 1532D revision 0",           /* 0x001e       */
-  "reserved",                                   /* 0x001f       */
-  "reserved",                                   /* 0x0020       */
-  "ATA/ATAPI-7 T13 1532D revision 4a",          /* 0x0021       */
-  "ATA/ATAPI-6 published, ANSI INCITS 361-2002" /* 0x0022       */
-};
-
-// NOTE ATA/ATAPI-4 REV 4 was the LAST revision where the device
-// attribute structures were NOT completely vendor specific.  So any
-// disk that is ATA/ATAPI-4 or above can not be trusted to show the
-// vendor values in sensible format.
-
-// Negative values below are because it doesn't support SMART
-const int actual_ver[] = { 
-  /* word 81 value: */
-  0,            /* 0x0000       WARNING:        */
-  1,            /* 0x0001       WARNING:        */
-  1,            /* 0x0002       WARNING:        */
-  1,            /* 0x0003       WARNING:        */
-  2,            /* 0x0004       WARNING:   This array           */
-  2,            /* 0x0005       WARNING:   corresponds          */
-  -3, /*<== */  /* 0x0006       WARNING:   *exactly*            */
-  2,            /* 0x0007       WARNING:   to the ATA/          */
-  -3, /*<== */  /* 0x0008       WARNING:   ATAPI version        */
-  2,            /* 0x0009       WARNING:   listed in            */
-  3,            /* 0x000a       WARNING:   the                  */
-  3,            /* 0x000b       WARNING:   minor_str            */
-  3,            /* 0x000c       WARNING:   array                */
-  4,            /* 0x000d       WARNING:   above.               */
-  4,            /* 0x000e       WARNING:                        */
-  4,            /* 0x000f       WARNING:   If you change        */
-  4,            /* 0x0010       WARNING:   that one,            */
-  4,            /* 0x0011       WARNING:   change this one      */
-  4,            /* 0x0012       WARNING:   too!!!               */
-  5,            /* 0x0013       WARNING:        */
-  4,            /* 0x0014       WARNING:        */
-  5,            /* 0x0015       WARNING:        */
-  5,            /* 0x0016       WARNING:        */
-  4,            /* 0x0017       WARNING:        */
-  6,            /* 0x0018       WARNING:        */
-  6,            /* 0x0019       WARNING:        */
-  7,            /* 0x001a       WARNING:        */
-  6,            /* 0x001b       WARNING:        */
-  6,            /* 0x001c       WARNING:        */
-  0,            /* 0x001d       WARNING:        */
-  7,            /* 0x001e       WARNING:        */
-  0,            /* 0x001f       WARNING:        */
-  0,            /* 0x0020       WARNING:        */
-  7,            /* 0x0021       WARNING:        */
-  6             /* 0x0022       WARNING:        */
-};
-
-// When you add additional items to this list, you should then:
-// 0 -- update this list
-// 1 -- modify the following function parse_attribute_def()
-// 2 -- if needed, modify ataPrintSmartAttribRawValue()
-// 3 -  if needed, modify ataPrintSmartAttribName()
-// 4 -- add #define PRESET_N_DESCRIPTION at top of knowndrives.c
-// 5 -- add drive in question into knowndrives[] table in knowndrives.c
-// 6 -- update smartctl.8
-// 7 -- update smartd.8
-// 8 -- do "make smartd.conf.5" to update smartd.conf.5
-// 9 -- update CHANGELOG file
-const char *vendorattributeargs[] = {
-  // 0  defs[9]=1
-  "9,minutes",
-  // 1  defs[9]=3
-  "9,seconds",
-  // 2  defs[9]=2
-  "9,temp",
-  // 3  defs[220]=1
-  "220,temp",
-  // 4  defs[*]=253
-  "N,raw8",
-  // 5  defs[*]=254
-  "N,raw16",
-  // 6  defs[*]=255
-  "N,raw48",
-  // 7  defs[200]=1
-  "200,writeerrorcount",
-  // 8  defs[9]=4
-  "9,halfminutes",
-  // 9  defs[194]=1
-  "194,10xCelsius",
-  // 10 defs[194]=2
-  "194,unknown",
-  // 11 defs[193]=1
-  "193,loadunload",
-  // 12 defs[201]=1
-  "201,detectedtacount",
-  // 13 defs[192]=1
-  "192,emergencyretractcyclect",
-  // 14 defs[198]=1
-  "198,offlinescanuncsectorct",
-  // NULL should always terminate the array
-  NULL
-};
-
-// This are the meanings of the Self-test failure checkpoint byte.
-// This is in the self-test log at offset 4 bytes into the self-test
-// descriptor and in the SMART READ DATA structure at byte offset
-// 371. These codes are not well documented.  The meanings returned by
-// this routine are used (at least) by Maxtor and IBM. Returns NULL if
-// not recognized.  Currently the maximum length is 15 bytes.
-const char *SelfTestFailureCodeName(unsigned char which){
-  
-  switch (which) {
-  case 0:
-    return "Write_Test";
-  case 1:
-    return "Servo_Basic";
-  case 2:
-    return "Servo_Random";
-  case 3:
-    return "G-list_Scan";
-  case 4:
-    return "Handling_Damage";
-  case 5:
-    return "Read_Scan";
-  default:
-    return NULL;
-  }
-}
-
-// This is a utility function for parsing pairs like "9,minutes" or
-// "220,temp", and putting the correct flag into the attributedefs
-// array.  Returns 1 if problem, 0 if pair has been recongized.
-int parse_attribute_def(char *pair, unsigned char **defsptr){
-  int i,j;
-  char temp[32];
-  unsigned char *defs;
-
-  // If array does not exist, allocate it
-  if (!*defsptr && !(*defsptr=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM, 1))){
-    pout("Out of memory in parse_attribute_def\n");
-    EXIT(1);
-  }
-
-  defs=*defsptr;
-
-  // look along list and see if we find the pair
-  for (i=0; vendorattributeargs[i] && strcmp(pair, vendorattributeargs[i]); i++);
-
-  switch (i) {
-  case 0:
-    // attribute 9 is power on time in minutes
-    defs[9]=1;
-    return 0;
-  case 1:
-    // attribute 9 is power-on-time in seconds
-    defs[9]=3;
-    return 0;
-  case 2:
-    // attribute 9 is temperature in celsius
-    defs[9]=2;
-    return 0;
-  case 3:
-    // attribute 220 is temperature in celsius
-    defs[220]=1;
-    return 0;
-  case 4:
-    // print all attributes in raw 8-bit form
-    for (j=0; j<MAX_ATTRIBUTE_NUM; j++)
-      defs[j]=253;
-    return 0;
-  case 5:
-    // print all attributes in raw 16-bit form
-    for (j=0; j<MAX_ATTRIBUTE_NUM; j++)
-      defs[j]=254;
-    return 0;
-  case 6:
-    // print all attributes in raw 48-bit form
-    for (j=0; j<MAX_ATTRIBUTE_NUM; j++)
-      defs[j]=255;
-    return 0;
-  case 7:
-    // attribute 200 is write error count
-    defs[200]=1;
-    return 0;
-  case 8:
-    // attribute 9 increments once every 30 seconds (power on time
-    // measure)
-    defs[9]=4;
-    return 0;
-  case 9:
-    // attribute 194 is ten times disk temp in Celsius
-    defs[194]=1;
-    return 0;
-  case 10:
-    // attribute 194 is unknown
-    defs[194]=2;
-    return 0;
-  case 11:
-    // Hitachi : Attributes 193 has 2 values : 1 load, 1 normal unload
-    defs[193]=1;
-    return 0;
-  case 12:
-    // Fujitsu
-    defs[201]=1;
-    return 0;
-  case 13:
-    // Fujitsu
-    defs[192]=1;
-    return 0;
-  case 14:
-    // Fujitsu
-    defs[198]=1;
-    return 0;
-  default:
-    // pair not found
-    break;
-  }
-  // At this point, either the pair was not found, or it is of the
-  // form N,uninterpreted, in which case we need to parse N
-  j=sscanf(pair,"%d,%14s", &i, temp);
-  // if no match to pattern, unrecognized
-  if (j!=2 || i<0 || i >255)
-    return 1;
-
-  // check for recognized strings
-  if (!strcmp(temp, "raw8")) {
-    defs[i]=253;
-    return 0;
-  }
-  
-  // check for recognized strings
-  if (!strcmp(temp, "raw16")) {
-    defs[i]=254;
-    return 0;
-  }
-  
-  // check for recognized strings
-  if (!strcmp(temp, "raw48")) {
-    defs[i]=255;
-    return 0;
-  }
-  // didn't recognize the string
-  return 1;
-}
-
-// Structure used in sorting the array vendorattributeargs[].
-typedef struct vaa_pair_s {
-  const char *vaa;
-  const char *padded_vaa;
-} vaa_pair;
-
-// Returns a copy of s with all numbers of less than three digits padded with
-// leading zeros.  Returns NULL if there isn't enough memory available.  The
-// memory for the string is dynamically allocated and should be freed by the
-// caller.
-char *pad_numbers(const char *s)
-{
-  char c, *t, *u;
-  const char *r;
-  int i, len, ndigits = 0;
-
-  // Allocate the maximum possible amount of memory needed.
-  if (!(t = (char *)malloc(strlen(s)*2+2)))
-    return NULL;
-
-  // Copy the string s to t, padding any numbers of less than three digits
-  // with leading zeros.  The string is copied backwards to simplify the code.
-  r = s + strlen(s);
-  u = t;
-  while (( r-- >= s)) {
-    if (isdigit((int)*r))
-      ndigits++;
-    else if (ndigits > 0) {
-      while (ndigits++ < 3)
-        *u++ = '0';
-      ndigits = 0;
-    }
-    *u++ = *r;
-  }
-  *u = '\0';
-
-  // Reverse the string in t.
-  len = strlen(t);
-  for (i = 0; i < len/2; i++) {
-    c          = t[i];
-    t[i]       = t[len-1-i];
-    t[len-1-i] = c;
-  }
-
-  return t;
-}
-
-// Comparison function for qsort().  Used by sort_vendorattributeargs().
-int compare_vaa_pairs(const void *a, const void *b)
-{
-  vaa_pair *p = (vaa_pair *)a;
-  vaa_pair *q = (vaa_pair *)b;
-
-  return strcmp(p->padded_vaa, q->padded_vaa);
-}
-
-// Returns a sorted list of vendorattributeargs or NULL if there is not enough
-// memory available.  The memory for the list is allocated dynamically and
-// should be freed by the caller.
-// To perform the sort, any numbers in the strings are padded out to three
-// digits by adding leading zeros.  For example,
-//
-//    "9,minutes"  becomes  "009,minutes"
-//    "N,raw16"    becomes  "N,raw016"
-//
-// and the original strings are paired with the padded strings.  The list of
-// pairs is then sorted by comparing the padded strings (using strcmp) and the
-// result is then the list of unpadded strings.
-//
-const char **sort_vendorattributeargs(void) {
-  const char **ps, **sorted_list = NULL;
-  vaa_pair *pairs, *pp;
-  int count, i;
-
-  // Figure out how many strings are in vendorattributeargs[] (not including
-  // the terminating NULL).
-  count = (sizeof vendorattributeargs) / sizeof(char *) - 1;
-
-  // Construct a list of pairs of strings from vendorattributeargs[] and their
-  // padded equivalents.
-  if (!(pairs = (vaa_pair *)malloc(sizeof(vaa_pair) * count)))
-    goto END;
-  for (ps = vendorattributeargs, pp = pairs; *ps; ps++, pp++) {
-    pp->vaa = *ps;
-    if (!(pp->padded_vaa = pad_numbers(*ps)))
-      goto END;
-  }
-
-  // Sort the array of vaa_pair structures by comparing the padded strings
-  // using strcmp().
-  qsort(pairs, count, sizeof(vaa_pair), compare_vaa_pairs);
-
-  // Construct the sorted list of strings.
-  if (!(sorted_list = (const char **)malloc(sizeof vendorattributeargs)))
-    goto END;
-  for (ps = sorted_list, pp = pairs, i = 0; i < count; ps++, pp++, i++)
-    *ps = pp->vaa;
-  *ps = NULL;
-
-END:
-  if (pairs) {
-    for (i = 0; i < count; i++)
-      if (pairs[i].padded_vaa)
-        free((void *)pairs[i].padded_vaa);
-    free((void *)pairs);
-  }
-
-  // If there was a problem creating the list then sorted_list should now
-  // contain NULL.
-  return sorted_list;
-}
-
-// Function to return a multiline string containing a list of the arguments in 
-// vendorattributeargs[].  The strings are preceeded by tabs and followed
-// (except for the last) by newlines.
-// This function allocates the required memory for the string and the caller
-// must use free() to free it.  It returns NULL if the required memory can't
-// be allocated.
-char *create_vendor_attribute_arg_list(void){
-  const char **ps, **sorted;
-  char *s;
-  int len;
-
-  // Get a sorted list of vendor attribute arguments.  If the sort fails
-  // (which should only happen if the system is really low on memory) then just
-  // use the unordered list.
-  if (!(sorted = (const char **) sort_vendorattributeargs()))
-    sorted = vendorattributeargs;
-
-  // Calculate the required number of characters
-  len = 1;                // At least one char ('\0')
-  for (ps = sorted; *ps != NULL; ps++) {
-    len += 1;             // For the tab
-    len += strlen(*ps);   // For the actual argument string
-    if (*(ps+1))
-      len++;              // For the newline if required
-  }
-
-  // Attempt to allocate memory for the string
-  if (!(s = (char *)malloc(len)))
-    return NULL;
-
-  // Construct the string
-  *s = '\0';
-  for (ps = sorted; *ps != NULL; ps++) {
-    strcat(s, "\t");
-    strcat(s, *ps);
-    if (*(ps+1))
-      strcat(s, "\n");
-  }
-
-  free((char **)sorted);
-
-  // Return a pointer to the string
-  return s;
-}
-
-// swap two bytes.  Point to low address
-void swap2(char *location){
-  char tmp=*location;
-  *location=*(location+1);
-  *(location+1)=tmp;
-  return;
-}
-
-// swap four bytes.  Point to low address
-void swap4(char *location){
-  char tmp=*location;
-  *location=*(location+3);
-  *(location+3)=tmp;
-  swap2(location+1);
-  return;
-}
-
-// swap eight bytes.  Points to low address
-void swap8(char *location){
-  char tmp=*location;
-  *location=*(location+7);
-  *(location+7)=tmp;
-  tmp=*(location+1);
-  *(location+1)=*(location+6);
-  *(location+6)=tmp;
-  swap4(location+2);
-  return;
-}
-
-static char *commandstrings[]={
-  "SMART ENABLE",
-  "SMART DISABLE",
-  "SMART AUTOMATIC ATTRIBUTE SAVE",
-  "SMART IMMEDIATE OFFLINE",
-  "SMART AUTO OFFLINE",
-  "SMART STATUS",
-  "SMART STATUS CHECK",
-  "SMART READ ATTRIBUTE VALUES",
-  "SMART READ ATTRIBUTE THRESHOLDS",
-  "SMART READ LOG",
-  "IDENTIFY DEVICE",
-  "IDENTIFY PACKET DEVICE",
-  "CHECK POWER MODE",
-  "SMART WRITE LOG",
-  "WARNING (UNDEFINED COMMAND -- CONTACT DEVELOPERS AT " PACKAGE_BUGREPORT ")\n"
-};
-
-void prettyprint(unsigned char *stuff, char *name){
-  int i,j;
-  pout("\n===== [%s] DATA START (BASE-16) =====\n", name);
-  for (i=0; i<32; i++){
-    pout("%03d-%03d: ", 16*i, 16*(i+1)-1);
-    for (j=0; j<15; j++)
-      pout("%02x ",*stuff++);
-    pout("%02x\n",*stuff++);
-  }
-  pout("===== [%s] DATA END (512 Bytes) =====\n\n", name);
-}
-
-// This function provides the pretty-print reporting for SMART
-// commands: it implements the various -r "reporting" options for ATA
-// ioctls.
-int smartcommandhandler(int device, smart_command_set command, int select, char *data){
-  int retval;
-
-  // This conditional is true for commands that return data
-  int getsdata=(command==PIDENTIFY || 
-                command==IDENTIFY || 
-                command==READ_LOG || 
-                command==READ_THRESHOLDS || 
-                command==READ_VALUES ||
-               command==CHECK_POWER_MODE);
-
-  int sendsdata=(command==WRITE_LOG);
-  
-  // If reporting is enabled, say what the command will be before it's executed
-  if (con->reportataioctl){
-          // conditional is true for commands that use parameters
-          int usesparam=(command==READ_LOG || 
-                         command==AUTO_OFFLINE || 
-                         command==AUTOSAVE || 
-                         command==IMMEDIATE_OFFLINE ||
-                         command==WRITE_LOG);
-                  
-    pout("\nREPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]);
-    if (usesparam)
-      pout(" InputParameter=%d\n", select);
-    else
-      pout("\n");
-  }
-  
-  if ((getsdata || sendsdata) && !data){
-    pout("REPORT-IOCTL: Unable to execute command %s : data destination address is NULL\n", commandstrings[command]);
-    return -1;
-  }
-  
-  // The reporting is cleaner, and we will find coding bugs faster, if
-  // the commands that failed clearly return empty (zeroed) data
-  // structures
-  if (getsdata) {
-    if (command==CHECK_POWER_MODE)
-      data[0]=0;
-    else
-      memset(data, '\0', 512);
-  }
-
-
-  // If reporting is enabled, say what input was sent to the command
-  if (con->reportataioctl && sendsdata){
-    pout("REPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]);
-    // if requested, pretty-print the output data structure
-    if (con->reportataioctl>1)
-      prettyprint((unsigned char *)data, commandstrings[command]);
-  }
-
-  // In case the command produces an error, we'll want to know what it is:
-  errno=0;
-  
-  // now execute the command
-  switch (con->controller_type) {
-  case CONTROLLER_3WARE_678K:
-  case CONTROLLER_3WARE_678K_CHAR:
-  case CONTROLLER_3WARE_9000_CHAR:
-    retval=escalade_command_interface(device, con->controller_port-1, con->controller_type, command, select, data);
-    if (retval &&  con->controller_port<=0)
-      pout("WARNING: apparently missing '-d 3ware,N' disk specification\n");
-    break;
-  case CONTROLLER_MARVELL_SATA:
-    retval=marvell_command_interface(device, command, select, data);
-    break;
-  default:
-    retval=ata_command_interface(device, command, select, data);
-  }
-
-  // If reporting is enabled, say what output was produced by the command
-  if (con->reportataioctl){
-    if (errno)
-      pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d errno=%d [%s]\n", 
-           device, commandstrings[command], retval, errno, strerror(errno));
-    else
-      pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d\n",
-           device, commandstrings[command], retval);
-    
-    // if requested, pretty-print the output data structure
-    if (con->reportataioctl>1 && getsdata) {
-      if (command==CHECK_POWER_MODE)
-       pout("Sector Count Register (BASE-16): %02x\n", (unsigned char)(*data));
-      else
-       prettyprint((unsigned char *)data, commandstrings[command]);
-    }
-  }
-  return retval;
-}
-
-
-// This function computes the checksum of a single disk sector (512
-// bytes).  Returns zero if checksum is OK, nonzero if the checksum is
-// incorrect.  The size (512) is correct for all SMART structures.
-unsigned char checksum(unsigned char *buffer){
-  unsigned char sum=0;
-  int i;
-  
-  for (i=0; i<512; i++)
-    sum+=buffer[i];
-
-  return sum;
-}
-
-// returns -1 if command fails or the device is in Sleep mode, else
-// value of Sector Count register.  Sector Count result values:
-//   00h device is in Standby mode. 
-//   80h device is in Idle mode.
-//   FFh device is in Active mode or Idle mode.
-
-int ataCheckPowerMode(int device) {
-  unsigned char result;
-
-  if ((smartcommandhandler(device, CHECK_POWER_MODE, 0, (char *)&result)))
-    return -1;
-
-  if (result!=0 && result!=0x80 && result!=0xff)
-    pout("ataCheckPowerMode(): ATA CHECK POWER MODE returned unknown Sector Count Register value %02x\n", result);
-
-  return (int)result;
-}
-
-
-
-
-// Reads current Device Identity info (512 bytes) into buf.  Returns 0
-// if all OK.  Returns -1 if no ATA Device identity can be
-// established.  Returns >0 if Device is ATA Packet Device (not SMART
-// capable).  The value of the integer helps identify the type of
-// Packet device, which is useful so that the user can connect the
-// formal device number with whatever object is inside their computer.
-int ataReadHDIdentity (int device, struct ata_identify_device *buf){
-  unsigned short *rawshort=(unsigned short *)buf;
-  unsigned char  *rawbyte =(unsigned char  *)buf;
-
-  // See if device responds either to IDENTIFY DEVICE or IDENTIFY
-  // PACKET DEVICE
-  if ((smartcommandhandler(device, IDENTIFY, 0, (char *)buf))){
-    if (smartcommandhandler(device, PIDENTIFY, 0, (char *)buf)){
-      return -1; 
-    }
-  }
-
-#ifndef __NetBSD__
-  // if machine is big-endian, swap byte order as needed
-  // (the NetBSD kernel does deliver the results in host byte order)
-  if (isbigendian()){
-    int i;
-    
-    // swap various capability words that are needed
-    for (i=0; i<33; i++)
-      swap2((char *)(buf->words047_079+i));
-    
-    for (i=80; i<=87; i++)
-      swap2((char *)(rawshort+i));
-    
-    for (i=0; i<168; i++)
-      swap2((char *)(buf->words088_255+i));
-  }
-#endif
-  
-  // If there is a checksum there, validate it
-  if ((rawshort[255] & 0x00ff) == 0x00a5 && checksum(rawbyte))
-    checksumwarning("Drive Identity Structure");
-  
-  // If this is a PACKET DEVICE, return device type
-  if (rawbyte[1] & 0x80)
-    return 1+(rawbyte[1] & 0x1f);
-  
-  // Not a PACKET DEVICE
-  return 0;
-}
-
-// Returns ATA version as an integer, and a pointer to a string
-// describing which revision.  Note that Revision 0 of ATA-3 does NOT
-// support SMART.  For this one case we return -3 rather than +3 as
-// the version number.  See notes above.
-int ataVersionInfo (const char** description, struct ata_identify_device *drive, unsigned short *minor){
-  unsigned short major;
-  int i;
-
-  // check that arrays at the top of this file are defined
-  // consistently
-  if (sizeof(minor_str) != sizeof(char *)*(1+MINOR_MAX)){
-    pout("Internal error in ataVersionInfo().  minor_str[] size %d\n"
-         "is not consistent with value of MINOR_MAX+1 = %d\n", 
-         (int)(sizeof(minor_str)/sizeof(char *)), MINOR_MAX+1);
-    fflush(NULL);
-    abort();
-  }
-  if (sizeof(actual_ver) != sizeof(int)*(1+MINOR_MAX)){
-    pout("Internal error in ataVersionInfo().  actual_ver[] size %d\n"
-         "is not consistent with value of MINOR_MAX = %d\n",
-         (int)(sizeof(actual_ver)/sizeof(int)), MINOR_MAX+1);
-    fflush(NULL);
-    abort();
-  }
-
-  // get major and minor ATA revision numbers
-  major=drive->major_rev_num;
-  *minor=drive->minor_rev_num;
-  
-  // First check if device has ANY ATA version information in it
-  if (major==NOVAL_0 || major==NOVAL_1) {
-    *description=NULL;
-    return -1;
-  }
-  
-  // The minor revision number has more information - try there first
-  if (*minor && (*minor<=MINOR_MAX)){
-    int std = actual_ver[*minor];
-    if (std) {
-      *description=minor_str[*minor];
-      return std;
-    }
-  }
-  
-  // HDPARM has a very complicated algorithm from here on. Since SMART only
-  // exists on ATA-3 and later standards, let's punt on this.  If you don't
-  // like it, please fix it.  The code's in CVS.
-  for (i=15; i>0; i--)
-    if (major & (0x1<<i))
-      break;
-  
-  *description=NULL; 
-  if (i==0)
-    return 1;
-  else
-    return i;
-}
-
-// returns 1 if SMART supported, 0 if SMART unsupported, -1 if can't tell
-int ataSmartSupport(struct ata_identify_device *drive){
-  unsigned short word82=drive->command_set_1;
-  unsigned short word83=drive->command_set_2;
-  
-  // check if words 82/83 contain valid info
-  if ((word83>>14) == 0x01)
-    // return value of SMART support bit 
-    return word82 & 0x0001;
-  
-  // since we can're rely on word 82, we don't know if SMART supported
-  return -1;
-}
-
-// returns 1 if SMART enabled, 0 if SMART disabled, -1 if can't tell
-int ataIsSmartEnabled(struct ata_identify_device *drive){
-  unsigned short word85=drive->cfs_enable_1;
-  unsigned short word87=drive->csf_default;
-  
-  // check if words 85/86/87 contain valid info
-  if ((word87>>14) == 0x01)
-    // return value of SMART enabled bit
-    return word85 & 0x0001;
-  
-  // Since we can't rely word85, we don't know if SMART is enabled.
-  return -1;
-}
-
-
-// Reads SMART attributes into *data
-int ataReadSmartValues(int device, struct ata_smart_values *data){      
-  
-  if (smartcommandhandler(device, READ_VALUES, 0, (char *)data)){
-    syserror("Error SMART Values Read failed");
-    return -1;
-  }
-
-  // compute checksum
-  if (checksum((unsigned char *)data))
-    checksumwarning("SMART Attribute Data Structure");
-  
-  // byte swap if needed
-  if (isbigendian()){
-    int i;
-    swap2((char *)&(data->revnumber));
-    swap2((char *)&(data->total_time_to_complete_off_line));
-    swap2((char *)&(data->smart_capability));
-    for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
-      struct ata_smart_attribute *x=data->vendor_attributes+i;
-      swap2((char *)&(x->flags));
-    }
-  }
-
-  return 0;
-}
-
-
-// This corrects some quantities that are byte reversed in the SMART
-// SELF TEST LOG
-void fixsamsungselftestlog(struct ata_smart_selftestlog *data){
-  int i;
-  
-  // bytes 508/509 (numbered from 0) swapped (swap of self-test index
-  // with one byte of reserved.
-  swap2((char *)&(data->mostrecenttest));
-
-  // LBA low register (here called 'selftestnumber", containing
-  // information about the TYPE of the self-test) is byte swapped with
-  // Self-test execution status byte.  These are bytes N, N+1 in the
-  // entries.
-  for (i=0; i<21; i++)
-    swap2((char *)&(data->selftest_struct[i].selftestnumber));
-
-  return;
-}
-
-// Reads the Self Test Log (log #6)
-int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){
-
-  // get data from device
-  if (smartcommandhandler(device, READ_LOG, 0x06, (char *)data)){
-    syserror("Error SMART Error Self-Test Log Read failed");
-    return -1;
-  }
-
-  // compute its checksum, and issue a warning if needed
-  if (checksum((unsigned char *)data))
-    checksumwarning("SMART Self-Test Log Structure");
-  
-  // fix firmware bugs in self-test log
-  if (con->fixfirmwarebug == FIX_SAMSUNG)
-    fixsamsungselftestlog(data);
-
-  // fix endian order, if needed
-  if (isbigendian()){
-    int i;
-    swap2((char*)&(data->revnumber));
-    for (i=0; i<21; i++){
-      struct ata_smart_selftestlog_struct *x=data->selftest_struct+i;
-      swap2((char *)&(x->timestamp));
-      swap4((char *)&(x->lbafirstfailure));
-    }
-  }
-
-  return 0;
-}
-
-
-// Reads the Log Directory (log #0).  Note: NO CHECKSUM!!
-int ataReadLogDirectory (int device, struct ata_smart_log_directory *data){     
-  
-  // get data from device
-  if (smartcommandhandler(device, READ_LOG, 0x00, (char *)data)){
-    return -1;
-  }
-
-  // swap endian order if needed
-  if (isbigendian()){
-    swap2((char *)&(data->logversion));
-  }
-  
-  return 0;
-}
-
-
-// Reads the selective self-test log (log #9)
-int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *data){  
-  
-  // get data from device
-  if (smartcommandhandler(device, READ_LOG, 0x09, (char *)data)){
-    syserror("Error SMART Read Selective Self-Test Log failed");
-    return -1;
-  }
-   
-  // compute its checksum, and issue a warning if needed
-  if (checksum((unsigned char *)data))
-    checksumwarning("SMART Selective Self-Test Log Structure");
-  
-  // swap endian order if needed
-  if (isbigendian()){
-    int i;
-    swap2((char *)&(data->logversion));
-    for (i=0;i<5;i++){
-      swap8((char *)&(data->span[i].start));
-      swap8((char *)&(data->span[i].end));
-    }
-    swap8((char *)&(data->currentlba));
-    swap2((char *)&(data->currentspan));
-    swap2((char *)&(data->flags));
-    swap2((char *)&(data->pendingtime));
-  }
-  
-  if (data->logversion != 1)
-    pout("SMART Selective Self-Test Log Data Structure Revision Number (%d) should be 1\n", data->logversion);
-  
-  return 0;
-}
-
-// Writes the selective self-test log (log #9)
-int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv){   
-  int i;
-  struct ata_selective_self_test_log sstlog, *data=&sstlog;
-  unsigned char cksum=0;
-  unsigned char *ptr=(unsigned char *)data;
-  
-  // Read log
-  if (ataReadSelectiveSelfTestLog(device, data)) {
-    pout("Since Read failed, will not attempt to WRITE Selective Self-test Log\n");
-    return -1;
-  }
-  
-  // Fix logversion if needed
-  if (data->logversion !=1) {
-    pout("Error SMART Selective Self-Test Log Data Structure Revision not recognized\n"
-        "Revision number should be 1 but is %d.  To be safe, aborting WRITE LOG\n", data->logversion);
-    return -2;
-  }
-
-  // Host is NOT allowed to write selective self-test log if a selective
-  // self-test is in progress.
-  if (0<data->currentspan && data->currentspan<6 && ((sv->self_test_exec_status)>>4)==15) {
-    pout("Error SMART Selective or other Self-Test in progress.\n");
-    return -4;
-  }
-  
-  // Clear spans
-  for (i=0; i<5; i++)
-    memset(data->span+i, 0, sizeof(struct test_span));
-  
-  // Set spans for testing 
-  for (i=0; i<con->smartselectivenumspans; i++){
-    data->span[i].start = con->smartselectivespan[i][0];
-    data->span[i].end   = con->smartselectivespan[i][1];
-  }
-
-  // host must initialize to zero before initiating selective self-test
-  data->currentlba=0;
-  data->currentspan=0;
-  
-  // Perform off-line scan after selective test?
-  if (1 == con->scanafterselect)
-    // NO
-    data->flags &= ~SELECTIVE_FLAG_DOSCAN;
-  else if (2 == con->scanafterselect)
-    // YES
-    data->flags |= SELECTIVE_FLAG_DOSCAN;
-  
-  // Must clear active and pending flags before writing
-  data->flags &= ~(SELECTIVE_FLAG_ACTIVE);  
-  data->flags &= ~(SELECTIVE_FLAG_PENDING);
-
-  // modify pending time?
-  if (con->pendingtime)
-    data->pendingtime=(unsigned short)(con->pendingtime-1);
-
-  // Set checksum to zero, then compute checksum
-  data->checksum=0;
-  for (i=0; i<512; i++)
-    cksum+=ptr[i];
-  cksum=~cksum;
-  cksum+=1;
-  data->checksum=cksum;
-
-    // swap endian order if needed
-  if (isbigendian()){
-    int i;
-    swap2((char *)&(data->logversion));
-    for (i=0;i<5;i++){
-      swap8((char *)&(data->span[i].start));
-      swap8((char *)&(data->span[i].end));
-    }
-    swap8((char *)&(data->currentlba));
-    swap2((char *)&(data->currentspan));
-    swap2((char *)&(data->flags));
-    swap2((char *)&(data->pendingtime));
-  }
-
-  // write new selective self-test log
-  if (smartcommandhandler(device, WRITE_LOG, 0x09, (char *)data)){
-    syserror("Error Write Selective Self-Test Log failed");
-    return -3;
-  }
-
-  return 0;
-}
-
-// This corrects some quantities that are byte reversed in the SMART
-// ATA ERROR LOG.
-void fixsamsungerrorlog(struct ata_smart_errorlog *data){
-  int i,j;
-  
-  // FIXED IN SAMSUNG -25 FIRMWARE???
-  // Device error count in bytes 452-3
-  swap2((char *)&(data->ata_error_count));
-  
-  // FIXED IN SAMSUNG -22a FIRMWARE
-  // step through 5 error log data structures
-  for (i=0; i<5; i++){
-    // step through 5 command data structures
-    for (j=0; j<5; j++)
-      // Command data structure 4-byte millisec timestamp.  These are
-      // bytes (N+8, N+9, N+10, N+11).
-      swap4((char *)&(data->errorlog_struct[i].commands[j].timestamp));
-    // Error data structure two-byte hour life timestamp.  These are
-    // bytes (N+28, N+29).
-    swap2((char *)&(data->errorlog_struct[i].error_struct.timestamp));
-  }
-  return;
-}
-
-// NEEDED ONLY FOR SAMSUNG -22 (some) -23 AND -24?? FIRMWARE
-void fixsamsungerrorlog2(struct ata_smart_errorlog *data){
-  // Device error count in bytes 452-3
-  swap2((char *)&(data->ata_error_count));
-  return;
-}
-
-// Reads the Summary SMART Error Log (log #1). The Comprehensive SMART
-// Error Log is #2, and the Extended Comprehensive SMART Error log is
-// #3
-int ataReadErrorLog (int device, struct ata_smart_errorlog *data){      
-  
-  // get data from device
-  if (smartcommandhandler(device, READ_LOG, 0x01, (char *)data)){
-    syserror("Error SMART Error Log Read failed");
-    return -1;
-  }
-  
-  // compute its checksum, and issue a warning if needed
-  if (checksum((unsigned char *)data))
-    checksumwarning("SMART ATA Error Log Structure");
-  
-  // Some disks have the byte order reversed in some SMART Summary
-  // Error log entries
-  if (con->fixfirmwarebug == FIX_SAMSUNG)
-    fixsamsungerrorlog(data);
-  else if (con->fixfirmwarebug == FIX_SAMSUNG2)
-    fixsamsungerrorlog2(data);
-
-  // Correct endian order if necessary
-  if (isbigendian()){
-    int i,j;
-    
-    // Device error count in bytes 452-3
-    swap2((char *)&(data->ata_error_count));
-    
-    // step through 5 error log data structures
-    for (i=0; i<5; i++){
-      // step through 5 command data structures
-      for (j=0; j<5; j++)
-        // Command data structure 4-byte millisec timestamp
-        swap4((char *)&(data->errorlog_struct[i].commands[j].timestamp));
-      // Error data structure life timestamp
-      swap2((char *)&(data->errorlog_struct[i].error_struct.timestamp));
-    }
-  }
-  
-  return 0;
-}
-
-int ataReadSmartThresholds (int device, struct ata_smart_thresholds_pvt *data){
-  
-  // get data from device
-  if (smartcommandhandler(device, READ_THRESHOLDS, 0, (char *)data)){
-    syserror("Error SMART Thresholds Read failed");
-    return -1;
-  }
-  
-  // compute its checksum, and issue a warning if needed
-  if (checksum((unsigned char *)data))
-    checksumwarning("SMART Attribute Thresholds Structure");
-  
-  // byte swap if needed
-  if (isbigendian())
-    swap2((char *)&(data->revnumber));
-
-  return 0;
-}
-
-int ataEnableSmart (int device ){       
-  if (smartcommandhandler(device, ENABLE, 0, NULL)){
-    syserror("Error SMART Enable failed");
-    return -1;
-  }
-  return 0;
-}
-
-int ataDisableSmart (int device ){      
-  
-  if (smartcommandhandler(device, DISABLE, 0, NULL)){
-    syserror("Error SMART Disable failed");
-    return -1;
-  }  
-  return 0;
-}
-
-int ataEnableAutoSave(int device){  
-  if (smartcommandhandler(device, AUTOSAVE, 241, NULL)){
-    syserror("Error SMART Enable Auto-save failed");
-    return -1;
-  }
-  return 0;
-}
-
-int ataDisableAutoSave(int device){
-  
-  if (smartcommandhandler(device, AUTOSAVE, 0, NULL)){
-    syserror("Error SMART Disable Auto-save failed");
-    return -1;
-  }
-  return 0;
-}
-
-// In *ALL* ATA standards the Enable/Disable AutoOffline command is
-// marked "OBSOLETE". It is defined in SFF-8035i Revision 2, and most
-// vendors still support it for backwards compatibility. IBM documents
-// it for some drives.
-int ataEnableAutoOffline (int device ){ 
-  
-  /* timer hard coded to 4 hours */  
-  if (smartcommandhandler(device, AUTO_OFFLINE, 248, NULL)){
-    syserror("Error SMART Enable Automatic Offline failed");
-    return -1;
-  }
-  return 0;
-}
-
-// Another Obsolete Command.  See comments directly above, associated
-// with the corresponding Enable command.
-int ataDisableAutoOffline (int device ){        
-  
-  if (smartcommandhandler(device, AUTO_OFFLINE, 0, NULL)){
-    syserror("Error SMART Disable Automatic Offline failed");
-    return -1;
-  }
-  return 0;
-}
-
-// If SMART is enabled, supported, and working, then this call is
-// guaranteed to return 1, else zero.  Note that it should return 1
-// regardless of whether the disk's SMART status is 'healthy' or
-// 'failing'.
-int ataDoesSmartWork(int device){
-  int retval=smartcommandhandler(device, STATUS, 0, NULL);
-
-  if (-1 == retval)
-    return 0;
-
-  return 1;
-}
-
-// This function uses a different interface (DRIVE_TASK) than the
-// other commands in this file.
-int ataSmartStatus2(int device){
-  return smartcommandhandler(device, STATUS_CHECK, 0, NULL);  
-}
-
-// This is the way to execute ALL tests: offline, short self-test,
-// extended self test, with and without captive mode, etc.
-int ataSmartTest(int device, int testtype, struct ata_smart_values *sv) {     
-  char cmdmsg[128],*type,*captive;
-  int errornum, cap, retval, select=0;
-
-  // Boolean, if set, says test is captive
-  cap=testtype & CAPTIVE_MASK;
-
-  // Set up strings that describe the type of test
-  if (cap)
-    captive="captive";
-  else
-    captive="off-line";
-  
-  if (testtype==OFFLINE_FULL_SCAN)
-    type="off-line";
-  else  if (testtype==SHORT_SELF_TEST || testtype==SHORT_CAPTIVE_SELF_TEST)
-    type="Short self-test";
-  else if (testtype==EXTEND_SELF_TEST || testtype==EXTEND_CAPTIVE_SELF_TEST)
-    type="Extended self-test";
-  else if (testtype==CONVEYANCE_SELF_TEST || testtype==CONVEYANCE_CAPTIVE_SELF_TEST)
-    type="Conveyance self-test";
-  else if ((select=(testtype==SELECTIVE_SELF_TEST || testtype==SELECTIVE_CAPTIVE_SELF_TEST)))
-    type="Selective self-test";
-  else
-    type="[Unrecognized] self-test";
-  
-  // If doing a selective self-test, first use WRITE_LOG to write the
-  // selective self-test log.
-  if (select && (retval=ataWriteSelectiveSelfTestLog(device, sv))) {
-    if (retval==-4)
-      pout("Can't start selective self-test without aborting current test: use '-X' option to smartctl.\n");
-    return retval;
-  }
-
-  //  Print ouf message that we are sending the command to test
-  if (testtype==ABORT_SELF_TEST)
-    sprintf(cmdmsg,"Abort SMART off-line mode self-test routine");
-  else
-    sprintf(cmdmsg,"Execute SMART %s routine immediately in %s mode",type,captive);
-  pout("Sending command: \"%s\".\n",cmdmsg);
-
-  if (select) {
-    int i;
-    pout("SPAN         STARTING_LBA           ENDING_LBA\n");
-    for (i = 0; i < con->smartselectivenumspans; i++)
-      pout("   %d %20"PRId64" %20"PRId64"\n", i,
-           con->smartselectivespan[i][0],
-           con->smartselectivespan[i][1]);
-  }
-  
-  // Now send the command to test
-  errornum=smartcommandhandler(device, IMMEDIATE_OFFLINE, testtype, NULL);
-  
-  if (errornum && !(cap && errno==EIO)){
-    char errormsg[128];
-    sprintf(errormsg,"Command \"%s\" failed",cmdmsg); 
-    syserror(errormsg);
-    pout("\n");
-    return -1;
-  }
-  
-  // Since the command succeeded, tell user
-  if (testtype==ABORT_SELF_TEST)
-    pout("Self-testing aborted!\n");
-  else
-    pout("Drive command \"%s\" successful.\nTesting has begun.\n",cmdmsg);
-  return 0;
-}
-
-/* Test Time Functions */
-int TestTime(struct ata_smart_values *data,int testtype){
-  switch (testtype){
-  case OFFLINE_FULL_SCAN:
-    return (int) data->total_time_to_complete_off_line;
-  case SHORT_SELF_TEST:
-  case SHORT_CAPTIVE_SELF_TEST:
-    return (int) data->short_test_completion_time;
-  case EXTEND_SELF_TEST:
-  case EXTEND_CAPTIVE_SELF_TEST:
-    return (int) data->extend_test_completion_time;
-  case CONVEYANCE_SELF_TEST:
-  case CONVEYANCE_CAPTIVE_SELF_TEST:
-    return (int) data->conveyance_test_completion_time;
-  default:
-    return 0;
-  }
-}
-
-// This function tells you both about the ATA error log and the
-// self-test error log capability (introduced in ATA-5).  The bit is
-// poorly documented in the ATA/ATAPI standard.  Starting with ATA-6,
-// SMART error logging is also indicated in bit 0 of DEVICE IDENTIFY
-// word 84 and 87.  Top two bits must match the pattern 01. BEFORE
-// ATA-6 these top two bits still had to match the pattern 01, but the
-// remaining bits were reserved (==0).
-int isSmartErrorLogCapable (struct ata_smart_values *data, struct ata_identify_device *identity){
-
-  unsigned short word84=identity->command_set_extension;
-  unsigned short word87=identity->csf_default;
-  int isata6=identity->major_rev_num & (0x01<<6);
-  int isata7=identity->major_rev_num & (0x01<<7);
-
-  if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x01))
-    return 1;
-  
-  if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x01))
-    return 1;
-  
-  // otherwise we'll use the poorly documented capability bit
-  return data->errorlog_capability & 0x01;
-}
-
-// See previous function.  If the error log exists then the self-test
-// log should (must?) also exist.
-int isSmartTestLogCapable (struct ata_smart_values *data, struct ata_identify_device *identity){
-
-  unsigned short word84=identity->command_set_extension;
-  unsigned short word87=identity->csf_default;
-  int isata6=identity->major_rev_num & (0x01<<6);
-  int isata7=identity->major_rev_num & (0x01<<7);
-
-  if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x02))
-    return 1;
-  
-  if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x02))
-    return 1;
-
-
-  // otherwise we'll use the poorly documented capability bit
-    return data->errorlog_capability & 0x01;
-}
-
-
-int isGeneralPurposeLoggingCapable(struct ata_identify_device *identity){
-  unsigned short word84=identity->command_set_extension;
-  unsigned short word87=identity->csf_default;
-
-  // If bit 14 of word 84 is set to one and bit 15 of word 84 is
-  // cleared to zero, the contents of word 84 contains valid support
-  // information. If not, support information is not valid in this
-  // word.
-  if ((word84>>14) == 0x01)
-    // If bit 5 of word 84 is set to one, the device supports the
-    // General Purpose Logging feature set.
-    return (word84 & (0x01 << 5));
-  
-  // If bit 14 of word 87 is set to one and bit 15 of word 87 is
-  // cleared to zero, the contents of words (87:85) contain valid
-  // information. If not, information is not valid in these words.  
-  if ((word87>>14) == 0x01)
-    // If bit 5 of word 87 is set to one, the device supports
-    // the General Purpose Logging feature set.
-    return (word87 & (0x01 << 5));
-
-  // not capable
-  return 0;
-}
-
-
-// SMART self-test capability is also indicated in bit 1 of DEVICE
-// IDENTIFY word 87 (if top two bits of word 87 match pattern 01).
-// However this was only introduced in ATA-6 (but self-test log was in
-// ATA-5).
-int isSupportExecuteOfflineImmediate(struct ata_smart_values *data){
-   return data->offline_data_collection_capability & 0x01;
-}
-// Note in the ATA-5 standard, the following bit is listed as "Vendor
-// Specific".  So it may not be reliable. The only use of this that I
-// have found is in IBM drives, where it is well-documented.  See for
-// example page 170, section 13.32.1.18 of the IBM Travelstar 40GNX
-// hard disk drive specifications page 164 Revision 1.1 22 Apr 2002.
-int isSupportAutomaticTimer(struct ata_smart_values *data){
-   return data->offline_data_collection_capability & 0x02;
-}
-int isSupportOfflineAbort(struct ata_smart_values *data){
-   return data->offline_data_collection_capability & 0x04;
-}
-int isSupportOfflineSurfaceScan(struct ata_smart_values *data){
-   return data->offline_data_collection_capability & 0x08;
-}
-int isSupportSelfTest (struct ata_smart_values *data){
-   return data->offline_data_collection_capability & 0x10;
-}
-int isSupportConveyanceSelfTest(struct ata_smart_values *data){
-   return data->offline_data_collection_capability & 0x20;
-}
-int isSupportSelectiveSelfTest(struct ata_smart_values *data){
-   return data->offline_data_collection_capability & 0x40;
-}
-
-
-
-// Loop over all valid attributes.  If they are prefailure attributes
-// and are at or below the threshold value, then return the ID of the
-// first failing attribute found.  Return 0 if all prefailure
-// attributes are in bounds.  The spec says "Bit 0
-// -Pre-failure/advisory - If the value of this bit equals zero, an
-// attribute value less than or equal to its corresponding attribute
-// threshold indicates an advisory condition where the usage or age of
-// the device has exceeded its intended design life period. If the
-// value of this bit equals one, an atribute value less than or equal
-// to its corresponding attribute threshold indicates a pre-failure
-// condition where imminent loss of data is being predicted."
-
-
-// onlyfailed=0 : are or were any age or prefailure attributes <= threshold
-// onlyfailed=1:  are any prefailure attributes <= threshold now
-int ataCheckSmart(struct ata_smart_values *data,
-                  struct ata_smart_thresholds_pvt *thresholds,
-                  int onlyfailed){
-  int i;
-  
-  // loop over all attributes
-  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
-
-    // pointers to disk's values and vendor's thresholds
-    struct ata_smart_attribute *disk=data->vendor_attributes+i;
-    struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i;
-    // consider only valid attributes
-    if (disk->id && thre->id){
-      int failednow,failedever;
-      
-      failednow =disk->current <= thre->threshold;
-      failedever=disk->worst   <= thre->threshold;
-      
-      if (!onlyfailed && failedever)
-        return disk->id;
-      
-      if (onlyfailed && failednow && ATTRIBUTE_FLAGS_PREFAILURE(disk->flags))
-        return disk->id;      
-    }
-  }
-  return 0;
-}
-
-
-
-// This checks the n'th attribute in the attribute list, NOT the
-// attribute with id==n.  If the attribute does not exist, or the
-// attribute is > threshold, then returns zero.  If the attribute is
-// <= threshold (failing) then we the attribute number if it is a
-// prefail attribute.  Else we return minus the attribute number if it
-// is a usage attribute.
-int ataCheckAttribute(struct ata_smart_values *data,
-                      struct ata_smart_thresholds_pvt *thresholds,
-                      int n){
-  struct ata_smart_attribute *disk;
-  struct ata_smart_threshold_entry *thre;
-  
-  if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !data || !thresholds)
-    return 0;
-  
-  // pointers to disk's values and vendor's thresholds
-  disk=data->vendor_attributes+n;
-  thre=thresholds->thres_entries+n;
-
-  if (!disk || !thre)
-    return 0;
-  
-  // consider only valid attributes, check for failure
-  if (!disk->id || !thre->id || (disk->id != thre->id) || disk->current> thre->threshold)
-    return 0;
-  
-  // We have found a failed attribute.  Return positive or negative? 
-  if (ATTRIBUTE_FLAGS_PREFAILURE(disk->flags))
-    return disk->id;
-  else
-    return -1*(disk->id);
-}
-
-
-// This routine prints the raw value of an attribute as a text string
-// into out. It also returns this 48-bit number as a long long.  The
-// array defs[] contains non-zero values if particular attributes have
-// non-default interpretations.
-
-int64_t ataPrintSmartAttribRawValue(char *out, 
-                                    struct ata_smart_attribute *attribute,
-                                    unsigned char *defs){
-  int64_t rawvalue;
-  unsigned word[3];
-  int j;
-  unsigned char select;
-  
-  // convert the six individual bytes to a long long (8 byte) integer.
-  // This is the value that we'll eventually return.
-  rawvalue = 0;
-  for (j=0; j<6; j++) {
-    // This looks a bit roundabout, but is necessary.  Don't
-    // succumb to the temptation to use raw[j]<<(8*j) since under
-    // the normal rules this will be promoted to the native type.
-    // On a 32 bit machine this might then overflow.
-    int64_t temp;
-    temp = attribute->raw[j];
-    temp <<= 8*j;
-    rawvalue |= temp;
-  }
-
-  // convert quantities to three two-byte words
-  for (j=0; j<3; j++){
-    word[j] = attribute->raw[2*j+1];
-    word[j] <<= 8;
-    word[j] |= attribute->raw[2*j];
-  }
-  
-  // if no data array, Attributes have default interpretations
-  if (defs)
-    select=defs[attribute->id];
-  else
-    select=0;
-
-  // Print six one-byte quantities.
-  if (select==253){
-    for (j=0; j<5; j++)
-      out+=sprintf(out, "%d ", attribute->raw[5-j]);
-    out+=sprintf(out, "%d ", attribute->raw[0]);
-    return rawvalue;
-  } 
-  
-  // Print three two-byte quantities
-  if (select==254){
-    out+=sprintf(out, "%d %d %d", word[2], word[1], word[0]); 
-    return rawvalue;
-  } 
-  
-  // Print one six-byte quantity
-  if (select==255){
-    out+=sprintf(out, "%"PRIu64, rawvalue);
-    return rawvalue;
-  }
-
-  // This switch statement is where we handle Raw attributes
-  // that are stored in an unusual vendor-specific format,
-  switch (attribute->id){
-    // Spin-up time
-  case 3:
-    out+=sprintf(out, "%d", word[0]);
-    // if second nonzero then it stores the average spin-up time
-    if (word[1])
-      out+=sprintf(out, " (Average %d)", word[1]);
-    break;
-    // Power on time
-  case 9:
-    if (select==1){
-      // minutes
-      int64_t tmp1=rawvalue/60;
-      int64_t tmp2=rawvalue%60;
-      out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m", tmp1, tmp2);
-    }
-    else if (select==3){
-      // seconds
-      int64_t hours=rawvalue/3600;
-      int64_t minutes=(rawvalue-3600*hours)/60;
-      int64_t seconds=rawvalue%60;
-      out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m+%02"PRIu64"s", hours, minutes, seconds);
-    }
-    else if (select==4){
-      // 30-second counter
-      int64_t tmp1=rawvalue/120;
-      int64_t tmp2=(rawvalue-120*tmp1)/2;
-      out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m", tmp1, tmp2);
-    }
-    else
-      // hours
-      out+=sprintf(out, "%"PRIu64, rawvalue);  //stored in hours
-    break;
-   // Load unload cycles
-  case 193:
-    if (select==1){
-      // loadunload
-      long load  =attribute->raw[0] + (attribute->raw[1]<<8) + (attribute->raw[2]<<16);
-      long unload=attribute->raw[3] + (attribute->raw[4]<<8) + (attribute->raw[5]<<16);
-      out+=sprintf(out, "%lu/%lu", load, unload);
-    }
-    else
-      // associated
-      out+=sprintf(out, "%"PRIu64, rawvalue);
-    break;
-    // Temperature
-  case 194:
-    if (select==1){
-      // ten times temperature in Celsius
-      int deg=word[0]/10;
-      int tenths=word[0]%10;
-      out+=sprintf(out, "%d.%d", deg, tenths);
-    }
-    else if (select==2)
-      // unknown attribute
-      out+=sprintf(out, "%"PRIu64, rawvalue);
-    else {
-      out+=sprintf(out, "%d", word[0]);
-      if (!(rawvalue==word[0])) {
-       int min=word[1]<word[2]?word[1]:word[2];
-       int max=word[1]>word[2]?word[1]:word[2];
-        // The other bytes are in use. Try IBM's model
-        out+=sprintf(out, " (Lifetime Min/Max %d/%d)", min, max);
-      }
-    }
-    break;
-  default:
-    out+=sprintf(out, "%"PRIu64, rawvalue);
-  }
-  
-  // Return the full value
-  return rawvalue;
-}
-
-
-// Note some attribute names appear redundant because different
-// manufacturers use different attribute IDs for an attribute with the
-// same name.  The variable val should contain a non-zero value if a particular
-// attributes has a non-default interpretation.
-void ataPrintSmartAttribName(char *out, unsigned char id, unsigned char *definitions){
-  char *name;
-  unsigned char val;
-
-  // If no data array, use default interpretations
-  if (definitions)
-    val=definitions[id];
-  else
-    val=0;
-
-  switch (id){
-    
-  case 1:
-    name="Raw_Read_Error_Rate";
-    break;
-  case 2:
-    name="Throughput_Performance";
-    break;
-  case 3:
-    name="Spin_Up_Time";
-    break;
-  case 4:
-    name="Start_Stop_Count";
-    break;
-  case 5:
-    name="Reallocated_Sector_Ct";
-    break;
-  case 6:
-    name="Read_Channel_Margin";
-    break;
-  case 7:
-    name="Seek_Error_Rate";
-    break;
-  case 8:
-    name="Seek_Time_Performance";
-    break;
-  case 9:
-    switch (val) {
-    case 1:
-      name="Power_On_Minutes";
-      break;
-    case 2:
-      name="Temperature_Celsius";
-      break;
-    case 3:
-      name="Power_On_Seconds";
-      break;
-    case 4:
-      name="Power_On_Half_Minutes";
-      break;
-    default:
-      name="Power_On_Hours";
-      break;
-    }
-    break;
-  case 10:
-    name="Spin_Retry_Count";
-    break;
-  case 11:
-    name="Calibration_Retry_Count";
-    break;
-  case 12:
-    name="Power_Cycle_Count";
-    break;
-  case 13:
-    name="Read_Soft_Error_Rate";
-    break;
-  case 191:
-    name="G-Sense_Error_Rate";
-    break;
-  case 192:
-    switch (val) {
-    case 1:
-      // Fujitsu
-      name="Emergency_Retract_Cycle_Ct";
-      break;
-    default:
-      name="Power-Off_Retract_Count";
-      break;
-    }
-    break;
-  case 193:
-    name="Load_Cycle_Count";
-    break;
-  case 194:
-    switch (val){
-    case 1:
-      // Samsung SV1204H with RK100-13 firmware
-      name="Temperature_Celsius_x10";
-      break;
-    case 2:
-      // for disks with no temperature Attribute
-      name="Unknown_Attribute";
-      break;
-    default:
-      name="Temperature_Celsius";
-      break;
-    }
-    break;
-  case 195:
-    // Fujitsu name="ECC_On_The_Fly_Count";
-    name="Hardware_ECC_Recovered";
-    break;
-  case 196:
-    name="Reallocated_Event_Count";
-    break;
-  case 197:
-    name="Current_Pending_Sector";
-    break;
-  case 198:
-    switch (val){
-    case 1:
-      // Fujitsu
-      name="Off-line_Scan_UNC_Sector_Ct";
-      break;
-    default:
-      name="Offline_Uncorrectable";
-      break;
-    }
-    break;
-  case 199:
-    name="UDMA_CRC_Error_Count";
-    break;
-  case 200:
-    switch (val) {
-    case 1:
-      // Fujitsu MHS2020AT
-      name="Write_Error_Count";
-      break;
-    default:
-      // Western Digital
-      name="Multi_Zone_Error_Rate";
-      break;
-    }
-    break;
-  case 201:
-    switch (val) {
-    case 1:
-      // Fujitsu
-      name="Detected_TA_Count";
-      break;
-    default:
-      name="Soft_Read_Error_Rate";
-      break;
-    }
-    break;
-  case 202:
-    // Fujitsu
-    name="TA_Increase_Count";
-    // Maxtor: Data Address Mark Errors
-    break;
-  case 203:
-    // Fujitsu
-    name="Run_Out_Cancel";
-    // Maxtor: ECC Errors
-    break;
-  case 204:
-    // Fujitsu
-    name="Shock_Count_Write_Opern";
-    // Maxtor: Soft ECC Correction
-    break;
-  case 205:
-    // Fujitsu
-    name="Shock_Rate_Write_Opern";
-    // Maxtor: Thermal Aspirates
-    break;
-  case 206:
-    // Fujitsu
-    name="Flying_Height";
-    break;
-  case 207:
-    // Maxtor
-    name="Spin_High_Current";
-    break;
-  case 208:
-    // Maxtor
-    name="Spin_Buzz";
-    break;
-  case 209:
-    // Maxtor
-    name="Offline_Seek_Performnce";
-    break;
-  case 220:
-    switch (val) {
-    case 1:
-      name="Temperature_Celsius";
-      break;
-    default:
-      name="Disk_Shift";
-      break;
-    }
-    break;
-  case 221:
-    name="G-Sense_Error_Rate";
-    break;
-  case 222:
-    name="Loaded_Hours";
-    break;
-  case 223:
-    name="Load_Retry_Count";
-    break;
-  case 224:
-    name="Load_Friction";
-    break;
-  case 225:
-    name="Load_Cycle_Count";
-    break;
-  case 226:
-    name="Load-in_Time";
-    break;
-  case 227:
-    name="Torq-amp_Count";
-    break;
-  case 228:
-    name="Power-off_Retract_Count";
-    break;
-  case 230:
-    // seen in IBM DTPA-353750
-    name="Head_Amplitude";
-    break;
-  case 231:
-    name="Temperature_Celsius";
-    break;
-  case 240:
-    name="Head_Flying_Hours";
-    break;
-  case 250:
-    name="Read_Error_Retry_Rate";
-    break;
-  default:
-    name="Unknown_Attribute";
-    break;
-  }
-  sprintf(out,"%3hu %s",(short int)id,name);
-  return;
-}
-
-// Returns raw value of Attribute with ID==id. This will be in the
-// range 0 to 2^48-1 inclusive.  If the Attribute does not exist,
-// return -1.
-int64_t ATAReturnAttributeRawValue(unsigned char id, struct ata_smart_values *data) {
-  int i;
-
-  // valid Attribute IDs are in the range 1 to 255 inclusive.
-  if (!id || !data)
-    return -1;
-  
-  // loop over Attributes to see if there is one with the desired ID
-  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++) {
-    struct ata_smart_attribute *this = data->vendor_attributes + i;
-    if (this->id == id) {
-      // we've found the desired Attribute.  Return its value
-      int64_t rawvalue=0;
-      int j;
-
-      for (j=0; j<6; j++) {
-       // This looks a bit roundabout, but is necessary.  Don't
-       // succumb to the temptation to use raw[j]<<(8*j) since under
-       // the normal rules this will be promoted to the native type.
-       // On a 32 bit machine this might then overflow.
-       int64_t temp;
-       temp = this->raw[j];
-       temp <<= 8*j;
-       rawvalue |= temp;
-      } // loop over j
-      return rawvalue;
-    } // found desired Attribute
-  } // loop over Attributes
-  
-  // fall-through: no such Attribute found
-  return -1;
-}
-
-
diff --git a/atacmds.cpp b/atacmds.cpp
new file mode 100644 (file)
index 0000000..b645f7a
--- /dev/null
@@ -0,0 +1,1929 @@
+/*
+ * atacmds.cpp
+ * 
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
+ * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ * 
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsiata.h"
+#include "extern.h"
+#include "utility.h"
+
+const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.176 2006/08/25 06:06:24 sxzzsf Exp $"
+ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSIATA_H_CVSID UTILITY_H_CVSID;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// for passing global control variables
+extern smartmonctrl *con;
+
+// These Drive Identity tables are taken from hdparm 5.2, and are also
+// given in the ATA/ATAPI specs for the IDENTIFY DEVICE command.  Note
+// that SMART was first added into the ATA/ATAPI-3 Standard with
+// Revision 3 of the document, July 25, 1995.  Look at the "Document
+// Status" revision commands at the beginning of
+// http://www.t13.org/project/d2008r6.pdf to see this.
+#define NOVAL_0                 0x0000
+#define NOVAL_1                 0xffff
+/* word 81: minor version number */
+#define MINOR_MAX 0x22
+const char *minor_str[] = {                     /* word 81 value: */
+  "Device does not report version",             /* 0x0000       */
+  "ATA-1 X3T9.2 781D prior to revision 4",      /* 0x0001       */
+  "ATA-1 published, ANSI X3.221-1994",          /* 0x0002       */
+  "ATA-1 X3T9.2 781D revision 4",               /* 0x0003       */
+  "ATA-2 published, ANSI X3.279-1996",          /* 0x0004       */
+  "ATA-2 X3T10 948D prior to revision 2k",      /* 0x0005       */
+  "ATA-3 X3T10 2008D revision 1",               /* 0x0006       */ /* SMART NOT INCLUDED */
+  "ATA-2 X3T10 948D revision 2k",               /* 0x0007       */
+  "ATA-3 X3T10 2008D revision 0",               /* 0x0008       */ 
+  "ATA-2 X3T10 948D revision 3",                /* 0x0009       */
+  "ATA-3 published, ANSI X3.298-199x",          /* 0x000a       */
+  "ATA-3 X3T10 2008D revision 6",               /* 0x000b       */ /* 1st VERSION WITH SMART */
+  "ATA-3 X3T13 2008D revision 7 and 7a",        /* 0x000c       */
+  "ATA/ATAPI-4 X3T13 1153D revision 6",         /* 0x000d       */
+  "ATA/ATAPI-4 T13 1153D revision 13",          /* 0x000e       */
+  "ATA/ATAPI-4 X3T13 1153D revision 7",         /* 0x000f       */
+  "ATA/ATAPI-4 T13 1153D revision 18",          /* 0x0010       */
+  "ATA/ATAPI-4 T13 1153D revision 15",          /* 0x0011       */
+  "ATA/ATAPI-4 published, ANSI NCITS 317-1998", /* 0x0012       */
+  "ATA/ATAPI-5 T13 1321D revision 3",           /* 0x0013       */
+  "ATA/ATAPI-4 T13 1153D revision 14",          /* 0x0014       */
+  "ATA/ATAPI-5 T13 1321D revision 1",           /* 0x0015       */
+  "ATA/ATAPI-5 published, ANSI NCITS 340-2000", /* 0x0016       */
+  "ATA/ATAPI-4 T13 1153D revision 17",          /* 0x0017       */
+  "ATA/ATAPI-6 T13 1410D revision 0",           /* 0x0018       */
+  "ATA/ATAPI-6 T13 1410D revision 3a",          /* 0x0019       */
+  "ATA/ATAPI-7 T13 1532D revision 1",           /* 0x001a       */
+  "ATA/ATAPI-6 T13 1410D revision 2",           /* 0x001b       */
+  "ATA/ATAPI-6 T13 1410D revision 1",           /* 0x001c       */
+  "ATA/ATAPI-7 published, ANSI INCITS 397-2005",/* 0x001d       */
+  "ATA/ATAPI-7 T13 1532D revision 0",           /* 0x001e       */
+  "reserved",                                   /* 0x001f       */
+  "reserved",                                   /* 0x0020       */
+  "ATA/ATAPI-7 T13 1532D revision 4a",          /* 0x0021       */
+  "ATA/ATAPI-6 published, ANSI INCITS 361-2002" /* 0x0022       */
+};
+
+// NOTE ATA/ATAPI-4 REV 4 was the LAST revision where the device
+// attribute structures were NOT completely vendor specific.  So any
+// disk that is ATA/ATAPI-4 or above can not be trusted to show the
+// vendor values in sensible format.
+
+// Negative values below are because it doesn't support SMART
+const int actual_ver[] = { 
+  /* word 81 value: */
+  0,            /* 0x0000       WARNING:        */
+  1,            /* 0x0001       WARNING:        */
+  1,            /* 0x0002       WARNING:        */
+  1,            /* 0x0003       WARNING:        */
+  2,            /* 0x0004       WARNING:   This array           */
+  2,            /* 0x0005       WARNING:   corresponds          */
+  -3, /*<== */  /* 0x0006       WARNING:   *exactly*            */
+  2,            /* 0x0007       WARNING:   to the ATA/          */
+  -3, /*<== */  /* 0x0008       WARNING:   ATAPI version        */
+  2,            /* 0x0009       WARNING:   listed in            */
+  3,            /* 0x000a       WARNING:   the                  */
+  3,            /* 0x000b       WARNING:   minor_str            */
+  3,            /* 0x000c       WARNING:   array                */
+  4,            /* 0x000d       WARNING:   above.               */
+  4,            /* 0x000e       WARNING:                        */
+  4,            /* 0x000f       WARNING:   If you change        */
+  4,            /* 0x0010       WARNING:   that one,            */
+  4,            /* 0x0011       WARNING:   change this one      */
+  4,            /* 0x0012       WARNING:   too!!!               */
+  5,            /* 0x0013       WARNING:        */
+  4,            /* 0x0014       WARNING:        */
+  5,            /* 0x0015       WARNING:        */
+  5,            /* 0x0016       WARNING:        */
+  4,            /* 0x0017       WARNING:        */
+  6,            /* 0x0018       WARNING:        */
+  6,            /* 0x0019       WARNING:        */
+  7,            /* 0x001a       WARNING:        */
+  6,            /* 0x001b       WARNING:        */
+  6,            /* 0x001c       WARNING:        */
+  7,            /* 0x001d       WARNING:        */
+  7,            /* 0x001e       WARNING:        */
+  0,            /* 0x001f       WARNING:        */
+  0,            /* 0x0020       WARNING:        */
+  7,            /* 0x0021       WARNING:        */
+  6             /* 0x0022       WARNING:        */
+};
+
+// When you add additional items to this list, you should then:
+// 0 -- update this list
+// 1 -- modify the following function parse_attribute_def()
+// 2 -- if needed, modify ataPrintSmartAttribRawValue()
+// 3 -  if needed, modify ataPrintSmartAttribName()
+// 4 -- add #define PRESET_N_DESCRIPTION at top of knowndrives.c
+// 5 -- add drive in question into knowndrives[] table in knowndrives.c
+// 6 -- update smartctl.8
+// 7 -- update smartd.8
+// 8 -- do "make smartd.conf.5" to update smartd.conf.5
+// 9 -- update CHANGELOG file
+const char *vendorattributeargs[] = {
+  // 0  defs[9]=1
+  "9,minutes",
+  // 1  defs[9]=3
+  "9,seconds",
+  // 2  defs[9]=2
+  "9,temp",
+  // 3  defs[220]=1
+  "220,temp",
+  // 4  defs[*]=253
+  "N,raw8",
+  // 5  defs[*]=254
+  "N,raw16",
+  // 6  defs[*]=255
+  "N,raw48",
+  // 7  defs[200]=1
+  "200,writeerrorcount",
+  // 8  defs[9]=4
+  "9,halfminutes",
+  // 9  defs[194]=1
+  "194,10xCelsius",
+  // 10 defs[194]=2
+  "194,unknown",
+  // 11 defs[193]=1
+  "193,loadunload",
+  // 12 defs[201]=1
+  "201,detectedtacount",
+  // 13 defs[192]=1
+  "192,emergencyretractcyclect",
+  // 14 defs[198]=1
+  "198,offlinescanuncsectorct",
+  // NULL should always terminate the array
+  NULL
+};
+
+// This are the meanings of the Self-test failure checkpoint byte.
+// This is in the self-test log at offset 4 bytes into the self-test
+// descriptor and in the SMART READ DATA structure at byte offset
+// 371. These codes are not well documented.  The meanings returned by
+// this routine are used (at least) by Maxtor and IBM. Returns NULL if
+// not recognized.  Currently the maximum length is 15 bytes.
+const char *SelfTestFailureCodeName(unsigned char which){
+  
+  switch (which) {
+  case 0:
+    return "Write_Test";
+  case 1:
+    return "Servo_Basic";
+  case 2:
+    return "Servo_Random";
+  case 3:
+    return "G-list_Scan";
+  case 4:
+    return "Handling_Damage";
+  case 5:
+    return "Read_Scan";
+  default:
+    return NULL;
+  }
+}
+
+// This is a utility function for parsing pairs like "9,minutes" or
+// "220,temp", and putting the correct flag into the attributedefs
+// array.  Returns 1 if problem, 0 if pair has been recongized.
+int parse_attribute_def(char *pair, unsigned char **defsptr){
+  int i,j;
+  char temp[32];
+  unsigned char *defs;
+
+  // If array does not exist, allocate it
+  if (!*defsptr && !(*defsptr=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM, 1))){
+    pout("Out of memory in parse_attribute_def\n");
+    EXIT(1);
+  }
+
+  defs=*defsptr;
+
+  // look along list and see if we find the pair
+  for (i=0; vendorattributeargs[i] && strcmp(pair, vendorattributeargs[i]); i++);
+
+  switch (i) {
+  case 0:
+    // attribute 9 is power on time in minutes
+    defs[9]=1;
+    return 0;
+  case 1:
+    // attribute 9 is power-on-time in seconds
+    defs[9]=3;
+    return 0;
+  case 2:
+    // attribute 9 is temperature in celsius
+    defs[9]=2;
+    return 0;
+  case 3:
+    // attribute 220 is temperature in celsius
+    defs[220]=1;
+    return 0;
+  case 4:
+    // print all attributes in raw 8-bit form
+    for (j=0; j<MAX_ATTRIBUTE_NUM; j++)
+      defs[j]=253;
+    return 0;
+  case 5:
+    // print all attributes in raw 16-bit form
+    for (j=0; j<MAX_ATTRIBUTE_NUM; j++)
+      defs[j]=254;
+    return 0;
+  case 6:
+    // print all attributes in raw 48-bit form
+    for (j=0; j<MAX_ATTRIBUTE_NUM; j++)
+      defs[j]=255;
+    return 0;
+  case 7:
+    // attribute 200 is write error count
+    defs[200]=1;
+    return 0;
+  case 8:
+    // attribute 9 increments once every 30 seconds (power on time
+    // measure)
+    defs[9]=4;
+    return 0;
+  case 9:
+    // attribute 194 is ten times disk temp in Celsius
+    defs[194]=1;
+    return 0;
+  case 10:
+    // attribute 194 is unknown
+    defs[194]=2;
+    return 0;
+  case 11:
+    // Hitachi : Attributes 193 has 2 values : 1 load, 1 normal unload
+    defs[193]=1;
+    return 0;
+  case 12:
+    // Fujitsu
+    defs[201]=1;
+    return 0;
+  case 13:
+    // Fujitsu
+    defs[192]=1;
+    return 0;
+  case 14:
+    // Fujitsu
+    defs[198]=1;
+    return 0;
+  default:
+    // pair not found
+    break;
+  }
+  // At this point, either the pair was not found, or it is of the
+  // form N,uninterpreted, in which case we need to parse N
+  j=sscanf(pair,"%d,%14s", &i, temp);
+  // if no match to pattern, unrecognized
+  if (j!=2 || i<0 || i >255)
+    return 1;
+
+  // check for recognized strings
+  if (!strcmp(temp, "raw8")) {
+    defs[i]=253;
+    return 0;
+  }
+  
+  // check for recognized strings
+  if (!strcmp(temp, "raw16")) {
+    defs[i]=254;
+    return 0;
+  }
+  
+  // check for recognized strings
+  if (!strcmp(temp, "raw48")) {
+    defs[i]=255;
+    return 0;
+  }
+  // didn't recognize the string
+  return 1;
+}
+
+// Structure used in sorting the array vendorattributeargs[].
+typedef struct vaa_pair_s {
+  const char *vaa;
+  const char *padded_vaa;
+} vaa_pair;
+
+// Returns a copy of s with all numbers of less than three digits padded with
+// leading zeros.  Returns NULL if there isn't enough memory available.  The
+// memory for the string is dynamically allocated and should be freed by the
+// caller.
+char *pad_numbers(const char *s)
+{
+  char c, *t, *u;
+  const char *r;
+  int i, len, ndigits = 0;
+
+  // Allocate the maximum possible amount of memory needed.
+  if (!(t = (char *)malloc(strlen(s)*2+2)))
+    return NULL;
+
+  // Copy the string s to t, padding any numbers of less than three digits
+  // with leading zeros.  The string is copied backwards to simplify the code.
+  r = s + strlen(s);
+  u = t;
+  while (( r-- >= s)) {
+    if (isdigit((int)*r))
+      ndigits++;
+    else if (ndigits > 0) {
+      while (ndigits++ < 3)
+        *u++ = '0';
+      ndigits = 0;
+    }
+    *u++ = *r;
+  }
+  *u = '\0';
+
+  // Reverse the string in t.
+  len = strlen(t);
+  for (i = 0; i < len/2; i++) {
+    c          = t[i];
+    t[i]       = t[len-1-i];
+    t[len-1-i] = c;
+  }
+
+  return t;
+}
+
+// Comparison function for qsort().  Used by sort_vendorattributeargs().
+int compare_vaa_pairs(const void *a, const void *b)
+{
+  vaa_pair *p = (vaa_pair *)a;
+  vaa_pair *q = (vaa_pair *)b;
+
+  return strcmp(p->padded_vaa, q->padded_vaa);
+}
+
+// Returns a sorted list of vendorattributeargs or NULL if there is not enough
+// memory available.  The memory for the list is allocated dynamically and
+// should be freed by the caller.
+// To perform the sort, any numbers in the strings are padded out to three
+// digits by adding leading zeros.  For example,
+//
+//    "9,minutes"  becomes  "009,minutes"
+//    "N,raw16"    becomes  "N,raw016"
+//
+// and the original strings are paired with the padded strings.  The list of
+// pairs is then sorted by comparing the padded strings (using strcmp) and the
+// result is then the list of unpadded strings.
+//
+const char **sort_vendorattributeargs(void) {
+  const char **ps, **sorted_list = NULL;
+  vaa_pair *pairs, *pp;
+  int count, i;
+
+  // Figure out how many strings are in vendorattributeargs[] (not including
+  // the terminating NULL).
+  count = (sizeof vendorattributeargs) / sizeof(char *) - 1;
+
+  // Construct a list of pairs of strings from vendorattributeargs[] and their
+  // padded equivalents.
+  if (!(pairs = (vaa_pair *)malloc(sizeof(vaa_pair) * count)))
+    goto END;
+  for (ps = vendorattributeargs, pp = pairs; *ps; ps++, pp++) {
+    pp->vaa = *ps;
+    if (!(pp->padded_vaa = pad_numbers(*ps)))
+      goto END;
+  }
+
+  // Sort the array of vaa_pair structures by comparing the padded strings
+  // using strcmp().
+  qsort(pairs, count, sizeof(vaa_pair), compare_vaa_pairs);
+
+  // Construct the sorted list of strings.
+  if (!(sorted_list = (const char **)malloc(sizeof vendorattributeargs)))
+    goto END;
+  for (ps = sorted_list, pp = pairs, i = 0; i < count; ps++, pp++, i++)
+    *ps = pp->vaa;
+  *ps = NULL;
+
+END:
+  if (pairs) {
+    for (i = 0; i < count; i++)
+      if (pairs[i].padded_vaa)
+        free((void *)pairs[i].padded_vaa);
+    free((void *)pairs);
+  }
+
+  // If there was a problem creating the list then sorted_list should now
+  // contain NULL.
+  return sorted_list;
+}
+
+// Function to return a multiline string containing a list of the arguments in 
+// vendorattributeargs[].  The strings are preceeded by tabs and followed
+// (except for the last) by newlines.
+// This function allocates the required memory for the string and the caller
+// must use free() to free it.  It returns NULL if the required memory can't
+// be allocated.
+char *create_vendor_attribute_arg_list(void){
+  const char **ps, **sorted;
+  char *s;
+  int len;
+
+  // Get a sorted list of vendor attribute arguments.  If the sort fails
+  // (which should only happen if the system is really low on memory) then just
+  // use the unordered list.
+  if (!(sorted = (const char **) sort_vendorattributeargs()))
+    sorted = vendorattributeargs;
+
+  // Calculate the required number of characters
+  len = 1;                // At least one char ('\0')
+  for (ps = sorted; *ps != NULL; ps++) {
+    len += 1;             // For the tab
+    len += strlen(*ps);   // For the actual argument string
+    if (*(ps+1))
+      len++;              // For the newline if required
+  }
+
+  // Attempt to allocate memory for the string
+  if (!(s = (char *)malloc(len)))
+    return NULL;
+
+  // Construct the string
+  *s = '\0';
+  for (ps = sorted; *ps != NULL; ps++) {
+    strcat(s, "\t");
+    strcat(s, *ps);
+    if (*(ps+1))
+      strcat(s, "\n");
+  }
+
+  free((char **)sorted);
+
+  // Return a pointer to the string
+  return s;
+}
+
+// swap two bytes.  Point to low address
+void swap2(char *location){
+  char tmp=*location;
+  *location=*(location+1);
+  *(location+1)=tmp;
+  return;
+}
+
+// swap four bytes.  Point to low address
+void swap4(char *location){
+  char tmp=*location;
+  *location=*(location+3);
+  *(location+3)=tmp;
+  swap2(location+1);
+  return;
+}
+
+// swap eight bytes.  Points to low address
+void swap8(char *location){
+  char tmp=*location;
+  *location=*(location+7);
+  *(location+7)=tmp;
+  tmp=*(location+1);
+  *(location+1)=*(location+6);
+  *(location+6)=tmp;
+  swap4(location+2);
+  return;
+}
+
+static char *commandstrings[]={
+  "SMART ENABLE",
+  "SMART DISABLE",
+  "SMART AUTOMATIC ATTRIBUTE SAVE",
+  "SMART IMMEDIATE OFFLINE",
+  "SMART AUTO OFFLINE",
+  "SMART STATUS",
+  "SMART STATUS CHECK",
+  "SMART READ ATTRIBUTE VALUES",
+  "SMART READ ATTRIBUTE THRESHOLDS",
+  "SMART READ LOG",
+  "IDENTIFY DEVICE",
+  "IDENTIFY PACKET DEVICE",
+  "CHECK POWER MODE",
+  "SMART WRITE LOG",
+  "WARNING (UNDEFINED COMMAND -- CONTACT DEVELOPERS AT " PACKAGE_BUGREPORT ")\n"
+};
+
+void prettyprint(unsigned char *stuff, char *name){
+  int i,j;
+  pout("\n===== [%s] DATA START (BASE-16) =====\n", name);
+  for (i=0; i<32; i++){
+    pout("%03d-%03d: ", 16*i, 16*(i+1)-1);
+    for (j=0; j<15; j++)
+      pout("%02x ",*stuff++);
+    pout("%02x\n",*stuff++);
+  }
+  pout("===== [%s] DATA END (512 Bytes) =====\n\n", name);
+}
+
+// This function provides the pretty-print reporting for SMART
+// commands: it implements the various -r "reporting" options for ATA
+// ioctls.
+int smartcommandhandler(int device, smart_command_set command, int select, char *data){
+  int retval;
+
+  // This conditional is true for commands that return data
+  int getsdata=(command==PIDENTIFY || 
+                command==IDENTIFY || 
+                command==READ_LOG || 
+                command==READ_THRESHOLDS || 
+                command==READ_VALUES ||
+                command==CHECK_POWER_MODE);
+
+  int sendsdata=(command==WRITE_LOG);
+  
+  // If reporting is enabled, say what the command will be before it's executed
+  if (con->reportataioctl){
+          // conditional is true for commands that use parameters
+          int usesparam=(command==READ_LOG || 
+                         command==AUTO_OFFLINE || 
+                         command==AUTOSAVE || 
+                         command==IMMEDIATE_OFFLINE ||
+                         command==WRITE_LOG);
+                  
+    pout("\nREPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]);
+    if (usesparam)
+      pout(" InputParameter=%d\n", select);
+    else
+      pout("\n");
+  }
+  
+  if ((getsdata || sendsdata) && !data){
+    pout("REPORT-IOCTL: Unable to execute command %s : data destination address is NULL\n", commandstrings[command]);
+    return -1;
+  }
+  
+  // The reporting is cleaner, and we will find coding bugs faster, if
+  // the commands that failed clearly return empty (zeroed) data
+  // structures
+  if (getsdata) {
+    if (command==CHECK_POWER_MODE)
+      data[0]=0;
+    else
+      memset(data, '\0', 512);
+  }
+
+
+  // If reporting is enabled, say what input was sent to the command
+  if (con->reportataioctl && sendsdata){
+    pout("REPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]);
+    // if requested, pretty-print the output data structure
+    if (con->reportataioctl>1)
+      prettyprint((unsigned char *)data, commandstrings[command]);
+  }
+
+  // In case the command produces an error, we'll want to know what it is:
+  errno=0;
+  
+  // now execute the command
+  switch (con->controller_type) {
+  case CONTROLLER_3WARE_678K:
+  case CONTROLLER_3WARE_678K_CHAR:
+  case CONTROLLER_3WARE_9000_CHAR:
+    retval=escalade_command_interface(device, con->controller_port-1, con->controller_type, command, select, data);
+    if (retval &&  con->controller_port<=0)
+      pout("WARNING: apparently missing '-d 3ware,N' disk specification\n");
+    break;
+  case CONTROLLER_MARVELL_SATA:
+    retval=marvell_command_interface(device, command, select, data);
+    break;
+  case CONTROLLER_SAT:
+    retval=sat_command_interface(device, command, select, data);
+    break;
+  case CONTROLLER_HPT:
+    retval=highpoint_command_interface(device, command, select, data);
+    break;
+  default:
+    retval=ata_command_interface(device, command, select, data);
+  }
+
+  // If reporting is enabled, say what output was produced by the command
+  if (con->reportataioctl){
+    if (errno)
+      pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d errno=%d [%s]\n", 
+           device, commandstrings[command], retval, errno, strerror(errno));
+    else
+      pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d\n",
+           device, commandstrings[command], retval);
+    
+    // if requested, pretty-print the output data structure
+    if (con->reportataioctl>1 && getsdata) {
+      if (command==CHECK_POWER_MODE)
+       pout("Sector Count Register (BASE-16): %02x\n", (unsigned char)(*data));
+      else
+       prettyprint((unsigned char *)data, commandstrings[command]);
+    }
+  }
+  return retval;
+}
+
+
+// This function computes the checksum of a single disk sector (512
+// bytes).  Returns zero if checksum is OK, nonzero if the checksum is
+// incorrect.  The size (512) is correct for all SMART structures.
+unsigned char checksum(unsigned char *buffer){
+  unsigned char sum=0;
+  int i;
+  
+  for (i=0; i<512; i++)
+    sum+=buffer[i];
+
+  return sum;
+}
+
+// returns -1 if command fails or the device is in Sleep mode, else
+// value of Sector Count register.  Sector Count result values:
+//   00h device is in Standby mode. 
+//   80h device is in Idle mode.
+//   FFh device is in Active mode or Idle mode.
+
+int ataCheckPowerMode(int device) {
+  unsigned char result;
+
+  if ((smartcommandhandler(device, CHECK_POWER_MODE, 0, (char *)&result)))
+    return -1;
+
+  if (result!=0 && result!=0x80 && result!=0xff)
+    pout("ataCheckPowerMode(): ATA CHECK POWER MODE returned unknown Sector Count Register value %02x\n", result);
+
+  return (int)result;
+}
+
+
+
+
+// Reads current Device Identity info (512 bytes) into buf.  Returns 0
+// if all OK.  Returns -1 if no ATA Device identity can be
+// established.  Returns >0 if Device is ATA Packet Device (not SMART
+// capable).  The value of the integer helps identify the type of
+// Packet device, which is useful so that the user can connect the
+// formal device number with whatever object is inside their computer.
+int ataReadHDIdentity (int device, struct ata_identify_device *buf){
+  unsigned short *rawshort=(unsigned short *)buf;
+  unsigned char  *rawbyte =(unsigned char  *)buf;
+
+  // See if device responds either to IDENTIFY DEVICE or IDENTIFY
+  // PACKET DEVICE
+  if ((smartcommandhandler(device, IDENTIFY, 0, (char *)buf))){
+    if (smartcommandhandler(device, PIDENTIFY, 0, (char *)buf)){
+      return -1; 
+    }
+  }
+
+#ifndef __NetBSD__
+  // if machine is big-endian, swap byte order as needed
+  // (the NetBSD kernel does deliver the results in host byte order)
+  if (isbigendian()){
+    int i;
+    
+    // swap various capability words that are needed
+    for (i=0; i<33; i++)
+      swap2((char *)(buf->words047_079+i));
+    
+    for (i=80; i<=87; i++)
+      swap2((char *)(rawshort+i));
+    
+    for (i=0; i<168; i++)
+      swap2((char *)(buf->words088_255+i));
+  }
+#endif
+  
+  // If there is a checksum there, validate it
+  if ((rawshort[255] & 0x00ff) == 0x00a5 && checksum(rawbyte))
+    checksumwarning("Drive Identity Structure");
+  
+  // If this is a PACKET DEVICE, return device type
+  if (rawbyte[1] & 0x80)
+    return 1+(rawbyte[1] & 0x1f);
+  
+  // Not a PACKET DEVICE
+  return 0;
+}
+
+// Returns ATA version as an integer, and a pointer to a string
+// describing which revision.  Note that Revision 0 of ATA-3 does NOT
+// support SMART.  For this one case we return -3 rather than +3 as
+// the version number.  See notes above.
+int ataVersionInfo (const char** description, struct ata_identify_device *drive, unsigned short *minor){
+  unsigned short major;
+  int i;
+
+  // check that arrays at the top of this file are defined
+  // consistently
+  if (sizeof(minor_str) != sizeof(char *)*(1+MINOR_MAX)){
+    pout("Internal error in ataVersionInfo().  minor_str[] size %d\n"
+         "is not consistent with value of MINOR_MAX+1 = %d\n", 
+         (int)(sizeof(minor_str)/sizeof(char *)), MINOR_MAX+1);
+    fflush(NULL);
+    abort();
+  }
+  if (sizeof(actual_ver) != sizeof(int)*(1+MINOR_MAX)){
+    pout("Internal error in ataVersionInfo().  actual_ver[] size %d\n"
+         "is not consistent with value of MINOR_MAX = %d\n",
+         (int)(sizeof(actual_ver)/sizeof(int)), MINOR_MAX+1);
+    fflush(NULL);
+    abort();
+  }
+
+  // get major and minor ATA revision numbers
+  major=drive->major_rev_num;
+  *minor=drive->minor_rev_num;
+  
+  // First check if device has ANY ATA version information in it
+  if (major==NOVAL_0 || major==NOVAL_1) {
+    *description=NULL;
+    return -1;
+  }
+  
+  // The minor revision number has more information - try there first
+  if (*minor && (*minor<=MINOR_MAX)){
+    int std = actual_ver[*minor];
+    if (std) {
+      *description=minor_str[*minor];
+      return std;
+    }
+  }
+  
+  // HDPARM has a very complicated algorithm from here on. Since SMART only
+  // exists on ATA-3 and later standards, let's punt on this.  If you don't
+  // like it, please fix it.  The code's in CVS.
+  for (i=15; i>0; i--)
+    if (major & (0x1<<i))
+      break;
+  
+  *description=NULL; 
+  if (i==0)
+    return 1;
+  else
+    return i;
+}
+
+// returns 1 if SMART supported, 0 if SMART unsupported, -1 if can't tell
+int ataSmartSupport(struct ata_identify_device *drive){
+  unsigned short word82=drive->command_set_1;
+  unsigned short word83=drive->command_set_2;
+  
+  // check if words 82/83 contain valid info
+  if ((word83>>14) == 0x01)
+    // return value of SMART support bit 
+    return word82 & 0x0001;
+  
+  // since we can're rely on word 82, we don't know if SMART supported
+  return -1;
+}
+
+// returns 1 if SMART enabled, 0 if SMART disabled, -1 if can't tell
+int ataIsSmartEnabled(struct ata_identify_device *drive){
+  unsigned short word85=drive->cfs_enable_1;
+  unsigned short word87=drive->csf_default;
+  
+  // check if words 85/86/87 contain valid info
+  if ((word87>>14) == 0x01)
+    // return value of SMART enabled bit
+    return word85 & 0x0001;
+  
+  // Since we can't rely word85, we don't know if SMART is enabled.
+  return -1;
+}
+
+
+// Reads SMART attributes into *data
+int ataReadSmartValues(int device, struct ata_smart_values *data){      
+  
+  if (smartcommandhandler(device, READ_VALUES, 0, (char *)data)){
+    syserror("Error SMART Values Read failed");
+    return -1;
+  }
+
+  // compute checksum
+  if (checksum((unsigned char *)data))
+    checksumwarning("SMART Attribute Data Structure");
+  
+  // byte swap if needed
+  if (isbigendian()){
+    int i;
+    swap2((char *)&(data->revnumber));
+    swap2((char *)&(data->total_time_to_complete_off_line));
+    swap2((char *)&(data->smart_capability));
+    for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
+      struct ata_smart_attribute *x=data->vendor_attributes+i;
+      swap2((char *)&(x->flags));
+    }
+  }
+
+  return 0;
+}
+
+
+// This corrects some quantities that are byte reversed in the SMART
+// SELF TEST LOG
+void fixsamsungselftestlog(struct ata_smart_selftestlog *data){
+  int i;
+  
+  // bytes 508/509 (numbered from 0) swapped (swap of self-test index
+  // with one byte of reserved.
+  swap2((char *)&(data->mostrecenttest));
+
+  // LBA low register (here called 'selftestnumber", containing
+  // information about the TYPE of the self-test) is byte swapped with
+  // Self-test execution status byte.  These are bytes N, N+1 in the
+  // entries.
+  for (i=0; i<21; i++)
+    swap2((char *)&(data->selftest_struct[i].selftestnumber));
+
+  return;
+}
+
+// Reads the Self Test Log (log #6)
+int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){
+
+  // get data from device
+  if (smartcommandhandler(device, READ_LOG, 0x06, (char *)data)){
+    syserror("Error SMART Error Self-Test Log Read failed");
+    return -1;
+  }
+
+  // compute its checksum, and issue a warning if needed
+  if (checksum((unsigned char *)data))
+    checksumwarning("SMART Self-Test Log Structure");
+  
+  // fix firmware bugs in self-test log
+  if (con->fixfirmwarebug == FIX_SAMSUNG)
+    fixsamsungselftestlog(data);
+
+  // fix endian order, if needed
+  if (isbigendian()){
+    int i;
+    swap2((char*)&(data->revnumber));
+    for (i=0; i<21; i++){
+      struct ata_smart_selftestlog_struct *x=data->selftest_struct+i;
+      swap2((char *)&(x->timestamp));
+      swap4((char *)&(x->lbafirstfailure));
+    }
+  }
+
+  return 0;
+}
+
+
+// Reads the Log Directory (log #0).  Note: NO CHECKSUM!!
+int ataReadLogDirectory (int device, struct ata_smart_log_directory *data){     
+  
+  // get data from device
+  if (smartcommandhandler(device, READ_LOG, 0x00, (char *)data)){
+    return -1;
+  }
+
+  // swap endian order if needed
+  if (isbigendian()){
+    swap2((char *)&(data->logversion));
+  }
+  
+  return 0;
+}
+
+
+// Reads the selective self-test log (log #9)
+int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *data){  
+  
+  // get data from device
+  if (smartcommandhandler(device, READ_LOG, 0x09, (char *)data)){
+    syserror("Error SMART Read Selective Self-Test Log failed");
+    return -1;
+  }
+   
+  // compute its checksum, and issue a warning if needed
+  if (checksum((unsigned char *)data))
+    checksumwarning("SMART Selective Self-Test Log Structure");
+  
+  // swap endian order if needed
+  if (isbigendian()){
+    int i;
+    swap2((char *)&(data->logversion));
+    for (i=0;i<5;i++){
+      swap8((char *)&(data->span[i].start));
+      swap8((char *)&(data->span[i].end));
+    }
+    swap8((char *)&(data->currentlba));
+    swap2((char *)&(data->currentspan));
+    swap2((char *)&(data->flags));
+    swap2((char *)&(data->pendingtime));
+  }
+  
+  if (data->logversion != 1)
+    pout("SMART Selective Self-Test Log Data Structure Revision Number (%d) should be 1\n", data->logversion);
+  
+  return 0;
+}
+
+// Writes the selective self-test log (log #9)
+int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv){   
+  int i;
+  struct ata_selective_self_test_log sstlog, *data=&sstlog;
+  unsigned char cksum=0;
+  unsigned char *ptr=(unsigned char *)data;
+  
+  // Read log
+  if (ataReadSelectiveSelfTestLog(device, data)) {
+    pout("Since Read failed, will not attempt to WRITE Selective Self-test Log\n");
+    return -1;
+  }
+  
+  // Fix logversion if needed
+  if (data->logversion !=1) {
+    pout("Error SMART Selective Self-Test Log Data Structure Revision not recognized\n"
+        "Revision number should be 1 but is %d.  To be safe, aborting WRITE LOG\n", data->logversion);
+    return -2;
+  }
+
+  // Host is NOT allowed to write selective self-test log if a selective
+  // self-test is in progress.
+  if (0<data->currentspan && data->currentspan<6 && ((sv->self_test_exec_status)>>4)==15) {
+    pout("Error SMART Selective or other Self-Test in progress.\n");
+    return -4;
+  }
+  
+  // Clear spans
+  for (i=0; i<5; i++)
+    memset(data->span+i, 0, sizeof(struct test_span));
+  
+  // Set spans for testing 
+  for (i=0; i<con->smartselectivenumspans; i++){
+    data->span[i].start = con->smartselectivespan[i][0];
+    data->span[i].end   = con->smartselectivespan[i][1];
+  }
+
+  // host must initialize to zero before initiating selective self-test
+  data->currentlba=0;
+  data->currentspan=0;
+  
+  // Perform off-line scan after selective test?
+  if (1 == con->scanafterselect)
+    // NO
+    data->flags &= ~SELECTIVE_FLAG_DOSCAN;
+  else if (2 == con->scanafterselect)
+    // YES
+    data->flags |= SELECTIVE_FLAG_DOSCAN;
+  
+  // Must clear active and pending flags before writing
+  data->flags &= ~(SELECTIVE_FLAG_ACTIVE);  
+  data->flags &= ~(SELECTIVE_FLAG_PENDING);
+
+  // modify pending time?
+  if (con->pendingtime)
+    data->pendingtime=(unsigned short)(con->pendingtime-1);
+
+  // Set checksum to zero, then compute checksum
+  data->checksum=0;
+  for (i=0; i<512; i++)
+    cksum+=ptr[i];
+  cksum=~cksum;
+  cksum+=1;
+  data->checksum=cksum;
+
+    // swap endian order if needed
+  if (isbigendian()){
+    int i;
+    swap2((char *)&(data->logversion));
+    for (i=0;i<5;i++){
+      swap8((char *)&(data->span[i].start));
+      swap8((char *)&(data->span[i].end));
+    }
+    swap8((char *)&(data->currentlba));
+    swap2((char *)&(data->currentspan));
+    swap2((char *)&(data->flags));
+    swap2((char *)&(data->pendingtime));
+  }
+
+  // write new selective self-test log
+  if (smartcommandhandler(device, WRITE_LOG, 0x09, (char *)data)){
+    syserror("Error Write Selective Self-Test Log failed");
+    return -3;
+  }
+
+  return 0;
+}
+
+// This corrects some quantities that are byte reversed in the SMART
+// ATA ERROR LOG.
+void fixsamsungerrorlog(struct ata_smart_errorlog *data){
+  int i,j;
+  
+  // FIXED IN SAMSUNG -25 FIRMWARE???
+  // Device error count in bytes 452-3
+  swap2((char *)&(data->ata_error_count));
+  
+  // FIXED IN SAMSUNG -22a FIRMWARE
+  // step through 5 error log data structures
+  for (i=0; i<5; i++){
+    // step through 5 command data structures
+    for (j=0; j<5; j++)
+      // Command data structure 4-byte millisec timestamp.  These are
+      // bytes (N+8, N+9, N+10, N+11).
+      swap4((char *)&(data->errorlog_struct[i].commands[j].timestamp));
+    // Error data structure two-byte hour life timestamp.  These are
+    // bytes (N+28, N+29).
+    swap2((char *)&(data->errorlog_struct[i].error_struct.timestamp));
+  }
+  return;
+}
+
+// NEEDED ONLY FOR SAMSUNG -22 (some) -23 AND -24?? FIRMWARE
+void fixsamsungerrorlog2(struct ata_smart_errorlog *data){
+  // Device error count in bytes 452-3
+  swap2((char *)&(data->ata_error_count));
+  return;
+}
+
+// Reads the Summary SMART Error Log (log #1). The Comprehensive SMART
+// Error Log is #2, and the Extended Comprehensive SMART Error log is
+// #3
+int ataReadErrorLog (int device, struct ata_smart_errorlog *data){      
+  
+  // get data from device
+  if (smartcommandhandler(device, READ_LOG, 0x01, (char *)data)){
+    syserror("Error SMART Error Log Read failed");
+    return -1;
+  }
+  
+  // compute its checksum, and issue a warning if needed
+  if (checksum((unsigned char *)data))
+    checksumwarning("SMART ATA Error Log Structure");
+  
+  // Some disks have the byte order reversed in some SMART Summary
+  // Error log entries
+  if (con->fixfirmwarebug == FIX_SAMSUNG)
+    fixsamsungerrorlog(data);
+  else if (con->fixfirmwarebug == FIX_SAMSUNG2)
+    fixsamsungerrorlog2(data);
+
+  // Correct endian order if necessary
+  if (isbigendian()){
+    int i,j;
+    
+    // Device error count in bytes 452-3
+    swap2((char *)&(data->ata_error_count));
+    
+    // step through 5 error log data structures
+    for (i=0; i<5; i++){
+      // step through 5 command data structures
+      for (j=0; j<5; j++)
+        // Command data structure 4-byte millisec timestamp
+        swap4((char *)&(data->errorlog_struct[i].commands[j].timestamp));
+      // Error data structure life timestamp
+      swap2((char *)&(data->errorlog_struct[i].error_struct.timestamp));
+    }
+  }
+  
+  return 0;
+}
+
+int ataReadSmartThresholds (int device, struct ata_smart_thresholds_pvt *data){
+  
+  // get data from device
+  if (smartcommandhandler(device, READ_THRESHOLDS, 0, (char *)data)){
+    syserror("Error SMART Thresholds Read failed");
+    return -1;
+  }
+  
+  // compute its checksum, and issue a warning if needed
+  if (checksum((unsigned char *)data))
+    checksumwarning("SMART Attribute Thresholds Structure");
+  
+  // byte swap if needed
+  if (isbigendian())
+    swap2((char *)&(data->revnumber));
+
+  return 0;
+}
+
+int ataEnableSmart (int device ){       
+  if (smartcommandhandler(device, ENABLE, 0, NULL)){
+    syserror("Error SMART Enable failed");
+    return -1;
+  }
+  return 0;
+}
+
+int ataDisableSmart (int device ){      
+  
+  if (smartcommandhandler(device, DISABLE, 0, NULL)){
+    syserror("Error SMART Disable failed");
+    return -1;
+  }  
+  return 0;
+}
+
+int ataEnableAutoSave(int device){  
+  if (smartcommandhandler(device, AUTOSAVE, 241, NULL)){
+    syserror("Error SMART Enable Auto-save failed");
+    return -1;
+  }
+  return 0;
+}
+
+int ataDisableAutoSave(int device){
+  
+  if (smartcommandhandler(device, AUTOSAVE, 0, NULL)){
+    syserror("Error SMART Disable Auto-save failed");
+    return -1;
+  }
+  return 0;
+}
+
+// In *ALL* ATA standards the Enable/Disable AutoOffline command is
+// marked "OBSOLETE". It is defined in SFF-8035i Revision 2, and most
+// vendors still support it for backwards compatibility. IBM documents
+// it for some drives.
+int ataEnableAutoOffline (int device ){ 
+  
+  /* timer hard coded to 4 hours */  
+  if (smartcommandhandler(device, AUTO_OFFLINE, 248, NULL)){
+    syserror("Error SMART Enable Automatic Offline failed");
+    return -1;
+  }
+  return 0;
+}
+
+// Another Obsolete Command.  See comments directly above, associated
+// with the corresponding Enable command.
+int ataDisableAutoOffline (int device ){        
+  
+  if (smartcommandhandler(device, AUTO_OFFLINE, 0, NULL)){
+    syserror("Error SMART Disable Automatic Offline failed");
+    return -1;
+  }
+  return 0;
+}
+
+// If SMART is enabled, supported, and working, then this call is
+// guaranteed to return 1, else zero.  Note that it should return 1
+// regardless of whether the disk's SMART status is 'healthy' or
+// 'failing'.
+int ataDoesSmartWork(int device){
+  int retval=smartcommandhandler(device, STATUS, 0, NULL);
+
+  if (-1 == retval)
+    return 0;
+
+  return 1;
+}
+
+// This function uses a different interface (DRIVE_TASK) than the
+// other commands in this file.
+int ataSmartStatus2(int device){
+  return smartcommandhandler(device, STATUS_CHECK, 0, NULL);  
+}
+
+// This is the way to execute ALL tests: offline, short self-test,
+// extended self test, with and without captive mode, etc.
+int ataSmartTest(int device, int testtype, struct ata_smart_values *sv) {     
+  char cmdmsg[128],*type,*captive;
+  int errornum, cap, retval, select=0;
+
+  // Boolean, if set, says test is captive
+  cap=testtype & CAPTIVE_MASK;
+
+  // Set up strings that describe the type of test
+  if (cap)
+    captive="captive";
+  else
+    captive="off-line";
+  
+  if (testtype==OFFLINE_FULL_SCAN)
+    type="off-line";
+  else  if (testtype==SHORT_SELF_TEST || testtype==SHORT_CAPTIVE_SELF_TEST)
+    type="Short self-test";
+  else if (testtype==EXTEND_SELF_TEST || testtype==EXTEND_CAPTIVE_SELF_TEST)
+    type="Extended self-test";
+  else if (testtype==CONVEYANCE_SELF_TEST || testtype==CONVEYANCE_CAPTIVE_SELF_TEST)
+    type="Conveyance self-test";
+  else if ((select=(testtype==SELECTIVE_SELF_TEST || testtype==SELECTIVE_CAPTIVE_SELF_TEST)))
+    type="Selective self-test";
+  else
+    type="[Unrecognized] self-test";
+  
+  // If doing a selective self-test, first use WRITE_LOG to write the
+  // selective self-test log.
+  if (select && (retval=ataWriteSelectiveSelfTestLog(device, sv))) {
+    if (retval==-4)
+      pout("Can't start selective self-test without aborting current test: use '-X' option to smartctl.\n");
+    return retval;
+  }
+
+  //  Print ouf message that we are sending the command to test
+  if (testtype==ABORT_SELF_TEST)
+    sprintf(cmdmsg,"Abort SMART off-line mode self-test routine");
+  else
+    sprintf(cmdmsg,"Execute SMART %s routine immediately in %s mode",type,captive);
+  pout("Sending command: \"%s\".\n",cmdmsg);
+
+  if (select) {
+    int i;
+    pout("SPAN         STARTING_LBA           ENDING_LBA\n");
+    for (i = 0; i < con->smartselectivenumspans; i++)
+      pout("   %d %20"PRId64" %20"PRId64"\n", i,
+           con->smartselectivespan[i][0],
+           con->smartselectivespan[i][1]);
+  }
+  
+  // Now send the command to test
+  errornum=smartcommandhandler(device, IMMEDIATE_OFFLINE, testtype, NULL);
+  
+  if (errornum && !(cap && errno==EIO)){
+    char errormsg[128];
+    sprintf(errormsg,"Command \"%s\" failed",cmdmsg); 
+    syserror(errormsg);
+    pout("\n");
+    return -1;
+  }
+  
+  // Since the command succeeded, tell user
+  if (testtype==ABORT_SELF_TEST)
+    pout("Self-testing aborted!\n");
+  else
+    pout("Drive command \"%s\" successful.\nTesting has begun.\n",cmdmsg);
+  return 0;
+}
+
+/* Test Time Functions */
+int TestTime(struct ata_smart_values *data,int testtype){
+  switch (testtype){
+  case OFFLINE_FULL_SCAN:
+    return (int) data->total_time_to_complete_off_line;
+  case SHORT_SELF_TEST:
+  case SHORT_CAPTIVE_SELF_TEST:
+    return (int) data->short_test_completion_time;
+  case EXTEND_SELF_TEST:
+  case EXTEND_CAPTIVE_SELF_TEST:
+    return (int) data->extend_test_completion_time;
+  case CONVEYANCE_SELF_TEST:
+  case CONVEYANCE_CAPTIVE_SELF_TEST:
+    return (int) data->conveyance_test_completion_time;
+  default:
+    return 0;
+  }
+}
+
+// This function tells you both about the ATA error log and the
+// self-test error log capability (introduced in ATA-5).  The bit is
+// poorly documented in the ATA/ATAPI standard.  Starting with ATA-6,
+// SMART error logging is also indicated in bit 0 of DEVICE IDENTIFY
+// word 84 and 87.  Top two bits must match the pattern 01. BEFORE
+// ATA-6 these top two bits still had to match the pattern 01, but the
+// remaining bits were reserved (==0).
+int isSmartErrorLogCapable (struct ata_smart_values *data, struct ata_identify_device *identity){
+
+  unsigned short word84=identity->command_set_extension;
+  unsigned short word87=identity->csf_default;
+  int isata6=identity->major_rev_num & (0x01<<6);
+  int isata7=identity->major_rev_num & (0x01<<7);
+
+  if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x01))
+    return 1;
+  
+  if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x01))
+    return 1;
+  
+  // otherwise we'll use the poorly documented capability bit
+  return data->errorlog_capability & 0x01;
+}
+
+// See previous function.  If the error log exists then the self-test
+// log should (must?) also exist.
+int isSmartTestLogCapable (struct ata_smart_values *data, struct ata_identify_device *identity){
+
+  unsigned short word84=identity->command_set_extension;
+  unsigned short word87=identity->csf_default;
+  int isata6=identity->major_rev_num & (0x01<<6);
+  int isata7=identity->major_rev_num & (0x01<<7);
+
+  if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x02))
+    return 1;
+  
+  if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x02))
+    return 1;
+
+
+  // otherwise we'll use the poorly documented capability bit
+    return data->errorlog_capability & 0x01;
+}
+
+
+int isGeneralPurposeLoggingCapable(struct ata_identify_device *identity){
+  unsigned short word84=identity->command_set_extension;
+  unsigned short word87=identity->csf_default;
+
+  // If bit 14 of word 84 is set to one and bit 15 of word 84 is
+  // cleared to zero, the contents of word 84 contains valid support
+  // information. If not, support information is not valid in this
+  // word.
+  if ((word84>>14) == 0x01)
+    // If bit 5 of word 84 is set to one, the device supports the
+    // General Purpose Logging feature set.
+    return (word84 & (0x01 << 5));
+  
+  // If bit 14 of word 87 is set to one and bit 15 of word 87 is
+  // cleared to zero, the contents of words (87:85) contain valid
+  // information. If not, information is not valid in these words.  
+  if ((word87>>14) == 0x01)
+    // If bit 5 of word 87 is set to one, the device supports
+    // the General Purpose Logging feature set.
+    return (word87 & (0x01 << 5));
+
+  // not capable
+  return 0;
+}
+
+
+// SMART self-test capability is also indicated in bit 1 of DEVICE
+// IDENTIFY word 87 (if top two bits of word 87 match pattern 01).
+// However this was only introduced in ATA-6 (but self-test log was in
+// ATA-5).
+int isSupportExecuteOfflineImmediate(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x01;
+}
+// Note in the ATA-5 standard, the following bit is listed as "Vendor
+// Specific".  So it may not be reliable. The only use of this that I
+// have found is in IBM drives, where it is well-documented.  See for
+// example page 170, section 13.32.1.18 of the IBM Travelstar 40GNX
+// hard disk drive specifications page 164 Revision 1.1 22 Apr 2002.
+int isSupportAutomaticTimer(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x02;
+}
+int isSupportOfflineAbort(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x04;
+}
+int isSupportOfflineSurfaceScan(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x08;
+}
+int isSupportSelfTest (struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x10;
+}
+int isSupportConveyanceSelfTest(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x20;
+}
+int isSupportSelectiveSelfTest(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x40;
+}
+
+
+
+// Loop over all valid attributes.  If they are prefailure attributes
+// and are at or below the threshold value, then return the ID of the
+// first failing attribute found.  Return 0 if all prefailure
+// attributes are in bounds.  The spec says "Bit 0
+// -Pre-failure/advisory - If the value of this bit equals zero, an
+// attribute value less than or equal to its corresponding attribute
+// threshold indicates an advisory condition where the usage or age of
+// the device has exceeded its intended design life period. If the
+// value of this bit equals one, an atribute value less than or equal
+// to its corresponding attribute threshold indicates a pre-failure
+// condition where imminent loss of data is being predicted."
+
+
+// onlyfailed=0 : are or were any age or prefailure attributes <= threshold
+// onlyfailed=1:  are any prefailure attributes <= threshold now
+int ataCheckSmart(struct ata_smart_values *data,
+                  struct ata_smart_thresholds_pvt *thresholds,
+                  int onlyfailed){
+  int i;
+  
+  // loop over all attributes
+  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
+
+    // pointers to disk's values and vendor's thresholds
+    struct ata_smart_attribute *disk=data->vendor_attributes+i;
+    struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i;
+    // consider only valid attributes
+    if (disk->id && thre->id){
+      int failednow,failedever;
+      
+      failednow =disk->current <= thre->threshold;
+      failedever=disk->worst   <= thre->threshold;
+      
+      if (!onlyfailed && failedever)
+        return disk->id;
+      
+      if (onlyfailed && failednow && ATTRIBUTE_FLAGS_PREFAILURE(disk->flags))
+        return disk->id;      
+    }
+  }
+  return 0;
+}
+
+
+
+// This checks the n'th attribute in the attribute list, NOT the
+// attribute with id==n.  If the attribute does not exist, or the
+// attribute is > threshold, then returns zero.  If the attribute is
+// <= threshold (failing) then we the attribute number if it is a
+// prefail attribute.  Else we return minus the attribute number if it
+// is a usage attribute.
+int ataCheckAttribute(struct ata_smart_values *data,
+                      struct ata_smart_thresholds_pvt *thresholds,
+                      int n){
+  struct ata_smart_attribute *disk;
+  struct ata_smart_threshold_entry *thre;
+  
+  if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !data || !thresholds)
+    return 0;
+  
+  // pointers to disk's values and vendor's thresholds
+  disk=data->vendor_attributes+n;
+  thre=thresholds->thres_entries+n;
+
+  if (!disk || !thre)
+    return 0;
+  
+  // consider only valid attributes, check for failure
+  if (!disk->id || !thre->id || (disk->id != thre->id) || disk->current> thre->threshold)
+    return 0;
+  
+  // We have found a failed attribute.  Return positive or negative? 
+  if (ATTRIBUTE_FLAGS_PREFAILURE(disk->flags))
+    return disk->id;
+  else
+    return -1*(disk->id);
+}
+
+
+// This routine prints the raw value of an attribute as a text string
+// into out. It also returns this 48-bit number as a long long.  The
+// array defs[] contains non-zero values if particular attributes have
+// non-default interpretations.
+
+int64_t ataPrintSmartAttribRawValue(char *out, 
+                                    struct ata_smart_attribute *attribute,
+                                    unsigned char *defs){
+  int64_t rawvalue;
+  unsigned word[3];
+  int j;
+  unsigned char select;
+  
+  // convert the six individual bytes to a long long (8 byte) integer.
+  // This is the value that we'll eventually return.
+  rawvalue = 0;
+  for (j=0; j<6; j++) {
+    // This looks a bit roundabout, but is necessary.  Don't
+    // succumb to the temptation to use raw[j]<<(8*j) since under
+    // the normal rules this will be promoted to the native type.
+    // On a 32 bit machine this might then overflow.
+    int64_t temp;
+    temp = attribute->raw[j];
+    temp <<= 8*j;
+    rawvalue |= temp;
+  }
+
+  // convert quantities to three two-byte words
+  for (j=0; j<3; j++){
+    word[j] = attribute->raw[2*j+1];
+    word[j] <<= 8;
+    word[j] |= attribute->raw[2*j];
+  }
+  
+  // if no data array, Attributes have default interpretations
+  if (defs)
+    select=defs[attribute->id];
+  else
+    select=0;
+
+  // Print six one-byte quantities.
+  if (select==253){
+    for (j=0; j<5; j++)
+      out+=sprintf(out, "%d ", attribute->raw[5-j]);
+    out+=sprintf(out, "%d ", attribute->raw[0]);
+    return rawvalue;
+  } 
+  
+  // Print three two-byte quantities
+  if (select==254){
+    out+=sprintf(out, "%d %d %d", word[2], word[1], word[0]); 
+    return rawvalue;
+  } 
+  
+  // Print one six-byte quantity
+  if (select==255){
+    out+=sprintf(out, "%"PRIu64, rawvalue);
+    return rawvalue;
+  }
+
+  // This switch statement is where we handle Raw attributes
+  // that are stored in an unusual vendor-specific format,
+  switch (attribute->id){
+    // Spin-up time
+  case 3:
+    out+=sprintf(out, "%d", word[0]);
+    // if second nonzero then it stores the average spin-up time
+    if (word[1])
+      out+=sprintf(out, " (Average %d)", word[1]);
+    break;
+    // Power on time
+  case 9:
+    if (select==1){
+      // minutes
+      int64_t tmp1=rawvalue/60;
+      int64_t tmp2=rawvalue%60;
+      out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m", tmp1, tmp2);
+    }
+    else if (select==3){
+      // seconds
+      int64_t hours=rawvalue/3600;
+      int64_t minutes=(rawvalue-3600*hours)/60;
+      int64_t seconds=rawvalue%60;
+      out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m+%02"PRIu64"s", hours, minutes, seconds);
+    }
+    else if (select==4){
+      // 30-second counter
+      int64_t tmp1=rawvalue/120;
+      int64_t tmp2=(rawvalue-120*tmp1)/2;
+      out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m", tmp1, tmp2);
+    }
+    else
+      // hours
+      out+=sprintf(out, "%"PRIu64, rawvalue);  //stored in hours
+    break;
+   // Load unload cycles
+  case 193:
+    if (select==1){
+      // loadunload
+      long load  =attribute->raw[0] + (attribute->raw[1]<<8) + (attribute->raw[2]<<16);
+      long unload=attribute->raw[3] + (attribute->raw[4]<<8) + (attribute->raw[5]<<16);
+      out+=sprintf(out, "%lu/%lu", load, unload);
+    }
+    else
+      // associated
+      out+=sprintf(out, "%"PRIu64, rawvalue);
+    break;
+    // Temperature
+  case 194:
+    if (select==1){
+      // ten times temperature in Celsius
+      int deg=word[0]/10;
+      int tenths=word[0]%10;
+      out+=sprintf(out, "%d.%d", deg, tenths);
+    }
+    else if (select==2)
+      // unknown attribute
+      out+=sprintf(out, "%"PRIu64, rawvalue);
+    else {
+      out+=sprintf(out, "%d", word[0]);
+      if (!(rawvalue==word[0])) {
+       int min=word[1]<word[2]?word[1]:word[2];
+       int max=word[1]>word[2]?word[1]:word[2];
+        // The other bytes are in use. Try IBM's model
+        out+=sprintf(out, " (Lifetime Min/Max %d/%d)", min, max);
+      }
+    }
+    break;
+  default:
+    out+=sprintf(out, "%"PRIu64, rawvalue);
+  }
+  
+  // Return the full value
+  return rawvalue;
+}
+
+
+// Note some attribute names appear redundant because different
+// manufacturers use different attribute IDs for an attribute with the
+// same name.  The variable val should contain a non-zero value if a particular
+// attributes has a non-default interpretation.
+void ataPrintSmartAttribName(char *out, unsigned char id, unsigned char *definitions){
+  char *name;
+  unsigned char val;
+
+  // If no data array, use default interpretations
+  if (definitions)
+    val=definitions[id];
+  else
+    val=0;
+
+  switch (id){
+    
+  case 1:
+    name="Raw_Read_Error_Rate";
+    break;
+  case 2:
+    name="Throughput_Performance";
+    break;
+  case 3:
+    name="Spin_Up_Time";
+    break;
+  case 4:
+    name="Start_Stop_Count";
+    break;
+  case 5:
+    name="Reallocated_Sector_Ct";
+    break;
+  case 6:
+    name="Read_Channel_Margin";
+    break;
+  case 7:
+    name="Seek_Error_Rate";
+    break;
+  case 8:
+    name="Seek_Time_Performance";
+    break;
+  case 9:
+    switch (val) {
+    case 1:
+      name="Power_On_Minutes";
+      break;
+    case 2:
+      name="Temperature_Celsius";
+      break;
+    case 3:
+      name="Power_On_Seconds";
+      break;
+    case 4:
+      name="Power_On_Half_Minutes";
+      break;
+    default:
+      name="Power_On_Hours";
+      break;
+    }
+    break;
+  case 10:
+    name="Spin_Retry_Count";
+    break;
+  case 11:
+    name="Calibration_Retry_Count";
+    break;
+  case 12:
+    name="Power_Cycle_Count";
+    break;
+  case 13:
+    name="Read_Soft_Error_Rate";
+    break;
+  case 190:
+    // Western Digital uses this for temperature.
+    // It's identical to Attribute 194 except that it
+    // has a failure threshold set to correspond to the
+    // max allowed operating temperature of the drive, which 
+    // is typically 55C.  So if this attribute has failed
+    // in the past, it indicates that the drive temp exceeded
+    // 55C sometime in the past.
+    name="Temperature_Celsius";
+    break;
+  case 191:
+    name="G-Sense_Error_Rate";
+    break;
+  case 192:
+    switch (val) {
+    case 1:
+      // Fujitsu
+      name="Emergency_Retract_Cycle_Ct";
+      break;
+    default:
+      name="Power-Off_Retract_Count";
+      break;
+    }
+    break;
+  case 193:
+    name="Load_Cycle_Count";
+    break;
+  case 194:
+    switch (val){
+    case 1:
+      // Samsung SV1204H with RK100-13 firmware
+      name="Temperature_Celsius_x10";
+      break;
+    case 2:
+      // for disks with no temperature Attribute
+      name="Unknown_Attribute";
+      break;
+    default:
+      name="Temperature_Celsius";
+      break;
+    }
+    break;
+  case 195:
+    // Fujitsu name="ECC_On_The_Fly_Count";
+    name="Hardware_ECC_Recovered";
+    break;
+  case 196:
+    name="Reallocated_Event_Count";
+    break;
+  case 197:
+    name="Current_Pending_Sector";
+    break;
+  case 198:
+    switch (val){
+    case 1:
+      // Fujitsu
+      name="Off-line_Scan_UNC_Sector_Ct";
+      break;
+    default:
+      name="Offline_Uncorrectable";
+      break;
+    }
+    break;
+  case 199:
+    name="UDMA_CRC_Error_Count";
+    break;
+  case 200:
+    switch (val) {
+    case 1:
+      // Fujitsu MHS2020AT
+      name="Write_Error_Count";
+      break;
+    default:
+      // Western Digital
+      name="Multi_Zone_Error_Rate";
+      break;
+    }
+    break;
+  case 201:
+    switch (val) {
+    case 1:
+      // Fujitsu
+      name="Detected_TA_Count";
+      break;
+    default:
+      name="Soft_Read_Error_Rate";
+      break;
+    }
+    break;
+  case 202:
+    // Fujitsu
+    name="TA_Increase_Count";
+    // Maxtor: Data Address Mark Errors
+    break;
+  case 203:
+    // Fujitsu
+    name="Run_Out_Cancel";
+    // Maxtor: ECC Errors
+    break;
+  case 204:
+    // Fujitsu
+    name="Shock_Count_Write_Opern";
+    // Maxtor: Soft ECC Correction
+    break;
+  case 205:
+    // Fujitsu
+    name="Shock_Rate_Write_Opern";
+    // Maxtor: Thermal Aspirates
+    break;
+  case 206:
+    // Fujitsu
+    name="Flying_Height";
+    break;
+  case 207:
+    // Maxtor
+    name="Spin_High_Current";
+    break;
+  case 208:
+    // Maxtor
+    name="Spin_Buzz";
+    break;
+  case 209:
+    // Maxtor
+    name="Offline_Seek_Performnce";
+    break;
+  case 220:
+    switch (val) {
+    case 1:
+      name="Temperature_Celsius";
+      break;
+    default:
+      name="Disk_Shift";
+      break;
+    }
+    break;
+  case 221:
+    name="G-Sense_Error_Rate";
+    break;
+  case 222:
+    name="Loaded_Hours";
+    break;
+  case 223:
+    name="Load_Retry_Count";
+    break;
+  case 224:
+    name="Load_Friction";
+    break;
+  case 225:
+    name="Load_Cycle_Count";
+    break;
+  case 226:
+    name="Load-in_Time";
+    break;
+  case 227:
+    name="Torq-amp_Count";
+    break;
+  case 228:
+    name="Power-off_Retract_Count";
+    break;
+  case 230:
+    // seen in IBM DTPA-353750
+    name="Head_Amplitude";
+    break;
+  case 231:
+    name="Temperature_Celsius";
+    break;
+  case 240:
+    name="Head_Flying_Hours";
+    break;
+  case 250:
+    name="Read_Error_Retry_Rate";
+    break;
+  default:
+    name="Unknown_Attribute";
+    break;
+  }
+  sprintf(out,"%3hu %s",(short int)id,name);
+  return;
+}
+
+// Returns raw value of Attribute with ID==id. This will be in the
+// range 0 to 2^48-1 inclusive.  If the Attribute does not exist,
+// return -1.
+int64_t ATAReturnAttributeRawValue(unsigned char id, struct ata_smart_values *data) {
+  int i;
+
+  // valid Attribute IDs are in the range 1 to 255 inclusive.
+  if (!id || !data)
+    return -1;
+  
+  // loop over Attributes to see if there is one with the desired ID
+  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+    struct ata_smart_attribute *ap = data->vendor_attributes + i;
+    if (ap->id == id) {
+      // we've found the desired Attribute.  Return its value
+      int64_t rawvalue=0;
+      int j;
+
+      for (j=0; j<6; j++) {
+       // This looks a bit roundabout, but is necessary.  Don't
+       // succumb to the temptation to use raw[j]<<(8*j) since under
+       // the normal rules this will be promoted to the native type.
+       // On a 32 bit machine this might then overflow.
+       int64_t temp;
+       temp = ap->raw[j];
+       temp <<= 8*j;
+       rawvalue |= temp;
+      } // loop over j
+      return rawvalue;
+    } // found desired Attribute
+  } // loop over Attributes
+  
+  // fall-through: no such Attribute found
+  return -1;
+}
+
+// Return Temperature Attribute raw value selected according to possible
+// non-default interpretations. If the Attribute does not exist, return 0
+unsigned char ATAReturnTemperatureValue(/*const*/ struct ata_smart_values *data, const unsigned char *defs){
+  int i;
+  for (i = 0; i < 3; i++) {
+    static const unsigned char ids[3] = {194, 9, 220};
+    unsigned char id = ids[i];
+    unsigned char select = (defs ? defs[id] : 0);
+    int64_t raw; unsigned temp;
+    if (!(   (id == 194 && select <= 1)   // ! -v 194,unknown
+          || (id == 9 && select == 2)     // -v 9,temp
+          || (id == 220 && select == 1))) // -v 220,temp
+      continue;
+    raw = ATAReturnAttributeRawValue(id, data);
+    if (raw < 0)
+      continue;
+    temp = (unsigned short)raw; // ignore possible min/max values in high words
+    if (id == 194 && select == 1) // -v 194,10xCelsius
+      temp = (temp+5) / 10;
+    if (!(0 < temp && temp <= 255))
+      continue;
+    return temp;
+  }
+  // No valid attribute found
+  return 0;
+}
index 01698d22cecba7fe925d980e6efd73daa5c339eb..b97b9daf999a1ac2de7c684c5d099dbdf95710f7 100644 (file)
--- a/atacmds.h
+++ b/atacmds.h
@@ -25,7 +25,7 @@
 #ifndef ATACMDS_H_
 #define ATACMDS_H_
 
-#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.81 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.83 2006/08/25 06:06:24 sxzzsf Exp $\n"
 
 // Macro to check expected size of struct at compile time using a
 // dummy typedef.  On size mismatch, compiler reports a negative array
@@ -490,6 +490,10 @@ void checksumwarning(const char *string);
 // return -1.
 int64_t ATAReturnAttributeRawValue(unsigned char id, struct ata_smart_values *data);
 
+// Return Temperature Attribute raw value selected according to possible
+// non-default interpretations. If the Attribute does not exist, return 0
+unsigned char ATAReturnTemperatureValue(/*const*/ struct ata_smart_values *data, const unsigned char *defs);
+
 
 // This are the meanings of the Self-test failure checkpoint byte.
 // This is in the self-test log at offset 4 bytes into the self-test
@@ -521,6 +525,7 @@ char *create_vendor_attribute_arg_list(void);
 int ata_command_interface(int device, smart_command_set command, int select, char *data);
 int escalade_command_interface(int fd, int escalade_port, int escalade_type, smart_command_set command, int select, char *data);
 int marvell_command_interface(int device, smart_command_set command, int select, char *data);
+int highpoint_command_interface(int device, smart_command_set command, int select, char *data);
 // Optional functions of os_*.c
 #ifdef HAVE_ATA_IDENTIFY_IS_CACHED
 // Return true if OS caches the ATA identify sector
diff --git a/ataprint.c b/ataprint.c
deleted file mode 100644 (file)
index 09cbe4d..0000000
+++ /dev/null
@@ -1,1864 +0,0 @@
-/*
- * ataprint.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
- */
-
-#include "config.h"
-
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-#ifdef HAVE_LOCALE_H
-#include <locale.h>
-#endif // #ifdef HAVE_LOCALE_H
-
-#include "int64.h"
-#include "atacmdnames.h"
-#include "atacmds.h"
-#include "ataprint.h"
-#include "smartctl.h"
-#include "extern.h"
-#include "utility.h"
-#include "knowndrives.h"
-
-const char *ataprint_c_cvsid="$Id: ataprint.c,v 1.164 2006/04/12 14:54:28 ballen4705 Exp $"
-ATACMDNAMES_H_CVSID ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
-
-// for passing global control variables
-extern smartmonctrl *con;
-
-// to hold onto exit code for atexit routine
-extern int exitstatus;
-
-// Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents
-// bytes.
-void swapbytes(char *out, const char *in, size_t n)
-{
-  size_t i;
-
-  for (i = 0; i < n; i += 2) {
-    out[i]   = in[i+1];
-    out[i+1] = in[i];
-  }
-}
-
-// Copies in to out, but removes leading and trailing whitespace.
-void trim(char *out, const char *in)
-{
-  int i, first, last;
-
-  // Find the first non-space character (maybe none).
-  first = -1;
-  for (i = 0; in[i]; i++)
-    if (!isspace((int)in[i])) {
-      first = i;
-      break;
-    }
-
-  if (first == -1) {
-    // There are no non-space characters.
-    out[0] = '\0';
-    return;
-  }
-
-  // Find the last non-space character.
-  for (i = strlen(in)-1; i >= first && isspace((int)in[i]); i--)
-    ;
-  last = i;
-
-  strncpy(out, in+first, last-first+1);
-  out[last-first+1] = '\0';
-}
-
-// Convenience function for formatting strings from ata_identify_device
-void formatdriveidstring(char *out, const char *in, int n)
-{
-  char tmp[65];
-
-  n = n > 64 ? 64 : n;
-#ifndef __NetBSD__
-  swapbytes(tmp, in, n);
-#else
-  strncpy(tmp, in, n); /* NetBSD delivers host byte order strings */  
-#endif
-  tmp[n] = '\0';
-  trim(out, tmp);
-}
-
-void infofound(char *output) {
-  if (*output)
-    pout("%s\n", output);
-  else
-    pout("[No Information Found]\n");
-}
-
-
-/* For the given Command Register (CR) and Features Register (FR), attempts
- * to construct a string that describes the contents of the Status
- * Register (ST) and Error Register (ER).  The string is dynamically allocated
- * memory and the return value is a pointer to this string.  It is up to the
- * caller to free this memory.  If there is insufficient memory or if the
- * meanings of the flags of the error register are not known for the given
- * command then it returns NULL.
- *
- * The meanings of the flags of the error register for all commands are
- * described in the ATA spec and could all be supported here in theory.
- * Currently, only a few commands are supported (those that have been seen
- * to produce errors).  If many more are to be added then this function
- * should probably be redesigned.
- */
-char *construct_st_er_desc(struct ata_smart_errorlog_struct *data) {
-  unsigned char CR=data->commands[4].commandreg;
-  unsigned char FR=data->commands[4].featuresreg;
-  unsigned char ST=data->error_struct.status;
-  unsigned char ER=data->error_struct.error_register;
-  char *s;
-  const char *error_flag[8];
-  int i, print_lba=0, print_sector=0;
-
-  // Set of character strings corresponding to different error codes.
-  // Please keep in alphabetic order if you add more.
-  const char  *abrt  = "ABRT";  // ABORTED
- const char   *amnf  = "AMNF";  // ADDRESS MARK NOT FOUND
- const char   *ccto  = "CCTO";  // COMMAND COMPLETTION TIMED OUT
- const char   *eom   = "EOM";   // END OF MEDIA
- const char   *icrc  = "ICRC";  // INTERFACE CRC ERROR
- const char   *idnf  = "IDNF";  // ID NOT FOUND
- const char   *ili   = "ILI";   // MEANING OF THIS BIT IS COMMAND-SET SPECIFIC
- const char   *mc    = "MC";    // MEDIA CHANGED 
- const char   *mcr   = "MCR";   // MEDIA CHANGE REQUEST
- const char   *nm    = "NM";    // NO MEDIA
- const char   *obs   = "obs";   // OBSOLETE
- const char   *tk0nf = "TK0NF"; // TRACK 0 NOT FOUND
- const char   *unc   = "UNC";   // UNCORRECTABLE
- const char   *wp    = "WP";    // WRITE PROTECTED
-
-  /* If for any command the Device Fault flag of the status register is
-   * not used then used_device_fault should be set to 0 (in the CR switch
-   * below)
-   */
-  int uses_device_fault = 1;
-
-  /* A value of NULL means that the error flag isn't used */
-  for (i = 0; i < 8; i++)
-    error_flag[i] = NULL;
-
-  switch (CR) {
-  case 0x10:  // RECALIBRATE
-    error_flag[2] = abrt;
-    error_flag[1] = tk0nf;
-    break;
-  case 0x20:  /* READ SECTOR(S) */
-  case 0x21:  // READ SECTOR(S)
-  case 0x24:  // READ SECTOR(S) EXT
-  case 0xC4:  /* READ MULTIPLE */
-  case 0x29:  // READ MULTIPLE EXT
-    error_flag[6] = unc;
-    error_flag[5] = mc;
-    error_flag[4] = idnf;
-    error_flag[3] = mcr;
-    error_flag[2] = abrt;
-    error_flag[1] = nm;
-    error_flag[0] = amnf;
-    print_lba=1;
-    break;
-  case 0x22:  // READ LONG (with retries)
-  case 0x23:  // READ LONG (without retries)
-    error_flag[4] = idnf;
-    error_flag[2] = abrt;
-    error_flag[0] = amnf;
-    print_lba=1;
-    break;
-  case 0x2a:  // READ STREAM DMA
-  case 0x2b:  // READ STREAM PIO
-    if (CR==0x2a)
-      error_flag[7] = icrc;
-    error_flag[6] = unc;
-    error_flag[5] = mc;
-    error_flag[4] = idnf;
-    error_flag[3] = mcr;
-    error_flag[2] = abrt;
-    error_flag[1] = nm;
-    error_flag[0] = ccto;
-    print_lba=1;
-    print_sector=(int)data->error_struct.sector_count;
-    break;
-  case 0x3A:  // WRITE STREAM DMA
-  case 0x3B:  // WRITE STREAM PIO
-    if (CR==0x3A)
-      error_flag[7] = icrc;
-    error_flag[6] = wp;
-    error_flag[5] = mc;
-    error_flag[4] = idnf;
-    error_flag[3] = mcr;
-    error_flag[2] = abrt;
-    error_flag[1] = nm;
-    error_flag[0] = ccto;
-    print_lba=1;
-    print_sector=(int)data->error_struct.sector_count;
-    break;
-  case 0x25:  /* READ DMA EXT */
-  case 0x26:  // READ DMA QUEUED EXT
-  case 0xC7:  // READ DMA QUEUED
-  case 0xC8:  /* READ DMA */
-  case 0xC9:
-    error_flag[7] = icrc;
-    error_flag[6] = unc;
-    error_flag[5] = mc;
-    error_flag[4] = idnf;
-    error_flag[3] = mcr;
-    error_flag[2] = abrt;
-    error_flag[1] = nm;
-    error_flag[0] = amnf;
-    print_lba=1;
-    if (CR==0x25 || CR==0xC8)
-      print_sector=(int)data->error_struct.sector_count;
-    break;
-  case 0x30:  /* WRITE SECTOR(S) */
-  case 0x31:  // WRITE SECTOR(S)
-  case 0x34:  // WRITE SECTOR(S) EXT
-  case 0xC5:  /* WRITE MULTIPLE */
-  case 0x39:  // WRITE MULTIPLE EXT
-  case 0xCE:  // WRITE MULTIPLE FUA EXT
-    error_flag[6] = wp;
-    error_flag[5] = mc;
-    error_flag[4] = idnf;
-    error_flag[3] = mcr;
-    error_flag[2] = abrt;
-    error_flag[1] = nm;
-    print_lba=1;
-    break;
-  case 0x32:  // WRITE LONG (with retries)
-  case 0x33:  // WRITE LONG (without retries)
-    error_flag[4] = idnf;
-    error_flag[2] = abrt;
-    print_lba=1;
-    break;
-  case 0x3C:  // WRITE VERIFY
-    error_flag[6] = unc;
-    error_flag[4] = idnf;
-    error_flag[2] = abrt;
-    error_flag[0] = amnf;
-    print_lba=1;
-    break;
-  case 0x40: // READ VERIFY SECTOR(S) with retries
-  case 0x41: // READ VERIFY SECTOR(S) without retries
-  case 0x42: // READ VERIFY SECTOR(S) EXT
-    error_flag[6] = unc;
-    error_flag[5] = mc;
-    error_flag[4] = idnf;
-    error_flag[3] = mcr;
-    error_flag[2] = abrt;
-    error_flag[1] = nm;
-    error_flag[0] = amnf;
-    print_lba=1;
-    break;
-  case 0xA0:  /* PACKET */
-    /* Bits 4-7 are all used for sense key (a 'command packet set specific error
-     * indication' according to the ATA/ATAPI-7 standard), so "Sense key" will
-     * be repeated in the error description string if more than one of those
-     * bits is set.
-     */
-    error_flag[7] = "Sense key (bit 3)",
-    error_flag[6] = "Sense key (bit 2)",
-    error_flag[5] = "Sense key (bit 1)",
-    error_flag[4] = "Sense key (bit 0)",
-    error_flag[2] = abrt;
-    error_flag[1] = eom;
-    error_flag[0] = ili;
-    break;
-  case 0xA1:  /* IDENTIFY PACKET DEVICE */
-  case 0xEF:  /* SET FEATURES */
-  case 0x00:  /* NOP */
-  case 0xC6:  /* SET MULTIPLE MODE */
-    error_flag[2] = abrt;
-    break;
-  case 0x2F:  // READ LOG EXT
-    error_flag[6] = unc;
-    error_flag[4] = idnf;
-    error_flag[2] = abrt;
-    error_flag[0] = obs;
-    break;
-  case 0x3F:  // WRITE LOG EXT
-    error_flag[4] = idnf;
-    error_flag[2] = abrt;
-    error_flag[0] = obs;
-    break;
-  case 0xB0:  /* SMART */
-    switch(FR) {
-    case 0xD0:  // SMART READ DATA
-    case 0xD1:  // SMART READ ATTRIBUTE THRESHOLDS
-    case 0xD5:  /* SMART READ LOG */
-      error_flag[6] = unc;
-      error_flag[4] = idnf;
-      error_flag[2] = abrt;
-      error_flag[0] = obs;
-      break;
-    case 0xD6:  /* SMART WRITE LOG */
-      error_flag[4] = idnf;
-      error_flag[2] = abrt;
-      error_flag[0] = obs;
-      break;
-    case 0xD2:  // Enable/Disable Attribute Autosave
-    case 0xD3:  // SMART SAVE ATTRIBUTE VALUES (ATA-3)
-    case 0xD8:  // SMART ENABLE OPERATIONS
-    case 0xD9:  /* SMART DISABLE OPERATIONS */
-    case 0xDA:  /* SMART RETURN STATUS */
-    case 0xDB:  // Enable/Disable Auto Offline (SFF)
-      error_flag[2] = abrt;
-      break;
-    case 0xD4:  // SMART EXECUTE IMMEDIATE OFFLINE
-      error_flag[4] = idnf;
-      error_flag[2] = abrt;
-      break;
-    default:
-      return NULL;
-      break;
-    }
-    break;
-  case 0xB1:  /* DEVICE CONFIGURATION */
-    switch (FR) {
-    case 0xC0:  /* DEVICE CONFIGURATION RESTORE */
-      error_flag[2] = abrt;
-      break;
-    default:
-      return NULL;
-      break;
-    }
-    break;
-  case 0xCA:  /* WRITE DMA */
-  case 0xCB:
-  case 0x35:  // WRITE DMA EXT
-  case 0x3D:  // WRITE DMA FUA EXT
-  case 0xCC:  // WRITE DMA QUEUED
-  case 0x36:  // WRITE DMA QUEUED EXT
-  case 0x3E:  // WRITE DMA QUEUED FUA EXT
-    error_flag[7] = icrc;
-    error_flag[6] = wp;
-    error_flag[5] = mc;
-    error_flag[4] = idnf;
-    error_flag[3] = mcr;
-    error_flag[2] = abrt;
-    error_flag[1] = nm;
-    error_flag[0] = amnf;
-    print_lba=1;
-    if (CR==0x35)
-      print_sector=(int)data->error_struct.sector_count;
-    break;
-  case 0xE4: // READ BUFFER
-  case 0xE8: // WRITE BUFFER
-    error_flag[2] = abrt;
-    break;
-  default:
-    return NULL;
-  }
-
-  /* 256 bytes -- that'll be plenty (OK, this is lazy!) */
-  if (!(s = (char *)malloc(256)))
-    return s;
-
-  s[0] = '\0';
-
-  /* We ignore any status flags other than Device Fault and Error */
-
-  if (uses_device_fault && (ST & (1 << 5))) {
-    strcat(s, "Device Fault");
-    if (ST & 1)  // Error flag
-      strcat(s, "; ");
-  }
-  if (ST & 1) {  // Error flag
-    int count = 0;
-
-    strcat(s, "Error: ");
-    for (i = 7; i >= 0; i--)
-      if ((ER & (1 << i)) && (error_flag[i])) {
-        if (count++ > 0)
-           strcat(s, ", ");
-        strcat(s, error_flag[i]);
-      }
-  }
-
-  // If the error was a READ or WRITE error, print the Logical Block
-  // Address (LBA) at which the read or write failed.
-  if (print_lba) {
-    char tmp[128];
-    int lba;
-
-    // bits 24-27: bits 0-3 of DH
-    lba   = 0xf & data->error_struct.drive_head;
-    lba <<= 8;
-    // bits 16-23: CH
-    lba  |= data->error_struct.cylinder_high;
-    lba <<= 8;
-    // bits 8-15:  CL
-    lba  |= data->error_struct.cylinder_low;
-    lba <<= 8;
-    // bits 0-7:   SN
-    lba  |= data->error_struct.sector_number;
-
-    // print number of sectors, if known, and append to print string
-    if (print_sector) {
-      snprintf(tmp, 128, " %d sectors", print_sector);
-      strcat(s, tmp);
-    }
-
-    // print LBA, and append to print string
-    snprintf(tmp, 128, " at LBA = 0x%08x = %d", lba, lba);
-    strcat(s, tmp);
-  }
-
-  return s;
-}
-
-// This returns the capacity of a disk drive and also prints this into
-// a string, using comma separators to make it easier to read.  If the
-// drive doesn't support LBA addressing or has no user writable
-// sectors (eg, CDROM or DVD) then routine returns zero.
-uint64_t determine_capacity(struct ata_identify_device *drive, char *pstring){
-
-  unsigned short command_set_2  = drive->command_set_2;
-  unsigned short capabilities_0 = drive->words047_079[49-47];
-  unsigned short sects_16       = drive->words047_079[60-47];
-  unsigned short sects_32       = drive->words047_079[61-47];
-  unsigned short lba_16         = drive->words088_255[100-88];
-  unsigned short lba_32         = drive->words088_255[101-88];
-  unsigned short lba_48         = drive->words088_255[102-88];
-  unsigned short lba_64         = drive->words088_255[103-88];
-  uint64_t capacity_short=0, capacity=0, threedigits, power_of_ten;
-  int started=0,k=1000000000;
-  char separator=',';
-
-  // get correct character to use as thousands separator
-#ifdef HAVE_LOCALE_H
-  struct lconv *currentlocale=NULL;
-  setlocale (LC_ALL, "");
-  currentlocale=localeconv();
-  if (*(currentlocale->thousands_sep))
-    separator=*(currentlocale->thousands_sep);
-#endif // #ifdef HAVE_LOCALE_H
-
-  // if drive supports LBA addressing, determine 32-bit LBA capacity
-  if (capabilities_0 & 0x0200) {
-    capacity_short = (unsigned int)sects_32 << 16 | 
-                     (unsigned int)sects_16 << 0  ;
-    
-    // if drive supports 48-bit addressing, determine THAT capacity
-    if ((command_set_2 & 0xc000) == 0x4000 && (command_set_2 & 0x0400))
-      capacity = (uint64_t)lba_64 << 48 | 
-                (uint64_t)lba_48 << 32 |
-                (uint64_t)lba_32 << 16 | 
-                (uint64_t)lba_16 << 0  ;
-    
-    // choose the larger of the two possible capacities
-    if (capacity_short>capacity)
-      capacity=capacity_short;
-  }
-
-  // turn sectors into bytes
-  capacity_short = (capacity *= 512);
-  
-  // print with locale-specific separators (default is comma)
-  power_of_ten =  k;
-  power_of_ten *= k;
-  
-  for (k=0; k<7; k++) {
-    threedigits = capacity/power_of_ten;
-    capacity   -= threedigits*power_of_ten;
-    if (started)
-      // we have already printed some digits
-      pstring += sprintf(pstring, "%c%03"PRIu64, separator, threedigits);
-    else if (threedigits || k==6) {
-      // these are the first digits that we are printing
-      pstring += sprintf(pstring, "%"PRIu64, threedigits);
-      started = 1;
-    }
-    if (k!=6)
-      power_of_ten /= 1000;
-  }
-  
-  return capacity_short;
-}
-
-void ataPrintDriveInfo (struct ata_identify_device *drive){
-  int version, drivetype;
-  const char *description;
-  char unknown[64], timedatetz[DATEANDEPOCHLEN];
-  unsigned short minorrev;
-  char model[64], serial[64], firm[64], capacity[64];
-
-  // format drive information (with byte swapping as needed)
-  formatdriveidstring(model, (char *)drive->model,40);
-  formatdriveidstring(serial, (char *)drive->serial_no,20);
-  formatdriveidstring(firm, (char *)drive->fw_rev,8);
-
-  // print out model, serial # and firmware versions  (byte-swap ASCI strings)
-  drivetype=lookupdrive(model, firm);
-
-  // Print model family if known
-  if (drivetype>=0 && knowndrives[drivetype].modelfamily)
-    pout("Model Family:     %s\n", knowndrives[drivetype].modelfamily);
-
-  pout("Device Model:     ");
-  infofound(model);
-  pout("Serial Number:    ");
-  infofound(serial);
-  pout("Firmware Version: ");
-  infofound(firm);
-
-  if (determine_capacity(drive, capacity))
-    pout("User Capacity:    %s bytes\n", capacity);
-  
-  // See if drive is recognized
-  pout("Device is:        %s\n", drivetype<0?
-       "Not in smartctl database [for details use: -P showall]":
-       "In smartctl database [for details use: -P show]");
-
-  // now get ATA version info
-  version=ataVersionInfo(&description,drive, &minorrev);
-
-  // unrecognized minor revision code
-  if (!description){
-    if (!minorrev)
-      sprintf(unknown, "Exact ATA specification draft version not indicated");
-    else
-      sprintf(unknown,"Not recognized. Minor revision code: 0x%02hx", minorrev);
-    description=unknown;
-  }
-  
-  
-  // SMART Support was first added into the ATA/ATAPI-3 Standard with
-  // Revision 3 of the document, July 25, 1995.  Look at the "Document
-  // Status" revision commands at the beginning of
-  // http://www.t13.org/project/d2008r6.pdf to see this.  So it's not
-  // enough to check if we are ATA-3.  Version=-3 indicates ATA-3
-  // BEFORE Revision 3.
-  pout("ATA Version is:   %d\n",(int)abs(version));
-  pout("ATA Standard is:  %s\n",description);
-  
-  // print current time and date and timezone
-  dateandtimezone(timedatetz);
-  pout("Local Time is:    %s\n", timedatetz);
-
-  // Print warning message, if there is one
-  if (drivetype>=0 && knowndrives[drivetype].warningmsg)
-    pout("\n==> WARNING: %s\n\n", knowndrives[drivetype].warningmsg);
-
-  if (version>=3)
-    return;
-  
-  pout("SMART is only available in ATA Version 3 Revision 3 or greater.\n");
-  pout("We will try to proceed in spite of this.\n");
-  return;
-}
-
-
-const char *OfflineDataCollectionStatus(unsigned char status_byte){
-  unsigned char stat=status_byte & 0x7f;
-  
-  switch(stat){
-  case 0x00:
-    return "was never started";
-  case 0x02:
-    return "was completed without error";
-  case 0x03:
-    if (status_byte == 0x03)
-      return "is in progress";
-    else
-      return "is in a Reserved state";
-  case 0x04:
-    return "was suspended by an interrupting command from host";
-  case 0x05:
-    return "was aborted by an interrupting command from host";
-  case 0x06:
-    return "was aborted by the device with a fatal error";
-  default:
-    if (stat >= 0x40)
-      return "is in a Vendor Specific state\n";
-    else
-      return "is in a Reserved state\n";
-  }
-}
-  
-  
-  /*  prints verbose value Off-line data collection status byte */
-  void PrintSmartOfflineStatus(struct ata_smart_values *data){
-  
-  pout("Offline data collection status:  (0x%02x)\t",
-       (int)data->offline_data_collection_status);
-    
-  // Off-line data collection status byte is not a reserved
-  // or vendor specific value
-  pout("Offline data collection activity\n"
-       "\t\t\t\t\t%s.\n", OfflineDataCollectionStatus(data->offline_data_collection_status));
-  
-  // Report on Automatic Data Collection Status.  Only IBM documents
-  // this bit.  See SFF 8035i Revision 2 for details.
-  if (data->offline_data_collection_status & 0x80)
-    pout("\t\t\t\t\tAuto Offline Data Collection: Enabled.\n");
-  else
-    pout("\t\t\t\t\tAuto Offline Data Collection: Disabled.\n");
-  
-  return;
-}
-
-void PrintSmartSelfExecStatus(struct ata_smart_values *data)
-{
-   pout("Self-test execution status:      ");
-   
-   switch (data->self_test_exec_status >> 4)
-   {
-      case 0:
-        pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t",
-                (int)data->self_test_exec_status);
-        pout("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n");
-        break;
-       case 1:
-         pout("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t",
-                 (int)data->self_test_exec_status);
-         pout("the host.\n");
-         break;
-       case 2:
-         pout("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t",
-                 (int)data->self_test_exec_status);
-         pout("by the host with a hard or soft reset.\n");
-         break;
-       case 3:
-          pout("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t",
-                  (int)data->self_test_exec_status);
-          pout("occurred while the device was executing\n\t\t\t\t\t");
-          pout("its self-test routine and the device \n\t\t\t\t\t");
-          pout("was unable to complete the self-test \n\t\t\t\t\t");
-          pout("routine.\n");
-          break;
-       case 4:
-          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
-                  (int)data->self_test_exec_status);
-          pout("a test element that failed and the test\n\t\t\t\t\t");
-          pout("element that failed is not known.\n");
-          break;
-       case 5:
-          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
-                  (int)data->self_test_exec_status);
-          pout("the electrical element of the test\n\t\t\t\t\t");
-          pout("failed.\n");
-          break;
-       case 6:
-          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
-                  (int)data->self_test_exec_status);
-          pout("the servo (and/or seek) element of the \n\t\t\t\t\t");
-          pout("test failed.\n");
-          break;
-       case 7:
-          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
-                  (int)data->self_test_exec_status);
-          pout("the read element of the test failed.\n");
-          break;
-       case 15:
-          pout("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t",
-                  (int)data->self_test_exec_status);
-          pout("%1d0%% of test remaining.\n", 
-                  (int)(data->self_test_exec_status & 0x0f));
-          break;
-       default:
-          pout("(%4d)\tReserved.\n",
-                  (int)data->self_test_exec_status);
-          break;
-   }
-        
-}
-
-
-
-void PrintSmartTotalTimeCompleteOffline ( struct ata_smart_values *data){
-  pout("Total time to complete Offline \n");
-  pout("data collection: \t\t (%4d) seconds.\n", 
-       (int)data->total_time_to_complete_off_line);
-}
-
-
-
-void PrintSmartOfflineCollectCap(struct ata_smart_values *data){
-  pout("Offline data collection\n");
-  pout("capabilities: \t\t\t (0x%02x) ",
-       (int)data->offline_data_collection_capability);
-  
-  if (data->offline_data_collection_capability == 0x00){
-    pout("\tOffline data collection not supported.\n");
-  } 
-  else {
-    pout( "%s\n", isSupportExecuteOfflineImmediate(data)?
-          "SMART execute Offline immediate." :
-          "No SMART execute Offline immediate.");
-    
-    pout( "\t\t\t\t\t%s\n", isSupportAutomaticTimer(data)? 
-          "Auto Offline data collection on/off support.":
-          "No Auto Offline data collection support.");
-    
-    pout( "\t\t\t\t\t%s\n", isSupportOfflineAbort(data)? 
-          "Abort Offline collection upon new\n\t\t\t\t\tcommand.":
-          "Suspend Offline collection upon new\n\t\t\t\t\tcommand.");
-    
-    pout( "\t\t\t\t\t%s\n", isSupportOfflineSurfaceScan(data)? 
-          "Offline surface scan supported.":
-          "No Offline surface scan supported.");
-    
-    pout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)? 
-          "Self-test supported.":
-          "No Self-test supported.");
-
-    pout( "\t\t\t\t\t%s\n", isSupportConveyanceSelfTest(data)? 
-          "Conveyance Self-test supported.":
-          "No Conveyance Self-test supported.");
-
-    pout( "\t\t\t\t\t%s\n", isSupportSelectiveSelfTest(data)? 
-          "Selective Self-test supported.":
-          "No Selective Self-test supported.");
-  }
-}
-
-
-
-void PrintSmartCapability ( struct ata_smart_values *data)
-{
-   pout("SMART capabilities:            ");
-   pout("(0x%04x)\t", (int)data->smart_capability);
-   
-   if (data->smart_capability == 0x00)
-   {
-       pout("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n");
-   } 
-   else 
-   {
-        
-      pout( "%s\n", (data->smart_capability & 0x01)? 
-              "Saves SMART data before entering\n\t\t\t\t\tpower-saving mode.":
-              "Does not save SMART data before\n\t\t\t\t\tentering power-saving mode.");
-                
-      if ( data->smart_capability & 0x02 )
-      {
-          pout("\t\t\t\t\tSupports SMART auto save timer.\n");
-      }
-   }
-}
-
-void PrintSmartErrorLogCapability (struct ata_smart_values *data, struct ata_identify_device *identity)
-{
-
-   pout("Error logging capability:       ");
-    
-   if ( isSmartErrorLogCapable(data, identity) )
-   {
-      pout(" (0x%02x)\tError logging supported.\n",
-               (int)data->errorlog_capability);
-   }
-   else {
-       pout(" (0x%02x)\tError logging NOT supported.\n",
-                (int)data->errorlog_capability);
-   }
-}
-
-void PrintSmartShortSelfTestPollingTime(struct ata_smart_values *data){
-  pout("Short self-test routine \n");
-  if (isSupportSelfTest(data))
-    pout("recommended polling time: \t (%4d) minutes.\n", 
-         (int)data->short_test_completion_time);
-  else
-    pout("recommended polling time: \t        Not Supported.\n");
-}
-
-void PrintSmartExtendedSelfTestPollingTime(struct ata_smart_values *data){
-  pout("Extended self-test routine\n");
-  if (isSupportSelfTest(data))
-    pout("recommended polling time: \t (%4d) minutes.\n", 
-         (int)data->extend_test_completion_time);
-  else
-    pout("recommended polling time: \t        Not Supported.\n");
-}
-
-void PrintSmartConveyanceSelfTestPollingTime(struct ata_smart_values *data){
-  pout("Conveyance self-test routine\n");
-  if (isSupportConveyanceSelfTest(data))
-    pout("recommended polling time: \t (%4d) minutes.\n", 
-         (int)data->conveyance_test_completion_time);
-  else
-    pout("recommended polling time: \t        Not Supported.\n");
-}
-
-// onlyfailed=0 : print all attribute values
-// onlyfailed=1:  just ones that are currently failed and have prefailure bit set
-// onlyfailed=2:  ones that are failed, or have failed with or without prefailure bit set
-void PrintSmartAttribWithThres (struct ata_smart_values *data, 
-                                struct ata_smart_thresholds_pvt *thresholds,
-                                int onlyfailed){
-  int i;
-  int needheader=1;
-  char rawstring[64];
-    
-  // step through all vendor attributes
-  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
-    char *status;
-    struct ata_smart_attribute *disk=data->vendor_attributes+i;
-    struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i;
-    
-    // consider only valid attributes (allowing some screw-ups in the
-    // thresholds page data to slip by)
-    if (disk->id){
-      char *type, *update;
-      int failednow,failedever;
-      char attributename[64];
-
-      failednow = (disk->current <= thre->threshold);
-      failedever= (disk->worst   <= thre->threshold);
-      
-      // These break out of the loop if we are only printing certain entries...
-      if (onlyfailed==1 && (!ATTRIBUTE_FLAGS_PREFAILURE(disk->flags) || !failednow))
-        continue;
-      
-      if (onlyfailed==2 && !failedever)
-        continue;
-      
-      // print header only if needed
-      if (needheader){
-        if (!onlyfailed){
-          pout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber);
-          pout("Vendor Specific SMART Attributes with Thresholds:\n");
-        }
-        pout("ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE\n");
-        needheader=0;
-      }
-      
-      // is this Attribute currently failed, or has it ever failed?
-      if (failednow)
-        status="FAILING_NOW";
-      else if (failedever)
-        status="In_the_past";
-      else
-        status="    -";
-
-      // Print name of attribute
-      ataPrintSmartAttribName(attributename,disk->id, con->attributedefs);
-      pout("%-28s",attributename);
-
-      // printing line for each valid attribute
-      type=ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)?"Pre-fail":"Old_age";
-      update=ATTRIBUTE_FLAGS_ONLINE(disk->flags)?"Always":"Offline";
-
-      pout("0x%04x   %.3d   %.3d   %.3d    %-10s%-9s%-12s", 
-             (int)disk->flags, (int)disk->current, (int)disk->worst,
-             (int)thre->threshold, type, update, status);
-
-      // print raw value of attribute
-      ataPrintSmartAttribRawValue(rawstring, disk, con->attributedefs);
-      pout("%s\n", rawstring);
-      
-      // print a warning if there is inconsistency here!
-      if (disk->id != thre->id){
-        char atdat[64],atthr[64];
-        ataPrintSmartAttribName(atdat, disk->id, con->attributedefs);
-        ataPrintSmartAttribName(atthr, thre->id, con->attributedefs);
-        pout("%-28s<== Data Page      |  WARNING: PREVIOUS ATTRIBUTE HAS TWO\n",atdat);
-        pout("%-28s<== Threshold Page |  INCONSISTENT IDENTITIES IN THE DATA\n",atthr);
-      }
-    }
-  }
-  if (!needheader) pout("\n");
-}
-
-void ataPrintGeneralSmartValues(struct ata_smart_values *data, struct ata_identify_device *drive){
-  pout("General SMART Values:\n");
-  
-  PrintSmartOfflineStatus(data); 
-  
-  if (isSupportSelfTest(data)){
-    PrintSmartSelfExecStatus (data);
-  }
-  
-  PrintSmartTotalTimeCompleteOffline(data);
-  PrintSmartOfflineCollectCap(data);
-  PrintSmartCapability(data);
-  
-  PrintSmartErrorLogCapability(data, drive);
-
-  pout( "\t\t\t\t\t%s\n", isGeneralPurposeLoggingCapable(drive)?
-        "General Purpose Logging supported.":
-        "No General Purpose Logging support.");
-
-  if (isSupportSelfTest(data)){
-    PrintSmartShortSelfTestPollingTime (data);
-    PrintSmartExtendedSelfTestPollingTime (data);
-  }
-  if (isSupportConveyanceSelfTest(data))
-    PrintSmartConveyanceSelfTestPollingTime (data);
-  
-  pout("\n");
-}
-
-int ataPrintLogDirectory(struct ata_smart_log_directory *data){
-  int i;
-  char *name;
-
-  pout("SMART Log Directory Logging Version %d%s\n",
-       data->logversion, data->logversion==1?" [multi-sector log support]":"");
-  for (i=0; i<=255; i++){
-    int numsect;
-    
-    // Directory log length
-    numsect = i? data->entry[i-1].numsectors : 1;
-    
-    // If the log is not empty, what is it's name
-    if (numsect){
-      switch (i) {
-      case 0:
-        name="Log Directory"; break;
-      case 1:
-        name="Summary SMART error log"; break;
-      case 2:
-        name="Comprehensive SMART error log"; break;
-      case 3:
-        name="Extended Comprehensive SMART error log"; break;
-      case 6:
-        name="SMART self-test log"; break;
-      case 7:
-        name="Extended self-test log"; break;
-      case 9:
-        name="Selective self-test log"; break;
-      case 0x20:
-        name="Streaming performance log"; break;
-      case 0x21:
-        name="Write stream error log"; break;
-      case 0x22:
-        name="Read stream error log"; break;
-      case 0x23:
-        name="Delayed sector log"; break;
-      default:
-        if (0xa0<=i && i<=0xbf) 
-          name="Device vendor specific log";
-        else if (0x80<=i && i<=0x9f)
-          name="Host vendor specific log";
-        else
-          name="Reserved log";
-        break;
-      }
-
-      // print name and length of log
-      pout("Log at address 0x%02x has %03d sectors [%s]\n",
-           i, numsect, name);
-    }
-  }
-  return 0;
-}
-
-// returns number of errors
-int ataPrintSmartErrorlog(struct ata_smart_errorlog *data){
-  int k;
-
-  pout("SMART Error Log Version: %d\n", (int)data->revnumber);
-  
-  // if no errors logged, return
-  if (!data->error_log_pointer){
-    pout("No Errors Logged\n\n");
-    return 0;
-  }
-  PRINT_ON(con);
-  // If log pointer out of range, return
-  if (data->error_log_pointer>5){
-    pout("Invalid Error Log index = 0x%02x (T13/1321D rev 1c "
-         "Section 8.41.6.8.2.2 gives valid range from 1 to 5)\n\n",
-         (int)data->error_log_pointer);
-    return 0;
-  }
-
-  // Some internal consistency checking of the data structures
-  if ((data->ata_error_count-data->error_log_pointer)%5 && con->fixfirmwarebug != FIX_SAMSUNG2) {
-    pout("Warning: ATA error count %d inconsistent with error log pointer %d\n\n",
-         data->ata_error_count,data->error_log_pointer);
-  }
-  
-  // starting printing error log info
-  if (data->ata_error_count<=5)
-    pout( "ATA Error Count: %d\n", (int)data->ata_error_count);
-  else
-    pout( "ATA Error Count: %d (device log contains only the most recent five errors)\n",
-           (int)data->ata_error_count);
-  PRINT_OFF(con);
-  pout("\tCR = Command Register [HEX]\n"
-       "\tFR = Features Register [HEX]\n"
-       "\tSC = Sector Count Register [HEX]\n"
-       "\tSN = Sector Number Register [HEX]\n"
-       "\tCL = Cylinder Low Register [HEX]\n"
-       "\tCH = Cylinder High Register [HEX]\n"
-       "\tDH = Device/Head Register [HEX]\n"
-       "\tDC = Device Command Register [HEX]\n"
-       "\tER = Error register [HEX]\n"
-       "\tST = Status register [HEX]\n"
-       "Powered_Up_Time is measured from power on, and printed as\n"
-       "DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes,\n"
-       "SS=sec, and sss=millisec. It \"wraps\" after 49.710 days.\n\n");
-  
-  // now step through the five error log data structures (table 39 of spec)
-  for (k = 4; k >= 0; k-- ) {
-    char *st_er_desc;
-
-    // The error log data structure entries are a circular buffer
-    int j, i=(data->error_log_pointer+k)%5;
-    struct ata_smart_errorlog_struct *elog=data->errorlog_struct+i;
-    struct ata_smart_errorlog_error_struct *summary=&(elog->error_struct);
-
-    // Spec says: unused error log structures shall be zero filled
-    if (nonempty((unsigned char*)elog,sizeof(*elog))){
-      // Table 57 of T13/1532D Volume 1 Revision 3
-      char *msgstate;
-      int bits=summary->state & 0x0f;
-      int days = (int)summary->timestamp/24;
-
-      switch (bits){
-      case 0x00: msgstate="in an unknown state";break;
-      case 0x01: msgstate="sleeping"; break;
-      case 0x02: msgstate="in standby mode"; break;
-      case 0x03: msgstate="active or idle"; break;
-      case 0x04: msgstate="doing SMART Offline or Self-test"; break;
-      default:   
-        if (bits<0x0b)
-          msgstate="in a reserved state";
-        else
-          msgstate="in a vendor specific state";
-      }
-
-      // See table 42 of ATA5 spec
-      PRINT_ON(con);
-      pout("Error %d occurred at disk power-on lifetime: %d hours (%d days + %d hours)\n",
-             (int)(data->ata_error_count+k-4), (int)summary->timestamp, days, (int)(summary->timestamp-24*days));
-      PRINT_OFF(con);
-      pout("  When the command that caused the error occurred, the device was %s.\n\n",msgstate);
-      pout("  After command completion occurred, registers were:\n"
-           "  ER ST SC SN CL CH DH\n"
-           "  -- -- -- -- -- -- --\n"
-           "  %02x %02x %02x %02x %02x %02x %02x",
-           (int)summary->error_register,
-           (int)summary->status,
-           (int)summary->sector_count,
-           (int)summary->sector_number,
-           (int)summary->cylinder_low,
-           (int)summary->cylinder_high,
-           (int)summary->drive_head);
-      // Add a description of the contents of the status and error registers
-      // if possible
-      st_er_desc = construct_st_er_desc(elog);
-      if (st_er_desc) {
-        pout("  %s", st_er_desc);
-        free(st_er_desc);
-      }
-      pout("\n\n");
-      pout("  Commands leading to the command that caused the error were:\n"
-           "  CR FR SC SN CL CH DH DC   Powered_Up_Time  Command/Feature_Name\n"
-           "  -- -- -- -- -- -- -- --  ----------------  --------------------\n");
-      for ( j = 4; j >= 0; j--){
-        struct ata_smart_errorlog_command_struct *thiscommand=elog->commands+j;
-
-        // Spec says: unused data command structures shall be zero filled
-        if (nonempty((unsigned char*)thiscommand,sizeof(*thiscommand))) {
-         char timestring[32];
-         
-         // Convert integer milliseconds to a text-format string
-         MsecToText(thiscommand->timestamp, timestring);
-         
-          pout("  %02x %02x %02x %02x %02x %02x %02x %02x  %16s  %s\n",
-               (int)thiscommand->commandreg,
-               (int)thiscommand->featuresreg,
-               (int)thiscommand->sector_count,
-               (int)thiscommand->sector_number,
-               (int)thiscommand->cylinder_low,
-               (int)thiscommand->cylinder_high,
-               (int)thiscommand->drive_head,
-               (int)thiscommand->devicecontrolreg,
-              timestring,
-               look_up_ata_command(thiscommand->commandreg, thiscommand->featuresreg));
-       }
-      }
-      pout("\n");
-    }
-  }
-  PRINT_ON(con);
-  if (con->printing_switchable)
-    pout("\n");
-  PRINT_OFF(con);
-  return data->ata_error_count;  
-}
-
-void ataPrintSelectiveSelfTestLog(struct ata_selective_self_test_log *log, struct ata_smart_values *sv) {
-  int i,field1,field2;
-  char *msg;
-  char tmp[64];
-  uint64_t maxl=0,maxr=0;
-  uint64_t current=log->currentlba;
-  uint64_t currentend=current+65535;
-
-  // print data structure revision number
-  pout("SMART Selective self-test log data structure revision number %d\n",(int)log->logversion);
-  if (1 != log->logversion)
-    pout("Warning: ATA Specification requires selective self-test log data structure revision number = 1\n");
-  
-  switch((sv->self_test_exec_status)>>4){
-  case  0:msg="Completed";
-    break;
-  case  1:msg="Aborted_by_host";
-    break;
-  case  2:msg="Interrupted";
-    break;
-  case  3:msg="Fatal_error";
-    break;
-  case  4:msg="Completed_unknown_failure";
-    break;
-  case  5:msg="Completed_electrical_failure";
-    break;
-  case  6:msg="Completed_servo/seek_failure";
-    break;
-  case  7:msg="Completed_read_failure";
-    break;
-  case  8:msg="Completed_handling_damage??";
-    break;
-  case 15:msg="Self_test_in_progress";
-    break;
-  default:msg="Unknown_status ";
-    break;
-  }
-
-  // find the number of columns needed for printing. If in use, the
-  // start/end of span being read-scanned...
-  if (log->currentspan>5) {
-    maxl=current;
-    maxr=currentend;
-  }
-  for (i=0; i<5; i++) {
-    uint64_t start=log->span[i].start;
-    uint64_t end  =log->span[i].end; 
-    // ... plus max start/end of each of the five test spans.
-    if (start>maxl)
-      maxl=start;
-    if (end > maxr)
-      maxr=end;
-  }
-  
-  // we need at least 7 characters wide fields to accomodate the
-  // labels
-  if ((field1=snprintf(tmp,64, "%"PRIu64, maxl))<7)
-    field1=7;
-  if ((field2=snprintf(tmp,64, "%"PRIu64, maxr))<7)
-    field2=7;
-
-  // now print the five test spans
-  pout(" SPAN  %*s  %*s  CURRENT_TEST_STATUS\n", field1, "MIN_LBA", field2, "MAX_LBA");
-
-  for (i=0; i<5; i++) {
-    uint64_t start=log->span[i].start;
-    uint64_t end=log->span[i].end;
-    
-    if ((i+1)==(int)log->currentspan)
-      // this span is currently under test
-      pout("    %d  %*"PRIu64"  %*"PRIu64"  %s [%01d0%% left] (%"PRIu64"-%"PRIu64")\n",
-          i+1, field1, start, field2, end, msg,
-          (int)(sv->self_test_exec_status & 0x7), current, currentend);
-    else
-      // this span is not currently under test
-      pout("    %d  %*"PRIu64"  %*"PRIu64"  Not_testing\n",
-          i+1, field1, start, field2, end);
-  }  
-  
-  // if we are currently read-scanning, print LBAs and the status of
-  // the read scan
-  if (log->currentspan>5)
-    pout("%5d  %*"PRIu64"  %*"PRIu64"  Read_scanning %s\n",
-        (int)log->currentspan, field1, current, field2, currentend,
-        OfflineDataCollectionStatus(sv->offline_data_collection_status));
-  
-  /* Print selective self-test flags.  Possible flag combinations are
-     (numbering bits from 0-15):
-     Bit-1 Bit-3   Bit-4
-     Scan  Pending Active
-     0     *       *       Don't scan
-     1     0       0       Will carry out scan after selective test
-     1     1       0       Waiting to carry out scan after powerup
-     1     0       1       Currently scanning       
-     1     1       1       Currently scanning
-  */
-  
-  pout("Selective self-test flags (0x%x):\n", (unsigned int)log->flags);
-  if (log->flags & SELECTIVE_FLAG_DOSCAN) {
-    if (log->flags & SELECTIVE_FLAG_ACTIVE)
-      pout("  Currently read-scanning the remainder of the disk.\n");
-    else if (log->flags & SELECTIVE_FLAG_PENDING)
-      pout("  Read-scan of remainder of disk interrupted; will resume %d min after power-up.\n",
-          (int)log->pendingtime);
-    else
-      pout("  After scanning selected spans, read-scan remainder of disk.\n");
-  }
-  else
-    pout("  After scanning selected spans, do NOT read-scan remainder of disk.\n");
-  
-  // print pending time
-  pout("If Selective self-test is pending on power-up, resume after %d minute delay.\n",
-       (int)log->pendingtime);
-
-  return; 
-}
-
-// return value is:
-// bottom 8 bits: number of entries found where self-test showed an error
-// remaining bits: if nonzero, power on hours of last self-test where error was found
-int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog *data,int allentries){
-  int i,j,noheaderprinted=1;
-  int retval=0, hours=0, testno=0;
-
-  if (allentries)
-    pout("SMART Self-test log structure revision number %d\n",(int)data->revnumber);
-  if ((data->revnumber!=0x0001) && allentries && con->fixfirmwarebug != FIX_SAMSUNG)
-    pout("Warning: ATA Specification requires self-test log structure revision number = 1\n");
-  if (data->mostrecenttest==0){
-    if (allentries)
-      pout("No self-tests have been logged.  [To run self-tests, use: smartctl -t]\n\n");
-    return 0;
-  }
-
-  // print log      
-  for (i=20;i>=0;i--){    
-    struct ata_smart_selftestlog_struct *log;
-
-    // log is a circular buffer
-    j=(i+data->mostrecenttest)%21;
-    log=data->selftest_struct+j;
-
-    if (nonempty((unsigned char*)log,sizeof(*log))){
-      char *msgtest,*msgstat,percent[64],firstlba[64];
-      int errorfound=0;
-      
-      // count entry based on non-empty structures -- needed for
-      // Seagate only -- other vendors don't have blank entries 'in
-      // the middle'
-      testno++;
-
-      // test name
-      switch(log->selftestnumber){
-      case   0: msgtest="Offline            "; break;
-      case   1: msgtest="Short offline      "; break;
-      case   2: msgtest="Extended offline   "; break;
-      case   3: msgtest="Conveyance offline "; break;
-      case   4: msgtest="Selective offline  "; break;
-      case 127: msgtest="Abort offline test "; break;
-      case 129: msgtest="Short captive      "; break;
-      case 130: msgtest="Extended captive   "; break;
-      case 131: msgtest="Conveyance captive "; break;
-      case 132: msgtest="Selective captive  "; break;
-      default:  
-        if ( log->selftestnumber>=192 ||
-            (log->selftestnumber>= 64 && log->selftestnumber<=126))
-          msgtest="Vendor offline     ";
-        else
-          msgtest="Reserved offline   ";
-      }
-      
-      // test status
-      switch((log->selfteststatus)>>4){
-      case  0:msgstat="Completed without error      "; break;
-      case  1:msgstat="Aborted by host              "; break;
-      case  2:msgstat="Interrupted (host reset)     "; break;
-      case  3:msgstat="Fatal or unknown error       "; errorfound=1; break;
-      case  4:msgstat="Completed: unknown failure   "; errorfound=1; break;
-      case  5:msgstat="Completed: electrical failure"; errorfound=1; break;
-      case  6:msgstat="Completed: servo/seek failure"; errorfound=1; break;
-      case  7:msgstat="Completed: read failure      "; errorfound=1; break;
-      case  8:msgstat="Completed: handling damage?? "; errorfound=1; break;
-      case 15:msgstat="Self-test routine in progress"; break;
-      default:msgstat="Unknown/reserved test status ";
-      }
-
-      retval+=errorfound;
-      sprintf(percent,"%1d0%%",(log->selfteststatus)&0xf);
-
-      // T13/1321D revision 1c: (Data structure Rev #1)
-
-      //The failing LBA shall be the LBA of the uncorrectable sector
-      //that caused the test to fail. If the device encountered more
-      //than one uncorrectable sector during the test, this field
-      //shall indicate the LBA of the first uncorrectable sector
-      //encountered. If the test passed or the test failed for some
-      //reason other than an uncorrectable sector, the value of this
-      //field is undefined.
-
-      // This is true in ALL ATA-5 specs
-      
-      if (!errorfound || log->lbafirstfailure==0xffffffff || log->lbafirstfailure==0x00000000)
-        sprintf(firstlba,"%s","-");
-      else      
-        sprintf(firstlba,"%u",log->lbafirstfailure);
-
-      // print out a header if needed
-      if (noheaderprinted && (allentries || errorfound)){
-        pout("Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error\n");
-        noheaderprinted=0;
-      }
-      
-      // print out an entry, either if we are printing all entries OR
-      // if an error was found
-      if (allentries || errorfound)
-        pout("#%2d  %s %s %s  %8d         %s\n", testno, msgtest, msgstat, percent, (int)log->timestamp, firstlba);
-
-      // keep track of time of most recent error
-      if (errorfound && !hours)
-        hours=log->timestamp;
-    }
-  }
-  if (!allentries && retval)
-    pout("\n");
-
-  hours = hours << 8;
-  return (retval | hours);
-}
-
-void ataPseudoCheckSmart ( struct ata_smart_values *data, 
-                           struct ata_smart_thresholds_pvt *thresholds) {
-  int i;
-  int failed = 0;
-  for (i = 0 ; i < NUMBER_ATA_SMART_ATTRIBUTES ; i++) {
-    if (data->vendor_attributes[i].id &&   
-        thresholds->thres_entries[i].id &&
-        ATTRIBUTE_FLAGS_PREFAILURE(data->vendor_attributes[i].flags) &&
-        (data->vendor_attributes[i].current <= thresholds->thres_entries[i].threshold) &&
-        (thresholds->thres_entries[i].threshold != 0xFE)){
-      pout("Attribute ID %d Failed\n",(int)data->vendor_attributes[i].id);
-      failed = 1;
-    } 
-  }   
-  pout("%s\n", ( failed )?
-         "SMART overall-health self-assessment test result: FAILED!\n"
-         "Drive failure expected in less than 24 hours. SAVE ALL DATA":
-         "SMART overall-health self-assessment test result: PASSED");
-}
-
-
-// Compares failure type to policy in effect, and either exits or
-// simply returns to the calling routine.
-void failuretest(int type, int returnvalue){
-
-  // If this is an error in an "optional" SMART command
-  if (type==OPTIONAL_CMD){
-    if (con->conservative){
-      pout("An optional SMART command failed: exiting.  Remove '-T conservative' option to continue.\n");
-      EXIT(returnvalue);
-    }
-    return;
-  }
-
-  // If this is an error in a "mandatory" SMART command
-  if (type==MANDATORY_CMD){
-    if (con->permissive--)
-      return;
-    pout("A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.\n");
-    EXIT(returnvalue);
-  }
-
-  pout("Smartctl internal error in failuretest(type=%d). Please contact developers at " PACKAGE_HOMEPAGE "\n",type);
-  EXIT(returnvalue|FAILCMD);
-}
-
-// Used to warn users about invalid checksums.  Action to be taken may be
-// altered by the user.
-void checksumwarning(const char *string){
-  // user has asked us to ignore checksum errors
-  if (con->checksumignore)
-        return;
-
-  pout("Warning! %s error: invalid SMART checksum.\n",string);
-
-  // user has asked us to fail on checksum errors
-  if (con->checksumfail)
-    EXIT(FAILSMART);
-
-  return;
-}
-
-// Initialize to zero just in case some SMART routines don't work
-struct ata_identify_device drive;
-struct ata_smart_values smartval;
-struct ata_smart_thresholds_pvt smartthres;
-struct ata_smart_errorlog smarterror;
-struct ata_smart_selftestlog smartselftest;
-
-int ataPrintMain (int fd){
-  int timewait,code;
-  int returnval=0, retid=0, supported=0, needupdate=0;
-
-  // Start by getting Drive ID information.  We need this, to know if SMART is supported.
-  if ((retid=ataReadHDIdentity(fd,&drive))<0){
-    pout("Smartctl: Device Read Identity Failed (not an ATA/ATAPI device)\n\n");
-    failuretest(MANDATORY_CMD, returnval|=FAILID);
-  }
-
-  // If requested, show which presets would be used for this drive and exit.
-  if (con->showpresets) {
-    showpresets(&drive);
-    EXIT(0);
-  }
-
-  // Use preset vendor attribute options unless user has requested otherwise.
-  if (!con->ignorepresets){
-    unsigned char *charptr;
-    if ((charptr=con->attributedefs))
-      applypresets(&drive, &charptr, con);
-    else {
-      pout("Fatal internal error in ataPrintMain()\n");
-      EXIT(returnval|=FAILCMD);
-    }
-  }
-
-  // Print most drive identity information if requested
-  if (con->driveinfo){
-    pout("=== START OF INFORMATION SECTION ===\n");
-    ataPrintDriveInfo(&drive);
-  }
-
-  // Was this a packet device?
-  if (retid>0){
-    pout("SMART support is: Unavailable - Packet Interface Devices [this device: %s] don't support ATA SMART\n", packetdevicetype(retid-1));
-    failuretest(MANDATORY_CMD, returnval|=FAILSMART);
-  }
-  
-  // if drive does not supports SMART it's time to exit
-  supported=ataSmartSupport(&drive);
-  if (supported != 1){
-    if (supported==0) {
-      pout("SMART support is: Unavailable - device lacks SMART capability.\n");
-      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
-      pout("                  Checking to be sure by trying SMART ENABLE command.\n");
-    }
-    else {
-      pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 82-83 don't show if SMART supported.\n");
-      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
-      pout("                  Checking for SMART support by trying SMART ENABLE command.\n");
-    }
-
-    if (ataEnableSmart(fd)){
-      pout("                  SMART ENABLE failed - this establishes that this device lacks SMART functionality.\n");
-      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
-      supported=0;
-    }
-    else {
-      pout("                  SMART ENABLE appeared to work!  Continuing.\n");
-      supported=1;
-    }
-    if (!con->driveinfo) pout("\n");
-  }
-  
-  // Now print remaining drive info: is SMART enabled?    
-  if (con->driveinfo){
-    int ison=ataIsSmartEnabled(&drive),isenabled=ison;
-    
-    if (ison==-1) {
-      pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 85-87 don't show if SMART is enabled.\n");
-      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
-      // check SMART support by trying a command
-      pout("                  Checking to be sure by trying SMART RETURN STATUS command.\n");
-      isenabled=ataDoesSmartWork(fd);
-    }
-    else {
-      pout("SMART support is: Available - device has SMART capability.\n");
-#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
-      if (ata_identify_is_cached(fd)) {
-        pout("                  %sabled status cached by OS, trying SMART RETURN STATUS cmd.\n",
-                    (isenabled?"En":"Dis"));
-        isenabled=ataDoesSmartWork(fd);
-      }
-#endif
-    }
-
-    if (isenabled)
-      pout("SMART support is: Enabled\n");
-    else {
-      if (ison==-1)
-        pout("SMART support is: Unavailable\n");
-      else
-        pout("SMART support is: Disabled\n");
-    }
-    pout("\n");
-  }
-  
-  // START OF THE ENABLE/DISABLE SECTION OF THE CODE
-  if (con->smartenable || con->smartdisable || 
-      con->smartautosaveenable || con->smartautosavedisable || 
-      con->smartautoofflineenable || con->smartautoofflinedisable)
-    pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
-  
-  // Enable/Disable SMART commands
-  if (con->smartenable){
-    if (ataEnableSmart(fd)) {
-      pout("Smartctl: SMART Enable Failed.\n\n");
-      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
-    }
-    else
-      pout("SMART Enabled.\n");
-  }
-  
-  // From here on, every command requires that SMART be enabled...
-  if (!ataDoesSmartWork(fd)) {
-    pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n");
-    return returnval;
-  }
-  
-  // Turn off SMART on device
-  if (con->smartdisable){    
-    if (ataDisableSmart(fd)) {
-      pout( "Smartctl: SMART Disable Failed.\n\n");
-      failuretest(MANDATORY_CMD,returnval|=FAILSMART);
-    }
-    pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n");
-    return returnval;           
-  }
-  
-  // Let's ALWAYS issue this command to get the SMART status
-  code=ataSmartStatus2(fd);
-  if (code==-1)
-    failuretest(MANDATORY_CMD, returnval|=FAILSMART);
-
-  // Enable/Disable Auto-save attributes
-  if (con->smartautosaveenable){
-    if (ataEnableAutoSave(fd)){
-      pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n");
-      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
-    }
-    else
-      pout("SMART Attribute Autosave Enabled.\n");
-  }
-  if (con->smartautosavedisable){
-    if (ataDisableAutoSave(fd)){
-      pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n");
-      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
-    }
-    else
-      pout("SMART Attribute Autosave Disabled.\n");
-  }
-  
-  // for everything else read values and thresholds are needed
-  if (ataReadSmartValues(fd, &smartval)){
-    pout("Smartctl: SMART Read Values failed.\n\n");
-    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-  }
-  if (ataReadSmartThresholds(fd, &smartthres)){
-    pout("Smartctl: SMART Read Thresholds failed.\n\n");
-    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-  }
-
-  // Enable/Disable Off-line testing
-  if (con->smartautoofflineenable){
-    if (!isSupportAutomaticTimer(&smartval)){
-      pout("Warning: device does not support SMART Automatic Timers.\n\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    needupdate=1;
-    if (ataEnableAutoOffline(fd)){
-      pout( "Smartctl: SMART Enable Automatic Offline Failed.\n\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    else
-      pout("SMART Automatic Offline Testing Enabled every four hours.\n");
-  }
-  if (con->smartautoofflinedisable){
-    if (!isSupportAutomaticTimer(&smartval)){
-      pout("Warning: device does not support SMART Automatic Timers.\n\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    needupdate=1;
-    if (ataDisableAutoOffline(fd)){
-      pout("Smartctl: SMART Disable Automatic Offline Failed.\n\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    else
-      pout("SMART Automatic Offline Testing Disabled.\n");
-  }
-
-  if (needupdate && ataReadSmartValues(fd, &smartval)){
-    pout("Smartctl: SMART Read Values failed.\n\n");
-    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-  }
-
-  // all this for a newline!
-  if (con->smartenable || con->smartdisable || 
-      con->smartautosaveenable || con->smartautosavedisable || 
-      con->smartautoofflineenable || con->smartautoofflinedisable)
-    pout("\n");
-
-  // START OF READ-ONLY OPTIONS APART FROM -V and -i
-  if (con->checksmart || con->generalsmartvalues || con->smartvendorattrib || con->smarterrorlog || con->smartselftestlog)
-    pout("=== START OF READ SMART DATA SECTION ===\n");
-  
-  // Check SMART status (use previously returned value)
-  if (con->checksmart){
-    switch (code) {
-
-    case 0:
-      // The case where the disk health is OK
-      pout("SMART overall-health self-assessment test result: PASSED\n");
-      if (ataCheckSmart(&smartval, &smartthres,0)){
-        if (con->smartvendorattrib)
-          pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
-        else {
-          PRINT_ON(con);
-          pout("Please note the following marginal Attributes:\n");
-          PrintSmartAttribWithThres(&smartval, &smartthres,2);
-        } 
-        returnval|=FAILAGE;
-      }
-      else
-        pout("\n");
-      break;
-      
-    case 1:
-      // The case where the disk health is NOT OK
-      PRINT_ON(con);
-      pout("SMART overall-health self-assessment test result: FAILED!\n"
-           "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
-      PRINT_OFF(con);
-      if (ataCheckSmart(&smartval, &smartthres,1)){
-        returnval|=FAILATTR;
-        if (con->smartvendorattrib)
-          pout("See vendor-specific Attribute list for failed Attributes.\n\n");
-        else {
-          PRINT_ON(con);
-          pout("Failed Attributes:\n");
-          PrintSmartAttribWithThres(&smartval, &smartthres,1);
-        }
-      }
-      else
-        pout("No failed Attributes found.\n\n");   
-      returnval|=FAILSTATUS;
-      PRINT_OFF(con);
-      break;
-
-    case -1:
-    default:
-      // The case where something went wrong with HDIO_DRIVE_TASK ioctl()
-      if (ataCheckSmart(&smartval, &smartthres,1)){
-        PRINT_ON(con);
-        pout("SMART overall-health self-assessment test result: FAILED!\n"
-             "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
-        PRINT_OFF(con);
-        returnval|=FAILATTR;
-        returnval|=FAILSTATUS;
-        if (con->smartvendorattrib)
-          pout("See vendor-specific Attribute list for failed Attributes.\n\n");
-        else {
-          PRINT_ON(con);
-          pout("Failed Attributes:\n");
-          PrintSmartAttribWithThres(&smartval, &smartthres,1);
-        }
-      }
-      else {
-        pout("SMART overall-health self-assessment test result: PASSED\n");
-        if (ataCheckSmart(&smartval, &smartthres,0)){
-          if (con->smartvendorattrib)
-            pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
-          else {
-            PRINT_ON(con);
-            pout("Please note the following marginal Attributes:\n");
-            PrintSmartAttribWithThres(&smartval, &smartthres,2);
-          } 
-          returnval|=FAILAGE;
-        }
-        else
-          pout("\n");
-      } 
-      PRINT_OFF(con);
-      break;
-    } // end of switch statement
-    
-    PRINT_OFF(con);
-  } // end of checking SMART Status
-  
-  // Print general SMART values
-  if (con->generalsmartvalues)
-    ataPrintGeneralSmartValues(&smartval, &drive); 
-
-  // Print vendor-specific attributes
-  if (con->smartvendorattrib){
-    PRINT_ON(con);
-    PrintSmartAttribWithThres(&smartval, &smartthres,con->printing_switchable?2:0);
-    PRINT_OFF(con);
-  }
-
-  // Print SMART log Directory
-  if (con->smartlogdirectory){
-    struct ata_smart_log_directory smartlogdirectory;
-    if (!isGeneralPurposeLoggingCapable(&drive)){
-      pout("Warning: device does not support General Purpose Logging\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    else {
-      PRINT_ON(con);
-      pout("Log Directory Supported\n");
-      if (ataReadLogDirectory(fd, &smartlogdirectory)){
-        PRINT_OFF(con);
-        pout("Read Log Directory failed.\n\n");
-        failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-      }
-      else
-        ataPrintLogDirectory( &smartlogdirectory);
-    }
-    PRINT_OFF(con);
-  }
-  
-  // Print SMART error log
-  if (con->smarterrorlog){
-    if (!isSmartErrorLogCapable(&smartval, &drive)){
-      pout("Warning: device does not support Error Logging\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    if (ataReadErrorLog(fd, &smarterror)){
-      pout("Smartctl: SMART Error Log Read Failed\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    else {
-      // quiet mode is turned on inside ataPrintSmartErrorLog()
-      if (ataPrintSmartErrorlog(&smarterror))
-       returnval|=FAILERR;
-      PRINT_OFF(con);
-    }
-  }
-  
-  // Print SMART self-test log
-  if (con->smartselftestlog){
-    if (!isSmartTestLogCapable(&smartval, &drive)){
-      pout("Warning: device does not support Self Test Logging\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }    
-    if(ataReadSelfTestLog(fd, &smartselftest)){
-      pout("Smartctl: SMART Self Test Log Read Failed\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    else {
-      PRINT_ON(con);
-      if (ataPrintSmartSelfTestlog(&smartselftest,!con->printing_switchable))
-       returnval|=FAILLOG;
-      PRINT_OFF(con);
-      pout("\n");
-    }
-  }
-
-  // Print SMART selective self-test log
-  if (con->selectivetestlog){
-    struct ata_selective_self_test_log log;
-    
-    if (!isSupportSelectiveSelfTest(&smartval))
-      pout("Device does not support Selective Self Tests/Logging\n");
-    else if(ataReadSelectiveSelfTestLog(fd, &log)) {
-      pout("Smartctl: SMART Selective Self Test Log Read Failed\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    else {
-      PRINT_ON(con);
-      ataPrintSelectiveSelfTestLog(&log, &smartval);
-      PRINT_OFF(con);
-      pout("\n");
-    }
-  }
-
-  // START OF THE TESTING SECTION OF THE CODE.  IF NO TESTING, RETURN
-  if (con->testcase==-1)
-    return returnval;
-  
-  pout("=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\n");
-  // if doing a self-test, be sure it's supported by the hardware
-  switch (con->testcase){
-  case OFFLINE_FULL_SCAN:
-    if (!isSupportExecuteOfflineImmediate(&smartval)){
-      pout("Warning: device does not support Execute Offline Immediate function.\n\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    break;
-  case ABORT_SELF_TEST:
-  case SHORT_SELF_TEST:
-  case EXTEND_SELF_TEST:
-  case SHORT_CAPTIVE_SELF_TEST:
-  case EXTEND_CAPTIVE_SELF_TEST:
-    if (!isSupportSelfTest(&smartval)){
-      pout("Warning: device does not support Self-Test functions.\n\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    break;
-  case CONVEYANCE_SELF_TEST:
-  case CONVEYANCE_CAPTIVE_SELF_TEST:
-    if (!isSupportConveyanceSelfTest(&smartval)){
-      pout("Warning: device does not support Conveyance Self-Test functions.\n\n");
-      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-    }
-    break;
-  case SELECTIVE_SELF_TEST:
-  case SELECTIVE_CAPTIVE_SELF_TEST:
-    if (!isSupportSelectiveSelfTest(&smartval)){
-      pout("Warning: device does not support Selective Self-Test functions.\n\n");
-      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
-    }
-    break;
-  default:
-    pout("Internal error in smartctl: con->testcase==%d not recognized\n", (int)con->testcase);
-    pout("Please contact smartmontools developers at %s.\n", PACKAGE_BUGREPORT);
-    EXIT(returnval|=FAILCMD);
-  }
-
-  // Now do the test.  Note ataSmartTest prints its own error/success
-  // messages
-  if (ataSmartTest(fd, con->testcase, &smartval))
-    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-  else {  
-    // Tell user how long test will take to complete.  This is tricky
-    // because in the case of an Offline Full Scan, the completion
-    // timer is volatile, and needs to be read AFTER the command is
-    // given. If this will interrupt the Offline Full Scan, we don't
-    // do it, just warn user.
-    if (con->testcase==OFFLINE_FULL_SCAN){
-      if (isSupportOfflineAbort(&smartval))
-       pout("Note: giving further SMART commands will abort Offline testing\n");
-      else if (ataReadSmartValues(fd, &smartval)){
-       pout("Smartctl: SMART Read Values failed.\n");
-       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-      }
-    }
-    
-    // Now say how long the test will take to complete
-    if ((timewait=TestTime(&smartval,con->testcase))){ 
-      time_t t=time(NULL);
-      if (con->testcase==OFFLINE_FULL_SCAN) {
-       t+=timewait;
-       pout("Please wait %d seconds for test to complete.\n", (int)timewait);
-      } else {
-       t+=timewait*60;
-       pout("Please wait %d minutes for test to complete.\n", (int)timewait);
-      }
-      pout("Test will complete after %s\n", ctime(&t));
-      
-      if (con->testcase!=SHORT_CAPTIVE_SELF_TEST && 
-         con->testcase!=EXTEND_CAPTIVE_SELF_TEST && 
-         con->testcase!=CONVEYANCE_CAPTIVE_SELF_TEST && 
-         con->testcase!=SELECTIVE_CAPTIVE_SELF_TEST)
-       pout("Use smartctl -X to abort test.\n"); 
-    }
-  }
-
-  return returnval;
-}
diff --git a/ataprint.cpp b/ataprint.cpp
new file mode 100644 (file)
index 0000000..da7f599
--- /dev/null
@@ -0,0 +1,1902 @@
+/*
+ * ataprint.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif // #ifdef HAVE_LOCALE_H
+
+#include "int64.h"
+#include "atacmdnames.h"
+#include "atacmds.h"
+#include "ataprint.h"
+#include "smartctl.h"
+#include "extern.h"
+#include "utility.h"
+#include "knowndrives.h"
+
+const char *ataprint_c_cvsid="$Id: ataprint.cpp,v 1.168 2006/09/17 09:34:29 shattered Exp $"
+ATACMDNAMES_H_CVSID ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
+
+// for passing global control variables
+extern smartmonctrl *con;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents
+// bytes.
+void swapbytes(char *out, const char *in, size_t n)
+{
+  size_t i;
+
+  for (i = 0; i < n; i += 2) {
+    out[i]   = in[i+1];
+    out[i+1] = in[i];
+  }
+}
+
+// Copies in to out, but removes leading and trailing whitespace.
+void trim(char *out, const char *in)
+{
+  int i, first, last;
+
+  // Find the first non-space character (maybe none).
+  first = -1;
+  for (i = 0; in[i]; i++)
+    if (!isspace((int)in[i])) {
+      first = i;
+      break;
+    }
+
+  if (first == -1) {
+    // There are no non-space characters.
+    out[0] = '\0';
+    return;
+  }
+
+  // Find the last non-space character.
+  for (i = strlen(in)-1; i >= first && isspace((int)in[i]); i--)
+    ;
+  last = i;
+
+  strncpy(out, in+first, last-first+1);
+  out[last-first+1] = '\0';
+}
+
+// Convenience function for formatting strings from ata_identify_device
+void formatdriveidstring(char *out, const char *in, int n)
+{
+  char tmp[65];
+
+  n = n > 64 ? 64 : n;
+#ifndef __NetBSD__
+  swapbytes(tmp, in, n);
+#else
+  if (isbigendian())
+    strncpy(tmp, in, n);
+  else
+    swapbytes(tmp, in, n);
+#endif
+  tmp[n] = '\0';
+  trim(out, tmp);
+}
+
+void infofound(char *output) {
+  if (*output)
+    pout("%s\n", output);
+  else
+    pout("[No Information Found]\n");
+}
+
+
+/* For the given Command Register (CR) and Features Register (FR), attempts
+ * to construct a string that describes the contents of the Status
+ * Register (ST) and Error Register (ER).  The string is dynamically allocated
+ * memory and the return value is a pointer to this string.  It is up to the
+ * caller to free this memory.  If there is insufficient memory or if the
+ * meanings of the flags of the error register are not known for the given
+ * command then it returns NULL.
+ *
+ * The meanings of the flags of the error register for all commands are
+ * described in the ATA spec and could all be supported here in theory.
+ * Currently, only a few commands are supported (those that have been seen
+ * to produce errors).  If many more are to be added then this function
+ * should probably be redesigned.
+ */
+char *construct_st_er_desc(struct ata_smart_errorlog_struct *data) {
+  unsigned char CR=data->commands[4].commandreg;
+  unsigned char FR=data->commands[4].featuresreg;
+  unsigned char ST=data->error_struct.status;
+  unsigned char ER=data->error_struct.error_register;
+  char *s;
+  const char *error_flag[8];
+  int i, print_lba=0, print_sector=0;
+
+  // Set of character strings corresponding to different error codes.
+  // Please keep in alphabetic order if you add more.
+  const char  *abrt  = "ABRT";  // ABORTED
+ const char   *amnf  = "AMNF";  // ADDRESS MARK NOT FOUND
+ const char   *ccto  = "CCTO";  // COMMAND COMPLETTION TIMED OUT
+ const char   *eom   = "EOM";   // END OF MEDIA
+ const char   *icrc  = "ICRC";  // INTERFACE CRC ERROR
+ const char   *idnf  = "IDNF";  // ID NOT FOUND
+ const char   *ili   = "ILI";   // MEANING OF THIS BIT IS COMMAND-SET SPECIFIC
+ const char   *mc    = "MC";    // MEDIA CHANGED 
+ const char   *mcr   = "MCR";   // MEDIA CHANGE REQUEST
+ const char   *nm    = "NM";    // NO MEDIA
+ const char   *obs   = "obs";   // OBSOLETE
+ const char   *tk0nf = "TK0NF"; // TRACK 0 NOT FOUND
+ const char   *unc   = "UNC";   // UNCORRECTABLE
+ const char   *wp    = "WP";    // WRITE PROTECTED
+
+  /* If for any command the Device Fault flag of the status register is
+   * not used then used_device_fault should be set to 0 (in the CR switch
+   * below)
+   */
+  int uses_device_fault = 1;
+
+  /* A value of NULL means that the error flag isn't used */
+  for (i = 0; i < 8; i++)
+    error_flag[i] = NULL;
+
+  switch (CR) {
+  case 0x10:  // RECALIBRATE
+    error_flag[2] = abrt;
+    error_flag[1] = tk0nf;
+    break;
+  case 0x20:  /* READ SECTOR(S) */
+  case 0x21:  // READ SECTOR(S)
+  case 0x24:  // READ SECTOR(S) EXT
+  case 0xC4:  /* READ MULTIPLE */
+  case 0x29:  // READ MULTIPLE EXT
+    error_flag[6] = unc;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = amnf;
+    print_lba=1;
+    break;
+  case 0x22:  // READ LONG (with retries)
+  case 0x23:  // READ LONG (without retries)
+    error_flag[4] = idnf;
+    error_flag[2] = abrt;
+    error_flag[0] = amnf;
+    print_lba=1;
+    break;
+  case 0x2a:  // READ STREAM DMA
+  case 0x2b:  // READ STREAM PIO
+    if (CR==0x2a)
+      error_flag[7] = icrc;
+    error_flag[6] = unc;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = ccto;
+    print_lba=1;
+    print_sector=(int)data->error_struct.sector_count;
+    break;
+  case 0x3A:  // WRITE STREAM DMA
+  case 0x3B:  // WRITE STREAM PIO
+    if (CR==0x3A)
+      error_flag[7] = icrc;
+    error_flag[6] = wp;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = ccto;
+    print_lba=1;
+    print_sector=(int)data->error_struct.sector_count;
+    break;
+  case 0x25:  /* READ DMA EXT */
+  case 0x26:  // READ DMA QUEUED EXT
+  case 0xC7:  // READ DMA QUEUED
+  case 0xC8:  /* READ DMA */
+  case 0xC9:
+    error_flag[7] = icrc;
+    error_flag[6] = unc;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = amnf;
+    print_lba=1;
+    if (CR==0x25 || CR==0xC8)
+      print_sector=(int)data->error_struct.sector_count;
+    break;
+  case 0x30:  /* WRITE SECTOR(S) */
+  case 0x31:  // WRITE SECTOR(S)
+  case 0x34:  // WRITE SECTOR(S) EXT
+  case 0xC5:  /* WRITE MULTIPLE */
+  case 0x39:  // WRITE MULTIPLE EXT
+  case 0xCE:  // WRITE MULTIPLE FUA EXT
+    error_flag[6] = wp;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    print_lba=1;
+    break;
+  case 0x32:  // WRITE LONG (with retries)
+  case 0x33:  // WRITE LONG (without retries)
+    error_flag[4] = idnf;
+    error_flag[2] = abrt;
+    print_lba=1;
+    break;
+  case 0x3C:  // WRITE VERIFY
+    error_flag[6] = unc;
+    error_flag[4] = idnf;
+    error_flag[2] = abrt;
+    error_flag[0] = amnf;
+    print_lba=1;
+    break;
+  case 0x40: // READ VERIFY SECTOR(S) with retries
+  case 0x41: // READ VERIFY SECTOR(S) without retries
+  case 0x42: // READ VERIFY SECTOR(S) EXT
+    error_flag[6] = unc;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = amnf;
+    print_lba=1;
+    break;
+  case 0xA0:  /* PACKET */
+    /* Bits 4-7 are all used for sense key (a 'command packet set specific error
+     * indication' according to the ATA/ATAPI-7 standard), so "Sense key" will
+     * be repeated in the error description string if more than one of those
+     * bits is set.
+     */
+    error_flag[7] = "Sense key (bit 3)",
+    error_flag[6] = "Sense key (bit 2)",
+    error_flag[5] = "Sense key (bit 1)",
+    error_flag[4] = "Sense key (bit 0)",
+    error_flag[2] = abrt;
+    error_flag[1] = eom;
+    error_flag[0] = ili;
+    break;
+  case 0xA1:  /* IDENTIFY PACKET DEVICE */
+  case 0xEF:  /* SET FEATURES */
+  case 0x00:  /* NOP */
+  case 0xC6:  /* SET MULTIPLE MODE */
+    error_flag[2] = abrt;
+    break;
+  case 0x2F:  // READ LOG EXT
+    error_flag[6] = unc;
+    error_flag[4] = idnf;
+    error_flag[2] = abrt;
+    error_flag[0] = obs;
+    break;
+  case 0x3F:  // WRITE LOG EXT
+    error_flag[4] = idnf;
+    error_flag[2] = abrt;
+    error_flag[0] = obs;
+    break;
+  case 0xB0:  /* SMART */
+    switch(FR) {
+    case 0xD0:  // SMART READ DATA
+    case 0xD1:  // SMART READ ATTRIBUTE THRESHOLDS
+    case 0xD5:  /* SMART READ LOG */
+      error_flag[6] = unc;
+      error_flag[4] = idnf;
+      error_flag[2] = abrt;
+      error_flag[0] = obs;
+      break;
+    case 0xD6:  /* SMART WRITE LOG */
+      error_flag[4] = idnf;
+      error_flag[2] = abrt;
+      error_flag[0] = obs;
+      break;
+    case 0xD2:  // Enable/Disable Attribute Autosave
+    case 0xD3:  // SMART SAVE ATTRIBUTE VALUES (ATA-3)
+    case 0xD8:  // SMART ENABLE OPERATIONS
+    case 0xD9:  /* SMART DISABLE OPERATIONS */
+    case 0xDA:  /* SMART RETURN STATUS */
+    case 0xDB:  // Enable/Disable Auto Offline (SFF)
+      error_flag[2] = abrt;
+      break;
+    case 0xD4:  // SMART EXECUTE IMMEDIATE OFFLINE
+      error_flag[4] = idnf;
+      error_flag[2] = abrt;
+      break;
+    default:
+      return NULL;
+      break;
+    }
+    break;
+  case 0xB1:  /* DEVICE CONFIGURATION */
+    switch (FR) {
+    case 0xC0:  /* DEVICE CONFIGURATION RESTORE */
+      error_flag[2] = abrt;
+      break;
+    default:
+      return NULL;
+      break;
+    }
+    break;
+  case 0xCA:  /* WRITE DMA */
+  case 0xCB:
+  case 0x35:  // WRITE DMA EXT
+  case 0x3D:  // WRITE DMA FUA EXT
+  case 0xCC:  // WRITE DMA QUEUED
+  case 0x36:  // WRITE DMA QUEUED EXT
+  case 0x3E:  // WRITE DMA QUEUED FUA EXT
+    error_flag[7] = icrc;
+    error_flag[6] = wp;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = amnf;
+    print_lba=1;
+    if (CR==0x35)
+      print_sector=(int)data->error_struct.sector_count;
+    break;
+  case 0xE4: // READ BUFFER
+  case 0xE8: // WRITE BUFFER
+    error_flag[2] = abrt;
+    break;
+  default:
+    return NULL;
+  }
+
+  /* 256 bytes -- that'll be plenty (OK, this is lazy!) */
+  if (!(s = (char *)malloc(256)))
+    return s;
+
+  s[0] = '\0';
+
+  /* We ignore any status flags other than Device Fault and Error */
+
+  if (uses_device_fault && (ST & (1 << 5))) {
+    strcat(s, "Device Fault");
+    if (ST & 1)  // Error flag
+      strcat(s, "; ");
+  }
+  if (ST & 1) {  // Error flag
+    int count = 0;
+
+    strcat(s, "Error: ");
+    for (i = 7; i >= 0; i--)
+      if ((ER & (1 << i)) && (error_flag[i])) {
+        if (count++ > 0)
+           strcat(s, ", ");
+        strcat(s, error_flag[i]);
+      }
+  }
+
+  // If the error was a READ or WRITE error, print the Logical Block
+  // Address (LBA) at which the read or write failed.
+  if (print_lba) {
+    char tmp[128];
+    int lba;
+
+    // bits 24-27: bits 0-3 of DH
+    lba   = 0xf & data->error_struct.drive_head;
+    lba <<= 8;
+    // bits 16-23: CH
+    lba  |= data->error_struct.cylinder_high;
+    lba <<= 8;
+    // bits 8-15:  CL
+    lba  |= data->error_struct.cylinder_low;
+    lba <<= 8;
+    // bits 0-7:   SN
+    lba  |= data->error_struct.sector_number;
+
+    // print number of sectors, if known, and append to print string
+    if (print_sector) {
+      snprintf(tmp, 128, " %d sectors", print_sector);
+      strcat(s, tmp);
+    }
+
+    // print LBA, and append to print string
+    snprintf(tmp, 128, " at LBA = 0x%08x = %d", lba, lba);
+    strcat(s, tmp);
+  }
+
+  return s;
+}
+
+// This returns the capacity of a disk drive and also prints this into
+// a string, using comma separators to make it easier to read.  If the
+// drive doesn't support LBA addressing or has no user writable
+// sectors (eg, CDROM or DVD) then routine returns zero.
+uint64_t determine_capacity(struct ata_identify_device *drive, char *pstring){
+
+  unsigned short command_set_2  = drive->command_set_2;
+  unsigned short capabilities_0 = drive->words047_079[49-47];
+  unsigned short sects_16       = drive->words047_079[60-47];
+  unsigned short sects_32       = drive->words047_079[61-47];
+  unsigned short lba_16         = drive->words088_255[100-88];
+  unsigned short lba_32         = drive->words088_255[101-88];
+  unsigned short lba_48         = drive->words088_255[102-88];
+  unsigned short lba_64         = drive->words088_255[103-88];
+  uint64_t capacity_short=0, capacity=0, threedigits, power_of_ten;
+  int started=0,k=1000000000;
+  char separator=',';
+
+  // get correct character to use as thousands separator
+#ifdef HAVE_LOCALE_H
+  struct lconv *currentlocale=NULL;
+  setlocale (LC_ALL, "");
+  currentlocale=localeconv();
+  if (*(currentlocale->thousands_sep))
+    separator=*(currentlocale->thousands_sep);
+#endif // #ifdef HAVE_LOCALE_H
+
+  // if drive supports LBA addressing, determine 32-bit LBA capacity
+  if (capabilities_0 & 0x0200) {
+    capacity_short = (unsigned int)sects_32 << 16 | 
+                     (unsigned int)sects_16 << 0  ;
+    
+    // if drive supports 48-bit addressing, determine THAT capacity
+    if ((command_set_2 & 0xc000) == 0x4000 && (command_set_2 & 0x0400))
+      capacity = (uint64_t)lba_64 << 48 | 
+                (uint64_t)lba_48 << 32 |
+                (uint64_t)lba_32 << 16 | 
+                (uint64_t)lba_16 << 0  ;
+    
+    // choose the larger of the two possible capacities
+    if (capacity_short>capacity)
+      capacity=capacity_short;
+  }
+
+  // turn sectors into bytes
+  capacity_short = (capacity *= 512);
+  
+  // print with locale-specific separators (default is comma)
+  power_of_ten =  k;
+  power_of_ten *= k;
+  
+  for (k=0; k<7; k++) {
+    threedigits = capacity/power_of_ten;
+    capacity   -= threedigits*power_of_ten;
+    if (started)
+      // we have already printed some digits
+      pstring += sprintf(pstring, "%c%03"PRIu64, separator, threedigits);
+    else if (threedigits || k==6) {
+      // these are the first digits that we are printing
+      pstring += sprintf(pstring, "%"PRIu64, threedigits);
+      started = 1;
+    }
+    if (k!=6)
+      power_of_ten /= 1000;
+  }
+  
+  return capacity_short;
+}
+
+int ataPrintDriveInfo (struct ata_identify_device *drive){
+  int version, drivetype;
+  const char *description;
+  char unknown[64], timedatetz[DATEANDEPOCHLEN];
+  unsigned short minorrev;
+  char model[64], serial[64], firm[64], capacity[64];
+
+  // format drive information (with byte swapping as needed)
+  formatdriveidstring(model, (char *)drive->model,40);
+  formatdriveidstring(serial, (char *)drive->serial_no,20);
+  formatdriveidstring(firm, (char *)drive->fw_rev,8);
+
+  // print out model, serial # and firmware versions  (byte-swap ASCI strings)
+  drivetype=lookupdrive(model, firm);
+
+  // Print model family if known
+  if (drivetype>=0 && knowndrives[drivetype].modelfamily)
+    pout("Model Family:     %s\n", knowndrives[drivetype].modelfamily);
+
+  pout("Device Model:     ");
+  infofound(model);
+  pout("Serial Number:    ");
+  infofound(serial);
+  pout("Firmware Version: ");
+  infofound(firm);
+
+  if (determine_capacity(drive, capacity))
+    pout("User Capacity:    %s bytes\n", capacity);
+  
+  // See if drive is recognized
+  pout("Device is:        %s\n", drivetype<0?
+       "Not in smartctl database [for details use: -P showall]":
+       "In smartctl database [for details use: -P show]");
+
+  // now get ATA version info
+  version=ataVersionInfo(&description,drive, &minorrev);
+
+  // unrecognized minor revision code
+  if (!description){
+    if (!minorrev)
+      sprintf(unknown, "Exact ATA specification draft version not indicated");
+    else
+      sprintf(unknown,"Not recognized. Minor revision code: 0x%02hx", minorrev);
+    description=unknown;
+  }
+  
+  
+  // SMART Support was first added into the ATA/ATAPI-3 Standard with
+  // Revision 3 of the document, July 25, 1995.  Look at the "Document
+  // Status" revision commands at the beginning of
+  // http://www.t13.org/project/d2008r6.pdf to see this.  So it's not
+  // enough to check if we are ATA-3.  Version=-3 indicates ATA-3
+  // BEFORE Revision 3.
+  pout("ATA Version is:   %d\n",(int)abs(version));
+  pout("ATA Standard is:  %s\n",description);
+  
+  // print current time and date and timezone
+  dateandtimezone(timedatetz);
+  pout("Local Time is:    %s\n", timedatetz);
+
+  // Print warning message, if there is one
+  if (drivetype>=0 && knowndrives[drivetype].warningmsg)
+    pout("\n==> WARNING: %s\n\n", knowndrives[drivetype].warningmsg);
+
+  if (version>=3)
+    return drivetype;
+  
+  pout("SMART is only available in ATA Version 3 Revision 3 or greater.\n");
+  pout("We will try to proceed in spite of this.\n");
+  return drivetype;
+}
+
+
+const char *OfflineDataCollectionStatus(unsigned char status_byte){
+  unsigned char stat=status_byte & 0x7f;
+  
+  switch(stat){
+  case 0x00:
+    return "was never started";
+  case 0x02:
+    return "was completed without error";
+  case 0x03:
+    if (status_byte == 0x03)
+      return "is in progress";
+    else
+      return "is in a Reserved state";
+  case 0x04:
+    return "was suspended by an interrupting command from host";
+  case 0x05:
+    return "was aborted by an interrupting command from host";
+  case 0x06:
+    return "was aborted by the device with a fatal error";
+  default:
+    if (stat >= 0x40)
+      return "is in a Vendor Specific state\n";
+    else
+      return "is in a Reserved state\n";
+  }
+}
+  
+  
+  /*  prints verbose value Off-line data collection status byte */
+  void PrintSmartOfflineStatus(struct ata_smart_values *data){
+  
+  pout("Offline data collection status:  (0x%02x)\t",
+       (int)data->offline_data_collection_status);
+    
+  // Off-line data collection status byte is not a reserved
+  // or vendor specific value
+  pout("Offline data collection activity\n"
+       "\t\t\t\t\t%s.\n", OfflineDataCollectionStatus(data->offline_data_collection_status));
+  
+  // Report on Automatic Data Collection Status.  Only IBM documents
+  // this bit.  See SFF 8035i Revision 2 for details.
+  if (data->offline_data_collection_status & 0x80)
+    pout("\t\t\t\t\tAuto Offline Data Collection: Enabled.\n");
+  else
+    pout("\t\t\t\t\tAuto Offline Data Collection: Disabled.\n");
+  
+  return;
+}
+
+void PrintSmartSelfExecStatus(struct ata_smart_values *data)
+{
+   pout("Self-test execution status:      ");
+   
+   switch (data->self_test_exec_status >> 4)
+   {
+      case 0:
+        pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t",
+                (int)data->self_test_exec_status);
+        pout("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n");
+        break;
+       case 1:
+         pout("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t",
+                 (int)data->self_test_exec_status);
+         pout("the host.\n");
+         break;
+       case 2:
+         pout("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t",
+                 (int)data->self_test_exec_status);
+         pout("by the host with a hard or soft reset.\n");
+         break;
+       case 3:
+          pout("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("occurred while the device was executing\n\t\t\t\t\t");
+          pout("its self-test routine and the device \n\t\t\t\t\t");
+          pout("was unable to complete the self-test \n\t\t\t\t\t");
+          pout("routine.\n");
+          break;
+       case 4:
+          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("a test element that failed and the test\n\t\t\t\t\t");
+          pout("element that failed is not known.\n");
+          break;
+       case 5:
+          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("the electrical element of the test\n\t\t\t\t\t");
+          pout("failed.\n");
+          break;
+       case 6:
+          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("the servo (and/or seek) element of the \n\t\t\t\t\t");
+          pout("test failed.\n");
+          break;
+       case 7:
+          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("the read element of the test failed.\n");
+          break;
+       case 15:
+          pout("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("%1d0%% of test remaining.\n", 
+                  (int)(data->self_test_exec_status & 0x0f));
+          break;
+       default:
+          pout("(%4d)\tReserved.\n",
+                  (int)data->self_test_exec_status);
+          break;
+   }
+        
+}
+
+
+
+void PrintSmartTotalTimeCompleteOffline ( struct ata_smart_values *data){
+  pout("Total time to complete Offline \n");
+  pout("data collection: \t\t (%4d) seconds.\n", 
+       (int)data->total_time_to_complete_off_line);
+}
+
+
+
+void PrintSmartOfflineCollectCap(struct ata_smart_values *data){
+  pout("Offline data collection\n");
+  pout("capabilities: \t\t\t (0x%02x) ",
+       (int)data->offline_data_collection_capability);
+  
+  if (data->offline_data_collection_capability == 0x00){
+    pout("\tOffline data collection not supported.\n");
+  } 
+  else {
+    pout( "%s\n", isSupportExecuteOfflineImmediate(data)?
+          "SMART execute Offline immediate." :
+          "No SMART execute Offline immediate.");
+    
+    pout( "\t\t\t\t\t%s\n", isSupportAutomaticTimer(data)? 
+          "Auto Offline data collection on/off support.":
+          "No Auto Offline data collection support.");
+    
+    pout( "\t\t\t\t\t%s\n", isSupportOfflineAbort(data)? 
+          "Abort Offline collection upon new\n\t\t\t\t\tcommand.":
+          "Suspend Offline collection upon new\n\t\t\t\t\tcommand.");
+    
+    pout( "\t\t\t\t\t%s\n", isSupportOfflineSurfaceScan(data)? 
+          "Offline surface scan supported.":
+          "No Offline surface scan supported.");
+    
+    pout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)? 
+          "Self-test supported.":
+          "No Self-test supported.");
+
+    pout( "\t\t\t\t\t%s\n", isSupportConveyanceSelfTest(data)? 
+          "Conveyance Self-test supported.":
+          "No Conveyance Self-test supported.");
+
+    pout( "\t\t\t\t\t%s\n", isSupportSelectiveSelfTest(data)? 
+          "Selective Self-test supported.":
+          "No Selective Self-test supported.");
+  }
+}
+
+
+
+void PrintSmartCapability ( struct ata_smart_values *data)
+{
+   pout("SMART capabilities:            ");
+   pout("(0x%04x)\t", (int)data->smart_capability);
+   
+   if (data->smart_capability == 0x00)
+   {
+       pout("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n");
+   } 
+   else 
+   {
+        
+      pout( "%s\n", (data->smart_capability & 0x01)? 
+              "Saves SMART data before entering\n\t\t\t\t\tpower-saving mode.":
+              "Does not save SMART data before\n\t\t\t\t\tentering power-saving mode.");
+                
+      if ( data->smart_capability & 0x02 )
+      {
+          pout("\t\t\t\t\tSupports SMART auto save timer.\n");
+      }
+   }
+}
+
+void PrintSmartErrorLogCapability (struct ata_smart_values *data, struct ata_identify_device *identity)
+{
+
+   pout("Error logging capability:       ");
+    
+   if ( isSmartErrorLogCapable(data, identity) )
+   {
+      pout(" (0x%02x)\tError logging supported.\n",
+               (int)data->errorlog_capability);
+   }
+   else {
+       pout(" (0x%02x)\tError logging NOT supported.\n",
+                (int)data->errorlog_capability);
+   }
+}
+
+void PrintSmartShortSelfTestPollingTime(struct ata_smart_values *data){
+  pout("Short self-test routine \n");
+  if (isSupportSelfTest(data))
+    pout("recommended polling time: \t (%4d) minutes.\n", 
+         (int)data->short_test_completion_time);
+  else
+    pout("recommended polling time: \t        Not Supported.\n");
+}
+
+void PrintSmartExtendedSelfTestPollingTime(struct ata_smart_values *data){
+  pout("Extended self-test routine\n");
+  if (isSupportSelfTest(data))
+    pout("recommended polling time: \t (%4d) minutes.\n", 
+         (int)data->extend_test_completion_time);
+  else
+    pout("recommended polling time: \t        Not Supported.\n");
+}
+
+void PrintSmartConveyanceSelfTestPollingTime(struct ata_smart_values *data){
+  pout("Conveyance self-test routine\n");
+  if (isSupportConveyanceSelfTest(data))
+    pout("recommended polling time: \t (%4d) minutes.\n", 
+         (int)data->conveyance_test_completion_time);
+  else
+    pout("recommended polling time: \t        Not Supported.\n");
+}
+
+// onlyfailed=0 : print all attribute values
+// onlyfailed=1:  just ones that are currently failed and have prefailure bit set
+// onlyfailed=2:  ones that are failed, or have failed with or without prefailure bit set
+void PrintSmartAttribWithThres (struct ata_smart_values *data, 
+                                struct ata_smart_thresholds_pvt *thresholds,
+                                int onlyfailed){
+  int i;
+  int needheader=1;
+  char rawstring[64];
+    
+  // step through all vendor attributes
+  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
+    char *status;
+    struct ata_smart_attribute *disk=data->vendor_attributes+i;
+    struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i;
+    
+    // consider only valid attributes (allowing some screw-ups in the
+    // thresholds page data to slip by)
+    if (disk->id){
+      const char *type, *update;
+      int failednow,failedever;
+      char attributename[64];
+
+      failednow = (disk->current <= thre->threshold);
+      failedever= (disk->worst   <= thre->threshold);
+      
+      // These break out of the loop if we are only printing certain entries...
+      if (onlyfailed==1 && (!ATTRIBUTE_FLAGS_PREFAILURE(disk->flags) || !failednow))
+        continue;
+      
+      if (onlyfailed==2 && !failedever)
+        continue;
+      
+      // print header only if needed
+      if (needheader){
+        if (!onlyfailed){
+          pout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber);
+          pout("Vendor Specific SMART Attributes with Thresholds:\n");
+        }
+        pout("ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE\n");
+        needheader=0;
+      }
+      
+      // is this Attribute currently failed, or has it ever failed?
+      if (failednow)
+        status="FAILING_NOW";
+      else if (failedever)
+        status="In_the_past";
+      else
+        status="    -";
+
+      // Print name of attribute
+      ataPrintSmartAttribName(attributename,disk->id, con->attributedefs);
+      pout("%-28s",attributename);
+
+      // printing line for each valid attribute
+      type=ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)?"Pre-fail":"Old_age";
+      update=ATTRIBUTE_FLAGS_ONLINE(disk->flags)?"Always":"Offline";
+
+      pout("0x%04x   %.3d   %.3d   %.3d    %-10s%-9s%-12s", 
+             (int)disk->flags, (int)disk->current, (int)disk->worst,
+             (int)thre->threshold, type, update, status);
+
+      // print raw value of attribute
+      ataPrintSmartAttribRawValue(rawstring, disk, con->attributedefs);
+      pout("%s\n", rawstring);
+      
+      // print a warning if there is inconsistency here!
+      if (disk->id != thre->id){
+        char atdat[64],atthr[64];
+        ataPrintSmartAttribName(atdat, disk->id, con->attributedefs);
+        ataPrintSmartAttribName(atthr, thre->id, con->attributedefs);
+        pout("%-28s<== Data Page      |  WARNING: PREVIOUS ATTRIBUTE HAS TWO\n",atdat);
+        pout("%-28s<== Threshold Page |  INCONSISTENT IDENTITIES IN THE DATA\n",atthr);
+      }
+    }
+  }
+  if (!needheader) pout("\n");
+}
+
+void ataPrintGeneralSmartValues(struct ata_smart_values *data, struct ata_identify_device *drive){
+  pout("General SMART Values:\n");
+  
+  PrintSmartOfflineStatus(data); 
+  
+  if (isSupportSelfTest(data)){
+    PrintSmartSelfExecStatus (data);
+  }
+  
+  PrintSmartTotalTimeCompleteOffline(data);
+  PrintSmartOfflineCollectCap(data);
+  PrintSmartCapability(data);
+  
+  PrintSmartErrorLogCapability(data, drive);
+
+  pout( "\t\t\t\t\t%s\n", isGeneralPurposeLoggingCapable(drive)?
+        "General Purpose Logging supported.":
+        "No General Purpose Logging support.");
+
+  if (isSupportSelfTest(data)){
+    PrintSmartShortSelfTestPollingTime (data);
+    PrintSmartExtendedSelfTestPollingTime (data);
+  }
+  if (isSupportConveyanceSelfTest(data))
+    PrintSmartConveyanceSelfTestPollingTime (data);
+  
+  pout("\n");
+}
+
+int ataPrintLogDirectory(struct ata_smart_log_directory *data){
+  int i;
+  char *name;
+
+  pout("SMART Log Directory Logging Version %d%s\n",
+       data->logversion, data->logversion==1?" [multi-sector log support]":"");
+  for (i=0; i<=255; i++){
+    int numsect;
+    
+    // Directory log length
+    numsect = i? data->entry[i-1].numsectors : 1;
+    
+    // If the log is not empty, what is it's name
+    if (numsect){
+      switch (i) {
+      case 0:
+        name="Log Directory"; break;
+      case 1:
+        name="Summary SMART error log"; break;
+      case 2:
+        name="Comprehensive SMART error log"; break;
+      case 3:
+        name="Extended Comprehensive SMART error log"; break;
+      case 6:
+        name="SMART self-test log"; break;
+      case 7:
+        name="Extended self-test log"; break;
+      case 9:
+        name="Selective self-test log"; break;
+      case 0x20:
+        name="Streaming performance log"; break;
+      case 0x21:
+        name="Write stream error log"; break;
+      case 0x22:
+        name="Read stream error log"; break;
+      case 0x23:
+        name="Delayed sector log"; break;
+      default:
+        if (0xa0<=i && i<=0xbf) 
+          name="Device vendor specific log";
+        else if (0x80<=i && i<=0x9f)
+          name="Host vendor specific log";
+        else
+          name="Reserved log";
+        break;
+      }
+
+      // print name and length of log
+      pout("Log at address 0x%02x has %03d sectors [%s]\n",
+           i, numsect, name);
+    }
+  }
+  return 0;
+}
+
+// returns number of errors
+int ataPrintSmartErrorlog(struct ata_smart_errorlog *data){
+  int k;
+
+  pout("SMART Error Log Version: %d\n", (int)data->revnumber);
+  
+  // if no errors logged, return
+  if (!data->error_log_pointer){
+    pout("No Errors Logged\n\n");
+    return 0;
+  }
+  PRINT_ON(con);
+  // If log pointer out of range, return
+  if (data->error_log_pointer>5){
+    pout("Invalid Error Log index = 0x%02x (T13/1321D rev 1c "
+         "Section 8.41.6.8.2.2 gives valid range from 1 to 5)\n\n",
+         (int)data->error_log_pointer);
+    return 0;
+  }
+
+  // Some internal consistency checking of the data structures
+  if ((data->ata_error_count-data->error_log_pointer)%5 && con->fixfirmwarebug != FIX_SAMSUNG2) {
+    pout("Warning: ATA error count %d inconsistent with error log pointer %d\n\n",
+         data->ata_error_count,data->error_log_pointer);
+  }
+  
+  // starting printing error log info
+  if (data->ata_error_count<=5)
+    pout( "ATA Error Count: %d\n", (int)data->ata_error_count);
+  else
+    pout( "ATA Error Count: %d (device log contains only the most recent five errors)\n",
+           (int)data->ata_error_count);
+  PRINT_OFF(con);
+  pout("\tCR = Command Register [HEX]\n"
+       "\tFR = Features Register [HEX]\n"
+       "\tSC = Sector Count Register [HEX]\n"
+       "\tSN = Sector Number Register [HEX]\n"
+       "\tCL = Cylinder Low Register [HEX]\n"
+       "\tCH = Cylinder High Register [HEX]\n"
+       "\tDH = Device/Head Register [HEX]\n"
+       "\tDC = Device Command Register [HEX]\n"
+       "\tER = Error register [HEX]\n"
+       "\tST = Status register [HEX]\n"
+       "Powered_Up_Time is measured from power on, and printed as\n"
+       "DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes,\n"
+       "SS=sec, and sss=millisec. It \"wraps\" after 49.710 days.\n\n");
+  
+  // now step through the five error log data structures (table 39 of spec)
+  for (k = 4; k >= 0; k-- ) {
+    char *st_er_desc;
+
+    // The error log data structure entries are a circular buffer
+    int j, i=(data->error_log_pointer+k)%5;
+    struct ata_smart_errorlog_struct *elog=data->errorlog_struct+i;
+    struct ata_smart_errorlog_error_struct *summary=&(elog->error_struct);
+
+    // Spec says: unused error log structures shall be zero filled
+    if (nonempty((unsigned char*)elog,sizeof(*elog))){
+      // Table 57 of T13/1532D Volume 1 Revision 3
+      char *msgstate;
+      int bits=summary->state & 0x0f;
+      int days = (int)summary->timestamp/24;
+
+      switch (bits){
+      case 0x00: msgstate="in an unknown state";break;
+      case 0x01: msgstate="sleeping"; break;
+      case 0x02: msgstate="in standby mode"; break;
+      case 0x03: msgstate="active or idle"; break;
+      case 0x04: msgstate="doing SMART Offline or Self-test"; break;
+      default:   
+        if (bits<0x0b)
+          msgstate="in a reserved state";
+        else
+          msgstate="in a vendor specific state";
+      }
+
+      // See table 42 of ATA5 spec
+      PRINT_ON(con);
+      pout("Error %d occurred at disk power-on lifetime: %d hours (%d days + %d hours)\n",
+             (int)(data->ata_error_count+k-4), (int)summary->timestamp, days, (int)(summary->timestamp-24*days));
+      PRINT_OFF(con);
+      pout("  When the command that caused the error occurred, the device was %s.\n\n",msgstate);
+      pout("  After command completion occurred, registers were:\n"
+           "  ER ST SC SN CL CH DH\n"
+           "  -- -- -- -- -- -- --\n"
+           "  %02x %02x %02x %02x %02x %02x %02x",
+           (int)summary->error_register,
+           (int)summary->status,
+           (int)summary->sector_count,
+           (int)summary->sector_number,
+           (int)summary->cylinder_low,
+           (int)summary->cylinder_high,
+           (int)summary->drive_head);
+      // Add a description of the contents of the status and error registers
+      // if possible
+      st_er_desc = construct_st_er_desc(elog);
+      if (st_er_desc) {
+        pout("  %s", st_er_desc);
+        free(st_er_desc);
+      }
+      pout("\n\n");
+      pout("  Commands leading to the command that caused the error were:\n"
+           "  CR FR SC SN CL CH DH DC   Powered_Up_Time  Command/Feature_Name\n"
+           "  -- -- -- -- -- -- -- --  ----------------  --------------------\n");
+      for ( j = 4; j >= 0; j--){
+        struct ata_smart_errorlog_command_struct *thiscommand=elog->commands+j;
+
+        // Spec says: unused data command structures shall be zero filled
+        if (nonempty((unsigned char*)thiscommand,sizeof(*thiscommand))) {
+         char timestring[32];
+         
+         // Convert integer milliseconds to a text-format string
+         MsecToText(thiscommand->timestamp, timestring);
+         
+          pout("  %02x %02x %02x %02x %02x %02x %02x %02x  %16s  %s\n",
+               (int)thiscommand->commandreg,
+               (int)thiscommand->featuresreg,
+               (int)thiscommand->sector_count,
+               (int)thiscommand->sector_number,
+               (int)thiscommand->cylinder_low,
+               (int)thiscommand->cylinder_high,
+               (int)thiscommand->drive_head,
+               (int)thiscommand->devicecontrolreg,
+              timestring,
+               look_up_ata_command(thiscommand->commandreg, thiscommand->featuresreg));
+       }
+      }
+      pout("\n");
+    }
+  }
+  PRINT_ON(con);
+  if (con->printing_switchable)
+    pout("\n");
+  PRINT_OFF(con);
+  return data->ata_error_count;  
+}
+
+void ataPrintSelectiveSelfTestLog(struct ata_selective_self_test_log *log, struct ata_smart_values *sv) {
+  int i,field1,field2;
+  char *msg;
+  char tmp[64];
+  uint64_t maxl=0,maxr=0;
+  uint64_t current=log->currentlba;
+  uint64_t currentend=current+65535;
+
+  // print data structure revision number
+  pout("SMART Selective self-test log data structure revision number %d\n",(int)log->logversion);
+  if (1 != log->logversion)
+    pout("Warning: ATA Specification requires selective self-test log data structure revision number = 1\n");
+  
+  switch((sv->self_test_exec_status)>>4){
+  case  0:msg="Completed";
+    break;
+  case  1:msg="Aborted_by_host";
+    break;
+  case  2:msg="Interrupted";
+    break;
+  case  3:msg="Fatal_error";
+    break;
+  case  4:msg="Completed_unknown_failure";
+    break;
+  case  5:msg="Completed_electrical_failure";
+    break;
+  case  6:msg="Completed_servo/seek_failure";
+    break;
+  case  7:msg="Completed_read_failure";
+    break;
+  case  8:msg="Completed_handling_damage??";
+    break;
+  case 15:msg="Self_test_in_progress";
+    break;
+  default:msg="Unknown_status ";
+    break;
+  }
+
+  // find the number of columns needed for printing. If in use, the
+  // start/end of span being read-scanned...
+  if (log->currentspan>5) {
+    maxl=current;
+    maxr=currentend;
+  }
+  for (i=0; i<5; i++) {
+    uint64_t start=log->span[i].start;
+    uint64_t end  =log->span[i].end; 
+    // ... plus max start/end of each of the five test spans.
+    if (start>maxl)
+      maxl=start;
+    if (end > maxr)
+      maxr=end;
+  }
+  
+  // we need at least 7 characters wide fields to accomodate the
+  // labels
+  if ((field1=snprintf(tmp,64, "%"PRIu64, maxl))<7)
+    field1=7;
+  if ((field2=snprintf(tmp,64, "%"PRIu64, maxr))<7)
+    field2=7;
+
+  // now print the five test spans
+  pout(" SPAN  %*s  %*s  CURRENT_TEST_STATUS\n", field1, "MIN_LBA", field2, "MAX_LBA");
+
+  for (i=0; i<5; i++) {
+    uint64_t start=log->span[i].start;
+    uint64_t end=log->span[i].end;
+    
+    if ((i+1)==(int)log->currentspan)
+      // this span is currently under test
+      pout("    %d  %*"PRIu64"  %*"PRIu64"  %s [%01d0%% left] (%"PRIu64"-%"PRIu64")\n",
+          i+1, field1, start, field2, end, msg,
+          (int)(sv->self_test_exec_status & 0x7), current, currentend);
+    else
+      // this span is not currently under test
+      pout("    %d  %*"PRIu64"  %*"PRIu64"  Not_testing\n",
+          i+1, field1, start, field2, end);
+  }  
+  
+  // if we are currently read-scanning, print LBAs and the status of
+  // the read scan
+  if (log->currentspan>5)
+    pout("%5d  %*"PRIu64"  %*"PRIu64"  Read_scanning %s\n",
+        (int)log->currentspan, field1, current, field2, currentend,
+        OfflineDataCollectionStatus(sv->offline_data_collection_status));
+  
+  /* Print selective self-test flags.  Possible flag combinations are
+     (numbering bits from 0-15):
+     Bit-1 Bit-3   Bit-4
+     Scan  Pending Active
+     0     *       *       Don't scan
+     1     0       0       Will carry out scan after selective test
+     1     1       0       Waiting to carry out scan after powerup
+     1     0       1       Currently scanning       
+     1     1       1       Currently scanning
+  */
+  
+  pout("Selective self-test flags (0x%x):\n", (unsigned int)log->flags);
+  if (log->flags & SELECTIVE_FLAG_DOSCAN) {
+    if (log->flags & SELECTIVE_FLAG_ACTIVE)
+      pout("  Currently read-scanning the remainder of the disk.\n");
+    else if (log->flags & SELECTIVE_FLAG_PENDING)
+      pout("  Read-scan of remainder of disk interrupted; will resume %d min after power-up.\n",
+          (int)log->pendingtime);
+    else
+      pout("  After scanning selected spans, read-scan remainder of disk.\n");
+  }
+  else
+    pout("  After scanning selected spans, do NOT read-scan remainder of disk.\n");
+  
+  // print pending time
+  pout("If Selective self-test is pending on power-up, resume after %d minute delay.\n",
+       (int)log->pendingtime);
+
+  return; 
+}
+
+// return value is:
+// bottom 8 bits: number of entries found where self-test showed an error
+// remaining bits: if nonzero, power on hours of last self-test where error was found
+int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog *data,int allentries){
+  int i,j,noheaderprinted=1;
+  int retval=0, hours=0, testno=0;
+
+  if (allentries)
+    pout("SMART Self-test log structure revision number %d\n",(int)data->revnumber);
+  if ((data->revnumber!=0x0001) && allentries && con->fixfirmwarebug != FIX_SAMSUNG)
+    pout("Warning: ATA Specification requires self-test log structure revision number = 1\n");
+  if (data->mostrecenttest==0){
+    if (allentries)
+      pout("No self-tests have been logged.  [To run self-tests, use: smartctl -t]\n\n");
+    return 0;
+  }
+
+  // print log      
+  for (i=20;i>=0;i--){    
+    struct ata_smart_selftestlog_struct *log;
+
+    // log is a circular buffer
+    j=(i+data->mostrecenttest)%21;
+    log=data->selftest_struct+j;
+
+    if (nonempty((unsigned char*)log,sizeof(*log))){
+      char *msgtest,*msgstat,percent[64],firstlba[64];
+      int errorfound=0;
+      
+      // count entry based on non-empty structures -- needed for
+      // Seagate only -- other vendors don't have blank entries 'in
+      // the middle'
+      testno++;
+
+      // test name
+      switch(log->selftestnumber){
+      case   0: msgtest="Offline            "; break;
+      case   1: msgtest="Short offline      "; break;
+      case   2: msgtest="Extended offline   "; break;
+      case   3: msgtest="Conveyance offline "; break;
+      case   4: msgtest="Selective offline  "; break;
+      case 127: msgtest="Abort offline test "; break;
+      case 129: msgtest="Short captive      "; break;
+      case 130: msgtest="Extended captive   "; break;
+      case 131: msgtest="Conveyance captive "; break;
+      case 132: msgtest="Selective captive  "; break;
+      default:  
+        if ( log->selftestnumber>=192 ||
+            (log->selftestnumber>= 64 && log->selftestnumber<=126))
+          msgtest="Vendor offline     ";
+        else
+          msgtest="Reserved offline   ";
+      }
+      
+      // test status
+      switch((log->selfteststatus)>>4){
+      case  0:msgstat="Completed without error      "; break;
+      case  1:msgstat="Aborted by host              "; break;
+      case  2:msgstat="Interrupted (host reset)     "; break;
+      case  3:msgstat="Fatal or unknown error       "; errorfound=1; break;
+      case  4:msgstat="Completed: unknown failure   "; errorfound=1; break;
+      case  5:msgstat="Completed: electrical failure"; errorfound=1; break;
+      case  6:msgstat="Completed: servo/seek failure"; errorfound=1; break;
+      case  7:msgstat="Completed: read failure      "; errorfound=1; break;
+      case  8:msgstat="Completed: handling damage?? "; errorfound=1; break;
+      case 15:msgstat="Self-test routine in progress"; break;
+      default:msgstat="Unknown/reserved test status ";
+      }
+
+      retval+=errorfound;
+      sprintf(percent,"%1d0%%",(log->selfteststatus)&0xf);
+
+      // T13/1321D revision 1c: (Data structure Rev #1)
+
+      //The failing LBA shall be the LBA of the uncorrectable sector
+      //that caused the test to fail. If the device encountered more
+      //than one uncorrectable sector during the test, this field
+      //shall indicate the LBA of the first uncorrectable sector
+      //encountered. If the test passed or the test failed for some
+      //reason other than an uncorrectable sector, the value of this
+      //field is undefined.
+
+      // This is true in ALL ATA-5 specs
+      
+      if (!errorfound || log->lbafirstfailure==0xffffffff || log->lbafirstfailure==0x00000000)
+        sprintf(firstlba,"%s","-");
+      else      
+        sprintf(firstlba,"%u",log->lbafirstfailure);
+
+      // print out a header if needed
+      if (noheaderprinted && (allentries || errorfound)){
+        pout("Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error\n");
+        noheaderprinted=0;
+      }
+      
+      // print out an entry, either if we are printing all entries OR
+      // if an error was found
+      if (allentries || errorfound)
+        pout("#%2d  %s %s %s  %8d         %s\n", testno, msgtest, msgstat, percent, (int)log->timestamp, firstlba);
+
+      // keep track of time of most recent error
+      if (errorfound && !hours)
+        hours=log->timestamp;
+    }
+  }
+  if (!allentries && retval)
+    pout("\n");
+
+  hours = hours << 8;
+  return (retval | hours);
+}
+
+void ataPseudoCheckSmart ( struct ata_smart_values *data, 
+                           struct ata_smart_thresholds_pvt *thresholds) {
+  int i;
+  int failed = 0;
+  for (i = 0 ; i < NUMBER_ATA_SMART_ATTRIBUTES ; i++) {
+    if (data->vendor_attributes[i].id &&   
+        thresholds->thres_entries[i].id &&
+        ATTRIBUTE_FLAGS_PREFAILURE(data->vendor_attributes[i].flags) &&
+        (data->vendor_attributes[i].current <= thresholds->thres_entries[i].threshold) &&
+        (thresholds->thres_entries[i].threshold != 0xFE)){
+      pout("Attribute ID %d Failed\n",(int)data->vendor_attributes[i].id);
+      failed = 1;
+    } 
+  }   
+  pout("%s\n", ( failed )?
+         "SMART overall-health self-assessment test result: FAILED!\n"
+         "Drive failure expected in less than 24 hours. SAVE ALL DATA":
+         "SMART overall-health self-assessment test result: PASSED");
+}
+
+
+// Compares failure type to policy in effect, and either exits or
+// simply returns to the calling routine.
+void failuretest(int type, int returnvalue){
+
+  // If this is an error in an "optional" SMART command
+  if (type==OPTIONAL_CMD){
+    if (con->conservative){
+      pout("An optional SMART command failed: exiting.  Remove '-T conservative' option to continue.\n");
+      EXIT(returnvalue);
+    }
+    return;
+  }
+
+  // If this is an error in a "mandatory" SMART command
+  if (type==MANDATORY_CMD){
+    if (con->permissive--)
+      return;
+    pout("A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.\n");
+    EXIT(returnvalue);
+  }
+
+  pout("Smartctl internal error in failuretest(type=%d). Please contact developers at " PACKAGE_HOMEPAGE "\n",type);
+  EXIT(returnvalue|FAILCMD);
+}
+
+// Used to warn users about invalid checksums.  Action to be taken may be
+// altered by the user.
+void checksumwarning(const char *string){
+  // user has asked us to ignore checksum errors
+  if (con->checksumignore)
+        return;
+
+  pout("Warning! %s error: invalid SMART checksum.\n",string);
+
+  // user has asked us to fail on checksum errors
+  if (con->checksumfail)
+    EXIT(FAILSMART);
+
+  return;
+}
+
+// Initialize to zero just in case some SMART routines don't work
+struct ata_identify_device drive;
+struct ata_smart_values smartval;
+struct ata_smart_thresholds_pvt smartthres;
+struct ata_smart_errorlog smarterror;
+struct ata_smart_selftestlog smartselftest;
+
+int ataPrintMain (int fd){
+  int timewait,code;
+  int returnval=0, retid=0, supported=0, needupdate=0, known=0;
+  const char * powername = 0; char powerchg = 0;
+
+  // If requested, check power mode first
+  if (con->powermode) {
+    unsigned char powerlimit = 0xff;
+    int powermode = ataCheckPowerMode(fd);
+    switch (powermode) {
+      case -1:
+        if (errno == ENOSYS) {
+          pout("CHECK POWER STATUS not implemented, ignoring -n Option\n"); break;
+        }
+        powername = "SLEEP";   powerlimit = 2;
+        break;
+      case 0:
+        powername = "STANDBY"; powerlimit = 3; break;
+      case 0x80:
+        powername = "IDLE";    powerlimit = 4; break;
+      case 0xff:
+        powername = "ACTIVE or IDLE"; break;
+      default:
+        pout("CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Option\n", powermode);
+        break;
+    }
+    if (powername) {
+      if (con->powermode >= powerlimit) {
+        pout("Device is in %s mode, exit(%d)\n", powername, FAILPOWER);
+        return FAILPOWER;
+      }
+      powerchg = (powermode != 0xff); // SMART tests will spin up drives
+    }
+  }
+
+  // Start by getting Drive ID information.  We need this, to know if SMART is supported.
+  if ((retid=ataReadHDIdentity(fd,&drive))<0){
+    pout("Smartctl: Device Read Identity Failed (not an ATA/ATAPI device)\n\n");
+    failuretest(MANDATORY_CMD, returnval|=FAILID);
+  }
+
+  // If requested, show which presets would be used for this drive and exit.
+  if (con->showpresets) {
+    showpresets(&drive);
+    EXIT(0);
+  }
+
+  // Use preset vendor attribute options unless user has requested otherwise.
+  if (!con->ignorepresets){
+    unsigned char *charptr;
+    if ((charptr=con->attributedefs))
+      applypresets(&drive, &charptr, con);
+    else {
+      pout("Fatal internal error in ataPrintMain()\n");
+      EXIT(returnval|=FAILCMD);
+    }
+  }
+
+  // Print most drive identity information if requested
+  if (con->driveinfo){
+    pout("=== START OF INFORMATION SECTION ===\n");
+    known = ataPrintDriveInfo(&drive);
+  }
+
+  // Was this a packet device?
+  if (retid>0){
+    pout("SMART support is: Unavailable - Packet Interface Devices [this device: %s] don't support ATA SMART\n", packetdevicetype(retid-1));
+    failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+  }
+  
+  // if drive does not supports SMART it's time to exit
+  supported=ataSmartSupport(&drive);
+  if (supported != 1){
+    if (supported==0) {
+      pout("SMART support is: Unavailable - device lacks SMART capability.\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+      pout("                  Checking to be sure by trying SMART ENABLE command.\n");
+    }
+    else {
+      pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 82-83 don't show if SMART supported.\n");
+      if (!known) failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+      pout("                  Checking for SMART support by trying SMART ENABLE command.\n");
+    }
+
+    if (ataEnableSmart(fd)){
+      pout("                  SMART ENABLE failed - this establishes that this device lacks SMART functionality.\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+      supported=0;
+    }
+    else {
+      pout("                  SMART ENABLE appeared to work!  Continuing.\n");
+      supported=1;
+    }
+    if (!con->driveinfo) pout("\n");
+  }
+  
+  // Now print remaining drive info: is SMART enabled?    
+  if (con->driveinfo){
+    int ison=ataIsSmartEnabled(&drive),isenabled=ison;
+    
+    if (ison==-1) {
+      pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 85-87 don't show if SMART is enabled.\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+      // check SMART support by trying a command
+      pout("                  Checking to be sure by trying SMART RETURN STATUS command.\n");
+      isenabled=ataDoesSmartWork(fd);
+    }
+    else {
+      pout("SMART support is: Available - device has SMART capability.\n");
+#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
+      if (ata_identify_is_cached(fd)) {
+        pout("                  %sabled status cached by OS, trying SMART RETURN STATUS cmd.\n",
+                    (isenabled?"En":"Dis"));
+        isenabled=ataDoesSmartWork(fd);
+      }
+#endif
+    }
+
+    if (isenabled)
+      pout("SMART support is: Enabled\n");
+    else {
+      if (ison==-1)
+        pout("SMART support is: Unavailable\n");
+      else
+        pout("SMART support is: Disabled\n");
+    }
+    // Print the (now possibly changed) power mode if available
+    if (powername)
+      pout("Power mode %s   %s\n", (powerchg?"was:":"is: "), powername);
+    pout("\n");
+  }
+  
+  // START OF THE ENABLE/DISABLE SECTION OF THE CODE
+  if (con->smartenable || con->smartdisable || 
+      con->smartautosaveenable || con->smartautosavedisable || 
+      con->smartautoofflineenable || con->smartautoofflinedisable)
+    pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
+  
+  // Enable/Disable SMART commands
+  if (con->smartenable){
+    if (ataEnableSmart(fd)) {
+      pout("Smartctl: SMART Enable Failed.\n\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+    }
+    else
+      pout("SMART Enabled.\n");
+  }
+  
+  // From here on, every command requires that SMART be enabled...
+  if (!ataDoesSmartWork(fd)) {
+    pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n");
+    return returnval;
+  }
+  
+  // Turn off SMART on device
+  if (con->smartdisable){    
+    if (ataDisableSmart(fd)) {
+      pout( "Smartctl: SMART Disable Failed.\n\n");
+      failuretest(MANDATORY_CMD,returnval|=FAILSMART);
+    }
+    pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n");
+    return returnval;           
+  }
+  
+  // Let's ALWAYS issue this command to get the SMART status
+  code=ataSmartStatus2(fd);
+  if (code==-1)
+    failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+
+  // Enable/Disable Auto-save attributes
+  if (con->smartautosaveenable){
+    if (ataEnableAutoSave(fd)){
+      pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+    }
+    else
+      pout("SMART Attribute Autosave Enabled.\n");
+  }
+  if (con->smartautosavedisable){
+    if (ataDisableAutoSave(fd)){
+      pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+    }
+    else
+      pout("SMART Attribute Autosave Disabled.\n");
+  }
+  
+  // for everything else read values and thresholds are needed
+  if (ataReadSmartValues(fd, &smartval)){
+    pout("Smartctl: SMART Read Values failed.\n\n");
+    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+  }
+  if (ataReadSmartThresholds(fd, &smartthres)){
+    pout("Smartctl: SMART Read Thresholds failed.\n\n");
+    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+  }
+
+  // Enable/Disable Off-line testing
+  if (con->smartautoofflineenable){
+    if (!isSupportAutomaticTimer(&smartval)){
+      pout("Warning: device does not support SMART Automatic Timers.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    needupdate=1;
+    if (ataEnableAutoOffline(fd)){
+      pout( "Smartctl: SMART Enable Automatic Offline Failed.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else
+      pout("SMART Automatic Offline Testing Enabled every four hours.\n");
+  }
+  if (con->smartautoofflinedisable){
+    if (!isSupportAutomaticTimer(&smartval)){
+      pout("Warning: device does not support SMART Automatic Timers.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    needupdate=1;
+    if (ataDisableAutoOffline(fd)){
+      pout("Smartctl: SMART Disable Automatic Offline Failed.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else
+      pout("SMART Automatic Offline Testing Disabled.\n");
+  }
+
+  if (needupdate && ataReadSmartValues(fd, &smartval)){
+    pout("Smartctl: SMART Read Values failed.\n\n");
+    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+  }
+
+  // all this for a newline!
+  if (con->smartenable || con->smartdisable || 
+      con->smartautosaveenable || con->smartautosavedisable || 
+      con->smartautoofflineenable || con->smartautoofflinedisable)
+    pout("\n");
+
+  // START OF READ-ONLY OPTIONS APART FROM -V and -i
+  if (con->checksmart || con->generalsmartvalues || con->smartvendorattrib || con->smarterrorlog || con->smartselftestlog)
+    pout("=== START OF READ SMART DATA SECTION ===\n");
+  
+  // Check SMART status (use previously returned value)
+  if (con->checksmart){
+    switch (code) {
+
+    case 0:
+      // The case where the disk health is OK
+      pout("SMART overall-health self-assessment test result: PASSED\n");
+      if (ataCheckSmart(&smartval, &smartthres,0)){
+        if (con->smartvendorattrib)
+          pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
+        else {
+          PRINT_ON(con);
+          pout("Please note the following marginal Attributes:\n");
+          PrintSmartAttribWithThres(&smartval, &smartthres,2);
+        } 
+        returnval|=FAILAGE;
+      }
+      else
+        pout("\n");
+      break;
+      
+    case 1:
+      // The case where the disk health is NOT OK
+      PRINT_ON(con);
+      pout("SMART overall-health self-assessment test result: FAILED!\n"
+           "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
+      PRINT_OFF(con);
+      if (ataCheckSmart(&smartval, &smartthres,1)){
+        returnval|=FAILATTR;
+        if (con->smartvendorattrib)
+          pout("See vendor-specific Attribute list for failed Attributes.\n\n");
+        else {
+          PRINT_ON(con);
+          pout("Failed Attributes:\n");
+          PrintSmartAttribWithThres(&smartval, &smartthres,1);
+        }
+      }
+      else
+        pout("No failed Attributes found.\n\n");   
+      returnval|=FAILSTATUS;
+      PRINT_OFF(con);
+      break;
+
+    case -1:
+    default:
+      // The case where something went wrong with HDIO_DRIVE_TASK ioctl()
+      if (ataCheckSmart(&smartval, &smartthres,1)){
+        PRINT_ON(con);
+        pout("SMART overall-health self-assessment test result: FAILED!\n"
+             "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
+        PRINT_OFF(con);
+        returnval|=FAILATTR;
+        returnval|=FAILSTATUS;
+        if (con->smartvendorattrib)
+          pout("See vendor-specific Attribute list for failed Attributes.\n\n");
+        else {
+          PRINT_ON(con);
+          pout("Failed Attributes:\n");
+          PrintSmartAttribWithThres(&smartval, &smartthres,1);
+        }
+      }
+      else {
+        pout("SMART overall-health self-assessment test result: PASSED\n");
+        if (ataCheckSmart(&smartval, &smartthres,0)){
+          if (con->smartvendorattrib)
+            pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
+          else {
+            PRINT_ON(con);
+            pout("Please note the following marginal Attributes:\n");
+            PrintSmartAttribWithThres(&smartval, &smartthres,2);
+          } 
+          returnval|=FAILAGE;
+        }
+        else
+          pout("\n");
+      } 
+      PRINT_OFF(con);
+      break;
+    } // end of switch statement
+    
+    PRINT_OFF(con);
+  } // end of checking SMART Status
+  
+  // Print general SMART values
+  if (con->generalsmartvalues)
+    ataPrintGeneralSmartValues(&smartval, &drive); 
+
+  // Print vendor-specific attributes
+  if (con->smartvendorattrib){
+    PRINT_ON(con);
+    PrintSmartAttribWithThres(&smartval, &smartthres,con->printing_switchable?2:0);
+    PRINT_OFF(con);
+  }
+
+  // Print SMART log Directory
+  if (con->smartlogdirectory){
+    struct ata_smart_log_directory smartlogdirectory;
+    if (!isGeneralPurposeLoggingCapable(&drive)){
+      pout("Warning: device does not support General Purpose Logging\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else {
+      PRINT_ON(con);
+      pout("Log Directory Supported\n");
+      if (ataReadLogDirectory(fd, &smartlogdirectory)){
+        PRINT_OFF(con);
+        pout("Read Log Directory failed.\n\n");
+        failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+      }
+      else
+        ataPrintLogDirectory( &smartlogdirectory);
+    }
+    PRINT_OFF(con);
+  }
+  
+  // Print SMART error log
+  if (con->smarterrorlog){
+    if (!isSmartErrorLogCapable(&smartval, &drive)){
+      pout("Warning: device does not support Error Logging\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    if (ataReadErrorLog(fd, &smarterror)){
+      pout("Smartctl: SMART Error Log Read Failed\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else {
+      // quiet mode is turned on inside ataPrintSmartErrorLog()
+      if (ataPrintSmartErrorlog(&smarterror))
+       returnval|=FAILERR;
+      PRINT_OFF(con);
+    }
+  }
+  
+  // Print SMART self-test log
+  if (con->smartselftestlog){
+    if (!isSmartTestLogCapable(&smartval, &drive)){
+      pout("Warning: device does not support Self Test Logging\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }    
+    if(ataReadSelfTestLog(fd, &smartselftest)){
+      pout("Smartctl: SMART Self Test Log Read Failed\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else {
+      PRINT_ON(con);
+      if (ataPrintSmartSelfTestlog(&smartselftest,!con->printing_switchable))
+       returnval|=FAILLOG;
+      PRINT_OFF(con);
+      pout("\n");
+    }
+  }
+
+  // Print SMART selective self-test log
+  if (con->selectivetestlog){
+    struct ata_selective_self_test_log log;
+    
+    if (!isSupportSelectiveSelfTest(&smartval))
+      pout("Device does not support Selective Self Tests/Logging\n");
+    else if(ataReadSelectiveSelfTestLog(fd, &log)) {
+      pout("Smartctl: SMART Selective Self Test Log Read Failed\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else {
+      PRINT_ON(con);
+      ataPrintSelectiveSelfTestLog(&log, &smartval);
+      PRINT_OFF(con);
+      pout("\n");
+    }
+  }
+
+  // START OF THE TESTING SECTION OF THE CODE.  IF NO TESTING, RETURN
+  if (con->testcase==-1)
+    return returnval;
+  
+  pout("=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\n");
+  // if doing a self-test, be sure it's supported by the hardware
+  switch (con->testcase){
+  case OFFLINE_FULL_SCAN:
+    if (!isSupportExecuteOfflineImmediate(&smartval)){
+      pout("Warning: device does not support Execute Offline Immediate function.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    break;
+  case ABORT_SELF_TEST:
+  case SHORT_SELF_TEST:
+  case EXTEND_SELF_TEST:
+  case SHORT_CAPTIVE_SELF_TEST:
+  case EXTEND_CAPTIVE_SELF_TEST:
+    if (!isSupportSelfTest(&smartval)){
+      pout("Warning: device does not support Self-Test functions.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    break;
+  case CONVEYANCE_SELF_TEST:
+  case CONVEYANCE_CAPTIVE_SELF_TEST:
+    if (!isSupportConveyanceSelfTest(&smartval)){
+      pout("Warning: device does not support Conveyance Self-Test functions.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    break;
+  case SELECTIVE_SELF_TEST:
+  case SELECTIVE_CAPTIVE_SELF_TEST:
+    if (!isSupportSelectiveSelfTest(&smartval)){
+      pout("Warning: device does not support Selective Self-Test functions.\n\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+    }
+    break;
+  default:
+    pout("Internal error in smartctl: con->testcase==%d not recognized\n", (int)con->testcase);
+    pout("Please contact smartmontools developers at %s.\n", PACKAGE_BUGREPORT);
+    EXIT(returnval|=FAILCMD);
+  }
+
+  // Now do the test.  Note ataSmartTest prints its own error/success
+  // messages
+  if (ataSmartTest(fd, con->testcase, &smartval))
+    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+  else {  
+    // Tell user how long test will take to complete.  This is tricky
+    // because in the case of an Offline Full Scan, the completion
+    // timer is volatile, and needs to be read AFTER the command is
+    // given. If this will interrupt the Offline Full Scan, we don't
+    // do it, just warn user.
+    if (con->testcase==OFFLINE_FULL_SCAN){
+      if (isSupportOfflineAbort(&smartval))
+       pout("Note: giving further SMART commands will abort Offline testing\n");
+      else if (ataReadSmartValues(fd, &smartval)){
+       pout("Smartctl: SMART Read Values failed.\n");
+       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+      }
+    }
+    
+    // Now say how long the test will take to complete
+    if ((timewait=TestTime(&smartval,con->testcase))){ 
+      time_t t=time(NULL);
+      if (con->testcase==OFFLINE_FULL_SCAN) {
+       t+=timewait;
+       pout("Please wait %d seconds for test to complete.\n", (int)timewait);
+      } else {
+       t+=timewait*60;
+       pout("Please wait %d minutes for test to complete.\n", (int)timewait);
+      }
+      pout("Test will complete after %s\n", ctime(&t));
+      
+      if (con->testcase!=SHORT_CAPTIVE_SELF_TEST && 
+         con->testcase!=EXTEND_CAPTIVE_SELF_TEST && 
+         con->testcase!=CONVEYANCE_CAPTIVE_SELF_TEST && 
+         con->testcase!=SELECTIVE_CAPTIVE_SELF_TEST)
+       pout("Use smartctl -X to abort test.\n"); 
+    }
+  }
+
+  return returnval;
+}
index d2e1e46fc4029c431f4836086dfc305599a1a491..968cfdd03d9ec04e06356a65348acdffac80c24e 100644 (file)
 #ifndef ATAPRINT_H_
 #define ATAPRINT_H_
 
-#define ATAPRINT_H_CVSID "$Id: ataprint.h,v 1.28 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define ATAPRINT_H_CVSID "$Id: ataprint.h,v 1.29 2006/09/17 10:12:51 shattered Exp $\n"
 
 #include <stdio.h>
 #include <stdlib.h>
 
 /* Prints ATA Drive Information and S.M.A.R.T. Capability */
-void ataPrintDriveInfo(struct ata_identify_device *);
+int ataPrintDriveInfo(struct ata_identify_device *);
 
 void ataPrintGeneralSmartValues(struct ata_smart_values *, struct ata_identify_device *);
 
diff --git a/config.guess b/config.guess
deleted file mode 100755 (executable)
index c28419d..0000000
+++ /dev/null
@@ -1,1450 +0,0 @@
-#! /bin/sh
-# Attempt to guess a canonical system name.
-#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-#   2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
-
-timestamp='2004-10-25'
-
-# 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.
-
-# Originally written by Per Bothner <per@bothner.com>.
-# Please send patches to <config-patches@gnu.org>.  Submit a context
-# diff and a properly formatted ChangeLog entry.
-#
-# 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 build system type.
-
-me=`echo "$0" | sed -e 's,.*/,,'`
-
-usage="\
-Usage: $0 [OPTION]
-
-Output the configuration name of the system \`$me' is run on.
-
-Operation modes:
-  -h, --help         print this help, then exit
-  -t, --time-stamp   print date of last modification, then exit
-  -v, --version      print version number, then exit
-
-Report bugs and patches to <config-patches@gnu.org>."
-
-version="\
-GNU config.guess ($timestamp)
-
-Originally written by Per Bothner.
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
-Free Software Foundation, Inc.
-
-This is free software; see the source for copying conditions.  There is NO
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
-
-help="
-Try \`$me --help' for more information."
-
-# Parse command line
-while test $# -gt 0 ; do
-  case $1 in
-    --time-stamp | --time* | -t )
-       echo "$timestamp" ; exit 0 ;;
-    --version | -v )
-       echo "$version" ; exit 0 ;;
-    --help | --h* | -h )
-       echo "$usage"; exit 0 ;;
-    -- )     # Stop option processing
-       shift; break ;;
-    - )        # Use stdin as input.
-       break ;;
-    -* )
-       echo "$me: invalid option $1$help" >&2
-       exit 1 ;;
-    * )
-       break ;;
-  esac
-done
-
-if test $# != 0; then
-  echo "$me: too many arguments$help" >&2
-  exit 1
-fi
-
-trap 'exit 1' 1 2 15
-
-# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
-# compiler to aid in system detection is discouraged as it requires
-# temporary files to be created and, as you can see below, it is a
-# headache to deal with in a portable fashion.
-
-# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
-# use `HOST_CC' if defined, but it is deprecated.
-
-# Portable tmp directory creation inspired by the Autoconf team.
-
-set_cc_for_build='
-trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
-trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
-: ${TMPDIR=/tmp} ;
- { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
- { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
- { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
- { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
-dummy=$tmp/dummy ;
-tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
-case $CC_FOR_BUILD,$HOST_CC,$CC in
- ,,)    echo "int x;" > $dummy.c ;
-       for c in cc gcc c89 c99 ; do
-         if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
-            CC_FOR_BUILD="$c"; break ;
-         fi ;
-       done ;
-       if test x"$CC_FOR_BUILD" = x ; then
-         CC_FOR_BUILD=no_compiler_found ;
-       fi
-       ;;
- ,,*)   CC_FOR_BUILD=$CC ;;
- ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
-esac ;'
-
-# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
-# (ghazi@noc.rutgers.edu 1994-08-24)
-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
-
-# Note: order is significant - the case branches are not exclusive.
-
-case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
-    *:NetBSD:*:*)
-       # NetBSD (nbsd) targets should (where applicable) match one or
-       # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
-       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
-       # switched to ELF, *-*-netbsd* would select the old
-       # object file format.  This provides both forward
-       # compatibility and a consistent mechanism for selecting the
-       # object file format.
-       #
-       # Note: NetBSD doesn't particularly care about the vendor
-       # portion of the name.  We always set it to "unknown".
-       sysctl="sysctl -n hw.machine_arch"
-       UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
-           /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
-       case "${UNAME_MACHINE_ARCH}" in
-           armeb) machine=armeb-unknown ;;
-           arm*) machine=arm-unknown ;;
-           sh3el) machine=shl-unknown ;;
-           sh3eb) machine=sh-unknown ;;
-           *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
-       esac
-       # The Operating System including object format, if it has switched
-       # to ELF recently, or will in the future.
-       case "${UNAME_MACHINE_ARCH}" in
-           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
-               eval $set_cc_for_build
-               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
-                       | grep __ELF__ >/dev/null
-               then
-                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
-                   # Return netbsd for either.  FIX?
-                   os=netbsd
-               else
-                   os=netbsdelf
-               fi
-               ;;
-           *)
-               os=netbsd
-               ;;
-       esac
-       # The OS release
-       # Debian GNU/NetBSD machines have a different userland, and
-       # thus, need a distinct triplet. However, they do not need
-       # kernel version information, so it can be replaced with a
-       # suitable tag, in the style of linux-gnu.
-       case "${UNAME_VERSION}" in
-           Debian*)
-               release='-gnu'
-               ;;
-           *)
-               release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
-               ;;
-       esac
-       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
-       # contains redundant information, the shorter form:
-       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
-       echo "${machine}-${os}${release}"
-       exit 0 ;;
-    amd64:OpenBSD:*:*)
-       echo x86_64-unknown-openbsd${UNAME_RELEASE}
-       exit 0 ;;
-    amiga:OpenBSD:*:*)
-       echo m68k-unknown-openbsd${UNAME_RELEASE}
-       exit 0 ;;
-    cats:OpenBSD:*:*)
-       echo arm-unknown-openbsd${UNAME_RELEASE}
-       exit 0 ;;
-    hp300:OpenBSD:*:*)
-       echo m68k-unknown-openbsd${UNAME_RELEASE}
-       exit 0 ;;
-    luna88k:OpenBSD:*:*)
-       echo m88k-unknown-openbsd${UNAME_RELEASE}
-       exit 0 ;;
-    mac68k:OpenBSD:*:*)
-       echo m68k-unknown-openbsd${UNAME_RELEASE}
-       exit 0 ;;
-    macppc:OpenBSD:*:*)
-       echo powerpc-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 ;;
-    mvmeppc:OpenBSD:*:*)
-       echo powerpc-unknown-openbsd${UNAME_RELEASE}
-       exit 0 ;;
-    sgi:OpenBSD:*:*)
-       echo mips64-unknown-openbsd${UNAME_RELEASE}
-       exit 0 ;;
-    sun3:OpenBSD:*:*)
-       echo m68k-unknown-openbsd${UNAME_RELEASE}
-       exit 0 ;;
-    *:OpenBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
-       exit 0 ;;
-    *:ekkoBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
-       exit 0 ;;
-    macppc:MirBSD:*:*)
-       echo powerppc-unknown-mirbsd${UNAME_RELEASE}
-       exit 0 ;;
-    *:MirBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
-       exit 0 ;;
-    alpha:OSF1:*:*)
-       case $UNAME_RELEASE in
-       *4.0)
-               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
-               ;;
-       *5.*)
-               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
-               ;;
-       esac
-       # According to Compaq, /usr/sbin/psrinfo has been available on
-       # OSF/1 and Tru64 systems produced since 1995.  I hope that
-       # covers most systems running today.  This code pipes the CPU
-       # types through head -n 1, so we only detect the type of CPU 0.
-       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
-       case "$ALPHA_CPU_TYPE" in
-           "EV4 (21064)")
-               UNAME_MACHINE="alpha" ;;
-           "EV4.5 (21064)")
-               UNAME_MACHINE="alpha" ;;
-           "LCA4 (21066/21068)")
-               UNAME_MACHINE="alpha" ;;
-           "EV5 (21164)")
-               UNAME_MACHINE="alphaev5" ;;
-           "EV5.6 (21164A)")
-               UNAME_MACHINE="alphaev56" ;;
-           "EV5.6 (21164PC)")
-               UNAME_MACHINE="alphapca56" ;;
-           "EV5.7 (21164PC)")
-               UNAME_MACHINE="alphapca57" ;;
-           "EV6 (21264)")
-               UNAME_MACHINE="alphaev6" ;;
-           "EV6.7 (21264A)")
-               UNAME_MACHINE="alphaev67" ;;
-           "EV6.8CB (21264C)")
-               UNAME_MACHINE="alphaev68" ;;
-           "EV6.8AL (21264B)")
-               UNAME_MACHINE="alphaev68" ;;
-           "EV6.8CX (21264D)")
-               UNAME_MACHINE="alphaev68" ;;
-           "EV6.9A (21264/EV69A)")
-               UNAME_MACHINE="alphaev69" ;;
-           "EV7 (21364)")
-               UNAME_MACHINE="alphaev7" ;;
-           "EV7.9 (21364A)")
-               UNAME_MACHINE="alphaev79" ;;
-       esac
-       # A Pn.n version is a patched version.
-       # 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.
-       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-       exit 0 ;;
-    Alpha\ *:Windows_NT*:*)
-       # How do we know it's Interix rather than the generic POSIX subsystem?
-       # Should we change UNAME_MACHINE based on the output of uname instead
-       # of the specific Alpha model?
-       echo alpha-pc-interix
-       exit 0 ;;
-    21064:Windows_NT:50:3)
-       echo alpha-dec-winnt3.5
-       exit 0 ;;
-    Amiga*:UNIX_System_V:4.0:*)
-       echo m68k-unknown-sysv4
-       exit 0;;
-    *:[Aa]miga[Oo][Ss]:*:*)
-       echo ${UNAME_MACHINE}-unknown-amigaos
-       exit 0 ;;
-    *:[Mm]orph[Oo][Ss]:*:*)
-       echo ${UNAME_MACHINE}-unknown-morphos
-       exit 0 ;;
-    *:OS/390:*:*)
-       echo i370-ibm-openedition
-       exit 0 ;;
-    *:OS400:*:*)
-        echo powerpc-ibm-os400
-       exit 0 ;;
-    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
-       echo arm-acorn-riscix${UNAME_RELEASE}
-       exit 0;;
-    SR2?01:HI-UX/MPP:*:* | SR8000: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 ;;
-    DRS?6000:unix:4.0:6*)
-       echo sparc-icl-nx6
-       exit 0 ;;
-    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
-       case `/usr/bin/uname -p` in
-           sparc) echo sparc-icl-nx7 && exit 0 ;;
-       esac ;;
-    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=`(sed 1q /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 ;;
-    # The situation for MiNT is a little confusing.  The machine name
-    # can be virtually everything (everything which is not
-    # "atarist" or "atariste" at least should have a processor
-    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
-    # to the lowercase version "mint" (or "freemint").  Finally
-    # the system name "TOS" denotes a system which is actually not
-    # MiNT.  But MiNT is downward compatible to TOS, so this should
-    # be no problem.
-    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
-        echo m68k-atari-mint${UNAME_RELEASE}
-       exit 0 ;;
-    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
-       echo m68k-atari-mint${UNAME_RELEASE}
-        exit 0 ;;
-    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
-        echo m68k-atari-mint${UNAME_RELEASE}
-       exit 0 ;;
-    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
-        echo m68k-milan-mint${UNAME_RELEASE}
-        exit 0 ;;
-    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
-        echo m68k-hades-mint${UNAME_RELEASE}
-        exit 0 ;;
-    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
-        echo m68k-unknown-mint${UNAME_RELEASE}
-        exit 0 ;;
-    m68k:machten:*:*)
-       echo m68k-apple-machten${UNAME_RELEASE}
-       exit 0 ;;
-    powerpc:machten:*:*)
-       echo powerpc-apple-machten${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:*:* | 2430:CLIX:*:*)
-       echo clipper-intergraph-clix${UNAME_RELEASE}
-       exit 0 ;;
-    mips:*:*:UMIPS | mips:*:*:RISCos)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-#ifdef __cplusplus
-#include <stdio.h>  /* for printf() prototype */
-       int main (int argc, char *argv[]) {
-#else
-       int main (argc, argv) int argc; char *argv[]; {
-#endif
-       #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_FOR_BUILD -o $dummy $dummy.c \
-         && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
-         && exit 0
-       echo mips-mips-riscos${UNAME_RELEASE}
-       exit 0 ;;
-    Motorola:PowerMAX_OS:*:*)
-       echo powerpc-motorola-powermax
-       exit 0 ;;
-    Motorola:*:4.3:PL8-*)
-       echo powerpc-harris-powermax
-       exit 0 ;;
-    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
-       echo powerpc-harris-powermax
-       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 ] || [ $UNAME_PROCESSOR = mc88110 ]
-       then
-           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
-              [ ${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 ;;
-    ia64:AIX:*:*)
-       if [ -x /usr/bin/oslevel ] ; then
-               IBM_REV=`/usr/bin/oslevel`
-       else
-               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
-       fi
-       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
-       exit 0 ;;
-    *:AIX:2:3)
-       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
-               eval $set_cc_for_build
-               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_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
-               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:*:[45])
-       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
-       if /usr/sbin/lsattr -El ${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=${UNAME_VERSION}.${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 BSD 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:*:*)
-       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
-       case "${UNAME_MACHINE}" in
-           9000/31? )            HP_ARCH=m68000 ;;
-           9000/[34]?? )         HP_ARCH=m68k ;;
-           9000/[678][0-9][0-9])
-               if [ -x /usr/bin/getconf ]; then
-                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
-                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
-                    case "${sc_cpu_version}" in
-                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
-                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
-                      532)                      # CPU_PA_RISC2_0
-                        case "${sc_kernel_bits}" in
-                          32) HP_ARCH="hppa2.0n" ;;
-                          64) HP_ARCH="hppa2.0w" ;;
-                         '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
-                        esac ;;
-                    esac
-               fi
-               if [ "${HP_ARCH}" = "" ]; then
-                   eval $set_cc_for_build
-                   sed 's/^              //' << EOF >$dummy.c
-
-              #define _HPUX_SOURCE
-              #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
-                   (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
-                   test -z "$HP_ARCH" && HP_ARCH=hppa
-               fi ;;
-       esac
-       if [ ${HP_ARCH} = "hppa2.0w" ]
-       then
-           # avoid double evaluation of $set_cc_for_build
-           test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
-           if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
-           then
-               HP_ARCH="hppa2.0w"
-           else
-               HP_ARCH="hppa64"
-           fi
-       fi
-       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
-       exit 0 ;;
-    ia64:HP-UX:*:*)
-       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
-       echo ia64-hp-hpux${HPUX_REV}
-       exit 0 ;;
-    3050*:HI-UX:*:*)
-       eval $set_cc_for_build
-       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_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
-       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 ;;
-    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
-       echo hppa1.0-hp-mpeix
-       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*Y-MP:*:*:*)
-       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       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/ \
-             -e 's/\.[^.]*$/.X/'
-       exit 0 ;;
-    CRAY*TS:*:*:*)
-       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit 0 ;;
-    CRAY*T3E:*:*:*)
-       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit 0 ;;
-    CRAY*SV1:*:*:*)
-       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit 0 ;;
-    *:UNICOS/mp:*:*)
-       echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit 0 ;;
-    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
-       FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
-        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
-        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
-        exit 0 ;;
-    5000:UNIX_System_V:4.*:*)
-        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
-        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
-        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
-       exit 0 ;;
-    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
-       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
-       exit 0 ;;
-    sparc*:BSD/OS:*:*)
-       echo sparc-unknown-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 ;;
-    i*:CYGWIN*:*)
-       echo ${UNAME_MACHINE}-pc-cygwin
-       exit 0 ;;
-    i*:MINGW*:*)
-       echo ${UNAME_MACHINE}-pc-mingw32
-       exit 0 ;;
-    i*:PW*:*)
-       echo ${UNAME_MACHINE}-pc-pw32
-       exit 0 ;;
-    x86:Interix*:[34]*)
-       echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
-       exit 0 ;;
-    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
-       echo i${UNAME_MACHINE}-pc-mks
-       exit 0 ;;
-    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
-       # How do we know it's Interix rather than the generic POSIX subsystem?
-       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
-       # UNAME_MACHINE based on the output of uname instead of i386?
-       echo i586-pc-interix
-       exit 0 ;;
-    i*:UWIN*:*)
-       echo ${UNAME_MACHINE}-pc-uwin
-       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:*:*)
-       # the GNU system
-       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
-       exit 0 ;;
-    *:GNU/*:*:*)
-       # other systems with GNU libc and userland
-       echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
-       exit 0 ;;
-    i*86:Minix:*:*)
-       echo ${UNAME_MACHINE}-pc-minix
-       exit 0 ;;
-    arm*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit 0 ;;
-    cris:Linux:*:*)
-       echo cris-axis-linux-gnu
-       exit 0 ;;
-    crisv32:Linux:*:*)
-       echo crisv32-axis-linux-gnu
-       exit 0 ;;
-    frv:Linux:*:*)
-       echo frv-unknown-linux-gnu
-       exit 0 ;;
-    ia64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit 0 ;;
-    m32r*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit 0 ;;
-    m68*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit 0 ;;
-    mips:Linux:*:*)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-       #undef CPU
-       #undef mips
-       #undef mipsel
-       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
-       CPU=mipsel
-       #else
-       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
-       CPU=mips
-       #else
-       CPU=
-       #endif
-       #endif
-EOF
-       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
-       test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
-       ;;
-    mips64:Linux:*:*)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-       #undef CPU
-       #undef mips64
-       #undef mips64el
-       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
-       CPU=mips64el
-       #else
-       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
-       CPU=mips64
-       #else
-       CPU=
-       #endif
-       #endif
-EOF
-       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
-       test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
-       ;;
-    ppc:Linux:*:*)
-       echo powerpc-unknown-linux-gnu
-       exit 0 ;;
-    ppc64:Linux:*:*)
-       echo powerpc64-unknown-linux-gnu
-       exit 0 ;;
-    alpha:Linux:*:*)
-       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
-         EV5)   UNAME_MACHINE=alphaev5 ;;
-         EV56)  UNAME_MACHINE=alphaev56 ;;
-         PCA56) UNAME_MACHINE=alphapca56 ;;
-         PCA57) UNAME_MACHINE=alphapca56 ;;
-         EV6)   UNAME_MACHINE=alphaev6 ;;
-         EV67)  UNAME_MACHINE=alphaev67 ;;
-         EV68*) UNAME_MACHINE=alphaev68 ;;
-        esac
-       objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
-       if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
-       echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
-       exit 0 ;;
-    parisc:Linux:*:* | hppa:Linux:*:*)
-       # Look for CPU level
-       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
-         PA7*) echo hppa1.1-unknown-linux-gnu ;;
-         PA8*) echo hppa2.0-unknown-linux-gnu ;;
-         *)    echo hppa-unknown-linux-gnu ;;
-       esac
-       exit 0 ;;
-    parisc64:Linux:*:* | hppa64:Linux:*:*)
-       echo hppa64-unknown-linux-gnu
-       exit 0 ;;
-    s390:Linux:*:* | s390x:Linux:*:*)
-       echo ${UNAME_MACHINE}-ibm-linux
-       exit 0 ;;
-    sh64*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit 0 ;;
-    sh*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit 0 ;;
-    sparc:Linux:*:* | sparc64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit 0 ;;
-    x86_64:Linux:*:*)
-       echo x86_64-unknown-linux-gnu
-       exit 0 ;;
-    i*86:Linux:*:*)
-       # The BFD linker knows what the default object file format is, so
-       # first see if it will tell us. cd to the root directory to prevent
-       # problems with other programs or directories called `ld' in the path.
-       # Set LC_ALL=C to ensure ld outputs messages in English.
-       ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
-                        | sed -ne '/supported targets:/!d
-                                   s/[         ][      ]*/ /g
-                                   s/.*supported targets: *//
-                                   s/ .*//
-                                   p'`
-        case "$ld_supported_targets" in
-         elf32-i386)
-               TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
-               ;;
-         a.out-i386-linux)
-               echo "${UNAME_MACHINE}-pc-linux-gnuaout"
-               exit 0 ;;
-         coff-i386)
-               echo "${UNAME_MACHINE}-pc-linux-gnucoff"
-               exit 0 ;;
-         "")
-               # Either a pre-BFD a.out linker (linux-gnuoldld) or
-               # one that does not give us useful --help.
-               echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
-               exit 0 ;;
-       esac
-       # Determine whether the default compiler is a.out or elf
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-       #include <features.h>
-       #ifdef __ELF__
-       # ifdef __GLIBC__
-       #  if __GLIBC__ >= 2
-       LIBC=gnu
-       #  else
-       LIBC=gnulibc1
-       #  endif
-       # else
-       LIBC=gnulibc1
-       # endif
-       #else
-       #ifdef __INTEL_COMPILER
-       LIBC=gnu
-       #else
-       LIBC=gnuaout
-       #endif
-       #endif
-       #ifdef __dietlibc__
-       LIBC=dietlibc
-       #endif
-EOF
-       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
-       test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
-       test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
-       ;;
-    i*86:DYNIX/ptx:4*:*)
-       # 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.
-       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:OS/2:*:*)
-       # If we were able to find `uname', then EMX Unix compatibility
-       # is probably installed.
-       echo ${UNAME_MACHINE}-pc-os2-emx
-       exit 0 ;;
-    i*86:XTS-300:*:STOP)
-       echo ${UNAME_MACHINE}-unknown-stop
-       exit 0 ;;
-    i*86:atheos:*:*)
-       echo ${UNAME_MACHINE}-unknown-atheos
-       exit 0 ;;
-       i*86:syllable:*:*)
-       echo ${UNAME_MACHINE}-pc-syllable
-       exit 0 ;;
-    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
-       echo i386-unknown-lynxos${UNAME_RELEASE}
-       exit 0 ;;
-    i*86:*DOS:*:*)
-       echo ${UNAME_MACHINE}-pc-msdosdjgpp
-       exit 0 ;;
-    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
-       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
-       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
-               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
-       else
-               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
-       fi
-       exit 0 ;;
-    i*86:*:5:[78]*)
-       case `/bin/uname -X | grep "^Machine"` in
-           *486*)           UNAME_MACHINE=i486 ;;
-           *Pentium)        UNAME_MACHINE=i586 ;;
-           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
-       esac
-       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
-       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|grep Release|sed -e 's/.*= //')`
-               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
-               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
-                       && UNAME_MACHINE=i586
-               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
-                       && UNAME_MACHINE=i686
-               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
-                       && UNAME_MACHINE=i686
-               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
-       else
-               echo ${UNAME_MACHINE}-pc-sysv32
-       fi
-       exit 0 ;;
-    pc:*:*:*)
-       # Left here for compatibility:
-        # 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 ;;
-    mc68k:UNIX:SYSTEM5:3.51m)
-       echo m68k-convergent-sysv
-       exit 0 ;;
-    M680?0:D-NIX:5.3:*)
-       echo m68k-diab-dnix
-       exit 0 ;;
-    M68*:*:R3V[5678]*:*)
-       test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
-    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*: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.*:* | m68*:LynxOS:3.0*:*)
-       echo m68k-unknown-lynxos${UNAME_RELEASE}
-       exit 0 ;;
-    mc68030:UNIX_System_V:4.*:*)
-       echo m68k-atari-sysv4
-       exit 0 ;;
-    TSUNAMI:LynxOS:2.*:*)
-       echo sparc-unknown-lynxos${UNAME_RELEASE}
-       exit 0 ;;
-    rs6000:LynxOS:2.*:*)
-       echo rs6000-unknown-lynxos${UNAME_RELEASE}
-       exit 0 ;;
-    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
-       echo powerpc-unknown-lynxos${UNAME_RELEASE}
-       exit 0 ;;
-    SM[BE]S:UNIX_SV:*:*)
-       echo mips-dde-sysv${UNAME_RELEASE}
-       exit 0 ;;
-    RM*:ReliantUNIX-*:*:*)
-       echo mips-sni-sysv4
-       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:*: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 ;;
-    *:VOS:*:*)
-       # From Paul.Green@stratus.com.
-       echo hppa1.1-stratus-vos
-       exit 0 ;;
-    mc68*:A/UX:*:*)
-       echo m68k-apple-aux${UNAME_RELEASE}
-       exit 0 ;;
-    news*:NEWS-OS:6*:*)
-       echo mips-sony-newsos6
-       exit 0 ;;
-    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000: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 ;;
-    SX-6:SUPER-UX:*:*)
-       echo sx6-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 ;;
-    *:Darwin:*:*)
-       UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
-       case $UNAME_PROCESSOR in
-           *86) UNAME_PROCESSOR=i686 ;;
-           unknown) UNAME_PROCESSOR=powerpc ;;
-       esac
-       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
-       exit 0 ;;
-    *:procnto*:*:* | *:QNX:[0123456789]*:*)
-       UNAME_PROCESSOR=`uname -p`
-       if test "$UNAME_PROCESSOR" = "x86"; then
-               UNAME_PROCESSOR=i386
-               UNAME_MACHINE=pc
-       fi
-       echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
-       exit 0 ;;
-    *:QNX:*:4*)
-       echo i386-pc-qnx
-       exit 0 ;;
-    NSR-?:NONSTOP_KERNEL:*:*)
-       echo nsr-tandem-nsk${UNAME_RELEASE}
-       exit 0 ;;
-    *:NonStop-UX:*:*)
-       echo mips-compaq-nonstopux
-       exit 0 ;;
-    BS2000:POSIX*:*:*)
-       echo bs2000-siemens-sysv
-       exit 0 ;;
-    DS/*:UNIX_System_V:*:*)
-       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
-       exit 0 ;;
-    *:Plan9:*:*)
-       # "uname -m" is not consistent, so use $cputype instead. 386
-       # is converted to i386 for consistency with other x86
-       # operating systems.
-       if test "$cputype" = "386"; then
-           UNAME_MACHINE=i386
-       else
-           UNAME_MACHINE="$cputype"
-       fi
-       echo ${UNAME_MACHINE}-unknown-plan9
-       exit 0 ;;
-    *:TOPS-10:*:*)
-       echo pdp10-unknown-tops10
-       exit 0 ;;
-    *:TENEX:*:*)
-       echo pdp10-unknown-tenex
-       exit 0 ;;
-    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
-       echo pdp10-dec-tops20
-       exit 0 ;;
-    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
-       echo pdp10-xkl-tops20
-       exit 0 ;;
-    *:TOPS-20:*:*)
-       echo pdp10-unknown-tops20
-       exit 0 ;;
-    *:ITS:*:*)
-       echo pdp10-unknown-its
-       exit 0 ;;
-    SEI:*:*:SEIUX)
-        echo mips-sei-seiux${UNAME_RELEASE}
-       exit 0 ;;
-    *:DragonFly:*:*)
-       echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
-       exit 0 ;;
-    *:*VMS:*:*)
-       UNAME_MACHINE=`(uname -p) 2>/dev/null`
-       case "${UNAME_MACHINE}" in
-           A*) echo alpha-dec-vms && exit 0 ;;
-           I*) echo ia64-dec-vms && exit 0 ;;
-           V*) echo vax-dec-vms && exit 0 ;;
-       esac ;;
-    *:XENIX:*:SysV)
-       echo i386-pc-xenix
-       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
-
-eval $set_cc_for_build
-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)
-#  include <sys/param.h>
-#  if defined (BSD)
-#   if BSD == 43
-      printf ("vax-dec-bsd4.3\n"); exit (0);
-#   else
-#    if BSD == 199006
-      printf ("vax-dec-bsd4.3reno\n"); exit (0);
-#    else
-      printf ("vax-dec-bsd\n"); exit (0);
-#    endif
-#   endif
-#  else
-    printf ("vax-dec-bsd\n"); exit (0);
-#  endif
-# 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_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
-
-# 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
-
-cat >&2 <<EOF
-$0: unable to guess system type
-
-This script, last modified $timestamp, has failed to recognize
-the operating system you are using. It is advised that you
-download the most up to date version of the config scripts from
-
-    ftp://ftp.gnu.org/pub/gnu/config/
-
-If the version you run ($0) is already up to date, please
-send the following data and any information you think might be
-pertinent to <config-patches@gnu.org> in order to provide the needed
-information to handle your system.
-
-config.guess timestamp = $timestamp
-
-uname -m = `(uname -m) 2>/dev/null || echo unknown`
-uname -r = `(uname -r) 2>/dev/null || echo unknown`
-uname -s = `(uname -s) 2>/dev/null || echo unknown`
-uname -v = `(uname -v) 2>/dev/null || echo unknown`
-
-/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
-/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
-
-hostinfo               = `(hostinfo) 2>/dev/null`
-/bin/universe          = `(/bin/universe) 2>/dev/null`
-/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
-/bin/arch              = `(/bin/arch) 2>/dev/null`
-/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
-/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
-
-UNAME_MACHINE = ${UNAME_MACHINE}
-UNAME_RELEASE = ${UNAME_RELEASE}
-UNAME_SYSTEM  = ${UNAME_SYSTEM}
-UNAME_VERSION = ${UNAME_VERSION}
-EOF
-
-exit 1
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "timestamp='"
-# time-stamp-format: "%:y-%02m-%02d"
-# time-stamp-end: "'"
-# End:
diff --git a/config.h.in b/config.h.in
deleted file mode 100644 (file)
index 348c066..0000000
+++ /dev/null
@@ -1,148 +0,0 @@
-/* config.h.in.  Generated from configure.in by autoheader.  */
-
-/* smartmontools CVS Tag */
-#undef CONFIG_H_CVSID
-
-/* use mailx as default mailer */
-#undef DEFAULT_MAILER
-
-/* Define to 1 if you have the `ata_identify_is_cached' function in os_*.c. */
-#undef HAVE_ATA_IDENTIFY_IS_CACHED
-
-/* Define to 1 if C compiler supports __attribute__((packed)) */
-#undef HAVE_ATTR_PACKED
-
-/* Define to 1 if you have the <dev/ata/atavar.h> header file. */
-#undef HAVE_DEV_ATA_ATAVAR_H
-
-/* Define to 1 if you have the `getdomainname' function. */
-#undef HAVE_GETDOMAINNAME
-
-/* Define to 1 if you have the `gethostbyname' function. */
-#undef HAVE_GETHOSTBYNAME
-
-/* Define to 1 if you have the `gethostname' function. */
-#undef HAVE_GETHOSTNAME
-
-/* Define to 1 if you have the `getopt' function. */
-#undef HAVE_GETOPT
-
-/* Define to 1 if you have the <getopt.h> header file. */
-#undef HAVE_GETOPT_H
-
-/* Define to 1 if you have the `getopt_long' function. */
-#undef HAVE_GETOPT_LONG
-
-/* Define to 1 if you have the `get_os_version_str' function in os_*.c. */
-#undef HAVE_GET_OS_VERSION_STR
-
-/* Define to 1 if the system has the type `int64_t'. */
-#undef HAVE_INT64_T
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#undef HAVE_INTTYPES_H
-
-/* Define to 1 if you have the <locale.h> header file. */
-#undef HAVE_LOCALE_H
-
-/* Define to 1 if you have the <memory.h> header file. */
-#undef HAVE_MEMORY_H
-
-/* Define to 1 if you have the <netdb.h> header file. */
-#undef HAVE_NETDB_H
-
-/* Define to 1 if you have the `sigset' function. */
-#undef HAVE_SIGSET
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#undef HAVE_STDINT_H
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#undef HAVE_STDLIB_H
-
-/* Define to 1 if you have the <strings.h> header file. */
-#undef HAVE_STRINGS_H
-
-/* Define to 1 if you have the <string.h> header file. */
-#undef HAVE_STRING_H
-
-/* Define to 1 if you have the `strtoull' function. */
-#undef HAVE_STRTOULL
-
-/* Define to 1 if you have the <sys/inttypes.h> header file. */
-#undef HAVE_SYS_INTTYPES_H
-
-/* Define to 1 if you have the <sys/int_types.h> header file. */
-#undef HAVE_SYS_INT_TYPES_H
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#undef HAVE_SYS_STAT_H
-
-/* Define to 1 if you have the <sys/tweio.h> header file. */
-#undef HAVE_SYS_TWEIO_H
-
-/* Define to 1 if you have the <sys/twereg.h> header file. */
-#undef HAVE_SYS_TWEREG_H
-
-/* Define to 1 if you have the <sys/tw_osl_ioctl.h> header file. */
-#undef HAVE_SYS_TW_OSL_IOCTL_H
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#undef HAVE_SYS_TYPES_H
-
-/* Define to 1 if the system has the type `uint64_t'. */
-#undef HAVE_UINT64_T
-
-/* Define to 1 if you have the `uname' function. */
-#undef HAVE_UNAME
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#undef HAVE_UNISTD_H
-
-/* Define to 1 if the `snprintf' function is sane */
-#undef HAVE_WORKING_SNPRINTF
-
-/* need assembly code os_solaris_ata.s */
-#undef NEED_SOLARIS_ATA_CODE
-
-/* Name of package */
-#undef PACKAGE
-
-/* Define to the address where bug reports for this package should be sent. */
-#undef PACKAGE_BUGREPORT
-
-/* smartmontools Home Page */
-#undef PACKAGE_HOMEPAGE
-
-/* Define to the full name of this package. */
-#undef PACKAGE_NAME
-
-/* Define to the full name and version of this package. */
-#undef PACKAGE_STRING
-
-/* Define to the one symbol short name of this package. */
-#undef PACKAGE_TARNAME
-
-/* Define to the version of this package. */
-#undef PACKAGE_VERSION
-
-/* smartmontools Build Host */
-#undef SMARTMONTOOLS_BUILD_HOST
-
-/* smartmontools Configure Arguments */
-#undef SMARTMONTOOLS_CONFIGURE_ARGS
-
-/* smartmontools Configure Date */
-#undef SMARTMONTOOLS_CONFIGURE_DATE
-
-/* smartmontools Release Date */
-#undef SMARTMONTOOLS_RELEASE_DATE
-
-/* smartmontools Release Time */
-#undef SMARTMONTOOLS_RELEASE_TIME
-
-/* Define to 1 if you have the ANSI C header files. */
-#undef STDC_HEADERS
-
-/* Version number of package */
-#undef VERSION
diff --git a/config.sub b/config.sub
deleted file mode 100755 (executable)
index edb6b66..0000000
+++ /dev/null
@@ -1,1555 +0,0 @@
-#! /bin/sh
-# Configuration validation subroutine script.
-#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-#   2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
-
-timestamp='2004-08-29'
-
-# 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.
-
-# Please send patches to <config-patches@gnu.org>.  Submit a context
-# diff and a properly formatted ChangeLog entry.
-#
-# 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.
-
-me=`echo "$0" | sed -e 's,.*/,,'`
-
-usage="\
-Usage: $0 [OPTION] CPU-MFR-OPSYS
-       $0 [OPTION] ALIAS
-
-Canonicalize a configuration name.
-
-Operation modes:
-  -h, --help         print this help, then exit
-  -t, --time-stamp   print date of last modification, then exit
-  -v, --version      print version number, then exit
-
-Report bugs and patches to <config-patches@gnu.org>."
-
-version="\
-GNU config.sub ($timestamp)
-
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
-Free Software Foundation, Inc.
-
-This is free software; see the source for copying conditions.  There is NO
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
-
-help="
-Try \`$me --help' for more information."
-
-# Parse command line
-while test $# -gt 0 ; do
-  case $1 in
-    --time-stamp | --time* | -t )
-       echo "$timestamp" ; exit 0 ;;
-    --version | -v )
-       echo "$version" ; exit 0 ;;
-    --help | --h* | -h )
-       echo "$usage"; exit 0 ;;
-    -- )     # Stop option processing
-       shift; break ;;
-    - )        # Use stdin as input.
-       break ;;
-    -* )
-       echo "$me: invalid option $1$help"
-       exit 1 ;;
-
-    *local*)
-       # First pass through any local machine types.
-       echo $1
-       exit 0;;
-
-    * )
-       break ;;
-  esac
-done
-
-case $# in
- 0) echo "$me: missing argument$help" >&2
-    exit 1;;
- 1) ;;
- *) echo "$me: too many arguments$help" >&2
-    exit 1;;
-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* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
-  kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
-    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 | -axis | -knuth | -cray)
-               os=
-               basic_machine=$1
-               ;;
-       -sim | -cisco | -oki | -wec | -winbond)
-               os=
-               basic_machine=$1
-               ;;
-       -scout)
-               ;;
-       -wrs)
-               os=-vxworks
-               basic_machine=$1
-               ;;
-       -chorusos*)
-               os=-chorusos
-               basic_machine=$1
-               ;;
-       -chorusrdb)
-               os=-chorusrdb
-               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.
-       1750a | 580 \
-       | a29k \
-       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
-       | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
-       | am33_2.0 \
-       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
-       | c4x | clipper \
-       | d10v | d30v | dlx | dsp16xx \
-       | fr30 | frv \
-       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
-       | i370 | i860 | i960 | ia64 \
-       | ip2k | iq2000 \
-       | m32r | m32rle | m68000 | m68k | m88k | mcore \
-       | mips | mipsbe | mipseb | mipsel | mipsle \
-       | mips16 \
-       | mips64 | mips64el \
-       | mips64vr | mips64vrel \
-       | mips64orion | mips64orionel \
-       | mips64vr4100 | mips64vr4100el \
-       | mips64vr4300 | mips64vr4300el \
-       | mips64vr5000 | mips64vr5000el \
-       | mipsisa32 | mipsisa32el \
-       | mipsisa32r2 | mipsisa32r2el \
-       | mipsisa64 | mipsisa64el \
-       | mipsisa64r2 | mipsisa64r2el \
-       | mipsisa64sb1 | mipsisa64sb1el \
-       | mipsisa64sr71k | mipsisa64sr71kel \
-       | mipstx39 | mipstx39el \
-       | mn10200 | mn10300 \
-       | msp430 \
-       | ns16k | ns32k \
-       | openrisc | or32 \
-       | pdp10 | pdp11 | pj | pjl \
-       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
-       | pyramid \
-       | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
-       | sh64 | sh64le \
-       | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \
-       | strongarm \
-       | tahoe | thumb | tic4x | tic80 | tron \
-       | v850 | v850e \
-       | we32k \
-       | x86 | xscale | xstormy16 | xtensa \
-       | z8k)
-               basic_machine=$basic_machine-unknown
-               ;;
-       m6811 | m68hc11 | m6812 | m68hc12)
-               # Motorola 68HC11/12.
-               basic_machine=$basic_machine-unknown
-               os=-none
-               ;;
-       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
-               ;;
-
-       # 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*86 | x86_64)
-         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.
-       580-* \
-       | a29k-* \
-       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
-       | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
-       | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
-       | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
-       | avr-* \
-       | bs2000-* \
-       | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
-       | clipper-* | craynv-* | cydra-* \
-       | d10v-* | d30v-* | dlx-* \
-       | elxsi-* \
-       | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
-       | h8300-* | h8500-* \
-       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
-       | i*86-* | i860-* | i960-* | ia64-* \
-       | ip2k-* | iq2000-* \
-       | m32r-* | m32rle-* \
-       | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
-       | m88110-* | m88k-* | mcore-* \
-       | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
-       | mips16-* \
-       | mips64-* | mips64el-* \
-       | mips64vr-* | mips64vrel-* \
-       | mips64orion-* | mips64orionel-* \
-       | mips64vr4100-* | mips64vr4100el-* \
-       | mips64vr4300-* | mips64vr4300el-* \
-       | mips64vr5000-* | mips64vr5000el-* \
-       | mipsisa32-* | mipsisa32el-* \
-       | mipsisa32r2-* | mipsisa32r2el-* \
-       | mipsisa64-* | mipsisa64el-* \
-       | mipsisa64r2-* | mipsisa64r2el-* \
-       | mipsisa64sb1-* | mipsisa64sb1el-* \
-       | mipsisa64sr71k-* | mipsisa64sr71kel-* \
-       | mipstx39-* | mipstx39el-* \
-       | mmix-* \
-       | msp430-* \
-       | none-* | np1-* | ns16k-* | ns32k-* \
-       | orion-* \
-       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
-       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
-       | pyramid-* \
-       | romp-* | rs6000-* \
-       | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
-       | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
-       | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
-       | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
-       | tahoe-* | thumb-* \
-       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
-       | tron-* \
-       | v850-* | v850e-* | vax-* \
-       | we32k-* \
-       | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
-       | xtensa-* \
-       | ymp-* \
-       | z8k-*)
-               ;;
-       # Recognize the various machine names and aliases which stand
-       # for a CPU type and a company and sometimes even an OS.
-       386bsd)
-               basic_machine=i386-unknown
-               os=-bsd
-               ;;
-       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
-               ;;
-       abacus)
-               basic_machine=abacus-unknown
-               ;;
-       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
-               ;;
-       amd64)
-               basic_machine=x86_64-pc
-               ;;
-       amd64-*)
-               basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       amdahl)
-               basic_machine=580-amdahl
-               os=-sysv
-               ;;
-       amiga | amiga-*)
-               basic_machine=m68k-unknown
-               ;;
-       amigaos | amigados)
-               basic_machine=m68k-unknown
-               os=-amigaos
-               ;;
-       amigaunix | amix)
-               basic_machine=m68k-unknown
-               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
-               ;;
-       c90)
-               basic_machine=c90-cray
-               os=-unicos
-               ;;
-       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 | j90)
-               basic_machine=j90-cray
-               os=-unicos
-               ;;
-       craynv)
-               basic_machine=craynv-cray
-               os=-unicosmp
-               ;;
-       cr16c)
-               basic_machine=cr16c-unknown
-               os=-elf
-               ;;
-       crds | unos)
-               basic_machine=m68k-crds
-               ;;
-       crisv32 | crisv32-* | etraxfs*)
-               basic_machine=crisv32-axis
-               ;;
-       cris | cris-* | etrax*)
-               basic_machine=cris-axis
-               ;;
-       crx)
-               basic_machine=crx-unknown
-               os=-elf
-               ;;
-       da30 | da30-*)
-               basic_machine=m68k-da30
-               ;;
-       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
-               basic_machine=mips-dec
-               ;;
-       decsystem10* | dec10*)
-               basic_machine=pdp10-dec
-               os=-tops10
-               ;;
-       decsystem20* | dec20*)
-               basic_machine=pdp10-dec
-               os=-tops20
-               ;;
-       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
-               ;;
-       go32)
-               basic_machine=i386-pc
-               os=-go32
-               ;;
-       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])
-               # FIXME: really hppa2.0-hp
-               basic_machine=hppa1.1-hp
-               ;;
-       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
-               # FIXME: really hppa2.0-hp
-               basic_machine=hppa1.1-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*86v32)
-               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
-               os=-sysv32
-               ;;
-       i*86v4*)
-               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
-               os=-sysv4
-               ;;
-       i*86v)
-               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
-               os=-sysv
-               ;;
-       i*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
-               ;;
-       iris | iris4d)
-               basic_machine=mips-sgi
-               case $os in
-                   -irix*)
-                       ;;
-                   *)
-                       os=-irix4
-                       ;;
-               esac
-               ;;
-       isi68 | isi)
-               basic_machine=m68k-isi
-               os=-sysv
-               ;;
-       m88k-omron*)
-               basic_machine=m88k-omron
-               ;;
-       magnum | m3230)
-               basic_machine=mips-mips
-               os=-sysv
-               ;;
-       merlin)
-               basic_machine=ns32k-utek
-               os=-sysv
-               ;;
-       mingw32)
-               basic_machine=i386-pc
-               os=-mingw32
-               ;;
-       miniframe)
-               basic_machine=m68000-convergent
-               ;;
-       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
-               basic_machine=m68k-atari
-               os=-mint
-               ;;
-       mips3*-*)
-               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
-               ;;
-       mips3*)
-               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
-               ;;
-       monitor)
-               basic_machine=m68k-rom68k
-               os=-coff
-               ;;
-       morphos)
-               basic_machine=powerpc-unknown
-               os=-morphos
-               ;;
-       msdos)
-               basic_machine=i386-pc
-               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
-               ;;
-       nonstopux)
-               basic_machine=mips-compaq
-               os=-nonstopux
-               ;;
-       np1)
-               basic_machine=np1-gould
-               ;;
-       nsr-tandem)
-               basic_machine=nsr-tandem
-               ;;
-       op50n-* | op60c-*)
-               basic_machine=hppa1.1-oki
-               os=-proelf
-               ;;
-       or32 | or32-*)
-               basic_machine=or32-unknown
-               os=-coff
-               ;;
-       os400)
-               basic_machine=powerpc-ibm
-               os=-os400
-               ;;
-       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
-               ;;
-       pentium | p5 | k5 | k6 | nexgen | viac3)
-               basic_machine=i586-pc
-               ;;
-       pentiumpro | p6 | 6x86 | athlon | athlon_*)
-               basic_machine=i686-pc
-               ;;
-       pentiumii | pentium2 | pentiumiii | pentium3)
-               basic_machine=i686-pc
-               ;;
-       pentium4)
-               basic_machine=i786-pc
-               ;;
-       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
-               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       pentiumpro-* | p6-* | 6x86-* | athlon-*)
-               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
-               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       pentium4-*)
-               basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       pn)
-               basic_machine=pn-gould
-               ;;
-       power)  basic_machine=power-ibm
-               ;;
-       ppc)    basic_machine=powerpc-unknown
-               ;;
-       ppc-*)  basic_machine=powerpc-`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/^[^-]*-//'`
-               ;;
-       ppc64)  basic_machine=powerpc64-unknown
-               ;;
-       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
-               basic_machine=powerpc64le-unknown
-               ;;
-       ppc64le-* | powerpc64little-*)
-               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       ps2)
-               basic_machine=i386-ibm
-               ;;
-       pw32)
-               basic_machine=i586-unknown
-               os=-pw32
-               ;;
-       rom68k)
-               basic_machine=m68k-rom68k
-               os=-coff
-               ;;
-       rm[46]00)
-               basic_machine=mips-siemens
-               ;;
-       rtpc | rtpc-*)
-               basic_machine=romp-ibm
-               ;;
-       s390 | s390-*)
-               basic_machine=s390-ibm
-               ;;
-       s390x | s390x-*)
-               basic_machine=s390x-ibm
-               ;;
-       sa29200)
-               basic_machine=a29k-amd
-               os=-udi
-               ;;
-       sb1)
-               basic_machine=mipsisa64sb1-unknown
-               ;;
-       sb1el)
-               basic_machine=mipsisa64sb1el-unknown
-               ;;
-       sei)
-               basic_machine=mips-sei
-               os=-seiux
-               ;;
-       sequent)
-               basic_machine=i386-sequent
-               ;;
-       sh)
-               basic_machine=sh-hitachi
-               os=-hms
-               ;;
-       sh64)
-               basic_machine=sh64-unknown
-               ;;
-       sparclite-wrs | simso-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=alphaev5-cray
-               os=-unicos
-               ;;
-       t90)
-               basic_machine=t90-cray
-               os=-unicos
-               ;;
-       tic54x | c54x*)
-               basic_machine=tic54x-unknown
-               os=-coff
-               ;;
-       tic55x | c55x*)
-               basic_machine=tic55x-unknown
-               os=-coff
-               ;;
-       tic6x | c6x*)
-               basic_machine=tic6x-unknown
-               os=-coff
-               ;;
-       tx39)
-               basic_machine=mipstx39-unknown
-               ;;
-       tx39el)
-               basic_machine=mipstx39el-unknown
-               ;;
-       toad1)
-               basic_machine=pdp10-xkl
-               os=-tops20
-               ;;
-       tower | tower-32)
-               basic_machine=m68k-ncr
-               ;;
-       tpf)
-               basic_machine=s390x-ibm
-               os=-tpf
-               ;;
-       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
-               ;;
-       xps | xps100)
-               basic_machine=xps100-honeywell
-               ;;
-       ymp)
-               basic_machine=ymp-cray
-               os=-unicos
-               ;;
-       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
-               ;;
-       romp)
-               basic_machine=romp-ibm
-               ;;
-       mmix)
-               basic_machine=mmix-knuth
-               ;;
-       rs6000)
-               basic_machine=rs6000-ibm
-               ;;
-       vax)
-               basic_machine=vax-dec
-               ;;
-       pdp10)
-               # there are many clones, so DEC is not a safe bet
-               basic_machine=pdp10-unknown
-               ;;
-       pdp11)
-               basic_machine=pdp11-dec
-               ;;
-       we32k)
-               basic_machine=we32k-att
-               ;;
-       sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
-               basic_machine=sh-unknown
-               ;;
-       sh64)
-               basic_machine=sh64-unknown
-               ;;
-       sparc | sparcv8 | sparcv9 | sparcv9b)
-               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
-               ;;
-       *-unknown)
-               # Make sure to match an already-canonicalized machine name.
-               ;;
-       *)
-               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* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
-             | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
-             | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
-             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
-             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
-             | -chorusos* | -chorusrdb* \
-             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
-             | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
-             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
-             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
-             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
-             | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
-             | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
-             | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*)
-       # Remember, each alternative MUST END IN *, to match a version number.
-               ;;
-       -qnx*)
-               case $basic_machine in
-                   x86-* | i*86-*)
-                       ;;
-                   *)
-                       os=-nto$os
-                       ;;
-               esac
-               ;;
-       -nto-qnx*)
-               ;;
-       -nto*)
-               os=`echo $os | sed -e 's|nto|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-dietlibc)
-               os=-linux-dietlibc
-               ;;
-       -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
-               ;;
-        -os400*)
-               os=-os400
-               ;;
-       -wince*)
-               os=-wince
-               ;;
-       -osfrose*)
-               os=-osfrose
-               ;;
-       -osf*)
-               os=-osf
-               ;;
-       -utek*)
-               os=-bsd
-               ;;
-       -dynix*)
-               os=-bsd
-               ;;
-       -acis*)
-               os=-aos
-               ;;
-       -atheos*)
-               os=-atheos
-               ;;
-       -syllable*)
-               os=-syllable
-               ;;
-       -386bsd)
-               os=-bsd
-               ;;
-       -ctix* | -uts*)
-               os=-sysv
-               ;;
-       -nova*)
-               os=-rtmk-nova
-               ;;
-       -ns2 )
-               os=-nextstep2
-               ;;
-       -nsk*)
-               os=-nsk
-               ;;
-       # Preserve the version number of sinix5.
-       -sinix5.*)
-               os=`echo $os | sed -e 's|sinix|sysv|'`
-               ;;
-       -sinix*)
-               os=-sysv4
-               ;;
-        -tpf*)
-               os=-tpf
-               ;;
-       -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[0-9]* | -*MiNT | -MiNT[0-9]*)
-               os=-mint
-               ;;
-       -aros*)
-               os=-aros
-               ;;
-       -kaos*)
-               os=-kaos
-               ;;
-       -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
-               ;;
-    c4x-* | tic4x-*)
-        os=-coff
-        ;;
-       # This must come before the *-dec entry.
-       pdp10-*)
-               os=-tops20
-               ;;
-       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
-               ;;
-       or32-*)
-               os=-coff
-               ;;
-       *-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
-               ;;
-       *-knuth)
-               os=-mmixware
-               ;;
-       *-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
-               ;;
-       f30[01]-fujitsu | f700-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
-                               ;;
-                       -os400*)
-                               vendor=ibm
-                               ;;
-                       -ptx*)
-                               vendor=sequent
-                               ;;
-                       -tpf*)
-                               vendor=ibm
-                               ;;
-                       -vxsim* | -vxworks* | -windiss*)
-                               vendor=wrs
-                               ;;
-                       -aux*)
-                               vendor=apple
-                               ;;
-                       -hms*)
-                               vendor=hitachi
-                               ;;
-                       -mpw* | -macos*)
-                               vendor=apple
-                               ;;
-                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
-                               vendor=atari
-                               ;;
-                       -vos*)
-                               vendor=stratus
-                               ;;
-               esac
-               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
-               ;;
-esac
-
-echo $basic_machine$os
-exit 0
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "timestamp='"
-# time-stamp-format: "%:y-%02m-%02d"
-# time-stamp-end: "'"
-# End:
diff --git a/configure b/configure
deleted file mode 100755 (executable)
index c5f73d5..0000000
--- a/configure
+++ /dev/null
@@ -1,8268 +0,0 @@
-#! /bin/sh
-# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.59 for smartmontools 5.36.
-#
-# Report bugs to <smartmontools-support@lists.sourceforge.net>.
-#
-# Copyright (C) 2003 Free Software Foundation, Inc.
-# This configure script is free software; the Free Software Foundation
-# gives unlimited permission to copy, distribute and modify it.
-## --------------------- ##
-## M4sh Initialization.  ##
-## --------------------- ##
-
-# Be Bourne compatible
-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
-  emulate sh
-  NULLCMD=:
-  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
-  # is contrary to our usage.  Disable this feature.
-  alias -g '${1+"$@"}'='"$@"'
-elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
-  set -o posix
-fi
-DUALCASE=1; export DUALCASE # for MKS sh
-
-# Support unset when possible.
-if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
-  as_unset=unset
-else
-  as_unset=false
-fi
-
-
-# Work around bugs in pre-3.0 UWIN ksh.
-$as_unset ENV MAIL MAILPATH
-PS1='$ '
-PS2='> '
-PS4='+ '
-
-# NLS nuisances.
-for as_var in \
-  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
-  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
-  LC_TELEPHONE LC_TIME
-do
-  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
-    eval $as_var=C; export $as_var
-  else
-    $as_unset $as_var
-  fi
-done
-
-# Required to use basename.
-if expr a : '\(a\)' >/dev/null 2>&1; then
-  as_expr=expr
-else
-  as_expr=false
-fi
-
-if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
-  as_basename=basename
-else
-  as_basename=false
-fi
-
-
-# Name of the executable.
-as_me=`$as_basename "$0" ||
-$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
-        X"$0" : 'X\(//\)$' \| \
-        X"$0" : 'X\(/\)$' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X/"$0" |
-    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
-         /^X\/\(\/\/\)$/{ s//\1/; q; }
-         /^X\/\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-
-
-# PATH needs CR, and LINENO needs CR and PATH.
-# Avoid depending upon Character Ranges.
-as_cr_letters='abcdefghijklmnopqrstuvwxyz'
-as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-as_cr_Letters=$as_cr_letters$as_cr_LETTERS
-as_cr_digits='0123456789'
-as_cr_alnum=$as_cr_Letters$as_cr_digits
-
-# The user is always right.
-if test "${PATH_SEPARATOR+set}" != set; then
-  echo "#! /bin/sh" >conf$$.sh
-  echo  "exit 0"   >>conf$$.sh
-  chmod +x conf$$.sh
-  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
-    PATH_SEPARATOR=';'
-  else
-    PATH_SEPARATOR=:
-  fi
-  rm -f conf$$.sh
-fi
-
-
-  as_lineno_1=$LINENO
-  as_lineno_2=$LINENO
-  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
-  test "x$as_lineno_1" != "x$as_lineno_2" &&
-  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
-  # Find who we are.  Look in the path if we contain no path at all
-  # relative or not.
-  case $0 in
-    *[\\/]* ) as_myself=$0 ;;
-    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
-done
-
-       ;;
-  esac
-  # We did not find ourselves, most probably we were run as `sh COMMAND'
-  # in which case we are not to be found in the path.
-  if test "x$as_myself" = x; then
-    as_myself=$0
-  fi
-  if test ! -f "$as_myself"; then
-    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
-   { (exit 1); exit 1; }; }
-  fi
-  case $CONFIG_SHELL in
-  '')
-    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for as_base in sh bash ksh sh5; do
-        case $as_dir in
-        /*)
-          if ("$as_dir/$as_base" -c '
-  as_lineno_1=$LINENO
-  as_lineno_2=$LINENO
-  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
-  test "x$as_lineno_1" != "x$as_lineno_2" &&
-  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
-            $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
-            $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
-            CONFIG_SHELL=$as_dir/$as_base
-            export CONFIG_SHELL
-            exec "$CONFIG_SHELL" "$0" ${1+"$@"}
-          fi;;
-        esac
-       done
-done
-;;
-  esac
-
-  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
-  # uniformly replaced by the line number.  The first 'sed' inserts a
-  # line-number line before each line; the second 'sed' does the real
-  # work.  The second script uses 'N' to pair each line-number line
-  # with the numbered line, and appends trailing '-' during
-  # substitution so that $LINENO is not a special case at line end.
-  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
-  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
-  sed '=' <$as_myself |
-    sed '
-      N
-      s,$,-,
-      : loop
-      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
-      t loop
-      s,-$,,
-      s,^['$as_cr_digits']*\n,,
-    ' >$as_me.lineno &&
-  chmod +x $as_me.lineno ||
-    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
-   { (exit 1); exit 1; }; }
-
-  # Don't try to exec as it changes $[0], causing all sort of problems
-  # (the dirname of $[0] is not the place where we might find the
-  # original and so on.  Autoconf is especially sensible to this).
-  . ./$as_me.lineno
-  # Exit status is that of the last command.
-  exit
-}
-
-
-case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
-  *c*,-n*) ECHO_N= ECHO_C='
-' ECHO_T='     ' ;;
-  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
-  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
-esac
-
-if expr a : '\(a\)' >/dev/null 2>&1; then
-  as_expr=expr
-else
-  as_expr=false
-fi
-
-rm -f conf$$ conf$$.exe conf$$.file
-echo >conf$$.file
-if ln -s conf$$.file conf$$ 2>/dev/null; then
-  # We could just check for DJGPP; but this test a) works b) is more generic
-  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
-  if test -f conf$$.exe; then
-    # Don't use ln at all; we don't have any links
-    as_ln_s='cp -p'
-  else
-    as_ln_s='ln -s'
-  fi
-elif ln conf$$.file conf$$ 2>/dev/null; then
-  as_ln_s=ln
-else
-  as_ln_s='cp -p'
-fi
-rm -f conf$$ conf$$.exe conf$$.file
-
-if mkdir -p . 2>/dev/null; then
-  as_mkdir_p=:
-else
-  test -d ./-p && rmdir ./-p
-  as_mkdir_p=false
-fi
-
-as_executable_p="test -f"
-
-# Sed expression to map a string onto a valid CPP name.
-as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
-
-# Sed expression to map a string onto a valid variable name.
-as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
-
-
-# IFS
-# We need space, tab and new line, in precisely that order.
-as_nl='
-'
-IFS="  $as_nl"
-
-# CDPATH.
-$as_unset CDPATH
-
-
-# Name of the host.
-# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
-# so uname gets run too.
-ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
-
-exec 6>&1
-
-#
-# Initializations.
-#
-ac_default_prefix=/usr/local
-ac_config_libobj_dir=.
-cross_compiling=no
-subdirs=
-MFLAGS=
-MAKEFLAGS=
-SHELL=${CONFIG_SHELL-/bin/sh}
-
-# Maximum number of lines to put in a shell here document.
-# This variable seems obsolete.  It should probably be removed, and
-# only ac_max_sed_lines should be used.
-: ${ac_max_here_lines=38}
-
-# Identity of this package.
-PACKAGE_NAME='smartmontools'
-PACKAGE_TARNAME='smartmontools'
-PACKAGE_VERSION='5.36'
-PACKAGE_STRING='smartmontools 5.36'
-PACKAGE_BUGREPORT='smartmontools-support@lists.sourceforge.net'
-
-ac_unique_file="smartctl.c"
-# Factoring default headers for most tests.
-ac_includes_default="\
-#include <stdio.h>
-#if HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#if HAVE_SYS_STAT_H
-# include <sys/stat.h>
-#endif
-#if STDC_HEADERS
-# include <stdlib.h>
-# include <stddef.h>
-#else
-# if HAVE_STDLIB_H
-#  include <stdlib.h>
-# endif
-#endif
-#if HAVE_STRING_H
-# if !STDC_HEADERS && HAVE_MEMORY_H
-#  include <memory.h>
-# endif
-# include <string.h>
-#endif
-#if HAVE_STRINGS_H
-# include <strings.h>
-#endif
-#if HAVE_INTTYPES_H
-# include <inttypes.h>
-#else
-# if HAVE_STDINT_H
-#  include <stdint.h>
-# endif
-#endif
-#if HAVE_UNISTD_H
-# include <unistd.h>
-#endif"
-
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CCAS CCASFLAGS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CPP EGREP libc_have_working_snprintf gcc_have_attr_packed ASFLAGS exampledir initddir docdir smartd_suffix SMARTD_SUFFIX_TRUE SMARTD_SUFFIX_FALSE releaseversion smartmontools_release_date smartmontools_release_time os_deps os_libs OS_DARWIN_TRUE OS_DARWIN_FALSE OS_SOLARIS_TRUE OS_SOLARIS_FALSE OS_WIN32_MINGW_TRUE OS_WIN32_MINGW_FALSE LIBOBJS LTLIBOBJS'
-ac_subst_files=''
-
-# Initialize some variables set by options.
-ac_init_help=
-ac_init_version=false
-# The variables have the same names as the options, with
-# dashes changed to underlines.
-cache_file=/dev/null
-exec_prefix=NONE
-no_create=
-no_recursion=
-prefix=NONE
-program_prefix=NONE
-program_suffix=NONE
-program_transform_name=s,x,x,
-silent=
-site=
-srcdir=
-verbose=
-x_includes=NONE
-x_libraries=NONE
-
-# Installation directory options.
-# These are left unexpanded so users can "make install exec_prefix=/foo"
-# and all the variables that are supposed to be based on exec_prefix
-# by default will actually change.
-# Use braces instead of parens because sh, perl, etc. also accept them.
-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'
-
-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
-
-  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
-
-  # 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_alias ;;
-  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
-    build_alias=$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 ;;
-
-  --config-cache | -C)
-    cache_file=config.cache ;;
-
-  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
-    ac_prev=datadir ;;
-  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
-  | --da=*)
-    datadir=$ac_optarg ;;
-
-  -disable-* | --disable-*)
-    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
-    # Reject names that are not valid shell variable names.
-    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
-      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
-   { (exit 1); exit 1; }; }
-    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
-    eval "enable_$ac_feature=no" ;;
-
-  -enable-* | --enable-*)
-    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
-    # Reject names that are not valid shell variable names.
-    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
-      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
-   { (exit 1); exit 1; }; }
-    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
-    case $ac_option in
-      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
-      *) 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 | -h)
-    ac_init_help=long ;;
-  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
-    ac_init_help=recursive ;;
-  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
-    ac_init_help=short ;;
-
-  -host | --host | --hos | --ho)
-    ac_prev=host_alias ;;
-  -host=* | --host=* | --hos=* | --ho=*)
-    host_alias=$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 | -n)
-    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_alias ;;
-  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
-    target_alias=$ac_optarg ;;
-
-  -v | -verbose | --verbose | --verbos | --verbo | --verb)
-    verbose=yes ;;
-
-  -version | --version | --versio | --versi | --vers | -V)
-    ac_init_version=: ;;
-
-  -with-* | --with-*)
-    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
-    # Reject names that are not valid shell variable names.
-    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
-      { echo "$as_me: error: invalid package name: $ac_package" >&2
-   { (exit 1); exit 1; }; }
-    ac_package=`echo $ac_package| sed 's/-/_/g'`
-    case $ac_option in
-      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
-      *) ac_optarg=yes ;;
-    esac
-    eval "with_$ac_package='$ac_optarg'" ;;
-
-  -without-* | --without-*)
-    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
-    # Reject names that are not valid shell variable names.
-    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
-      { echo "$as_me: error: invalid package name: $ac_package" >&2
-   { (exit 1); exit 1; }; }
-    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 "$as_me: error: unrecognized option: $ac_option
-Try \`$0 --help' for more information." >&2
-   { (exit 1); exit 1; }; }
-    ;;
-
-  *=*)
-    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
-    # Reject names that are not valid shell variable names.
-    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
-      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
-   { (exit 1); exit 1; }; }
-    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
-    eval "$ac_envvar='$ac_optarg'"
-    export $ac_envvar ;;
-
-  *)
-    # FIXME: should be removed in autoconf 3.0.
-    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
-    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
-      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
-    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
-    ;;
-
-  esac
-done
-
-if test -n "$ac_prev"; then
-  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
-  { echo "$as_me: error: missing argument to $ac_option" >&2
-   { (exit 1); exit 1; }; }
-fi
-
-# Be sure to have absolute paths.
-for ac_var in exec_prefix prefix
-do
-  eval ac_val=$`echo $ac_var`
-  case $ac_val in
-    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
-    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
-   { (exit 1); exit 1; }; };;
-  esac
-done
-
-# Be sure to have absolute paths.
-for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
-             localstatedir libdir includedir oldincludedir infodir mandir
-do
-  eval ac_val=$`echo $ac_var`
-  case $ac_val in
-    [\\/$]* | ?:[\\/]* ) ;;
-    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
-   { (exit 1); exit 1; }; };;
-  esac
-done
-
-# There might be people who depend on the old broken behavior: `$host'
-# used to hold the argument of --host etc.
-# FIXME: To remove some day.
-build=$build_alias
-host=$host_alias
-target=$target_alias
-
-# FIXME: To remove some day.
-if test "x$host_alias" != x; then
-  if test "x$build_alias" = x; then
-    cross_compiling=maybe
-    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
-    If a cross compiler is detected then cross compile mode will be used." >&2
-  elif test "x$build_alias" != "x$host_alias"; then
-    cross_compiling=yes
-  fi
-fi
-
-ac_tool_prefix=
-test -n "$host_alias" && ac_tool_prefix=$host_alias-
-
-test "$silent" = yes && exec 6>/dev/null
-
-
-# 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_confdir=`(dirname "$0") 2>/dev/null ||
-$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$0" : 'X\(//\)[^/]' \| \
-        X"$0" : 'X\(//\)$' \| \
-        X"$0" : 'X\(/\)' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X"$0" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
-         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
-         /^X\(\/\/\)$/{ s//\1/; q; }
-         /^X\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-  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 "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
-   { (exit 1); exit 1; }; }
-  else
-    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
-   { (exit 1); exit 1; }; }
-  fi
-fi
-(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
-  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
-   { (exit 1); exit 1; }; }
-srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
-ac_env_build_alias_set=${build_alias+set}
-ac_env_build_alias_value=$build_alias
-ac_cv_env_build_alias_set=${build_alias+set}
-ac_cv_env_build_alias_value=$build_alias
-ac_env_host_alias_set=${host_alias+set}
-ac_env_host_alias_value=$host_alias
-ac_cv_env_host_alias_set=${host_alias+set}
-ac_cv_env_host_alias_value=$host_alias
-ac_env_target_alias_set=${target_alias+set}
-ac_env_target_alias_value=$target_alias
-ac_cv_env_target_alias_set=${target_alias+set}
-ac_cv_env_target_alias_value=$target_alias
-ac_env_CC_set=${CC+set}
-ac_env_CC_value=$CC
-ac_cv_env_CC_set=${CC+set}
-ac_cv_env_CC_value=$CC
-ac_env_CFLAGS_set=${CFLAGS+set}
-ac_env_CFLAGS_value=$CFLAGS
-ac_cv_env_CFLAGS_set=${CFLAGS+set}
-ac_cv_env_CFLAGS_value=$CFLAGS
-ac_env_LDFLAGS_set=${LDFLAGS+set}
-ac_env_LDFLAGS_value=$LDFLAGS
-ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
-ac_cv_env_LDFLAGS_value=$LDFLAGS
-ac_env_CPPFLAGS_set=${CPPFLAGS+set}
-ac_env_CPPFLAGS_value=$CPPFLAGS
-ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
-ac_cv_env_CPPFLAGS_value=$CPPFLAGS
-ac_env_CCAS_set=${CCAS+set}
-ac_env_CCAS_value=$CCAS
-ac_cv_env_CCAS_set=${CCAS+set}
-ac_cv_env_CCAS_value=$CCAS
-ac_env_CCASFLAGS_set=${CCASFLAGS+set}
-ac_env_CCASFLAGS_value=$CCASFLAGS
-ac_cv_env_CCASFLAGS_set=${CCASFLAGS+set}
-ac_cv_env_CCASFLAGS_value=$CCASFLAGS
-ac_env_CPP_set=${CPP+set}
-ac_env_CPP_value=$CPP
-ac_cv_env_CPP_set=${CPP+set}
-ac_cv_env_CPP_value=$CPP
-
-#
-# Report the --help message.
-#
-if test "$ac_init_help" = "long"; then
-  # 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 <<_ACEOF
-\`configure' configures smartmontools 5.36 to adapt to many kinds of systems.
-
-Usage: $0 [OPTION]... [VAR=VALUE]...
-
-To assign environment variables (e.g., CC, CFLAGS...), specify them as
-VAR=VALUE.  See below for descriptions of some of the useful variables.
-
-Defaults for the options are specified in brackets.
-
-Configuration:
-  -h, --help              display this help and exit
-      --help=short        display options specific to this package
-      --help=recursive    display the short help of all the included packages
-  -V, --version           display version information and exit
-  -q, --quiet, --silent   do not print \`checking...' messages
-      --cache-file=FILE   cache test results in FILE [disabled]
-  -C, --config-cache      alias for \`--cache-file=config.cache'
-  -n, --no-create         do not create output files
-      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
-
-_ACEOF
-
-  cat <<_ACEOF
-Installation directories:
-  --prefix=PREFIX         install architecture-independent files in PREFIX
-                         [$ac_default_prefix]
-  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                         [PREFIX]
-
-By default, \`make install' will install all the files in
-\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
-an installation prefix other than \`$ac_default_prefix' using \`--prefix',
-for instance \`--prefix=\$HOME'.
-
-For better control, use the options below.
-
-Fine tuning of the installation directories:
-  --bindir=DIR           user executables [EPREFIX/bin]
-  --sbindir=DIR          system admin executables [EPREFIX/sbin]
-  --libexecdir=DIR       program executables [EPREFIX/libexec]
-  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
-  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
-  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
-  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
-  --libdir=DIR           object code libraries [EPREFIX/lib]
-  --includedir=DIR       C header files [PREFIX/include]
-  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
-  --infodir=DIR          info documentation [PREFIX/info]
-  --mandir=DIR           man documentation [PREFIX/man]
-_ACEOF
-
-  cat <<\_ACEOF
-
-Program names:
-  --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
-
-System types:
-  --build=BUILD     configure for building on BUILD [guessed]
-  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
-_ACEOF
-fi
-
-if test -n "$ac_init_help"; then
-  case $ac_init_help in
-     short | recursive ) echo "Configuration of smartmontools 5.36:";;
-   esac
-  cat <<\_ACEOF
-
-Optional Features:
-  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
-  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
-  --enable-maintainer-mode  enable make rules and dependencies not useful
-                         (and sometimes confusing) to the casual installer
-  --disable-dependency-tracking  speeds up one-time build
-  --enable-dependency-tracking   do not reject slow dependency extractors
-  --enable-sample         Enables appending .sample to the installed smartd rc
-                          script and configuration file
-
-Optional Packages:
-  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
-  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
-  --with-initscriptdir=dir
-                          Location of init scripts (default is
-                          ${sysconfdir}/rc.d/init.d)
-  --with-docdir=dir       Location of documentation (default is
-                          ${prefix}/share/doc/smartmontools-5.X)
-
-Some influential environment variables:
-  CC          C compiler command
-  CFLAGS      C compiler flags
-  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
-  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
-              headers in a nonstandard directory <include dir>
-  CCAS        assembler compiler command (defaults to CC)
-  CCASFLAGS   assembler compiler flags (defaults to CFLAGS)
-  CPP         C preprocessor
-
-Use these variables to override the choices made by `configure' or to help
-it to find libraries and programs with nonstandard names/locations.
-
-Report bugs to <smartmontools-support@lists.sourceforge.net>.
-_ACEOF
-fi
-
-if test "$ac_init_help" = "recursive"; then
-  # If there are subdirs, report their specific --help.
-  ac_popdir=`pwd`
-  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
-    test -d $ac_dir || continue
-    ac_builddir=.
-
-if test "$ac_dir" != .; then
-  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
-  # A "../" for each directory in $ac_dir_suffix.
-  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
-else
-  ac_dir_suffix= ac_top_builddir=
-fi
-
-case $srcdir in
-  .)  # No --srcdir option.  We are building in place.
-    ac_srcdir=.
-    if test -z "$ac_top_builddir"; then
-       ac_top_srcdir=.
-    else
-       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
-    fi ;;
-  [\\/]* | ?:[\\/]* )  # Absolute path.
-    ac_srcdir=$srcdir$ac_dir_suffix;
-    ac_top_srcdir=$srcdir ;;
-  *) # Relative path.
-    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
-    ac_top_srcdir=$ac_top_builddir$srcdir ;;
-esac
-
-# Do not use `cd foo && pwd` to compute absolute paths, because
-# the directories may not exist.
-case `pwd` in
-.) ac_abs_builddir="$ac_dir";;
-*)
-  case "$ac_dir" in
-  .) ac_abs_builddir=`pwd`;;
-  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
-  *) ac_abs_builddir=`pwd`/"$ac_dir";;
-  esac;;
-esac
-case $ac_abs_builddir in
-.) ac_abs_top_builddir=${ac_top_builddir}.;;
-*)
-  case ${ac_top_builddir}. in
-  .) ac_abs_top_builddir=$ac_abs_builddir;;
-  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
-  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
-  esac;;
-esac
-case $ac_abs_builddir in
-.) ac_abs_srcdir=$ac_srcdir;;
-*)
-  case $ac_srcdir in
-  .) ac_abs_srcdir=$ac_abs_builddir;;
-  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
-  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
-  esac;;
-esac
-case $ac_abs_builddir in
-.) ac_abs_top_srcdir=$ac_top_srcdir;;
-*)
-  case $ac_top_srcdir in
-  .) ac_abs_top_srcdir=$ac_abs_builddir;;
-  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
-  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
-  esac;;
-esac
-
-    cd $ac_dir
-    # Check for guested configure; otherwise get Cygnus style configure.
-    if test -f $ac_srcdir/configure.gnu; then
-      echo
-      $SHELL $ac_srcdir/configure.gnu  --help=recursive
-    elif test -f $ac_srcdir/configure; then
-      echo
-      $SHELL $ac_srcdir/configure  --help=recursive
-    elif test -f $ac_srcdir/configure.ac ||
-          test -f $ac_srcdir/configure.in; then
-      echo
-      $ac_configure --help
-    else
-      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
-    fi
-    cd $ac_popdir
-  done
-fi
-
-test -n "$ac_init_help" && exit 0
-if $ac_init_version; then
-  cat <<\_ACEOF
-smartmontools configure 5.36
-generated by GNU Autoconf 2.59
-
-Copyright (C) 2003 Free Software Foundation, Inc.
-This configure script is free software; the Free Software Foundation
-gives unlimited permission to copy, distribute and modify it.
-_ACEOF
-  exit 0
-fi
-exec 5>config.log
-cat >&5 <<_ACEOF
-This file contains any messages produced by compilers while
-running configure, to aid debugging if configure makes a mistake.
-
-It was created by smartmontools $as_me 5.36, which was
-generated by GNU Autoconf 2.59.  Invocation command line was
-
-  $ $0 $@
-
-_ACEOF
-{
-cat <<_ASUNAME
-## --------- ##
-## Platform. ##
-## --------- ##
-
-hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
-uname -m = `(uname -m) 2>/dev/null || echo unknown`
-uname -r = `(uname -r) 2>/dev/null || echo unknown`
-uname -s = `(uname -s) 2>/dev/null || echo unknown`
-uname -v = `(uname -v) 2>/dev/null || echo unknown`
-
-/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
-/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
-
-/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
-/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
-/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
-hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
-/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
-/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
-/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
-
-_ASUNAME
-
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  echo "PATH: $as_dir"
-done
-
-} >&5
-
-cat >&5 <<_ACEOF
-
-
-## ----------- ##
-## Core tests. ##
-## ----------- ##
-
-_ACEOF
-
-
-# Keep a trace of the command line.
-# Strip out --no-create and --no-recursion so they do not pile up.
-# Strip out --silent because we don't want to record it for future runs.
-# Also quote any args containing shell meta-characters.
-# Make two passes to allow for proper duplicate-argument suppression.
-ac_configure_args=
-ac_configure_args0=
-ac_configure_args1=
-ac_sep=
-ac_must_keep_next=false
-for ac_pass in 1 2
-do
-  for ac_arg
-  do
-    case $ac_arg in
-    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
-    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
-    | -silent | --silent | --silen | --sile | --sil)
-      continue ;;
-    *" "*|*"   "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
-      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
-    esac
-    case $ac_pass in
-    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
-    2)
-      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
-      if test $ac_must_keep_next = true; then
-       ac_must_keep_next=false # Got value, back to normal.
-      else
-       case $ac_arg in
-         *=* | --config-cache | -C | -disable-* | --disable-* \
-         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
-         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
-         | -with-* | --with-* | -without-* | --without-* | --x)
-           case "$ac_configure_args0 " in
-             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
-           esac
-           ;;
-         -* ) ac_must_keep_next=true ;;
-       esac
-      fi
-      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
-      # Get rid of the leading space.
-      ac_sep=" "
-      ;;
-    esac
-  done
-done
-$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
-$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
-
-# When interrupted or exit'd, cleanup temporary files, and complete
-# config.log.  We remove comments because anyway the quotes in there
-# would cause problems or look ugly.
-# WARNING: Be sure not to use single quotes in there, as some shells,
-# such as our DU 5.0 friend, will then `close' the trap.
-trap 'exit_status=$?
-  # Save into config.log some information that might help in debugging.
-  {
-    echo
-
-    cat <<\_ASBOX
-## ---------------- ##
-## Cache variables. ##
-## ---------------- ##
-_ASBOX
-    echo
-    # The following way of writing the cache mishandles newlines in values,
-{
-  (set) 2>&1 |
-    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
-    *ac_space=\ *)
-      sed -n \
-       "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
-         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
-      ;;
-    *)
-      sed -n \
-       "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
-      ;;
-    esac;
-}
-    echo
-
-    cat <<\_ASBOX
-## ----------------- ##
-## Output variables. ##
-## ----------------- ##
-_ASBOX
-    echo
-    for ac_var in $ac_subst_vars
-    do
-      eval ac_val=$`echo $ac_var`
-      echo "$ac_var='"'"'$ac_val'"'"'"
-    done | sort
-    echo
-
-    if test -n "$ac_subst_files"; then
-      cat <<\_ASBOX
-## ------------- ##
-## Output files. ##
-## ------------- ##
-_ASBOX
-      echo
-      for ac_var in $ac_subst_files
-      do
-       eval ac_val=$`echo $ac_var`
-       echo "$ac_var='"'"'$ac_val'"'"'"
-      done | sort
-      echo
-    fi
-
-    if test -s confdefs.h; then
-      cat <<\_ASBOX
-## ----------- ##
-## confdefs.h. ##
-## ----------- ##
-_ASBOX
-      echo
-      sed "/^$/d" confdefs.h | sort
-      echo
-    fi
-    test "$ac_signal" != 0 &&
-      echo "$as_me: caught signal $ac_signal"
-    echo "$as_me: exit $exit_status"
-  } >&5
-  rm -f core *.core &&
-  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
-    exit $exit_status
-     ' 0
-for ac_signal in 1 2 13 15; do
-  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
-done
-ac_signal=0
-
-# 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
-
-# Predefined preprocessor variables.
-
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_NAME "$PACKAGE_NAME"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_VERSION "$PACKAGE_VERSION"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_STRING "$PACKAGE_STRING"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
-_ACEOF
-
-
-# Let the site file select an alternate cache file if it wants to.
-# 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 "$as_me:$LINENO: loading site script $ac_site_file" >&5
-echo "$as_me: loading site script $ac_site_file" >&6;}
-    sed 's/^/| /' "$ac_site_file" >&5
-    . "$ac_site_file"
-  fi
-done
-
-if test -r "$cache_file"; then
-  # Some versions of bash will fail to source /dev/null (special
-  # files actually), so we avoid doing that.
-  if test -f "$cache_file"; then
-    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
-echo "$as_me: loading cache $cache_file" >&6;}
-    case $cache_file in
-      [\\/]* | ?:[\\/]* ) . $cache_file;;
-      *)                      . ./$cache_file;;
-    esac
-  fi
-else
-  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
-echo "$as_me: creating cache $cache_file" >&6;}
-  >$cache_file
-fi
-
-# Check that the precious variables saved in the cache have kept the same
-# value.
-ac_cache_corrupted=false
-for ac_var in `(set) 2>&1 |
-              sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
-  eval ac_old_set=\$ac_cv_env_${ac_var}_set
-  eval ac_new_set=\$ac_env_${ac_var}_set
-  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
-  eval ac_new_val="\$ac_env_${ac_var}_value"
-  case $ac_old_set,$ac_new_set in
-    set,)
-      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
-echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
-      ac_cache_corrupted=: ;;
-    ,set)
-      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
-echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
-      ac_cache_corrupted=: ;;
-    ,);;
-    *)
-      if test "x$ac_old_val" != "x$ac_new_val"; then
-       { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
-echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
-       { echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
-echo "$as_me:   former value:  $ac_old_val" >&2;}
-       { echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
-echo "$as_me:   current value: $ac_new_val" >&2;}
-       ac_cache_corrupted=:
-      fi;;
-  esac
-  # Pass precious variables to config.status.
-  if test "$ac_new_set" = set; then
-    case $ac_new_val in
-    *" "*|*"   "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
-      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
-    *) ac_arg=$ac_var=$ac_new_val ;;
-    esac
-    case " $ac_configure_args " in
-      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
-      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
-    esac
-  fi
-done
-if $ac_cache_corrupted; then
-  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
-echo "$as_me: error: changes in the environment can compromise the build" >&2;}
-  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
-echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
-   { (exit 1); exit 1; }; }
-fi
-
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-smartmontools_configure_date=`date -u +"%Y/%m/%d %T %Z"`
-smartmontools_cvs_tag=`echo '$Id: configure.in,v 1.113 2005/11/27 20:01:29 chrfranke Exp $'`
-smartmontools_release_date=2006/04/12
-smartmontools_release_time="17:39:01 UTC"
-
-
-cat >>confdefs.h <<_ACEOF
-#define SMARTMONTOOLS_CONFIGURE_ARGS "$ac_configure_args"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define SMARTMONTOOLS_CONFIGURE_DATE "$smartmontools_configure_date"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define SMARTMONTOOLS_RELEASE_DATE "$smartmontools_release_date"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define SMARTMONTOOLS_RELEASE_TIME "$smartmontools_release_time"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define CONFIG_H_CVSID "$smartmontools_cvs_tag"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_HOMEPAGE "http://smartmontools.sourceforge.net/"
-_ACEOF
-
-
-          ac_config_headers="$ac_config_headers config.h"
-
-
-am__api_version="1.9"
-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
-  elif test -f $ac_dir/shtool; then
-    ac_aux_dir=$ac_dir
-    ac_install_sh="$ac_aux_dir/shtool install -c"
-    break
-  fi
-done
-if test -z "$ac_aux_dir"; then
-  { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
-echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-ac_config_guess="$SHELL $ac_aux_dir/config.guess"
-ac_config_sub="$SHELL $ac_aux_dir/config.sub"
-ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
-
-# 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
-# AmigaOS /C/install, which installs bootblocks on floppy discs
-# 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"
-# OS/2's system install, which has a completely different semantic
-# ./install, which can be erroneously created by make from ./install.sh.
-echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
-echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
-if test -z "$INSTALL"; then
-if test "${ac_cv_path_install+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  # Account for people who put trailing slashes in PATH elements.
-case $as_dir/ in
-  ./ | .// | /cC/* | \
-  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
-  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
-  /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
-      for ac_exec_ext in '' $ac_executable_extensions; do
-       if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
-         if test $ac_prog = install &&
-           grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
-           # AIX install.  It has an incompatible calling convention.
-           :
-         elif test $ac_prog = install &&
-           grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
-           # program-specific install script used by HP pwplus--don't use.
-           :
-         else
-           ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
-           break 3
-         fi
-       fi
-      done
-    done
-    ;;
-esac
-done
-
-
-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 "$as_me:$LINENO: result: $INSTALL" >&5
-echo "${ECHO_T}$INSTALL" >&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}'
-
-test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
-
-echo "$as_me:$LINENO: checking whether build environment is sane" >&5
-echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6
-# Just in case
-sleep 1
-echo timestamp > conftest.file
-# Do `set' in a subshell so we don't clobber the current shell's
-# arguments.  Must try -L first in case configure is actually a
-# symlink; some systems play weird games with the mod time of symlinks
-# (eg FreeBSD returns the mod time of the symlink's containing
-# directory).
-if (
-   set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
-   if test "$*" = "X"; then
-      # -L didn't work.
-      set X `ls -t $srcdir/configure conftest.file`
-   fi
-   rm -f conftest.file
-   if test "$*" != "X $srcdir/configure conftest.file" \
-      && test "$*" != "X conftest.file $srcdir/configure"; then
-
-      # If neither matched, then we have a broken ls.  This can happen
-      # if, for instance, CONFIG_SHELL is bash and it inherits a
-      # broken ls alias from the environment.  This has actually
-      # happened.  Such a system could not be considered "sane".
-      { { echo "$as_me:$LINENO: error: ls -t appears to fail.  Make sure there is not a broken
-alias in your environment" >&5
-echo "$as_me: error: ls -t appears to fail.  Make sure there is not a broken
-alias in your environment" >&2;}
-   { (exit 1); exit 1; }; }
-   fi
-
-   test "$2" = conftest.file
-   )
-then
-   # Ok.
-   :
-else
-   { { echo "$as_me:$LINENO: error: newly created file is older than distributed files!
-Check your system clock" >&5
-echo "$as_me: error: newly created file is older than distributed files!
-Check your system clock" >&2;}
-   { (exit 1); exit 1; }; }
-fi
-echo "$as_me:$LINENO: result: yes" >&5
-echo "${ECHO_T}yes" >&6
-test "$program_prefix" != NONE &&
-  program_transform_name="s,^,$program_prefix,;$program_transform_name"
-# Use a double $ so make ignores it.
-test "$program_suffix" != NONE &&
-  program_transform_name="s,\$,$program_suffix,;$program_transform_name"
-# Double any \ or $.  echo might interpret backslashes.
-# By default was `s,x,x', remove it if useless.
-cat <<\_ACEOF >conftest.sed
-s/[\\$]/&&/g;s/;s,x,x,$//
-_ACEOF
-program_transform_name=`echo $program_transform_name | sed -f conftest.sed`
-rm conftest.sed
-
-# expand $ac_aux_dir to an absolute path
-am_aux_dir=`cd $ac_aux_dir && pwd`
-
-test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
-# Use eval to expand $SHELL
-if eval "$MISSING --run true"; then
-  am_missing_run="$MISSING --run "
-else
-  am_missing_run=
-  { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5
-echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
-fi
-
-if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
-  # We used to keeping the `.' as first argument, in order to
-  # allow $(mkdir_p) to be used without argument.  As in
-  #   $(mkdir_p) $(somedir)
-  # where $(somedir) is conditionally defined.  However this is wrong
-  # for two reasons:
-  #  1. if the package is installed by a user who cannot write `.'
-  #     make install will fail,
-  #  2. the above comment should most certainly read
-  #     $(mkdir_p) $(DESTDIR)$(somedir)
-  #     so it does not work when $(somedir) is undefined and
-  #     $(DESTDIR) is not.
-  #  To support the latter case, we have to write
-  #     test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
-  #  so the `.' trick is pointless.
-  mkdir_p='mkdir -p --'
-else
-  # On NextStep and OpenStep, the `mkdir' command does not
-  # recognize any option.  It will interpret all options as
-  # directories to create, and then abort because `.' already
-  # exists.
-  for d in ./-p ./--version;
-  do
-    test -d $d && rmdir $d
-  done
-  # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
-  if test -f "$ac_aux_dir/mkinstalldirs"; then
-    mkdir_p='$(mkinstalldirs)'
-  else
-    mkdir_p='$(install_sh) -d'
-  fi
-fi
-
-for ac_prog in gawk mawk nawk awk
-do
-  # Extract the first word of "$ac_prog", so it can be a program name with args.
-set dummy $ac_prog; ac_word=$2
-echo "$as_me:$LINENO: checking for $ac_word" >&5
-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
-if test "${ac_cv_prog_AWK+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$AWK"; then
-  ac_cv_prog_AWK="$AWK" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_exec_ext in '' $ac_executable_extensions; do
-  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_AWK="$ac_prog"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-
-fi
-fi
-AWK=$ac_cv_prog_AWK
-if test -n "$AWK"; then
-  echo "$as_me:$LINENO: result: $AWK" >&5
-echo "${ECHO_T}$AWK" >&6
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-fi
-
-  test -n "$AWK" && break
-done
-
-echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
-echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6
-set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'`
-if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.make <<\_ACEOF
-all:
-       @echo 'ac_maketemp="$(MAKE)"'
-_ACEOF
-# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
-eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=`
-if test -n "$ac_maketemp"; then
-  eval ac_cv_prog_make_${ac_make}_set=yes
-else
-  eval ac_cv_prog_make_${ac_make}_set=no
-fi
-rm -f conftest.make
-fi
-if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
-  echo "$as_me:$LINENO: result: yes" >&5
-echo "${ECHO_T}yes" >&6
-  SET_MAKE=
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-  SET_MAKE="MAKE=${MAKE-make}"
-fi
-
-rm -rf .tst 2>/dev/null
-mkdir .tst 2>/dev/null
-if test -d .tst; then
-  am__leading_dot=.
-else
-  am__leading_dot=_
-fi
-rmdir .tst 2>/dev/null
-
-# test to see if srcdir already configured
-if test "`cd $srcdir && pwd`" != "`pwd`" &&
-   test -f $srcdir/config.status; then
-  { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5
-echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;}
-   { (exit 1); exit 1; }; }
-fi
-
-# test whether we have cygpath
-if test -z "$CYGPATH_W"; then
-  if (cygpath --version) >/dev/null 2>/dev/null; then
-    CYGPATH_W='cygpath -w'
-  else
-    CYGPATH_W=echo
-  fi
-fi
-
-
-# Define the identity of the package.
- PACKAGE='smartmontools'
- VERSION='5.36'
-
-
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE "$PACKAGE"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define VERSION "$VERSION"
-_ACEOF
-
-# Some tools Automake needs.
-
-ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
-
-
-AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
-
-
-AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
-
-
-AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
-
-
-MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
-
-install_sh=${install_sh-"$am_aux_dir/install-sh"}
-
-# Installed binaries are usually stripped using `strip' when the user
-# run `make install-strip'.  However `strip' might not be the right
-# tool to use in cross-compilation environments, therefore Automake
-# will honor the `STRIP' environment variable to overrule this program.
-if test "$cross_compiling" != no; then
-  if test -n "$ac_tool_prefix"; then
-  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
-set dummy ${ac_tool_prefix}strip; ac_word=$2
-echo "$as_me:$LINENO: checking for $ac_word" >&5
-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
-if test "${ac_cv_prog_STRIP+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$STRIP"; then
-  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_exec_ext in '' $ac_executable_extensions; do
-  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-
-fi
-fi
-STRIP=$ac_cv_prog_STRIP
-if test -n "$STRIP"; then
-  echo "$as_me:$LINENO: result: $STRIP" >&5
-echo "${ECHO_T}$STRIP" >&6
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-fi
-
-fi
-if test -z "$ac_cv_prog_STRIP"; then
-  ac_ct_STRIP=$STRIP
-  # Extract the first word of "strip", so it can be a program name with args.
-set dummy strip; ac_word=$2
-echo "$as_me:$LINENO: checking for $ac_word" >&5
-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
-if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$ac_ct_STRIP"; then
-  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_exec_ext in '' $ac_executable_extensions; do
-  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ac_ct_STRIP="strip"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-
-  test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":"
-fi
-fi
-ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
-if test -n "$ac_ct_STRIP"; then
-  echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
-echo "${ECHO_T}$ac_ct_STRIP" >&6
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-fi
-
-  STRIP=$ac_ct_STRIP
-else
-  STRIP="$ac_cv_prog_STRIP"
-fi
-
-fi
-INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
-
-# We need awk for the "check" target.  The system "awk" is bad on
-# some platforms.
-# Always define AMTAR for backward compatibility.
-
-AMTAR=${AMTAR-"${am_missing_run}tar"}
-
-am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
-
-
-
-
-
-
-echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5
-echo $ECHO_N "checking whether to enable maintainer-specific portions of Makefiles... $ECHO_C" >&6
-    # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given.
-if test "${enable_maintainer_mode+set}" = set; then
-  enableval="$enable_maintainer_mode"
-  USE_MAINTAINER_MODE=$enableval
-else
-  USE_MAINTAINER_MODE=no
-fi;
-  echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5
-echo "${ECHO_T}$USE_MAINTAINER_MODE" >&6
-
-
-if test $USE_MAINTAINER_MODE = yes; then
-  MAINTAINER_MODE_TRUE=
-  MAINTAINER_MODE_FALSE='#'
-else
-  MAINTAINER_MODE_TRUE='#'
-  MAINTAINER_MODE_FALSE=
-fi
-
-  MAINT=$MAINTAINER_MODE_TRUE
-
-
-
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-if test -n "$ac_tool_prefix"; then
-  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
-set dummy ${ac_tool_prefix}gcc; ac_word=$2
-echo "$as_me:$LINENO: checking for $ac_word" >&5
-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
-if test "${ac_cv_prog_CC+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$CC"; then
-  ac_cv_prog_CC="$CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_exec_ext in '' $ac_executable_extensions; do
-  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_CC="${ac_tool_prefix}gcc"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
-  echo "$as_me:$LINENO: result: $CC" >&5
-echo "${ECHO_T}$CC" >&6
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-fi
-
-fi
-if test -z "$ac_cv_prog_CC"; then
-  ac_ct_CC=$CC
-  # Extract the first word of "gcc", so it can be a program name with args.
-set dummy gcc; ac_word=$2
-echo "$as_me:$LINENO: checking for $ac_word" >&5
-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$ac_ct_CC"; then
-  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_exec_ext in '' $ac_executable_extensions; do
-  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ac_ct_CC="gcc"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-
-fi
-fi
-ac_ct_CC=$ac_cv_prog_ac_ct_CC
-if test -n "$ac_ct_CC"; then
-  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
-echo "${ECHO_T}$ac_ct_CC" >&6
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-fi
-
-  CC=$ac_ct_CC
-else
-  CC="$ac_cv_prog_CC"
-fi
-
-if test -z "$CC"; then
-  if test -n "$ac_tool_prefix"; then
-  # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
-set dummy ${ac_tool_prefix}cc; ac_word=$2
-echo "$as_me:$LINENO: checking for $ac_word" >&5
-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
-if test "${ac_cv_prog_CC+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$CC"; then
-  ac_cv_prog_CC="$CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_exec_ext in '' $ac_executable_extensions; do
-  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_CC="${ac_tool_prefix}cc"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
-  echo "$as_me:$LINENO: result: $CC" >&5
-echo "${ECHO_T}$CC" >&6
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-fi
-
-fi
-if test -z "$ac_cv_prog_CC"; then
-  ac_ct_CC=$CC
-  # Extract the first word of "cc", so it can be a program name with args.
-set dummy cc; ac_word=$2
-echo "$as_me:$LINENO: checking for $ac_word" >&5
-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$ac_ct_CC"; then
-  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_exec_ext in '' $ac_executable_extensions; do
-  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ac_ct_CC="cc"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-
-fi
-fi
-ac_ct_CC=$ac_cv_prog_ac_ct_CC
-if test -n "$ac_ct_CC"; then
-  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
-echo "${ECHO_T}$ac_ct_CC" >&6
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-fi
-
-  CC=$ac_ct_CC
-else
-  CC="$ac_cv_prog_CC"
-fi
-
-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 "$as_me:$LINENO: checking for $ac_word" >&5
-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
-if test "${ac_cv_prog_CC+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$CC"; then
-  ac_cv_prog_CC="$CC" # Let the user override the test.
-else
-  ac_prog_rejected=no
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_exec_ext in '' $ac_executable_extensions; do
-  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
-       ac_prog_rejected=yes
-       continue
-     fi
-    ac_cv_prog_CC="cc"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-
-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 $# != 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
-    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
-  fi
-fi
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
-  echo "$as_me:$LINENO: result: $CC" >&5
-echo "${ECHO_T}$CC" >&6
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-fi
-
-fi
-if test -z "$CC"; then
-  if test -n "$ac_tool_prefix"; then
-  for ac_prog in cl
-  do
-    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
-set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-echo "$as_me:$LINENO: checking for $ac_word" >&5
-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
-if test "${ac_cv_prog_CC+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$CC"; then
-  ac_cv_prog_CC="$CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_exec_ext in '' $ac_executable_extensions; do
-  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-
-fi
-fi
-CC=$ac_cv_prog_CC
-if test -n "$CC"; then
-  echo "$as_me:$LINENO: result: $CC" >&5
-echo "${ECHO_T}$CC" >&6
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-fi
-
-    test -n "$CC" && break
-  done
-fi
-if test -z "$CC"; then
-  ac_ct_CC=$CC
-  for ac_prog in cl
-do
-  # Extract the first word of "$ac_prog", so it can be a program name with args.
-set dummy $ac_prog; ac_word=$2
-echo "$as_me:$LINENO: checking for $ac_word" >&5
-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$ac_ct_CC"; then
-  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_exec_ext in '' $ac_executable_extensions; do
-  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_ac_ct_CC="$ac_prog"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-
-fi
-fi
-ac_ct_CC=$ac_cv_prog_ac_ct_CC
-if test -n "$ac_ct_CC"; then
-  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
-echo "${ECHO_T}$ac_ct_CC" >&6
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-fi
-
-  test -n "$ac_ct_CC" && break
-done
-
-  CC=$ac_ct_CC
-fi
-
-fi
-
-
-test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
-See \`config.log' for more details." >&5
-echo "$as_me: error: no acceptable C compiler found in \$PATH
-See \`config.log' for more details." >&2;}
-   { (exit 1); exit 1; }; }
-
-# Provide some information about the compiler.
-echo "$as_me:$LINENO:" \
-     "checking for C compiler version" >&5
-ac_compiler=`set X $ac_compile; echo $2`
-{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
-  (eval $ac_compiler --version </dev/null >&5) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }
-{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
-  (eval $ac_compiler -v </dev/null >&5) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }
-{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
-  (eval $ac_compiler -V </dev/null >&5) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }
-
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-
-int
-main ()
-{
-
-  ;
-  return 0;
-}
-_ACEOF
-ac_clean_files_save=$ac_clean_files
-ac_clean_files="$ac_clean_files a.out a.exe b.out"
-# Try to create an executable without -o first, disregard a.out.
-# It will help us diagnose broken compilers, and finding out an intuition
-# of exeext.
-echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
-echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
-ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
-if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
-  (eval $ac_link_default) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; then
-  # Find the output, starting from the most likely.  This scheme is
-# not robust to junk in `.', hence go to wildcards (a.*) only as a last
-# resort.
-
-# Be careful to initialize this variable, since it used to be cached.
-# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
-ac_cv_exeext=
-# b.out is created by i960 compilers.
-for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
-do
-  test -f "$ac_file" || continue
-  case $ac_file in
-    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
-       ;;
-    conftest.$ac_ext )
-       # This is the source file.
-       ;;
-    [ab].out )
-       # We found the default executable, but exeext='' is most
-       # certainly right.
-       break;;
-    *.* )
-       ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
-       # FIXME: I believe we export ac_cv_exeext for Libtool,
-       # but it would be cool to find out if it's true.  Does anybody
-       # maintain Libtool? --akim.
-       export ac_cv_exeext
-       break;;
-    * )
-       break;;
-  esac
-done
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
-See \`config.log' for more details." >&5
-echo "$as_me: error: C compiler cannot create executables
-See \`config.log' for more details." >&2;}
-   { (exit 77); exit 77; }; }
-fi
-
-ac_exeext=$ac_cv_exeext
-echo "$as_me:$LINENO: result: $ac_file" >&5
-echo "${ECHO_T}$ac_file" >&6
-
-# Check the compiler produces executables we can run.  If not, either
-# the compiler is broken, or we cross compile.
-echo "$as_me:$LINENO: checking whether the C compiler works" >&5
-echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
-# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
-# If not cross compiling, check that we can run a simple program.
-if test "$cross_compiling" != yes; then
-  if { ac_try='./$ac_file'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-    cross_compiling=no
-  else
-    if test "$cross_compiling" = maybe; then
-       cross_compiling=yes
-    else
-       { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
-If you meant to cross compile, use \`--host'.
-See \`config.log' for more details." >&5
-echo "$as_me: error: cannot run C compiled programs.
-If you meant to cross compile, use \`--host'.
-See \`config.log' for more details." >&2;}
-   { (exit 1); exit 1; }; }
-    fi
-  fi
-fi
-echo "$as_me:$LINENO: result: yes" >&5
-echo "${ECHO_T}yes" >&6
-
-rm -f a.out a.exe conftest$ac_cv_exeext b.out
-ac_clean_files=$ac_clean_files_save
-# Check the compiler produces executables we can run.  If not, either
-# the compiler is broken, or we cross compile.
-echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
-echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
-echo "$as_me:$LINENO: result: $cross_compiling" >&5
-echo "${ECHO_T}$cross_compiling" >&6
-
-echo "$as_me:$LINENO: checking for suffix of executables" >&5
-echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; then
-  # If both `conftest.exe' and `conftest' are `present' (well, observable)
-# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
-# work properly (i.e., refer to `conftest.exe'), while it won't with
-# `rm'.
-for ac_file in conftest.exe conftest conftest.*; do
-  test -f "$ac_file" || continue
-  case $ac_file in
-    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
-    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
-         export ac_cv_exeext
-         break;;
-    * ) break;;
-  esac
-done
-else
-  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
-See \`config.log' for more details." >&5
-echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
-See \`config.log' for more details." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-
-rm -f conftest$ac_cv_exeext
-echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
-echo "${ECHO_T}$ac_cv_exeext" >&6
-
-rm -f conftest.$ac_ext
-EXEEXT=$ac_cv_exeext
-ac_exeext=$EXEEXT
-echo "$as_me:$LINENO: checking for suffix of object files" >&5
-echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
-if test "${ac_cv_objext+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-
-int
-main ()
-{
-
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.o conftest.obj
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; then
-  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
-  case $ac_file in
-    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
-    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
-       break;;
-  esac
-done
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
-See \`config.log' for more details." >&5
-echo "$as_me: error: cannot compute suffix of object files: cannot compile
-See \`config.log' for more details." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-
-rm -f conftest.$ac_cv_objext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
-echo "${ECHO_T}$ac_cv_objext" >&6
-OBJEXT=$ac_cv_objext
-ac_objext=$OBJEXT
-echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
-echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
-if test "${ac_cv_c_compiler_gnu+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-
-int
-main ()
-{
-#ifndef __GNUC__
-       choke me
-#endif
-
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_compiler_gnu=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_compiler_gnu=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-ac_cv_c_compiler_gnu=$ac_compiler_gnu
-
-fi
-echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
-echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
-GCC=`test $ac_compiler_gnu = yes && echo yes`
-ac_test_CFLAGS=${CFLAGS+set}
-ac_save_CFLAGS=$CFLAGS
-CFLAGS="-g"
-echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
-echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
-if test "${ac_cv_prog_cc_g+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-
-int
-main ()
-{
-
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_cv_prog_cc_g=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_cv_prog_cc_g=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
-echo "${ECHO_T}$ac_cv_prog_cc_g" >&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
-echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
-echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
-if test "${ac_cv_prog_cc_stdc+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  ac_cv_prog_cc_stdc=no
-ac_save_CC=$CC
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <stdarg.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
-struct buf { int x; };
-FILE * (*rcsopen) (struct buf *, struct stat *, int);
-static char *e (p, i)
-     char **p;
-     int i;
-{
-  return p[i];
-}
-static char *f (char * (*g) (char **, int), char **p, ...)
-{
-  char *s;
-  va_list v;
-  va_start (v,p);
-  s = g (p, va_arg (v,int));
-  va_end (v);
-  return s;
-}
-
-/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
-   function prototypes and stuff, but not '\xHH' hex character constants.
-   These don't provoke an error unfortunately, instead are silently treated
-   as 'x'.  The following induces an error, until -std1 is added to get
-   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
-   array size at least.  It's necessary to write '\x00'==0 to get something
-   that's true only with -std1.  */
-int osf4_cc_array ['\x00' == 0 ? 1 : -1];
-
-int test (int i, double x);
-struct s1 {int (*f) (int a);};
-struct s2 {int (*f) (double a);};
-int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
-int argc;
-char **argv;
-int
-main ()
-{
-return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
-  ;
-  return 0;
-}
-_ACEOF
-# Don't try gcc -ansi; that turns off useful extensions and
-# breaks some systems' header files.
-# AIX                  -qlanglvl=ansi
-# Ultrix and OSF/1     -std1
-# HP-UX 10.20 and later        -Ae
-# HP-UX older versions -Aa -D_HPUX_SOURCE
-# SVR4                 -Xc -D__EXTENSIONS__
-for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
-do
-  CC="$ac_save_CC $ac_arg"
-  rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_cv_prog_cc_stdc=$ac_arg
-break
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-fi
-rm -f conftest.err conftest.$ac_objext
-done
-rm -f conftest.$ac_ext conftest.$ac_objext
-CC=$ac_save_CC
-
-fi
-
-case "x$ac_cv_prog_cc_stdc" in
-  x|xno)
-    echo "$as_me:$LINENO: result: none needed" >&5
-echo "${ECHO_T}none needed" >&6 ;;
-  *)
-    echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
-echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
-    CC="$CC $ac_cv_prog_cc_stdc" ;;
-esac
-
-# Some people use a C++ compiler to compile C.  Since we use `exit',
-# in C++ we need to declare it.  In case someone uses the same compiler
-# for both compiling C and C++ we need to have the C++ compiler decide
-# the declaration of exit, since it's the most demanding environment.
-cat >conftest.$ac_ext <<_ACEOF
-#ifndef __cplusplus
-  choke me
-#endif
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  for ac_declaration in \
-   '' \
-   'extern "C" void std::exit (int) throw (); using std::exit;' \
-   'extern "C" void std::exit (int); using std::exit;' \
-   'extern "C" void exit (int) throw ();' \
-   'extern "C" void exit (int);' \
-   'void exit (int);'
-do
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_declaration
-#include <stdlib.h>
-int
-main ()
-{
-exit (42);
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  :
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-continue
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_declaration
-int
-main ()
-{
-exit (42);
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  break
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-done
-rm -f conftest*
-if test -n "$ac_declaration"; then
-  echo '#ifdef __cplusplus' >>confdefs.h
-  echo $ac_declaration      >>confdefs.h
-  echo '#endif'             >>confdefs.h
-fi
-
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-DEPDIR="${am__leading_dot}deps"
-
-          ac_config_commands="$ac_config_commands depfiles"
-
-
-am_make=${MAKE-make}
-cat > confinc << 'END'
-am__doit:
-       @echo done
-.PHONY: am__doit
-END
-# If we don't find an include directive, just comment out the code.
-echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5
-echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6
-am__include="#"
-am__quote=
-_am_result=none
-# First try GNU make style include.
-echo "include confinc" > confmf
-# We grep out `Entering directory' and `Leaving directory'
-# messages which can occur if `w' ends up in MAKEFLAGS.
-# In particular we don't look at `^make:' because GNU make might
-# be invoked under some other name (usually "gmake"), in which
-# case it prints its new name instead of `make'.
-if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
-   am__include=include
-   am__quote=
-   _am_result=GNU
-fi
-# Now try BSD make style include.
-if test "$am__include" = "#"; then
-   echo '.include "confinc"' > confmf
-   if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
-      am__include=.include
-      am__quote="\""
-      _am_result=BSD
-   fi
-fi
-
-
-echo "$as_me:$LINENO: result: $_am_result" >&5
-echo "${ECHO_T}$_am_result" >&6
-rm -f confinc confmf
-
-# Check whether --enable-dependency-tracking or --disable-dependency-tracking was given.
-if test "${enable_dependency_tracking+set}" = set; then
-  enableval="$enable_dependency_tracking"
-
-fi;
-if test "x$enable_dependency_tracking" != xno; then
-  am_depcomp="$ac_aux_dir/depcomp"
-  AMDEPBACKSLASH='\'
-fi
-
-
-if test "x$enable_dependency_tracking" != xno; then
-  AMDEP_TRUE=
-  AMDEP_FALSE='#'
-else
-  AMDEP_TRUE='#'
-  AMDEP_FALSE=
-fi
-
-
-
-
-depcc="$CC"   am_compiler_list=
-
-echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
-echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6
-if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
-  # We make a subdir and do the tests there.  Otherwise we can end up
-  # making bogus files that we don't know about and never remove.  For
-  # instance it was reported that on HP-UX the gcc test will end up
-  # making a dummy file named `D' -- because `-MD' means `put the output
-  # in D'.
-  mkdir conftest.dir
-  # Copy depcomp to subdir because otherwise we won't find it if we're
-  # using a relative directory.
-  cp "$am_depcomp" conftest.dir
-  cd conftest.dir
-  # We will build objects and dependencies in a subdirectory because
-  # it helps to detect inapplicable dependency modes.  For instance
-  # both Tru64's cc and ICC support -MD to output dependencies as a
-  # side effect of compilation, but ICC will put the dependencies in
-  # the current directory while Tru64 will put them in the object
-  # directory.
-  mkdir sub
-
-  am_cv_CC_dependencies_compiler_type=none
-  if test "$am_compiler_list" = ""; then
-     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
-  fi
-  for depmode in $am_compiler_list; do
-    # Setup a source with many dependencies, because some compilers
-    # like to wrap large dependency lists on column 80 (with \), and
-    # we should not choose a depcomp mode which is confused by this.
-    #
-    # We need to recreate these files for each test, as the compiler may
-    # overwrite some of them when testing with obscure command lines.
-    # This happens at least with the AIX C compiler.
-    : > sub/conftest.c
-    for i in 1 2 3 4 5 6; do
-      echo '#include "conftst'$i'.h"' >> sub/conftest.c
-      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
-      # Solaris 8's {/usr,}/bin/sh.
-      touch sub/conftst$i.h
-    done
-    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
-
-    case $depmode in
-    nosideeffect)
-      # after this tag, mechanisms are not by side-effect, so they'll
-      # only be used when explicitly requested
-      if test "x$enable_dependency_tracking" = xyes; then
-       continue
-      else
-       break
-      fi
-      ;;
-    none) break ;;
-    esac
-    # We check with `-c' and `-o' for the sake of the "dashmstdout"
-    # mode.  It turns out that the SunPro C++ compiler does not properly
-    # handle `-M -o', and we need to detect this.
-    if depmode=$depmode \
-       source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
-       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
-       $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
-         >/dev/null 2>conftest.err &&
-       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
-       grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
-       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
-      # icc doesn't choke on unknown options, it will just issue warnings
-      # or remarks (even with -Werror).  So we grep stderr for any message
-      # that says an option was ignored or not supported.
-      # When given -MP, icc 7.0 and 7.1 complain thusly:
-      #   icc: Command line warning: ignoring option '-M'; no argument required
-      # The diagnosis changed in icc 8.0:
-      #   icc: Command line remark: option '-MP' not supported
-      if (grep 'ignoring option' conftest.err ||
-          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
-        am_cv_CC_dependencies_compiler_type=$depmode
-        break
-      fi
-    fi
-  done
-
-  cd ..
-  rm -rf conftest.dir
-else
-  am_cv_CC_dependencies_compiler_type=none
-fi
-
-fi
-echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5
-echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6
-CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
-
-
-
-if
-  test "x$enable_dependency_tracking" != xno \
-  && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
-  am__fastdepCC_TRUE=
-  am__fastdepCC_FALSE='#'
-else
-  am__fastdepCC_TRUE='#'
-  am__fastdepCC_FALSE=
-fi
-
-
-# By default we simply use the C compiler to build assembly code.
-
-test "${CCAS+set}" = set || CCAS=$CC
-test "${CCASFLAGS+set}" = set || CCASFLAGS=$CFLAGS
-
-
-
-# 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
-# AmigaOS /C/install, which installs bootblocks on floppy discs
-# 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"
-# OS/2's system install, which has a completely different semantic
-# ./install, which can be erroneously created by make from ./install.sh.
-echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
-echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
-if test -z "$INSTALL"; then
-if test "${ac_cv_path_install+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  # Account for people who put trailing slashes in PATH elements.
-case $as_dir/ in
-  ./ | .// | /cC/* | \
-  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
-  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
-  /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
-      for ac_exec_ext in '' $ac_executable_extensions; do
-       if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
-         if test $ac_prog = install &&
-           grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
-           # AIX install.  It has an incompatible calling convention.
-           :
-         elif test $ac_prog = install &&
-           grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
-           # program-specific install script used by HP pwplus--don't use.
-           :
-         else
-           ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
-           break 3
-         fi
-       fi
-      done
-    done
-    ;;
-esac
-done
-
-
-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 "$as_me:$LINENO: result: $INSTALL" >&5
-echo "${ECHO_T}$INSTALL" >&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}'
-
-test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
-
-
-# Make sure we can run config.sub.
-$ac_config_sub sun4 >/dev/null 2>&1 ||
-  { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5
-echo "$as_me: error: cannot run $ac_config_sub" >&2;}
-   { (exit 1); exit 1; }; }
-
-echo "$as_me:$LINENO: checking build system type" >&5
-echo $ECHO_N "checking build system type... $ECHO_C" >&6
-if test "${ac_cv_build+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  ac_cv_build_alias=$build_alias
-test -z "$ac_cv_build_alias" &&
-  ac_cv_build_alias=`$ac_config_guess`
-test -z "$ac_cv_build_alias" &&
-  { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
-echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
-   { (exit 1); exit 1; }; }
-ac_cv_build=`$ac_config_sub $ac_cv_build_alias` ||
-  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5
-echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;}
-   { (exit 1); exit 1; }; }
-
-fi
-echo "$as_me:$LINENO: result: $ac_cv_build" >&5
-echo "${ECHO_T}$ac_cv_build" >&6
-build=$ac_cv_build
-build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
-build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
-build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
-
-
-echo "$as_me:$LINENO: checking host system type" >&5
-echo $ECHO_N "checking host system type... $ECHO_C" >&6
-if test "${ac_cv_host+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  ac_cv_host_alias=$host_alias
-test -z "$ac_cv_host_alias" &&
-  ac_cv_host_alias=$ac_cv_build_alias
-ac_cv_host=`$ac_config_sub $ac_cv_host_alias` ||
-  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5
-echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;}
-   { (exit 1); exit 1; }; }
-
-fi
-echo "$as_me:$LINENO: result: $ac_cv_host" >&5
-echo "${ECHO_T}$ac_cv_host" >&6
-host=$ac_cv_host
-host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
-host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
-host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
-
-
-case "${host}" in
-       *-*-mingw*)
-               CPPFLAGS="$CPPFLAGS -mno-cygwin"
-               LDFLAGS="$LDFLAGS -mno-cygwin"
-               CPPFLAGS="$CPPFLAGS -idirafter ${srcdir}/posix -idirafter ${srcdir}/os_win32"
-esac
-
-#  AC_SEARCH_LIBS (FUNCTION, SEARCH-LIBS, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], [OTHER-LIBRARIES])
-echo "$as_me:$LINENO: checking for library containing gethostbyname" >&5
-echo $ECHO_N "checking for library containing gethostbyname... $ECHO_C" >&6
-if test "${ac_cv_search_gethostbyname+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  ac_func_search_save_LIBS=$LIBS
-ac_cv_search_gethostbyname=no
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char gethostbyname ();
-int
-main ()
-{
-gethostbyname ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_cv_search_gethostbyname="none required"
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-if test "$ac_cv_search_gethostbyname" = no; then
-  for ac_lib in nsl; do
-    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
-    cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char gethostbyname ();
-int
-main ()
-{
-gethostbyname ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_cv_search_gethostbyname="-l$ac_lib"
-break
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-  done
-fi
-LIBS=$ac_func_search_save_LIBS
-fi
-echo "$as_me:$LINENO: result: $ac_cv_search_gethostbyname" >&5
-echo "${ECHO_T}$ac_cv_search_gethostbyname" >&6
-if test "$ac_cv_search_gethostbyname" != no; then
-  test "$ac_cv_search_gethostbyname" = "none required" || LIBS="$ac_cv_search_gethostbyname $LIBS"
-
-else
-  echo "$as_me:$LINENO: checking for library containing gethostbyname" >&5
-echo $ECHO_N "checking for library containing gethostbyname... $ECHO_C" >&6
-if test "${ac_cv_search_gethostbyname+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  ac_func_search_save_LIBS=$LIBS
-ac_cv_search_gethostbyname=no
-
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char gethostbyname ();
-int
-main ()
-{
-gethostbyname ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_cv_search_gethostbyname="none required"
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-if test "$ac_cv_search_gethostbyname" = no; then
-  for ac_lib in nsl; do
-    LIBS="-l$ac_lib -lsocket $ac_func_search_save_LIBS"
-    cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char gethostbyname ();
-int
-main ()
-{
-gethostbyname ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_cv_search_gethostbyname="-l$ac_lib"
-break
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-  done
-fi
-LIBS=$ac_func_search_save_LIBS
-fi
-echo "$as_me:$LINENO: result: $ac_cv_search_gethostbyname" >&5
-echo "${ECHO_T}$ac_cv_search_gethostbyname" >&6
-if test "$ac_cv_search_gethostbyname" != no; then
-  test "$ac_cv_search_gethostbyname" = "none required" || LIBS="$ac_cv_search_gethostbyname $LIBS"
-
-fi
-
-fi
-
-
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
-echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
-# On Suns, sometimes $CPP names a directory.
-if test -n "$CPP" && test -d "$CPP"; then
-  CPP=
-fi
-if test -z "$CPP"; then
-  if test "${ac_cv_prog_CPP+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-      # Double quotes because CPP needs to be expanded
-    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
-    do
-      ac_preproc_ok=false
-for ac_c_preproc_warn_flag in '' yes
-do
-  # Use a header file that comes with gcc, so configuring glibc
-  # with a fresh cross-compiler works.
-  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-  # <limits.h> exists even on freestanding compilers.
-  # On the NeXT, cc -E runs the code through the compiler's parser,
-  # not just through cpp. "Syntax error" is here to catch this case.
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
-                    Syntax error
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  :
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  # Broken: fails on valid input.
-continue
-fi
-rm -f conftest.err conftest.$ac_ext
-
-  # OK, works on sane cases.  Now check whether non-existent headers
-  # can be detected and how.
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <ac_nonexistent.h>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  # Broken: success on invalid input.
-continue
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  # Passes both tests.
-ac_preproc_ok=:
-break
-fi
-rm -f conftest.err conftest.$ac_ext
-
-done
-# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.err conftest.$ac_ext
-if $ac_preproc_ok; then
-  break
-fi
-
-    done
-    ac_cv_prog_CPP=$CPP
-
-fi
-  CPP=$ac_cv_prog_CPP
-else
-  ac_cv_prog_CPP=$CPP
-fi
-echo "$as_me:$LINENO: result: $CPP" >&5
-echo "${ECHO_T}$CPP" >&6
-ac_preproc_ok=false
-for ac_c_preproc_warn_flag in '' yes
-do
-  # Use a header file that comes with gcc, so configuring glibc
-  # with a fresh cross-compiler works.
-  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-  # <limits.h> exists even on freestanding compilers.
-  # On the NeXT, cc -E runs the code through the compiler's parser,
-  # not just through cpp. "Syntax error" is here to catch this case.
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
-                    Syntax error
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  :
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  # Broken: fails on valid input.
-continue
-fi
-rm -f conftest.err conftest.$ac_ext
-
-  # OK, works on sane cases.  Now check whether non-existent headers
-  # can be detected and how.
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <ac_nonexistent.h>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  # Broken: success on invalid input.
-continue
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  # Passes both tests.
-ac_preproc_ok=:
-break
-fi
-rm -f conftest.err conftest.$ac_ext
-
-done
-# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.err conftest.$ac_ext
-if $ac_preproc_ok; then
-  :
-else
-  { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
-See \`config.log' for more details." >&5
-echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
-See \`config.log' for more details." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-
-echo "$as_me:$LINENO: checking for egrep" >&5
-echo $ECHO_N "checking for egrep... $ECHO_C" >&6
-if test "${ac_cv_prog_egrep+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if echo a | (grep -E '(a|b)') >/dev/null 2>&1
-    then ac_cv_prog_egrep='grep -E'
-    else ac_cv_prog_egrep='egrep'
-    fi
-fi
-echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
-echo "${ECHO_T}$ac_cv_prog_egrep" >&6
- EGREP=$ac_cv_prog_egrep
-
-
-echo "$as_me:$LINENO: checking for ANSI C header files" >&5
-echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
-if test "${ac_cv_header_stdc+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <float.h>
-
-int
-main ()
-{
-
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_cv_header_stdc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_cv_header_stdc=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-
-if test $ac_cv_header_stdc = yes; then
-  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <string.h>
-
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
-  $EGREP "memchr" >/dev/null 2>&1; then
-  :
-else
-  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 <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <stdlib.h>
-
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
-  $EGREP "free" >/dev/null 2>&1; then
-  :
-else
-  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 <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <ctype.h>
-#if ((' ' & 0x0FF) == 0x020)
-# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
-# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
-#else
-# define ISLOWER(c) \
-                  (('a' <= (c) && (c) <= 'i') \
-                    || ('j' <= (c) && (c) <= 'r') \
-                    || ('s' <= (c) && (c) <= 'z'))
-# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
-#endif
-
-#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);
-}
-_ACEOF
-rm -f conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  :
-else
-  echo "$as_me: program exited with status $ac_status" >&5
-echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-( exit $ac_status )
-ac_cv_header_stdc=no
-fi
-rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
-fi
-fi
-fi
-echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
-echo "${ECHO_T}$ac_cv_header_stdc" >&6
-if test $ac_cv_header_stdc = yes; then
-
-cat >>confdefs.h <<\_ACEOF
-#define STDC_HEADERS 1
-_ACEOF
-
-fi
-
-# On IRIX 5.3, sys/types and inttypes.h are conflicting.
-
-
-
-
-
-
-
-
-
-for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
-                 inttypes.h stdint.h unistd.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  eval "$as_ac_Header=yes"
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-eval "$as_ac_Header=no"
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-
-
-for ac_header in locale.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-else
-  # Is the header compilable?
-echo "$as_me:$LINENO: checking $ac_header usability" >&5
-echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_header_compiler=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_header_compiler=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
-echo "${ECHO_T}$ac_header_compiler" >&6
-
-# Is the header present?
-echo "$as_me:$LINENO: checking $ac_header presence" >&5
-echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <$ac_header>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  ac_header_preproc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  ac_header_preproc=no
-fi
-rm -f conftest.err conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
-echo "${ECHO_T}$ac_header_preproc" >&6
-
-# So?  What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
-  yes:no: )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
-echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
-    ac_header_preproc=yes
-    ;;
-  no:yes:* )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
-echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
-echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
-echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
-echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
-echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
-    (
-      cat <<\_ASBOX
-## ---------------------------------------------------------- ##
-## Report this to smartmontools-support@lists.sourceforge.net ##
-## ---------------------------------------------------------- ##
-_ASBOX
-    ) |
-      sed "s/^/$as_me: WARNING:     /" >&2
-    ;;
-esac
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-
-fi
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-
-for ac_header in getopt.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-else
-  # Is the header compilable?
-echo "$as_me:$LINENO: checking $ac_header usability" >&5
-echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_header_compiler=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_header_compiler=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
-echo "${ECHO_T}$ac_header_compiler" >&6
-
-# Is the header present?
-echo "$as_me:$LINENO: checking $ac_header presence" >&5
-echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <$ac_header>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  ac_header_preproc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  ac_header_preproc=no
-fi
-rm -f conftest.err conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
-echo "${ECHO_T}$ac_header_preproc" >&6
-
-# So?  What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
-  yes:no: )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
-echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
-    ac_header_preproc=yes
-    ;;
-  no:yes:* )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
-echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
-echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
-echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
-echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
-echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
-    (
-      cat <<\_ASBOX
-## ---------------------------------------------------------- ##
-## Report this to smartmontools-support@lists.sourceforge.net ##
-## ---------------------------------------------------------- ##
-_ASBOX
-    ) |
-      sed "s/^/$as_me: WARNING:     /" >&2
-    ;;
-esac
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-
-fi
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-
-for ac_header in dev/ata/atavar.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-else
-  # Is the header compilable?
-echo "$as_me:$LINENO: checking $ac_header usability" >&5
-echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_header_compiler=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_header_compiler=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
-echo "${ECHO_T}$ac_header_compiler" >&6
-
-# Is the header present?
-echo "$as_me:$LINENO: checking $ac_header presence" >&5
-echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <$ac_header>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  ac_header_preproc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  ac_header_preproc=no
-fi
-rm -f conftest.err conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
-echo "${ECHO_T}$ac_header_preproc" >&6
-
-# So?  What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
-  yes:no: )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
-echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
-    ac_header_preproc=yes
-    ;;
-  no:yes:* )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
-echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
-echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
-echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
-echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
-echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
-    (
-      cat <<\_ASBOX
-## ---------------------------------------------------------- ##
-## Report this to smartmontools-support@lists.sourceforge.net ##
-## ---------------------------------------------------------- ##
-_ASBOX
-    ) |
-      sed "s/^/$as_me: WARNING:     /" >&2
-    ;;
-esac
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-
-fi
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-
-for ac_header in netdb.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-else
-  # Is the header compilable?
-echo "$as_me:$LINENO: checking $ac_header usability" >&5
-echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_header_compiler=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_header_compiler=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
-echo "${ECHO_T}$ac_header_compiler" >&6
-
-# Is the header present?
-echo "$as_me:$LINENO: checking $ac_header presence" >&5
-echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <$ac_header>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  ac_header_preproc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  ac_header_preproc=no
-fi
-rm -f conftest.err conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
-echo "${ECHO_T}$ac_header_preproc" >&6
-
-# So?  What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
-  yes:no: )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
-echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
-    ac_header_preproc=yes
-    ;;
-  no:yes:* )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
-echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
-echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
-echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
-echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
-echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
-    (
-      cat <<\_ASBOX
-## ---------------------------------------------------------- ##
-## Report this to smartmontools-support@lists.sourceforge.net ##
-## ---------------------------------------------------------- ##
-_ASBOX
-    ) |
-      sed "s/^/$as_me: WARNING:     /" >&2
-    ;;
-esac
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-
-fi
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-
-for ac_header in inttypes.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-else
-  # Is the header compilable?
-echo "$as_me:$LINENO: checking $ac_header usability" >&5
-echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_header_compiler=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_header_compiler=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
-echo "${ECHO_T}$ac_header_compiler" >&6
-
-# Is the header present?
-echo "$as_me:$LINENO: checking $ac_header presence" >&5
-echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <$ac_header>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  ac_header_preproc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  ac_header_preproc=no
-fi
-rm -f conftest.err conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
-echo "${ECHO_T}$ac_header_preproc" >&6
-
-# So?  What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
-  yes:no: )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
-echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
-    ac_header_preproc=yes
-    ;;
-  no:yes:* )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
-echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
-echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
-echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
-echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
-echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
-    (
-      cat <<\_ASBOX
-## ---------------------------------------------------------- ##
-## Report this to smartmontools-support@lists.sourceforge.net ##
-## ---------------------------------------------------------- ##
-_ASBOX
-    ) |
-      sed "s/^/$as_me: WARNING:     /" >&2
-    ;;
-esac
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-
-fi
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-for ac_header in stdint.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-else
-  # Is the header compilable?
-echo "$as_me:$LINENO: checking $ac_header usability" >&5
-echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_header_compiler=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_header_compiler=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
-echo "${ECHO_T}$ac_header_compiler" >&6
-
-# Is the header present?
-echo "$as_me:$LINENO: checking $ac_header presence" >&5
-echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <$ac_header>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  ac_header_preproc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  ac_header_preproc=no
-fi
-rm -f conftest.err conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
-echo "${ECHO_T}$ac_header_preproc" >&6
-
-# So?  What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
-  yes:no: )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
-echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
-    ac_header_preproc=yes
-    ;;
-  no:yes:* )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
-echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
-echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
-echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
-echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
-echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
-    (
-      cat <<\_ASBOX
-## ---------------------------------------------------------- ##
-## Report this to smartmontools-support@lists.sourceforge.net ##
-## ---------------------------------------------------------- ##
-_ASBOX
-    ) |
-      sed "s/^/$as_me: WARNING:     /" >&2
-    ;;
-esac
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-
-fi
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-for ac_header in sys/inttypes.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-else
-  # Is the header compilable?
-echo "$as_me:$LINENO: checking $ac_header usability" >&5
-echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_header_compiler=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_header_compiler=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
-echo "${ECHO_T}$ac_header_compiler" >&6
-
-# Is the header present?
-echo "$as_me:$LINENO: checking $ac_header presence" >&5
-echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <$ac_header>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  ac_header_preproc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  ac_header_preproc=no
-fi
-rm -f conftest.err conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
-echo "${ECHO_T}$ac_header_preproc" >&6
-
-# So?  What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
-  yes:no: )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
-echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
-    ac_header_preproc=yes
-    ;;
-  no:yes:* )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
-echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
-echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
-echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
-echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
-echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
-    (
-      cat <<\_ASBOX
-## ---------------------------------------------------------- ##
-## Report this to smartmontools-support@lists.sourceforge.net ##
-## ---------------------------------------------------------- ##
-_ASBOX
-    ) |
-      sed "s/^/$as_me: WARNING:     /" >&2
-    ;;
-esac
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-
-fi
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-for ac_header in sys/int_types.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-else
-  # Is the header compilable?
-echo "$as_me:$LINENO: checking $ac_header usability" >&5
-echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_header_compiler=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_header_compiler=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
-echo "${ECHO_T}$ac_header_compiler" >&6
-
-# Is the header present?
-echo "$as_me:$LINENO: checking $ac_header presence" >&5
-echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <$ac_header>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  ac_header_preproc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  ac_header_preproc=no
-fi
-rm -f conftest.err conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
-echo "${ECHO_T}$ac_header_preproc" >&6
-
-# So?  What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
-  yes:no: )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
-echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
-    ac_header_preproc=yes
-    ;;
-  no:yes:* )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
-echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
-echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
-echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
-echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
-echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
-    (
-      cat <<\_ASBOX
-## ---------------------------------------------------------- ##
-## Report this to smartmontools-support@lists.sourceforge.net ##
-## ---------------------------------------------------------- ##
-_ASBOX
-    ) |
-      sed "s/^/$as_me: WARNING:     /" >&2
-    ;;
-esac
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-
-fi
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-for ac_header in sys/tweio.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-else
-  # Is the header compilable?
-echo "$as_me:$LINENO: checking $ac_header usability" >&5
-echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_header_compiler=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_header_compiler=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
-echo "${ECHO_T}$ac_header_compiler" >&6
-
-# Is the header present?
-echo "$as_me:$LINENO: checking $ac_header presence" >&5
-echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <$ac_header>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  ac_header_preproc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  ac_header_preproc=no
-fi
-rm -f conftest.err conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
-echo "${ECHO_T}$ac_header_preproc" >&6
-
-# So?  What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
-  yes:no: )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
-echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
-    ac_header_preproc=yes
-    ;;
-  no:yes:* )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
-echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
-echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
-echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
-echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
-echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
-    (
-      cat <<\_ASBOX
-## ---------------------------------------------------------- ##
-## Report this to smartmontools-support@lists.sourceforge.net ##
-## ---------------------------------------------------------- ##
-_ASBOX
-    ) |
-      sed "s/^/$as_me: WARNING:     /" >&2
-    ;;
-esac
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-
-fi
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-
-for ac_header in sys/twereg.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-else
-  # Is the header compilable?
-echo "$as_me:$LINENO: checking $ac_header usability" >&5
-echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_header_compiler=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_header_compiler=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
-echo "${ECHO_T}$ac_header_compiler" >&6
-
-# Is the header present?
-echo "$as_me:$LINENO: checking $ac_header presence" >&5
-echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <$ac_header>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  ac_header_preproc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  ac_header_preproc=no
-fi
-rm -f conftest.err conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
-echo "${ECHO_T}$ac_header_preproc" >&6
-
-# So?  What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
-  yes:no: )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
-echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
-    ac_header_preproc=yes
-    ;;
-  no:yes:* )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
-echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
-echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
-echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
-echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
-echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
-    (
-      cat <<\_ASBOX
-## ---------------------------------------------------------- ##
-## Report this to smartmontools-support@lists.sourceforge.net ##
-## ---------------------------------------------------------- ##
-_ASBOX
-    ) |
-      sed "s/^/$as_me: WARNING:     /" >&2
-    ;;
-esac
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-
-fi
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-
-for ac_header in sys/tw_osl_ioctl.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-else
-  # Is the header compilable?
-echo "$as_me:$LINENO: checking $ac_header usability" >&5
-echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_header_compiler=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_header_compiler=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
-echo "${ECHO_T}$ac_header_compiler" >&6
-
-# Is the header present?
-echo "$as_me:$LINENO: checking $ac_header presence" >&5
-echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <$ac_header>
-_ACEOF
-if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
-  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } >/dev/null; then
-  if test -s conftest.err; then
-    ac_cpp_err=$ac_c_preproc_warn_flag
-    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
-  else
-    ac_cpp_err=
-  fi
-else
-  ac_cpp_err=yes
-fi
-if test -z "$ac_cpp_err"; then
-  ac_header_preproc=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-  ac_header_preproc=no
-fi
-rm -f conftest.err conftest.$ac_ext
-echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
-echo "${ECHO_T}$ac_header_preproc" >&6
-
-# So?  What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
-  yes:no: )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
-echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
-    ac_header_preproc=yes
-    ;;
-  no:yes:* )
-    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
-echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
-echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
-echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
-echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
-echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
-    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
-echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
-    (
-      cat <<\_ASBOX
-## ---------------------------------------------------------- ##
-## Report this to smartmontools-support@lists.sourceforge.net ##
-## ---------------------------------------------------------- ##
-_ASBOX
-    ) |
-      sed "s/^/$as_me: WARNING:     /" >&2
-    ;;
-esac
-echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
-if eval "test \"\${$as_ac_Header+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
-
-fi
-if test `eval echo '${'$as_ac_Header'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-
-done
-
-
-echo "$as_me:$LINENO: checking for int64_t" >&5
-echo $ECHO_N "checking for int64_t... $ECHO_C" >&6
-if test "${ac_cv_type_int64_t+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-int
-main ()
-{
-if ((int64_t *) 0)
-  return 0;
-if (sizeof (int64_t))
-  return 0;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_cv_type_int64_t=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_cv_type_int64_t=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: $ac_cv_type_int64_t" >&5
-echo "${ECHO_T}$ac_cv_type_int64_t" >&6
-if test $ac_cv_type_int64_t = yes; then
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_INT64_T 1
-_ACEOF
-
-
-fi
-echo "$as_me:$LINENO: checking for uint64_t" >&5
-echo $ECHO_N "checking for uint64_t... $ECHO_C" >&6
-if test "${ac_cv_type_uint64_t+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-$ac_includes_default
-int
-main ()
-{
-if ((uint64_t *) 0)
-  return 0;
-if (sizeof (uint64_t))
-  return 0;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  ac_cv_type_uint64_t=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-ac_cv_type_uint64_t=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: $ac_cv_type_uint64_t" >&5
-echo "${ECHO_T}$ac_cv_type_uint64_t" >&6
-if test $ac_cv_type_uint64_t = yes; then
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_UINT64_T 1
-_ACEOF
-
-
-fi
-
-
-
-for ac_func in getopt
-do
-as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
-echo "$as_me:$LINENO: checking for $ac_func" >&5
-echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
-if eval "test \"\${$as_ac_var+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
-   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
-#define $ac_func innocuous_$ac_func
-
-/* System header to define __stub macros and hopefully few prototypes,
-    which can conflict with char $ac_func (); below.
-    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-    <limits.h> exists even on freestanding compilers.  */
-
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
-
-#undef $ac_func
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char $ac_func ();
-/* 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_$ac_func) || defined (__stub___$ac_func)
-choke me
-#else
-char (*f) () = $ac_func;
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-int
-main ()
-{
-return f != $ac_func;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  eval "$as_ac_var=yes"
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-eval "$as_ac_var=no"
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
-if test `eval echo '${'$as_ac_var'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-done
-
-
-for ac_func in getopt_long
-do
-as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
-echo "$as_me:$LINENO: checking for $ac_func" >&5
-echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
-if eval "test \"\${$as_ac_var+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
-   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
-#define $ac_func innocuous_$ac_func
-
-/* System header to define __stub macros and hopefully few prototypes,
-    which can conflict with char $ac_func (); below.
-    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-    <limits.h> exists even on freestanding compilers.  */
-
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
-
-#undef $ac_func
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char $ac_func ();
-/* 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_$ac_func) || defined (__stub___$ac_func)
-choke me
-#else
-char (*f) () = $ac_func;
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-int
-main ()
-{
-return f != $ac_func;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  eval "$as_ac_var=yes"
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-eval "$as_ac_var=no"
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
-if test `eval echo '${'$as_ac_var'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-done
-
-
-for ac_func in getdomainname
-do
-as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
-echo "$as_me:$LINENO: checking for $ac_func" >&5
-echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
-if eval "test \"\${$as_ac_var+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
-   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
-#define $ac_func innocuous_$ac_func
-
-/* System header to define __stub macros and hopefully few prototypes,
-    which can conflict with char $ac_func (); below.
-    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-    <limits.h> exists even on freestanding compilers.  */
-
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
-
-#undef $ac_func
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char $ac_func ();
-/* 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_$ac_func) || defined (__stub___$ac_func)
-choke me
-#else
-char (*f) () = $ac_func;
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-int
-main ()
-{
-return f != $ac_func;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  eval "$as_ac_var=yes"
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-eval "$as_ac_var=no"
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
-if test `eval echo '${'$as_ac_var'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-done
-
-
-for ac_func in gethostname
-do
-as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
-echo "$as_me:$LINENO: checking for $ac_func" >&5
-echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
-if eval "test \"\${$as_ac_var+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
-   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
-#define $ac_func innocuous_$ac_func
-
-/* System header to define __stub macros and hopefully few prototypes,
-    which can conflict with char $ac_func (); below.
-    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-    <limits.h> exists even on freestanding compilers.  */
-
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
-
-#undef $ac_func
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char $ac_func ();
-/* 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_$ac_func) || defined (__stub___$ac_func)
-choke me
-#else
-char (*f) () = $ac_func;
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-int
-main ()
-{
-return f != $ac_func;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  eval "$as_ac_var=yes"
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-eval "$as_ac_var=no"
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
-if test `eval echo '${'$as_ac_var'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-done
-
-
-for ac_func in gethostbyname
-do
-as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
-echo "$as_me:$LINENO: checking for $ac_func" >&5
-echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
-if eval "test \"\${$as_ac_var+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
-   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
-#define $ac_func innocuous_$ac_func
-
-/* System header to define __stub macros and hopefully few prototypes,
-    which can conflict with char $ac_func (); below.
-    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-    <limits.h> exists even on freestanding compilers.  */
-
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
-
-#undef $ac_func
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char $ac_func ();
-/* 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_$ac_func) || defined (__stub___$ac_func)
-choke me
-#else
-char (*f) () = $ac_func;
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-int
-main ()
-{
-return f != $ac_func;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  eval "$as_ac_var=yes"
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-eval "$as_ac_var=no"
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
-if test `eval echo '${'$as_ac_var'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-done
-
-
-for ac_func in sigset
-do
-as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
-echo "$as_me:$LINENO: checking for $ac_func" >&5
-echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
-if eval "test \"\${$as_ac_var+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
-   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
-#define $ac_func innocuous_$ac_func
-
-/* System header to define __stub macros and hopefully few prototypes,
-    which can conflict with char $ac_func (); below.
-    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-    <limits.h> exists even on freestanding compilers.  */
-
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
-
-#undef $ac_func
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char $ac_func ();
-/* 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_$ac_func) || defined (__stub___$ac_func)
-choke me
-#else
-char (*f) () = $ac_func;
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-int
-main ()
-{
-return f != $ac_func;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  eval "$as_ac_var=yes"
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-eval "$as_ac_var=no"
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
-if test `eval echo '${'$as_ac_var'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-done
-
-
-for ac_func in strtoull
-do
-as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
-echo "$as_me:$LINENO: checking for $ac_func" >&5
-echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
-if eval "test \"\${$as_ac_var+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
-   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
-#define $ac_func innocuous_$ac_func
-
-/* System header to define __stub macros and hopefully few prototypes,
-    which can conflict with char $ac_func (); below.
-    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-    <limits.h> exists even on freestanding compilers.  */
-
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
-
-#undef $ac_func
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char $ac_func ();
-/* 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_$ac_func) || defined (__stub___$ac_func)
-choke me
-#else
-char (*f) () = $ac_func;
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-int
-main ()
-{
-return f != $ac_func;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  eval "$as_ac_var=yes"
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-eval "$as_ac_var=no"
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
-if test `eval echo '${'$as_ac_var'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-done
-
-
-for ac_func in uname
-do
-as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
-echo "$as_me:$LINENO: checking for $ac_func" >&5
-echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
-if eval "test \"\${$as_ac_var+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
-   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
-#define $ac_func innocuous_$ac_func
-
-/* System header to define __stub macros and hopefully few prototypes,
-    which can conflict with char $ac_func (); below.
-    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
-    <limits.h> exists even on freestanding compilers.  */
-
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
-
-#undef $ac_func
-
-/* Override any gcc2 internal prototype to avoid an error.  */
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-/* We use char because int might match the return type of a gcc2
-   builtin and then its argument prototype would still apply.  */
-char $ac_func ();
-/* 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_$ac_func) || defined (__stub___$ac_func)
-choke me
-#else
-char (*f) () = $ac_func;
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-int
-main ()
-{
-return f != $ac_func;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  eval "$as_ac_var=yes"
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-eval "$as_ac_var=no"
-fi
-rm -f conftest.err conftest.$ac_objext \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
-echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
-if test `eval echo '${'$as_ac_var'}'` = yes; then
-  cat >>confdefs.h <<_ACEOF
-#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
-_ACEOF
-
-fi
-done
-
-
-# Check whether snprintf appends null char and returns expected length on overflow
-
-
-echo "$as_me:$LINENO: checking for working snprintf" >&5
-echo $ECHO_N "checking for working snprintf... $ECHO_C" >&6
-if test "$cross_compiling" = yes; then
-  { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling
-See \`config.log' for more details." >&5
-echo "$as_me: error: cannot run test program while cross compiling
-See \`config.log' for more details." >&2;}
-   { (exit 1); exit 1; }; }
-else
-  cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-#include <stdio.h>
-int
-main ()
-{
- char buf[]="ABCDEFGHI";
-               int i=snprintf(buf,8,"12345678"); return !(!buf[7] && i==8);
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest$ac_exeext
-if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
-  (eval $ac_link) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  libc_have_working_snprintf=yes
-else
-  echo "$as_me: program exited with status $ac_status" >&5
-echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-( exit $ac_status )
-libc_have_working_snprintf=no
-fi
-rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
-fi
-
-if test "$libc_have_working_snprintf" = "yes"; then
-  cat >>confdefs.h <<\_ACEOF
-#define HAVE_WORKING_SNPRINTF 1
-_ACEOF
-
-fi
-echo "$as_me:$LINENO: result: $libc_have_working_snprintf" >&5
-echo "${ECHO_T}$libc_have_working_snprintf" >&6
-
-# check for __attribute__((packed))
-
-
-echo "$as_me:$LINENO: checking whether C compiler supports __attribute__((packed))" >&5
-echo $ECHO_N "checking whether C compiler supports __attribute__((packed))... $ECHO_C" >&6
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-
-int
-main ()
-{
-struct a { int b; } __attribute__((packed));
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext
-if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
-  (eval $ac_compile) 2>conftest.er1
-  ac_status=$?
-  grep -v '^ *+' conftest.er1 >conftest.err
-  rm -f conftest.er1
-  cat conftest.err >&5
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } &&
-        { ac_try='test -z "$ac_c_werror_flag"
-                        || test ! -s conftest.err'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; } &&
-        { ac_try='test -s conftest.$ac_objext'
-  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
-  (eval $ac_try) 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; }; then
-  gcc_have_attr_packed=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-gcc_have_attr_packed=no
-fi
-rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
-
-if test "$gcc_have_attr_packed" = "yes"; then
-  cat >>confdefs.h <<\_ACEOF
-#define HAVE_ATTR_PACKED 1
-_ACEOF
-
-fi
-echo "$as_me:$LINENO: result: $gcc_have_attr_packed" >&5
-echo "${ECHO_T}$gcc_have_attr_packed" >&6
-
-
-
-
-
-exampledir='${docdir}/examplescripts'
-
-
-
-# Check whether --with-initscriptdir or --without-initscriptdir was given.
-if test "${with_initscriptdir+set}" = set; then
-  withval="$with_initscriptdir"
-  initddir="$withval"
-else
-  initddir='${sysconfdir}/rc.d/init.d'
-fi;
-
-
-
-# Check whether --with-docdir or --without-docdir was given.
-if test "${with_docdir+set}" = set; then
-  withval="$with_docdir"
-  docdir="$withval"
-else
-  docdir='${prefix}/share/doc/${PACKAGE}-${VERSION}'
-fi;
-
-
-# Check whether --enable-sample or --disable-sample was given.
-if test "${enable_sample+set}" = set; then
-  enableval="$enable_sample"
-  smartd_suffix='.sample'
-else
-  smartd_suffix=''
-fi;
-
-
-
-if test $smartd_suffix; then
-  SMARTD_SUFFIX_TRUE=
-  SMARTD_SUFFIX_FALSE='#'
-else
-  SMARTD_SUFFIX_TRUE='#'
-  SMARTD_SUFFIX_FALSE=
-fi
-
-
-if test "$prefix" = "NONE"; then
-        if test "$mandir" = '${prefix}/man'; then
-       mandir='${prefix}/share/man'
-
-    fi
-fi
-
-releaseversion='${PACKAGE}-${VERSION}'
-
-
-
-
-case "${host}" in
-       *-*-linux-gnu*)
-               os_deps='os_linux.o'
-
-               os_libs=''
- ;;
-       *-*-linux*)
-               os_deps='os_linux.o'
-
-               os_libs=''
- ;;
-       *-*-freebsd*)
-               os_deps='os_freebsd.o'
-
-               os_libs='-lcam'
-;;
-       sparc-*-solaris*)
-
-cat >>confdefs.h <<_ACEOF
-#define DEFAULT_MAILER "mailx"
-_ACEOF
-
-
-cat >>confdefs.h <<_ACEOF
-#define NEED_SOLARIS_ATA_CODE "os_solaris_ata.s"
-_ACEOF
-
-               os_deps='os_solaris.o os_solaris_ata.o'
-
-               os_libs=''
- ;;
-       *-pc-solaris*)
-
-cat >>confdefs.h <<_ACEOF
-#define DEFAULT_MAILER "mailx"
-_ACEOF
-
-               os_deps='os_solaris.o'
-
-               os_libs=''
- ;;
-       *-*-netbsd*)
-               os_deps='os_netbsd.o'
-
-               os_libs='-lutil'
- ;;
-       *-*-openbsd*)
-               os_deps='os_openbsd.o'
-
-               os_libs='-lutil'
- ;;
-       *-*-cygwin*)
-               os_deps='os_win32.o'
-
-               os_libs=''
- ;;
-       *-*-mingw*)
-               os_deps='os_win32.o'
-
-               os_libs=''
- ;;
-       *-*-darwin*)
-               os_deps='os_darwin.o'
-
-               os_libs='-framework CoreFoundation -framework IOKit'
- ;;
-       *)
-               os_deps='os_generic.o'
-
-               os_libs=''
- ;;
-esac
-
-# Define symbols for optional functions in OS specific module
-case "${os_deps}" in
-  os_win32*)
-
-cat >>confdefs.h <<\_ACEOF
-#define HAVE_ATA_IDENTIFY_IS_CACHED 1
-_ACEOF
- ;;
-esac
-case "${os_deps}" in
-  os_win32*)
-
-cat >>confdefs.h <<\_ACEOF
-#define HAVE_GET_OS_VERSION_STR 1
-_ACEOF
- ;;
-esac
-
-
-
-if echo $host_os | grep '^darwin' > /dev/null; then
-  OS_DARWIN_TRUE=
-  OS_DARWIN_FALSE='#'
-else
-  OS_DARWIN_TRUE='#'
-  OS_DARWIN_FALSE=
-fi
-
-
-
-if echo $host_os | grep '^solaris' > /dev/null; then
-  OS_SOLARIS_TRUE=
-  OS_SOLARIS_FALSE='#'
-else
-  OS_SOLARIS_TRUE='#'
-  OS_SOLARIS_FALSE=
-fi
-
-
-
-if echo $host_os | grep '^mingw' > /dev/null; then
-  OS_WIN32_MINGW_TRUE=
-  OS_WIN32_MINGW_FALSE='#'
-else
-  OS_WIN32_MINGW_TRUE='#'
-  OS_WIN32_MINGW_FALSE=
-fi
-
-
-if test "x$GCC" = "xyes"; then
-  if test -z "`echo "$CFLAGS" | grep "\-Wall" 2> /dev/null`" ; then
-      CFLAGS="$CFLAGS -Wall"
-  fi
-# In the next line, do NOT delete the 2 spaces inside double quotes.
-  if test -z "`echo "$CFLAGS " | grep "\-W " 2> /dev/null`" ; then
-      CFLAGS="$CFLAGS -W"
-  fi
-  case "${host}" in
-    *-*-mingw*)
-      # MinGW uses MSVCRT.DLL which uses printf format "%I64d" and not "%lld" for int64_t
-      CFLAGS="$CFLAGS -Wno-format";;
-  esac
-else
-  case "${host}" in
-       *-*-solaris*)
-                    if test -z "`echo "$CFLAGS" | grep "\-xmemalign" 2> /dev/null`" ; then
-                        CFLAGS="-xmemalign=1i $CFLAGS"
-          fi
-          if test -z "`echo "$CFLAGS" | grep "\-xCC" 2> /dev/null`" ; then
-                        CFLAGS="-xCC $CFLAGS"
-          fi
-          if test -z "`echo "$CFLAGS" | grep "\-xO" 2> /dev/null`" ; then
-                        CFLAGS="-xO2 $CFLAGS"
-          fi
- esac
-fi
-
-
-cat >>confdefs.h <<_ACEOF
-#define SMARTMONTOOLS_BUILD_HOST "${host}"
-_ACEOF
-
-
-
-
-                    ac_config_files="$ac_config_files Makefile examplescripts/Makefile"
-cat >confcache <<\_ACEOF
-# 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, see configure's option --config-cache.
-# It is not useful on other systems.  If it contains results you don't
-# want to keep, you may remove or edit it.
-#
-# config.status only pays attention to the cache file if you give it
-# the --recheck option to rerun configure.
-#
-# `ac_cv_env_foo' variables (set or unset) will be overridden when
-# loading this file, other *unset* `ac_cv_foo' will be assigned the
-# following values.
-
-_ACEOF
-
-# 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 \
-       "s/'/'\\\\''/g;
-         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
-      ;;
-    *)
-      # `set' quotes correctly as required by POSIX, so do not add quotes.
-      sed -n \
-       "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
-      ;;
-    esac;
-} |
-  sed '
-     t clear
-     : clear
-     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
-     t end
-     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
-     : end' >>confcache
-if diff $cache_file confcache >/dev/null 2>&1; then :; else
-  if test -w $cache_file; then
-    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
-    cat confcache >$cache_file
-  else
-    echo "not updating unwritable cache $cache_file"
-  fi
-fi
-rm -f confcache
-
-test "x$prefix" = xNONE && prefix=$ac_default_prefix
-# Let make expand exec_prefix.
-test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
-
-# VPATH may cause trouble with some makes, so we remove $(srcdir),
-# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
-# trailing colons and then remove the whole line if VPATH becomes empty
-# (actually we leave an empty line to preserve line numbers).
-if test "x$srcdir" = x.; then
-  ac_vpsub='/^[         ]*VPATH[        ]*=/{
-s/:*\$(srcdir):*/:/;
-s/:*\${srcdir}:*/:/;
-s/:*@srcdir@:*/:/;
-s/^\([^=]*=[    ]*\):*/\1/;
-s/:*$//;
-s/^[^=]*=[      ]*$//;
-}'
-fi
-
-DEFS=-DHAVE_CONFIG_H
-
-ac_libobjs=
-ac_ltlibobjs=
-for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
-  # 1. Remove the extension, and $U if already installed.
-  ac_i=`echo "$ac_i" |
-        sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
-  # 2. Add them.
-  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
-  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
-done
-LIBOBJS=$ac_libobjs
-
-LTLIBOBJS=$ac_ltlibobjs
-
-
-if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
-  { { echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined.
-Usually this means the macro was only invoked conditionally." >&5
-echo "$as_me: error: conditional \"MAINTAINER_MODE\" was never defined.
-Usually this means the macro was only invoked conditionally." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
-  { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined.
-Usually this means the macro was only invoked conditionally." >&5
-echo "$as_me: error: conditional \"AMDEP\" was never defined.
-Usually this means the macro was only invoked conditionally." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
-  { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined.
-Usually this means the macro was only invoked conditionally." >&5
-echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
-Usually this means the macro was only invoked conditionally." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-if test -z "${SMARTD_SUFFIX_TRUE}" && test -z "${SMARTD_SUFFIX_FALSE}"; then
-  { { echo "$as_me:$LINENO: error: conditional \"SMARTD_SUFFIX\" was never defined.
-Usually this means the macro was only invoked conditionally." >&5
-echo "$as_me: error: conditional \"SMARTD_SUFFIX\" was never defined.
-Usually this means the macro was only invoked conditionally." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-if test -z "${OS_DARWIN_TRUE}" && test -z "${OS_DARWIN_FALSE}"; then
-  { { echo "$as_me:$LINENO: error: conditional \"OS_DARWIN\" was never defined.
-Usually this means the macro was only invoked conditionally." >&5
-echo "$as_me: error: conditional \"OS_DARWIN\" was never defined.
-Usually this means the macro was only invoked conditionally." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-if test -z "${OS_SOLARIS_TRUE}" && test -z "${OS_SOLARIS_FALSE}"; then
-  { { echo "$as_me:$LINENO: error: conditional \"OS_SOLARIS\" was never defined.
-Usually this means the macro was only invoked conditionally." >&5
-echo "$as_me: error: conditional \"OS_SOLARIS\" was never defined.
-Usually this means the macro was only invoked conditionally." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-if test -z "${OS_WIN32_MINGW_TRUE}" && test -z "${OS_WIN32_MINGW_FALSE}"; then
-  { { echo "$as_me:$LINENO: error: conditional \"OS_WIN32_MINGW\" was never defined.
-Usually this means the macro was only invoked conditionally." >&5
-echo "$as_me: error: conditional \"OS_WIN32_MINGW\" was never defined.
-Usually this means the macro was only invoked conditionally." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-
-: ${CONFIG_STATUS=./config.status}
-ac_clean_files_save=$ac_clean_files
-ac_clean_files="$ac_clean_files $CONFIG_STATUS"
-{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
-echo "$as_me: creating $CONFIG_STATUS" >&6;}
-cat >$CONFIG_STATUS <<_ACEOF
-#! $SHELL
-# Generated by $as_me.
-# Run this file to recreate the current configuration.
-# Compiler output produced by configure, useful for debugging
-# configure, is in config.log if it exists.
-
-debug=false
-ac_cs_recheck=false
-ac_cs_silent=false
-SHELL=\${CONFIG_SHELL-$SHELL}
-_ACEOF
-
-cat >>$CONFIG_STATUS <<\_ACEOF
-## --------------------- ##
-## M4sh Initialization.  ##
-## --------------------- ##
-
-# Be Bourne compatible
-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
-  emulate sh
-  NULLCMD=:
-  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
-  # is contrary to our usage.  Disable this feature.
-  alias -g '${1+"$@"}'='"$@"'
-elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
-  set -o posix
-fi
-DUALCASE=1; export DUALCASE # for MKS sh
-
-# Support unset when possible.
-if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
-  as_unset=unset
-else
-  as_unset=false
-fi
-
-
-# Work around bugs in pre-3.0 UWIN ksh.
-$as_unset ENV MAIL MAILPATH
-PS1='$ '
-PS2='> '
-PS4='+ '
-
-# NLS nuisances.
-for as_var in \
-  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
-  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
-  LC_TELEPHONE LC_TIME
-do
-  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
-    eval $as_var=C; export $as_var
-  else
-    $as_unset $as_var
-  fi
-done
-
-# Required to use basename.
-if expr a : '\(a\)' >/dev/null 2>&1; then
-  as_expr=expr
-else
-  as_expr=false
-fi
-
-if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
-  as_basename=basename
-else
-  as_basename=false
-fi
-
-
-# Name of the executable.
-as_me=`$as_basename "$0" ||
-$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
-        X"$0" : 'X\(//\)$' \| \
-        X"$0" : 'X\(/\)$' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X/"$0" |
-    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
-         /^X\/\(\/\/\)$/{ s//\1/; q; }
-         /^X\/\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-
-
-# PATH needs CR, and LINENO needs CR and PATH.
-# Avoid depending upon Character Ranges.
-as_cr_letters='abcdefghijklmnopqrstuvwxyz'
-as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
-as_cr_Letters=$as_cr_letters$as_cr_LETTERS
-as_cr_digits='0123456789'
-as_cr_alnum=$as_cr_Letters$as_cr_digits
-
-# The user is always right.
-if test "${PATH_SEPARATOR+set}" != set; then
-  echo "#! /bin/sh" >conf$$.sh
-  echo  "exit 0"   >>conf$$.sh
-  chmod +x conf$$.sh
-  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
-    PATH_SEPARATOR=';'
-  else
-    PATH_SEPARATOR=:
-  fi
-  rm -f conf$$.sh
-fi
-
-
-  as_lineno_1=$LINENO
-  as_lineno_2=$LINENO
-  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
-  test "x$as_lineno_1" != "x$as_lineno_2" &&
-  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
-  # Find who we are.  Look in the path if we contain no path at all
-  # relative or not.
-  case $0 in
-    *[\\/]* ) as_myself=$0 ;;
-    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
-done
-
-       ;;
-  esac
-  # We did not find ourselves, most probably we were run as `sh COMMAND'
-  # in which case we are not to be found in the path.
-  if test "x$as_myself" = x; then
-    as_myself=$0
-  fi
-  if test ! -f "$as_myself"; then
-    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
-echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
-   { (exit 1); exit 1; }; }
-  fi
-  case $CONFIG_SHELL in
-  '')
-    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for as_base in sh bash ksh sh5; do
-        case $as_dir in
-        /*)
-          if ("$as_dir/$as_base" -c '
-  as_lineno_1=$LINENO
-  as_lineno_2=$LINENO
-  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
-  test "x$as_lineno_1" != "x$as_lineno_2" &&
-  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
-            $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
-            $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
-            CONFIG_SHELL=$as_dir/$as_base
-            export CONFIG_SHELL
-            exec "$CONFIG_SHELL" "$0" ${1+"$@"}
-          fi;;
-        esac
-       done
-done
-;;
-  esac
-
-  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
-  # uniformly replaced by the line number.  The first 'sed' inserts a
-  # line-number line before each line; the second 'sed' does the real
-  # work.  The second script uses 'N' to pair each line-number line
-  # with the numbered line, and appends trailing '-' during
-  # substitution so that $LINENO is not a special case at line end.
-  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
-  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
-  sed '=' <$as_myself |
-    sed '
-      N
-      s,$,-,
-      : loop
-      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
-      t loop
-      s,-$,,
-      s,^['$as_cr_digits']*\n,,
-    ' >$as_me.lineno &&
-  chmod +x $as_me.lineno ||
-    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
-echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
-   { (exit 1); exit 1; }; }
-
-  # Don't try to exec as it changes $[0], causing all sort of problems
-  # (the dirname of $[0] is not the place where we might find the
-  # original and so on.  Autoconf is especially sensible to this).
-  . ./$as_me.lineno
-  # Exit status is that of the last command.
-  exit
-}
-
-
-case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
-  *c*,-n*) ECHO_N= ECHO_C='
-' ECHO_T='     ' ;;
-  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
-  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
-esac
-
-if expr a : '\(a\)' >/dev/null 2>&1; then
-  as_expr=expr
-else
-  as_expr=false
-fi
-
-rm -f conf$$ conf$$.exe conf$$.file
-echo >conf$$.file
-if ln -s conf$$.file conf$$ 2>/dev/null; then
-  # We could just check for DJGPP; but this test a) works b) is more generic
-  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
-  if test -f conf$$.exe; then
-    # Don't use ln at all; we don't have any links
-    as_ln_s='cp -p'
-  else
-    as_ln_s='ln -s'
-  fi
-elif ln conf$$.file conf$$ 2>/dev/null; then
-  as_ln_s=ln
-else
-  as_ln_s='cp -p'
-fi
-rm -f conf$$ conf$$.exe conf$$.file
-
-if mkdir -p . 2>/dev/null; then
-  as_mkdir_p=:
-else
-  test -d ./-p && rmdir ./-p
-  as_mkdir_p=false
-fi
-
-as_executable_p="test -f"
-
-# Sed expression to map a string onto a valid CPP name.
-as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
-
-# Sed expression to map a string onto a valid variable name.
-as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
-
-
-# IFS
-# We need space, tab and new line, in precisely that order.
-as_nl='
-'
-IFS="  $as_nl"
-
-# CDPATH.
-$as_unset CDPATH
-
-exec 6>&1
-
-# Open the log real soon, to keep \$[0] and so on meaningful, and to
-# report actual input values of CONFIG_FILES etc. instead of their
-# values after options handling.  Logging --version etc. is OK.
-exec 5>>config.log
-{
-  echo
-  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
-## Running $as_me. ##
-_ASBOX
-} >&5
-cat >&5 <<_CSEOF
-
-This file was extended by smartmontools $as_me 5.36, which was
-generated by GNU Autoconf 2.59.  Invocation command line was
-
-  CONFIG_FILES    = $CONFIG_FILES
-  CONFIG_HEADERS  = $CONFIG_HEADERS
-  CONFIG_LINKS    = $CONFIG_LINKS
-  CONFIG_COMMANDS = $CONFIG_COMMANDS
-  $ $0 $@
-
-_CSEOF
-echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
-echo >&5
-_ACEOF
-
-# Files that config.status was made for.
-if test -n "$ac_config_files"; then
-  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
-fi
-
-if test -n "$ac_config_headers"; then
-  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
-fi
-
-if test -n "$ac_config_links"; then
-  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
-fi
-
-if test -n "$ac_config_commands"; then
-  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
-fi
-
-cat >>$CONFIG_STATUS <<\_ACEOF
-
-ac_cs_usage="\
-\`$as_me' instantiates files from templates according to the
-current configuration.
-
-Usage: $0 [OPTIONS] [FILE]...
-
-  -h, --help       print this help, then exit
-  -V, --version    print version number, then exit
-  -q, --quiet      do not print progress messages
-  -d, --debug      don't remove temporary files
-      --recheck    update $as_me by reconfiguring in the same conditions
-  --file=FILE[:TEMPLATE]
-                  instantiate the configuration file FILE
-  --header=FILE[:TEMPLATE]
-                  instantiate the configuration header FILE
-
-Configuration files:
-$config_files
-
-Configuration headers:
-$config_headers
-
-Configuration commands:
-$config_commands
-
-Report bugs to <bug-autoconf@gnu.org>."
-_ACEOF
-
-cat >>$CONFIG_STATUS <<_ACEOF
-ac_cs_version="\\
-smartmontools config.status 5.36
-configured by $0, generated by GNU Autoconf 2.59,
-  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
-
-Copyright (C) 2003 Free Software Foundation, Inc.
-This config.status script is free software; the Free Software Foundation
-gives unlimited permission to copy, distribute and modify it."
-srcdir=$srcdir
-INSTALL="$INSTALL"
-_ACEOF
-
-cat >>$CONFIG_STATUS <<\_ACEOF
-# If no file are specified by the user, then we need to provide default
-# value.  By we need to know if files were specified by the user.
-ac_need_defaults=:
-while test $# != 0
-do
-  case $1 in
-  --*=*)
-    ac_option=`expr "x$1" : 'x\([^=]*\)='`
-    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
-    ac_shift=:
-    ;;
-  -*)
-    ac_option=$1
-    ac_optarg=$2
-    ac_shift=shift
-    ;;
-  *) # This is not an option, so the user has probably given explicit
-     # arguments.
-     ac_option=$1
-     ac_need_defaults=false;;
-  esac
-
-  case $ac_option in
-  # Handling of the options.
-_ACEOF
-cat >>$CONFIG_STATUS <<\_ACEOF
-  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
-    ac_cs_recheck=: ;;
-  --version | --vers* | -V )
-    echo "$ac_cs_version"; exit 0 ;;
-  --he | --h)
-    # Conflict between --help and --header
-    { { echo "$as_me:$LINENO: error: ambiguous option: $1
-Try \`$0 --help' for more information." >&5
-echo "$as_me: error: ambiguous option: $1
-Try \`$0 --help' for more information." >&2;}
-   { (exit 1); exit 1; }; };;
-  --help | --hel | -h )
-    echo "$ac_cs_usage"; exit 0 ;;
-  --debug | --d* | -d )
-    debug=: ;;
-  --file | --fil | --fi | --f )
-    $ac_shift
-    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
-    ac_need_defaults=false;;
-  --header | --heade | --head | --hea )
-    $ac_shift
-    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
-    ac_need_defaults=false;;
-  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
-  | -silent | --silent | --silen | --sile | --sil | --si | --s)
-    ac_cs_silent=: ;;
-
-  # This is an error.
-  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
-Try \`$0 --help' for more information." >&5
-echo "$as_me: error: unrecognized option: $1
-Try \`$0 --help' for more information." >&2;}
-   { (exit 1); exit 1; }; } ;;
-
-  *) ac_config_targets="$ac_config_targets $1" ;;
-
-  esac
-  shift
-done
-
-ac_configure_extra_args=
-
-if $ac_cs_silent; then
-  exec 6>/dev/null
-  ac_configure_extra_args="$ac_configure_extra_args --silent"
-fi
-
-_ACEOF
-cat >>$CONFIG_STATUS <<_ACEOF
-if \$ac_cs_recheck; then
-  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
-  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
-fi
-
-_ACEOF
-
-cat >>$CONFIG_STATUS <<_ACEOF
-#
-# INIT-COMMANDS section.
-#
-
-AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
-
-_ACEOF
-
-
-
-cat >>$CONFIG_STATUS <<\_ACEOF
-for ac_config_target in $ac_config_targets
-do
-  case "$ac_config_target" in
-  # Handling of arguments.
-  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
-  "examplescripts/Makefile" ) CONFIG_FILES="$CONFIG_FILES examplescripts/Makefile" ;;
-  "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
-  "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
-  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
-echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
-   { (exit 1); exit 1; }; };;
-  esac
-done
-
-# If the user did not use the arguments to specify the items to instantiate,
-# then the envvar interface is used.  Set only those that are not.
-# We use the long form for the default assignment because of an extremely
-# bizarre bug on SunOS 4.1.3.
-if $ac_need_defaults; then
-  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
-  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
-  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
-fi
-
-# Have a temporary directory for convenience.  Make it in the build tree
-# simply because there is no reason to put it here, and in addition,
-# creating and moving files from /tmp can sometimes cause problems.
-# Create a temporary directory, and hook for its removal unless debugging.
-$debug ||
-{
-  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
-  trap '{ (exit 1); exit 1; }' 1 2 13 15
-}
-
-# Create a (secure) tmp directory for tmp files.
-
-{
-  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
-  test -n "$tmp" && test -d "$tmp"
-}  ||
-{
-  tmp=./confstat$$-$RANDOM
-  (umask 077 && mkdir $tmp)
-} ||
-{
-   echo "$me: cannot create a temporary directory in ." >&2
-   { (exit 1); exit 1; }
-}
-
-_ACEOF
-
-cat >>$CONFIG_STATUS <<_ACEOF
-
-#
-# CONFIG_FILES section.
-#
-
-# No need to generate the scripts if there are no CONFIG_FILES.
-# This happens for instance when ./config.status config.h
-if test -n "\$CONFIG_FILES"; then
-  # Protect against being on the right side of a sed subst in config.status.
-  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
-   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
-s,@SHELL@,$SHELL,;t t
-s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
-s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
-s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
-s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
-s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
-s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
-s,@exec_prefix@,$exec_prefix,;t t
-s,@prefix@,$prefix,;t t
-s,@program_transform_name@,$program_transform_name,;t t
-s,@bindir@,$bindir,;t t
-s,@sbindir@,$sbindir,;t t
-s,@libexecdir@,$libexecdir,;t t
-s,@datadir@,$datadir,;t t
-s,@sysconfdir@,$sysconfdir,;t t
-s,@sharedstatedir@,$sharedstatedir,;t t
-s,@localstatedir@,$localstatedir,;t t
-s,@libdir@,$libdir,;t t
-s,@includedir@,$includedir,;t t
-s,@oldincludedir@,$oldincludedir,;t t
-s,@infodir@,$infodir,;t t
-s,@mandir@,$mandir,;t t
-s,@build_alias@,$build_alias,;t t
-s,@host_alias@,$host_alias,;t t
-s,@target_alias@,$target_alias,;t t
-s,@DEFS@,$DEFS,;t t
-s,@ECHO_C@,$ECHO_C,;t t
-s,@ECHO_N@,$ECHO_N,;t t
-s,@ECHO_T@,$ECHO_T,;t t
-s,@LIBS@,$LIBS,;t t
-s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
-s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
-s,@INSTALL_DATA@,$INSTALL_DATA,;t t
-s,@CYGPATH_W@,$CYGPATH_W,;t t
-s,@PACKAGE@,$PACKAGE,;t t
-s,@VERSION@,$VERSION,;t t
-s,@ACLOCAL@,$ACLOCAL,;t t
-s,@AUTOCONF@,$AUTOCONF,;t t
-s,@AUTOMAKE@,$AUTOMAKE,;t t
-s,@AUTOHEADER@,$AUTOHEADER,;t t
-s,@MAKEINFO@,$MAKEINFO,;t t
-s,@install_sh@,$install_sh,;t t
-s,@STRIP@,$STRIP,;t t
-s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t
-s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t
-s,@mkdir_p@,$mkdir_p,;t t
-s,@AWK@,$AWK,;t t
-s,@SET_MAKE@,$SET_MAKE,;t t
-s,@am__leading_dot@,$am__leading_dot,;t t
-s,@AMTAR@,$AMTAR,;t t
-s,@am__tar@,$am__tar,;t t
-s,@am__untar@,$am__untar,;t t
-s,@MAINTAINER_MODE_TRUE@,$MAINTAINER_MODE_TRUE,;t t
-s,@MAINTAINER_MODE_FALSE@,$MAINTAINER_MODE_FALSE,;t t
-s,@MAINT@,$MAINT,;t t
-s,@CC@,$CC,;t t
-s,@CFLAGS@,$CFLAGS,;t t
-s,@LDFLAGS@,$LDFLAGS,;t t
-s,@CPPFLAGS@,$CPPFLAGS,;t t
-s,@ac_ct_CC@,$ac_ct_CC,;t t
-s,@EXEEXT@,$EXEEXT,;t t
-s,@OBJEXT@,$OBJEXT,;t t
-s,@DEPDIR@,$DEPDIR,;t t
-s,@am__include@,$am__include,;t t
-s,@am__quote@,$am__quote,;t t
-s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t
-s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t
-s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t
-s,@CCDEPMODE@,$CCDEPMODE,;t t
-s,@am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t
-s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t
-s,@CCAS@,$CCAS,;t t
-s,@CCASFLAGS@,$CCASFLAGS,;t t
-s,@build@,$build,;t t
-s,@build_cpu@,$build_cpu,;t t
-s,@build_vendor@,$build_vendor,;t t
-s,@build_os@,$build_os,;t t
-s,@host@,$host,;t t
-s,@host_cpu@,$host_cpu,;t t
-s,@host_vendor@,$host_vendor,;t t
-s,@host_os@,$host_os,;t t
-s,@CPP@,$CPP,;t t
-s,@EGREP@,$EGREP,;t t
-s,@libc_have_working_snprintf@,$libc_have_working_snprintf,;t t
-s,@gcc_have_attr_packed@,$gcc_have_attr_packed,;t t
-s,@ASFLAGS@,$ASFLAGS,;t t
-s,@exampledir@,$exampledir,;t t
-s,@initddir@,$initddir,;t t
-s,@docdir@,$docdir,;t t
-s,@smartd_suffix@,$smartd_suffix,;t t
-s,@SMARTD_SUFFIX_TRUE@,$SMARTD_SUFFIX_TRUE,;t t
-s,@SMARTD_SUFFIX_FALSE@,$SMARTD_SUFFIX_FALSE,;t t
-s,@releaseversion@,$releaseversion,;t t
-s,@smartmontools_release_date@,$smartmontools_release_date,;t t
-s,@smartmontools_release_time@,$smartmontools_release_time,;t t
-s,@os_deps@,$os_deps,;t t
-s,@os_libs@,$os_libs,;t t
-s,@OS_DARWIN_TRUE@,$OS_DARWIN_TRUE,;t t
-s,@OS_DARWIN_FALSE@,$OS_DARWIN_FALSE,;t t
-s,@OS_SOLARIS_TRUE@,$OS_SOLARIS_TRUE,;t t
-s,@OS_SOLARIS_FALSE@,$OS_SOLARIS_FALSE,;t t
-s,@OS_WIN32_MINGW_TRUE@,$OS_WIN32_MINGW_TRUE,;t t
-s,@OS_WIN32_MINGW_FALSE@,$OS_WIN32_MINGW_FALSE,;t t
-s,@LIBOBJS@,$LIBOBJS,;t t
-s,@LTLIBOBJS@,$LTLIBOBJS,;t t
-CEOF
-
-_ACEOF
-
-  cat >>$CONFIG_STATUS <<\_ACEOF
-  # 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_lines=48
-  ac_sed_frag=1 # Number of current file.
-  ac_beg=1 # First line for current file.
-  ac_end=$ac_max_sed_lines # 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" $tmp/subs.sed >$tmp/subs.frag
-    else
-      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
-    fi
-    if test ! -s $tmp/subs.frag; then
-      ac_more_lines=false
-    else
-      # The purpose of the label and of the branching condition is to
-      # speed up the sed processing (if there are no `@' at all, there
-      # is no need to browse any of the substitutions).
-      # These are the two extra sed commands mentioned above.
-      (echo ':t
-  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
-      if test -z "$ac_sed_cmds"; then
-       ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
-      else
-       ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
-      fi
-      ac_sed_frag=`expr $ac_sed_frag + 1`
-      ac_beg=$ac_end
-      ac_end=`expr $ac_end + $ac_max_sed_lines`
-    fi
-  done
-  if test -z "$ac_sed_cmds"; then
-    ac_sed_cmds=cat
-  fi
-fi # test -n "$CONFIG_FILES"
-
-_ACEOF
-cat >>$CONFIG_STATUS <<\_ACEOF
-for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
-  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
-  case $ac_file in
-  - | *:- | *:-:* ) # input from stdin
-       cat >$tmp/stdin
-       ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
-       ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
-  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
-       ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
-  * )   ac_file_in=$ac_file.in ;;
-  esac
-
-  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
-  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
-$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$ac_file" : 'X\(//\)[^/]' \| \
-        X"$ac_file" : 'X\(//\)$' \| \
-        X"$ac_file" : 'X\(/\)' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X"$ac_file" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
-         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
-         /^X\(\/\/\)$/{ s//\1/; q; }
-         /^X\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-  { if $as_mkdir_p; then
-    mkdir -p "$ac_dir"
-  else
-    as_dir="$ac_dir"
-    as_dirs=
-    while test ! -d "$as_dir"; do
-      as_dirs="$as_dir $as_dirs"
-      as_dir=`(dirname "$as_dir") 2>/dev/null ||
-$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$as_dir" : 'X\(//\)[^/]' \| \
-        X"$as_dir" : 'X\(//\)$' \| \
-        X"$as_dir" : 'X\(/\)' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X"$as_dir" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
-         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
-         /^X\(\/\/\)$/{ s//\1/; q; }
-         /^X\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-    done
-    test ! -n "$as_dirs" || mkdir $as_dirs
-  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
-echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
-   { (exit 1); exit 1; }; }; }
-
-  ac_builddir=.
-
-if test "$ac_dir" != .; then
-  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
-  # A "../" for each directory in $ac_dir_suffix.
-  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
-else
-  ac_dir_suffix= ac_top_builddir=
-fi
-
-case $srcdir in
-  .)  # No --srcdir option.  We are building in place.
-    ac_srcdir=.
-    if test -z "$ac_top_builddir"; then
-       ac_top_srcdir=.
-    else
-       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
-    fi ;;
-  [\\/]* | ?:[\\/]* )  # Absolute path.
-    ac_srcdir=$srcdir$ac_dir_suffix;
-    ac_top_srcdir=$srcdir ;;
-  *) # Relative path.
-    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
-    ac_top_srcdir=$ac_top_builddir$srcdir ;;
-esac
-
-# Do not use `cd foo && pwd` to compute absolute paths, because
-# the directories may not exist.
-case `pwd` in
-.) ac_abs_builddir="$ac_dir";;
-*)
-  case "$ac_dir" in
-  .) ac_abs_builddir=`pwd`;;
-  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
-  *) ac_abs_builddir=`pwd`/"$ac_dir";;
-  esac;;
-esac
-case $ac_abs_builddir in
-.) ac_abs_top_builddir=${ac_top_builddir}.;;
-*)
-  case ${ac_top_builddir}. in
-  .) ac_abs_top_builddir=$ac_abs_builddir;;
-  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
-  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
-  esac;;
-esac
-case $ac_abs_builddir in
-.) ac_abs_srcdir=$ac_srcdir;;
-*)
-  case $ac_srcdir in
-  .) ac_abs_srcdir=$ac_abs_builddir;;
-  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
-  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
-  esac;;
-esac
-case $ac_abs_builddir in
-.) ac_abs_top_srcdir=$ac_top_srcdir;;
-*)
-  case $ac_top_srcdir in
-  .) ac_abs_top_srcdir=$ac_abs_builddir;;
-  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
-  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
-  esac;;
-esac
-
-
-  case $INSTALL in
-  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
-  *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
-  esac
-
-  if test x"$ac_file" != x-; then
-    { echo "$as_me:$LINENO: creating $ac_file" >&5
-echo "$as_me: creating $ac_file" >&6;}
-    rm -f "$ac_file"
-  fi
-  # Let's still pretend it is `configure' which instantiates (i.e., don't
-  # use $as_me), people would be surprised to read:
-  #    /* config.h.  Generated by config.status.  */
-  if test x"$ac_file" = x-; then
-    configure_input=
-  else
-    configure_input="$ac_file.  "
-  fi
-  configure_input=$configure_input"Generated from `echo $ac_file_in |
-                                    sed 's,.*/,,'` by configure."
-
-  # First look for the input files in the build tree, otherwise in the
-  # src tree.
-  ac_file_inputs=`IFS=:
-    for f in $ac_file_in; do
-      case $f in
-      -) echo $tmp/stdin ;;
-      [\\/$]*)
-        # Absolute (can't be DOS-style, as IFS=:)
-        test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
-echo "$as_me: error: cannot find input file: $f" >&2;}
-   { (exit 1); exit 1; }; }
-        echo "$f";;
-      *) # Relative
-        if test -f "$f"; then
-          # Build tree
-          echo "$f"
-        elif test -f "$srcdir/$f"; then
-          # Source tree
-          echo "$srcdir/$f"
-        else
-          # /dev/null tree
-          { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
-echo "$as_me: error: cannot find input file: $f" >&2;}
-   { (exit 1); exit 1; }; }
-        fi;;
-      esac
-    done` || { (exit 1); exit 1; }
-_ACEOF
-cat >>$CONFIG_STATUS <<_ACEOF
-  sed "$ac_vpsub
-$extrasub
-_ACEOF
-cat >>$CONFIG_STATUS <<\_ACEOF
-:t
-/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
-s,@configure_input@,$configure_input,;t t
-s,@srcdir@,$ac_srcdir,;t t
-s,@abs_srcdir@,$ac_abs_srcdir,;t t
-s,@top_srcdir@,$ac_top_srcdir,;t t
-s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
-s,@builddir@,$ac_builddir,;t t
-s,@abs_builddir@,$ac_abs_builddir,;t t
-s,@top_builddir@,$ac_top_builddir,;t t
-s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
-s,@INSTALL@,$ac_INSTALL,;t t
-" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
-  rm -f $tmp/stdin
-  if test x"$ac_file" != x-; then
-    mv $tmp/out $ac_file
-  else
-    cat $tmp/out
-    rm -f $tmp/out
-  fi
-
-done
-_ACEOF
-cat >>$CONFIG_STATUS <<\_ACEOF
-
-#
-# CONFIG_HEADER section.
-#
-
-# 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=' '
-ac_dD=',;t'
-# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
-ac_uA='s,^\([   ]*\)#\([        ]*\)undef\([    ][      ]*\)'
-ac_uB='$,\1#\2define\3'
-ac_uC=' '
-ac_uD=',;t'
-
-for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
-  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
-  case $ac_file in
-  - | *:- | *:-:* ) # input from stdin
-       cat >$tmp/stdin
-       ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
-       ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
-  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
-       ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
-  * )   ac_file_in=$ac_file.in ;;
-  esac
-
-  test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
-echo "$as_me: creating $ac_file" >&6;}
-
-  # First look for the input files in the build tree, otherwise in the
-  # src tree.
-  ac_file_inputs=`IFS=:
-    for f in $ac_file_in; do
-      case $f in
-      -) echo $tmp/stdin ;;
-      [\\/$]*)
-        # Absolute (can't be DOS-style, as IFS=:)
-        test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
-echo "$as_me: error: cannot find input file: $f" >&2;}
-   { (exit 1); exit 1; }; }
-        # Do quote $f, to prevent DOS paths from being IFS'd.
-        echo "$f";;
-      *) # Relative
-        if test -f "$f"; then
-          # Build tree
-          echo "$f"
-        elif test -f "$srcdir/$f"; then
-          # Source tree
-          echo "$srcdir/$f"
-        else
-          # /dev/null tree
-          { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
-echo "$as_me: error: cannot find input file: $f" >&2;}
-   { (exit 1); exit 1; }; }
-        fi;;
-      esac
-    done` || { (exit 1); exit 1; }
-  # Remove the trailing spaces.
-  sed 's/[      ]*$//' $ac_file_inputs >$tmp/in
-
-_ACEOF
-
-# Transform confdefs.h into two sed scripts, `conftest.defines' and
-# `conftest.undefs', that substitutes the proper values into
-# config.h.in to produce config.h.  The first handles `#define'
-# templates, and the second `#undef' templates.
-# 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.defines conftest.undefs
-# Using a here document instead of a string reduces the quoting nightmare.
-# Putting comments in sed scripts is not portable.
-#
-# `end' is used to avoid that the second main sed command (meant for
-# 0-ary CPP macros) applies to n-ary macro definitions.
-# See the Autoconf documentation for `clear'.
-cat >confdef2sed.sed <<\_ACEOF
-s/[\\&,]/\\&/g
-s,[\\$`],\\&,g
-t clear
-: clear
-s,^[    ]*#[    ]*define[       ][      ]*\([^  (][^    (]*\)\(([^)]*)\)[       ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
-t end
-s,^[    ]*#[    ]*define[       ][      ]*\([^  ][^     ]*\)[   ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
-: end
-_ACEOF
-# If some macros were called several times there might be several times
-# the same #defines, which is useless.  Nevertheless, we may not want to
-# sort them, since we want the *last* AC-DEFINE to be honored.
-uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
-sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
-rm -f confdef2sed.sed
-
-# 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.undefs <<\_ACEOF
-s,^[    ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
-_ACEOF
-
-# Break up conftest.defines because some shells have a limit on the size
-# of here documents, and old seds have small limits too (100 cmds).
-echo '  # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
-echo '  if grep "^[     ]*#[    ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
-echo '  # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
-echo '  :' >>$CONFIG_STATUS
-rm -f conftest.tail
-while grep . conftest.defines >/dev/null
-do
-  # Write a limited-size here document to $tmp/defines.sed.
-  echo '  cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
-  # Speed up: don't consider the non `#define' lines.
-  echo '/^[     ]*#[    ]*define/!b' >>$CONFIG_STATUS
-  # Work around the forget-to-reset-the-flag bug.
-  echo 't clr' >>$CONFIG_STATUS
-  echo ': clr' >>$CONFIG_STATUS
-  sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
-  echo 'CEOF
-  sed -f $tmp/defines.sed $tmp/in >$tmp/out
-  rm -f $tmp/in
-  mv $tmp/out $tmp/in
-' >>$CONFIG_STATUS
-  sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
-  rm -f conftest.defines
-  mv conftest.tail conftest.defines
-done
-rm -f conftest.defines
-echo '  fi # grep' >>$CONFIG_STATUS
-echo >>$CONFIG_STATUS
-
-# Break up conftest.undefs because some shells have a limit on the size
-# of here documents, and old seds have small limits too (100 cmds).
-echo '  # Handle all the #undef templates' >>$CONFIG_STATUS
-rm -f conftest.tail
-while grep . conftest.undefs >/dev/null
-do
-  # Write a limited-size here document to $tmp/undefs.sed.
-  echo '  cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
-  # Speed up: don't consider the non `#undef'
-  echo '/^[     ]*#[    ]*undef/!b' >>$CONFIG_STATUS
-  # Work around the forget-to-reset-the-flag bug.
-  echo 't clr' >>$CONFIG_STATUS
-  echo ': clr' >>$CONFIG_STATUS
-  sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
-  echo 'CEOF
-  sed -f $tmp/undefs.sed $tmp/in >$tmp/out
-  rm -f $tmp/in
-  mv $tmp/out $tmp/in
-' >>$CONFIG_STATUS
-  sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
-  rm -f conftest.undefs
-  mv conftest.tail conftest.undefs
-done
-rm -f conftest.undefs
-
-cat >>$CONFIG_STATUS <<\_ACEOF
-  # Let's still pretend it is `configure' which instantiates (i.e., don't
-  # use $as_me), people would be surprised to read:
-  #    /* config.h.  Generated by config.status.  */
-  if test x"$ac_file" = x-; then
-    echo "/* Generated by configure.  */" >$tmp/config.h
-  else
-    echo "/* $ac_file.  Generated by configure.  */" >$tmp/config.h
-  fi
-  cat $tmp/in >>$tmp/config.h
-  rm -f $tmp/in
-  if test x"$ac_file" != x-; then
-    if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
-      { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
-echo "$as_me: $ac_file is unchanged" >&6;}
-    else
-      ac_dir=`(dirname "$ac_file") 2>/dev/null ||
-$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$ac_file" : 'X\(//\)[^/]' \| \
-        X"$ac_file" : 'X\(//\)$' \| \
-        X"$ac_file" : 'X\(/\)' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X"$ac_file" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
-         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
-         /^X\(\/\/\)$/{ s//\1/; q; }
-         /^X\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-      { if $as_mkdir_p; then
-    mkdir -p "$ac_dir"
-  else
-    as_dir="$ac_dir"
-    as_dirs=
-    while test ! -d "$as_dir"; do
-      as_dirs="$as_dir $as_dirs"
-      as_dir=`(dirname "$as_dir") 2>/dev/null ||
-$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$as_dir" : 'X\(//\)[^/]' \| \
-        X"$as_dir" : 'X\(//\)$' \| \
-        X"$as_dir" : 'X\(/\)' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X"$as_dir" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
-         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
-         /^X\(\/\/\)$/{ s//\1/; q; }
-         /^X\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-    done
-    test ! -n "$as_dirs" || mkdir $as_dirs
-  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
-echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
-   { (exit 1); exit 1; }; }; }
-
-      rm -f $ac_file
-      mv $tmp/config.h $ac_file
-    fi
-  else
-    cat $tmp/config.h
-    rm -f $tmp/config.h
-  fi
-# Compute $ac_file's index in $config_headers.
-_am_stamp_count=1
-for _am_header in $config_headers :; do
-  case $_am_header in
-    $ac_file | $ac_file:* )
-      break ;;
-    * )
-      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
-  esac
-done
-echo "timestamp for $ac_file" >`(dirname $ac_file) 2>/dev/null ||
-$as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X$ac_file : 'X\(//\)[^/]' \| \
-        X$ac_file : 'X\(//\)$' \| \
-        X$ac_file : 'X\(/\)' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X$ac_file |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
-         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
-         /^X\(\/\/\)$/{ s//\1/; q; }
-         /^X\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`/stamp-h$_am_stamp_count
-done
-_ACEOF
-cat >>$CONFIG_STATUS <<\_ACEOF
-
-#
-# CONFIG_COMMANDS section.
-#
-for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue
-  ac_dest=`echo "$ac_file" | sed 's,:.*,,'`
-  ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'`
-  ac_dir=`(dirname "$ac_dest") 2>/dev/null ||
-$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$ac_dest" : 'X\(//\)[^/]' \| \
-        X"$ac_dest" : 'X\(//\)$' \| \
-        X"$ac_dest" : 'X\(/\)' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X"$ac_dest" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
-         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
-         /^X\(\/\/\)$/{ s//\1/; q; }
-         /^X\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-  { if $as_mkdir_p; then
-    mkdir -p "$ac_dir"
-  else
-    as_dir="$ac_dir"
-    as_dirs=
-    while test ! -d "$as_dir"; do
-      as_dirs="$as_dir $as_dirs"
-      as_dir=`(dirname "$as_dir") 2>/dev/null ||
-$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$as_dir" : 'X\(//\)[^/]' \| \
-        X"$as_dir" : 'X\(//\)$' \| \
-        X"$as_dir" : 'X\(/\)' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X"$as_dir" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
-         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
-         /^X\(\/\/\)$/{ s//\1/; q; }
-         /^X\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-    done
-    test ! -n "$as_dirs" || mkdir $as_dirs
-  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
-echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
-   { (exit 1); exit 1; }; }; }
-
-  ac_builddir=.
-
-if test "$ac_dir" != .; then
-  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
-  # A "../" for each directory in $ac_dir_suffix.
-  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
-else
-  ac_dir_suffix= ac_top_builddir=
-fi
-
-case $srcdir in
-  .)  # No --srcdir option.  We are building in place.
-    ac_srcdir=.
-    if test -z "$ac_top_builddir"; then
-       ac_top_srcdir=.
-    else
-       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
-    fi ;;
-  [\\/]* | ?:[\\/]* )  # Absolute path.
-    ac_srcdir=$srcdir$ac_dir_suffix;
-    ac_top_srcdir=$srcdir ;;
-  *) # Relative path.
-    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
-    ac_top_srcdir=$ac_top_builddir$srcdir ;;
-esac
-
-# Do not use `cd foo && pwd` to compute absolute paths, because
-# the directories may not exist.
-case `pwd` in
-.) ac_abs_builddir="$ac_dir";;
-*)
-  case "$ac_dir" in
-  .) ac_abs_builddir=`pwd`;;
-  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
-  *) ac_abs_builddir=`pwd`/"$ac_dir";;
-  esac;;
-esac
-case $ac_abs_builddir in
-.) ac_abs_top_builddir=${ac_top_builddir}.;;
-*)
-  case ${ac_top_builddir}. in
-  .) ac_abs_top_builddir=$ac_abs_builddir;;
-  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
-  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
-  esac;;
-esac
-case $ac_abs_builddir in
-.) ac_abs_srcdir=$ac_srcdir;;
-*)
-  case $ac_srcdir in
-  .) ac_abs_srcdir=$ac_abs_builddir;;
-  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
-  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
-  esac;;
-esac
-case $ac_abs_builddir in
-.) ac_abs_top_srcdir=$ac_top_srcdir;;
-*)
-  case $ac_top_srcdir in
-  .) ac_abs_top_srcdir=$ac_abs_builddir;;
-  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
-  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
-  esac;;
-esac
-
-
-  { echo "$as_me:$LINENO: executing $ac_dest commands" >&5
-echo "$as_me: executing $ac_dest commands" >&6;}
-  case $ac_dest in
-    depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do
-  # Strip MF so we end up with the name of the file.
-  mf=`echo "$mf" | sed -e 's/:.*$//'`
-  # Check whether this is an Automake generated Makefile or not.
-  # We used to match only the files named `Makefile.in', but
-  # some people rename them; so instead we look at the file content.
-  # Grep'ing the first line is not enough: some people post-process
-  # each Makefile.in and add a new line on top of each file to say so.
-  # So let's grep whole file.
-  if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
-    dirpart=`(dirname "$mf") 2>/dev/null ||
-$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$mf" : 'X\(//\)[^/]' \| \
-        X"$mf" : 'X\(//\)$' \| \
-        X"$mf" : 'X\(/\)' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X"$mf" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
-         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
-         /^X\(\/\/\)$/{ s//\1/; q; }
-         /^X\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-  else
-    continue
-  fi
-  # Extract the definition of DEPDIR, am__include, and am__quote
-  # from the Makefile without running `make'.
-  DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
-  test -z "$DEPDIR" && continue
-  am__include=`sed -n 's/^am__include = //p' < "$mf"`
-  test -z "am__include" && continue
-  am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
-  # When using ansi2knr, U may be empty or an underscore; expand it
-  U=`sed -n 's/^U = //p' < "$mf"`
-  # Find all dependency output files, they are included files with
-  # $(DEPDIR) in their names.  We invoke sed twice because it is the
-  # simplest approach to changing $(DEPDIR) to its actual value in the
-  # expansion.
-  for file in `sed -n "
-    s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
-       sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
-    # Make sure the directory exists.
-    test -f "$dirpart/$file" && continue
-    fdir=`(dirname "$file") 2>/dev/null ||
-$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$file" : 'X\(//\)[^/]' \| \
-        X"$file" : 'X\(//\)$' \| \
-        X"$file" : 'X\(/\)' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X"$file" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
-         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
-         /^X\(\/\/\)$/{ s//\1/; q; }
-         /^X\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-    { if $as_mkdir_p; then
-    mkdir -p $dirpart/$fdir
-  else
-    as_dir=$dirpart/$fdir
-    as_dirs=
-    while test ! -d "$as_dir"; do
-      as_dirs="$as_dir $as_dirs"
-      as_dir=`(dirname "$as_dir") 2>/dev/null ||
-$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-        X"$as_dir" : 'X\(//\)[^/]' \| \
-        X"$as_dir" : 'X\(//\)$' \| \
-        X"$as_dir" : 'X\(/\)' \| \
-        .     : '\(.\)' 2>/dev/null ||
-echo X"$as_dir" |
-    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
-         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
-         /^X\(\/\/\)$/{ s//\1/; q; }
-         /^X\(\/\).*/{ s//\1/; q; }
-         s/.*/./; q'`
-    done
-    test ! -n "$as_dirs" || mkdir $as_dirs
-  fi || { { echo "$as_me:$LINENO: error: cannot create directory $dirpart/$fdir" >&5
-echo "$as_me: error: cannot create directory $dirpart/$fdir" >&2;}
-   { (exit 1); exit 1; }; }; }
-
-    # echo "creating $dirpart/$file"
-    echo '# dummy' > "$dirpart/$file"
-  done
-done
- ;;
-  esac
-done
-_ACEOF
-
-cat >>$CONFIG_STATUS <<\_ACEOF
-
-{ (exit 0); exit 0; }
-_ACEOF
-chmod +x $CONFIG_STATUS
-ac_clean_files=$ac_clean_files_save
-
-
-# configure is writing to config.log, and then calls config.status.
-# config.status does its own redirection, appending to config.log.
-# Unfortunately, on DOS this fails, as config.log is still kept open
-# by configure, so config.status won't be able to write to it; its
-# output is simply discarded.  So we exec the FD to /dev/null,
-# effectively closing config.log, so it can be properly (re)opened and
-# appended to by config.status.  When coming back to configure, we
-# need to make the FD available again.
-if test "$no_create" != yes; then
-  ac_cs_success=:
-  ac_config_status_args=
-  test "$silent" = yes &&
-    ac_config_status_args="$ac_config_status_args --quiet"
-  exec 5>/dev/null
-  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
-  exec 5>>config.log
-  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
-  # would make configure fail if this is the last instruction.
-  $ac_cs_success || { (exit 1); exit 1; }
-fi
-
-echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
-echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6
-set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'`
-if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.make <<\_ACEOF
-all:
-       @echo 'ac_maketemp="$(MAKE)"'
-_ACEOF
-# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
-eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=`
-if test -n "$ac_maketemp"; then
-  eval ac_cv_prog_make_${ac_make}_set=yes
-else
-  eval ac_cv_prog_make_${ac_make}_set=no
-fi
-rm -f conftest.make
-fi
-if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
-  echo "$as_me:$LINENO: result: yes" >&5
-echo "${ECHO_T}yes" >&6
-  SET_MAKE=
-else
-  echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6
-  SET_MAKE="MAKE=${MAKE-make}"
-fi
-
index e4274486749dc73cefd41a1b9fb7e07b87adbf68..a6c30dc1959e67e1b2e5603a13319ad962b8c51d 100644 (file)
@@ -1,13 +1,13 @@
 #
-# $Id: configure.in,v 1.114 2006/04/12 17:39:32 ballen4705 Exp $
+# $Id: configure.in,v 1.118 2006/08/12 05:41:13 card_captor Exp $
 #
 dnl Process this file with autoconf to produce a configure script.
 AC_PREREQ(2.50)
-AC_INIT(smartmontools, 5.36, smartmontools-support@lists.sourceforge.net)
-AC_CONFIG_SRCDIR(smartctl.c)
+AC_INIT(smartmontools, 5.37, smartmontools-support@lists.sourceforge.net)
+AC_CONFIG_SRCDIR(smartctl.cpp)
 
 smartmontools_configure_date=`date -u +"%Y/%m/%d %T %Z"`
-smartmontools_cvs_tag=`echo '$Id: configure.in,v 1.114 2006/04/12 17:39:32 ballen4705 Exp $'`
+smartmontools_cvs_tag=`echo '$Id: configure.in,v 1.118 2006/08/12 05:41:13 card_captor Exp $'`
 smartmontools_release_date=2006/04/12
 smartmontools_release_time="17:39:01 UTC"
 
@@ -24,9 +24,9 @@ AM_INIT_AUTOMAKE
 
 AM_MAINTAINER_MODE
 
-AC_LANG_C
+AC_LANG_CPLUSPLUS
 dnl Checks for programs.
-AC_PROG_CC
+AC_PROG_CXX
 AM_PROG_AS
 AC_PROG_INSTALL
 
@@ -78,7 +78,9 @@ AH_TEMPLATE(HAVE_WORKING_SNPRINTF, [Define to 1 if the `snprintf' function is sa
 AC_MSG_CHECKING([for working snprintf])
 AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include <stdio.h>]], [[ char buf[]="ABCDEFGHI";
                int i=snprintf(buf,8,"12345678"); return !(!buf[7] && i==8); ]])],
-              [libc_have_working_snprintf=yes], [libc_have_working_snprintf=no])
+              [libc_have_working_snprintf=yes],
+             [libc_have_working_snprintf=no],
+             [libc_have_working_snprintf=no])
 AC_SUBST(libc_have_working_snprintf)
 if test "$libc_have_working_snprintf" = "yes"; then
   AC_DEFINE(HAVE_WORKING_SNPRINTF)
@@ -86,8 +88,8 @@ fi
 AC_MSG_RESULT([$libc_have_working_snprintf])
 
 # check for __attribute__((packed))
-AH_TEMPLATE(HAVE_ATTR_PACKED, [Define to 1 if C compiler supports __attribute__((packed))])
-AC_MSG_CHECKING([whether C compiler supports __attribute__((packed))])
+AH_TEMPLATE(HAVE_ATTR_PACKED, [Define to 1 if C++ compiler supports __attribute__((packed))])
+AC_MSG_CHECKING([whether C++ compiler supports __attribute__((packed))])
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM(, [[struct a { int b; } __attribute__((packed));]])],
                   [gcc_have_attr_packed=yes], [gcc_have_attr_packed=no])
 AC_SUBST(gcc_have_attr_packed)
@@ -180,41 +182,42 @@ AM_CONDITIONAL(OS_WIN32_MINGW, [echo $host_os | grep '^mingw' > /dev/null])
 
 dnl Add -Wall and -W if using gcc and its not already specified.
 if test "x$GCC" = "xyes"; then
-  if test -z "`echo "$CFLAGS" | grep "\-Wall" 2> /dev/null`" ; then
-      CFLAGS="$CFLAGS -Wall"
+  if test -z "`echo "$CXXFLAGS" | grep "\-Wall" 2> /dev/null`" ; then
+      CXXFLAGS="$CXXFLAGS -Wall"
   fi
 # In the next line, do NOT delete the 2 spaces inside double quotes.
-  if test -z "`echo "$CFLAGS " | grep "\-W " 2> /dev/null`" ; then
-      CFLAGS="$CFLAGS -W"
+  if test -z "`echo "$CXXFLAGS " | grep "\-W " 2> /dev/null`" ; then
+      CXXFLAGS="$CXXFLAGS -W"
   fi
   case "${host}" in
     *-*-mingw*)
       # MinGW uses MSVCRT.DLL which uses printf format "%I64d" and not "%lld" for int64_t
-      CFLAGS="$CFLAGS -Wno-format";;
+      CXXFLAGS="$CXXFLAGS -Wno-format";;
   esac
 else
  dnl We are NOT using gcc, so enable host-specific compiler flags
  case "${host}" in
        *-*-solaris*) 
-          dnl set CFLAGS for Solaris C compiler
-          if test -z "`echo "$CFLAGS" | grep "\-xmemalign" 2> /dev/null`" ; then
+          dnl set CXXFLAGS for Solaris C++ compiler
+          if test -z "`echo "$CXXFLAGS" | grep "\-xmemalign" 2> /dev/null`" ; then
             dnl we have to tell the compilers about packed ATA structures
-            CFLAGS="-xmemalign=1i $CFLAGS"
+            CXXFLAGS="-xmemalign=1i $CXXFLAGS"
           fi
-          if test -z "`echo "$CFLAGS" | grep "\-xCC" 2> /dev/null`" ; then
-            dnl we have to tell the compiler to ignore C++ style comments
-            CFLAGS="-xCC $CFLAGS"
-          fi
-          if test -z "`echo "$CFLAGS" | grep "\-xO" 2> /dev/null`" ; then
+          if test -z "`echo "$CXXFLAGS" | grep "\-xO" 2> /dev/null`" ; then
             dnl turn on optimization if user has not explicitly set its value
-            CFLAGS="-xO2 $CFLAGS"
+            CXXFLAGS="-xO2 $CXXFLAGS"
           fi
+          if test -z "`echo "$CXXFLAGS" | grep "\-erroff" 2> /dev/null`" ; then
+           dnl suppress warnings on use of string literal (const char[]) as
+           dnl char*. TODO: Sun Studio 10 (Sun C++ 5.7) or above only?
+           CXXFLAGS="-erroff=%none,wbadinitl,wbadasgl,badargtypel2w $CXXFLAGS"
+         fi
  esac
 fi
 
 AC_DEFINE_UNQUOTED(SMARTMONTOOLS_BUILD_HOST,     "${host}",                       [smartmontools Build Host])
 
-AC_SUBST(CFLAGS)
+AC_SUBST(CXXFLAGS)
 
 AC_OUTPUT(Makefile examplescripts/Makefile)
 AC_PROG_MAKE_SET
diff --git a/depcomp b/depcomp
deleted file mode 100755 (executable)
index 11e2d3b..0000000
--- a/depcomp
+++ /dev/null
@@ -1,522 +0,0 @@
-#! /bin/sh
-# depcomp - compile a program generating dependencies as side-effects
-
-scriptversion=2004-05-31.23
-
-# Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc.
-
-# 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, 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.
-
-# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
-
-case $1 in
-  '')
-     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
-     exit 1;
-     ;;
-  -h | --h*)
-    cat <<\EOF
-Usage: depcomp [--help] [--version] PROGRAM [ARGS]
-
-Run PROGRAMS ARGS to compile a file, generating dependencies
-as side-effects.
-
-Environment variables:
-  depmode     Dependency tracking mode.
-  source      Source file read by `PROGRAMS ARGS'.
-  object      Object file output by `PROGRAMS ARGS'.
-  DEPDIR      directory where to store dependencies.
-  depfile     Dependency file to output.
-  tmpdepfile  Temporary file to use when outputing dependencies.
-  libtool     Whether libtool is used (yes/no).
-
-Report bugs to <bug-automake@gnu.org>.
-EOF
-    exit 0
-    ;;
-  -v | --v*)
-    echo "depcomp $scriptversion"
-    exit 0
-    ;;
-esac
-
-if test -z "$depmode" || test -z "$source" || test -z "$object"; then
-  echo "depcomp: Variables source, object and depmode must be set" 1>&2
-  exit 1
-fi
-
-# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
-depfile=${depfile-`echo "$object" |
-  sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
-tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
-
-rm -f "$tmpdepfile"
-
-# Some modes work just like other modes, but use different flags.  We
-# parameterize here, but still list the modes in the big case below,
-# to make depend.m4 easier to write.  Note that we *cannot* use a case
-# here, because this file can only contain one case statement.
-if test "$depmode" = hp; then
-  # HP compiler uses -M and no extra arg.
-  gccflag=-M
-  depmode=gcc
-fi
-
-if test "$depmode" = dashXmstdout; then
-   # This is just like dashmstdout with a different argument.
-   dashmflag=-xM
-   depmode=dashmstdout
-fi
-
-case "$depmode" in
-gcc3)
-## gcc 3 implements dependency tracking that does exactly what
-## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like
-## it if -MD -MP comes after the -MF stuff.  Hmm.
-  "$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
-  stat=$?
-  if test $stat -eq 0; then :
-  else
-    rm -f "$tmpdepfile"
-    exit $stat
-  fi
-  mv "$tmpdepfile" "$depfile"
-  ;;
-
-gcc)
-## There are various ways to get dependency output from gcc.  Here's
-## why we pick this rather obscure method:
-## - Don't want to use -MD because we'd like the dependencies to end
-##   up in a subdir.  Having to rename by hand is ugly.
-##   (We might end up doing this anyway to support other compilers.)
-## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
-##   -MM, not -M (despite what the docs say).
-## - Using -M directly means running the compiler twice (even worse
-##   than renaming).
-  if test -z "$gccflag"; then
-    gccflag=-MD,
-  fi
-  "$@" -Wp,"$gccflag$tmpdepfile"
-  stat=$?
-  if test $stat -eq 0; then :
-  else
-    rm -f "$tmpdepfile"
-    exit $stat
-  fi
-  rm -f "$depfile"
-  echo "$object : \\" > "$depfile"
-  alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
-## The second -e expression handles DOS-style file names with drive letters.
-  sed -e 's/^[^:]*: / /' \
-      -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
-## This next piece of magic avoids the `deleted header file' problem.
-## The problem is that when a header file which appears in a .P file
-## is deleted, the dependency causes make to die (because there is
-## typically no way to rebuild the header).  We avoid this by adding
-## dummy dependencies for each header file.  Too bad gcc doesn't do
-## this for us directly.
-  tr ' ' '
-' < "$tmpdepfile" |
-## Some versions of gcc put a space before the `:'.  On the theory
-## that the space means something, we add a space to the output as
-## well.
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
-  rm -f "$tmpdepfile"
-  ;;
-
-hp)
-  # This case exists only to let depend.m4 do its work.  It works by
-  # looking at the text of this script.  This case will never be run,
-  # since it is checked for above.
-  exit 1
-  ;;
-
-sgi)
-  if test "$libtool" = yes; then
-    "$@" "-Wp,-MDupdate,$tmpdepfile"
-  else
-    "$@" -MDupdate "$tmpdepfile"
-  fi
-  stat=$?
-  if test $stat -eq 0; then :
-  else
-    rm -f "$tmpdepfile"
-    exit $stat
-  fi
-  rm -f "$depfile"
-
-  if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
-    echo "$object : \\" > "$depfile"
-
-    # Clip off the initial element (the dependent).  Don't try to be
-    # clever and replace this with sed code, as IRIX sed won't handle
-    # lines with more than a fixed number of characters (4096 in
-    # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
-    # the IRIX cc adds comments like `#:fec' to the end of the
-    # dependency line.
-    tr ' ' '
-' < "$tmpdepfile" \
-    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
-    tr '
-' ' ' >> $depfile
-    echo >> $depfile
-
-    # The second pass generates a dummy entry for each header file.
-    tr ' ' '
-' < "$tmpdepfile" \
-   | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
-   >> $depfile
-  else
-    # The sourcefile does not contain any dependencies, so just
-    # store a dummy comment line, to avoid errors with the Makefile
-    # "include basename.Plo" scheme.
-    echo "#dummy" > "$depfile"
-  fi
-  rm -f "$tmpdepfile"
-  ;;
-
-aix)
-  # The C for AIX Compiler uses -M and outputs the dependencies
-  # in a .u file.  In older versions, this file always lives in the
-  # current directory.  Also, the AIX compiler puts `$object:' at the
-  # start of each line; $object doesn't have directory information.
-  # Version 6 uses the directory in both cases.
-  stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
-  tmpdepfile="$stripped.u"
-  if test "$libtool" = yes; then
-    "$@" -Wc,-M
-  else
-    "$@" -M
-  fi
-  stat=$?
-
-  if test -f "$tmpdepfile"; then :
-  else
-    stripped=`echo "$stripped" | sed 's,^.*/,,'`
-    tmpdepfile="$stripped.u"
-  fi
-
-  if test $stat -eq 0; then :
-  else
-    rm -f "$tmpdepfile"
-    exit $stat
-  fi
-
-  if test -f "$tmpdepfile"; then
-    outname="$stripped.o"
-    # Each line is of the form `foo.o: dependent.h'.
-    # Do two passes, one to just change these to
-    # `$object: dependent.h' and one to simply `dependent.h:'.
-    sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
-    sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
-  else
-    # The sourcefile does not contain any dependencies, so just
-    # store a dummy comment line, to avoid errors with the Makefile
-    # "include basename.Plo" scheme.
-    echo "#dummy" > "$depfile"
-  fi
-  rm -f "$tmpdepfile"
-  ;;
-
-icc)
-  # Intel's C compiler understands `-MD -MF file'.  However on
-  #    icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
-  # ICC 7.0 will fill foo.d with something like
-  #    foo.o: sub/foo.c
-  #    foo.o: sub/foo.h
-  # which is wrong.  We want:
-  #    sub/foo.o: sub/foo.c
-  #    sub/foo.o: sub/foo.h
-  #    sub/foo.c:
-  #    sub/foo.h:
-  # ICC 7.1 will output
-  #    foo.o: sub/foo.c sub/foo.h
-  # and will wrap long lines using \ :
-  #    foo.o: sub/foo.c ... \
-  #     sub/foo.h ... \
-  #     ...
-
-  "$@" -MD -MF "$tmpdepfile"
-  stat=$?
-  if test $stat -eq 0; then :
-  else
-    rm -f "$tmpdepfile"
-    exit $stat
-  fi
-  rm -f "$depfile"
-  # Each line is of the form `foo.o: dependent.h',
-  # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
-  # Do two passes, one to just change these to
-  # `$object: dependent.h' and one to simply `dependent.h:'.
-  sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
-  # Some versions of the HPUX 10.20 sed can't process this invocation
-  # correctly.  Breaking it into two sed invocations is a workaround.
-  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
-    sed -e 's/$/ :/' >> "$depfile"
-  rm -f "$tmpdepfile"
-  ;;
-
-tru64)
-   # The Tru64 compiler uses -MD to generate dependencies as a side
-   # effect.  `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
-   # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
-   # dependencies in `foo.d' instead, so we check for that too.
-   # Subdirectories are respected.
-   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
-   test "x$dir" = "x$object" && dir=
-   base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
-
-   if test "$libtool" = yes; then
-      # Dependencies are output in .lo.d with libtool 1.4.
-      # With libtool 1.5 they are output both in $dir.libs/$base.o.d
-      # and in $dir.libs/$base.o.d and $dir$base.o.d.  We process the
-      # latter, because the former will be cleaned when $dir.libs is
-      # erased.
-      tmpdepfile1="$dir.libs/$base.lo.d"
-      tmpdepfile2="$dir$base.o.d"
-      tmpdepfile3="$dir.libs/$base.d"
-      "$@" -Wc,-MD
-   else
-      tmpdepfile1="$dir$base.o.d"
-      tmpdepfile2="$dir$base.d"
-      tmpdepfile3="$dir$base.d"
-      "$@" -MD
-   fi
-
-   stat=$?
-   if test $stat -eq 0; then :
-   else
-      rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
-      exit $stat
-   fi
-
-   if test -f "$tmpdepfile1"; then
-      tmpdepfile="$tmpdepfile1"
-   elif test -f "$tmpdepfile2"; then
-      tmpdepfile="$tmpdepfile2"
-   else
-      tmpdepfile="$tmpdepfile3"
-   fi
-   if test -f "$tmpdepfile"; then
-      sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
-      # That's a tab and a space in the [].
-      sed -e 's,^.*\.[a-z]*:[   ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
-   else
-      echo "#dummy" > "$depfile"
-   fi
-   rm -f "$tmpdepfile"
-   ;;
-
-#nosideeffect)
-  # This comment above is used by automake to tell side-effect
-  # dependency tracking mechanisms from slower ones.
-
-dashmstdout)
-  # Important note: in order to support this mode, a compiler *must*
-  # always write the preprocessed file to stdout, regardless of -o.
-  "$@" || exit $?
-
-  # Remove the call to Libtool.
-  if test "$libtool" = yes; then
-    while test $1 != '--mode=compile'; do
-      shift
-    done
-    shift
-  fi
-
-  # Remove `-o $object'.
-  IFS=" "
-  for arg
-  do
-    case $arg in
-    -o)
-      shift
-      ;;
-    $object)
-      shift
-      ;;
-    *)
-      set fnord "$@" "$arg"
-      shift # fnord
-      shift # $arg
-      ;;
-    esac
-  done
-
-  test -z "$dashmflag" && dashmflag=-M
-  # Require at least two characters before searching for `:'
-  # in the target name.  This is to cope with DOS-style filenames:
-  # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
-  "$@" $dashmflag |
-    sed 's:^[  ]*[^: ][^:][^:]*\:[    ]*:'"$object"'\: :' > "$tmpdepfile"
-  rm -f "$depfile"
-  cat < "$tmpdepfile" > "$depfile"
-  tr ' ' '
-' < "$tmpdepfile" | \
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
-  rm -f "$tmpdepfile"
-  ;;
-
-dashXmstdout)
-  # This case only exists to satisfy depend.m4.  It is never actually
-  # run, as this mode is specially recognized in the preamble.
-  exit 1
-  ;;
-
-makedepend)
-  "$@" || exit $?
-  # Remove any Libtool call
-  if test "$libtool" = yes; then
-    while test $1 != '--mode=compile'; do
-      shift
-    done
-    shift
-  fi
-  # X makedepend
-  shift
-  cleared=no
-  for arg in "$@"; do
-    case $cleared in
-    no)
-      set ""; shift
-      cleared=yes ;;
-    esac
-    case "$arg" in
-    -D*|-I*)
-      set fnord "$@" "$arg"; shift ;;
-    # Strip any option that makedepend may not understand.  Remove
-    # the object too, otherwise makedepend will parse it as a source file.
-    -*|$object)
-      ;;
-    *)
-      set fnord "$@" "$arg"; shift ;;
-    esac
-  done
-  obj_suffix="`echo $object | sed 's/^.*\././'`"
-  touch "$tmpdepfile"
-  ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
-  rm -f "$depfile"
-  cat < "$tmpdepfile" > "$depfile"
-  sed '1,2d' "$tmpdepfile" | tr ' ' '
-' | \
-## Some versions of the HPUX 10.20 sed can't process this invocation
-## correctly.  Breaking it into two sed invocations is a workaround.
-    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
-  rm -f "$tmpdepfile" "$tmpdepfile".bak
-  ;;
-
-cpp)
-  # Important note: in order to support this mode, a compiler *must*
-  # always write the preprocessed file to stdout.
-  "$@" || exit $?
-
-  # Remove the call to Libtool.
-  if test "$libtool" = yes; then
-    while test $1 != '--mode=compile'; do
-      shift
-    done
-    shift
-  fi
-
-  # Remove `-o $object'.
-  IFS=" "
-  for arg
-  do
-    case $arg in
-    -o)
-      shift
-      ;;
-    $object)
-      shift
-      ;;
-    *)
-      set fnord "$@" "$arg"
-      shift # fnord
-      shift # $arg
-      ;;
-    esac
-  done
-
-  "$@" -E |
-    sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
-    sed '$ s: \\$::' > "$tmpdepfile"
-  rm -f "$depfile"
-  echo "$object : \\" > "$depfile"
-  cat < "$tmpdepfile" >> "$depfile"
-  sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
-  rm -f "$tmpdepfile"
-  ;;
-
-msvisualcpp)
-  # Important note: in order to support this mode, a compiler *must*
-  # always write the preprocessed file to stdout, regardless of -o,
-  # because we must use -o when running libtool.
-  "$@" || exit $?
-  IFS=" "
-  for arg
-  do
-    case "$arg" in
-    "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
-       set fnord "$@"
-       shift
-       shift
-       ;;
-    *)
-       set fnord "$@" "$arg"
-       shift
-       shift
-       ;;
-    esac
-  done
-  "$@" -E |
-  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
-  rm -f "$depfile"
-  echo "$object : \\" > "$depfile"
-  . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::   \1 \\:p' >> "$depfile"
-  echo "       " >> "$depfile"
-  . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
-  rm -f "$tmpdepfile"
-  ;;
-
-none)
-  exec "$@"
-  ;;
-
-*)
-  echo "Unknown depmode $depmode" 1>&2
-  exit 1
-  ;;
-esac
-
-exit 0
-
-# Local Variables:
-# mode: shell-script
-# sh-indentation: 2
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-end: "$"
-# End:
diff --git a/do_release b/do_release
new file mode 100755 (executable)
index 0000000..b1f79c0
--- /dev/null
@@ -0,0 +1,113 @@
+#!/bin/bash -ev
+#
+# do a smartmontools release
+# (C) 2003-6 Bruce Allen <ballen4705@users.sourceforge.net>, 
+#          Guido Guenther <agx@sigxcpu.org>
+# $Id: do_release,v 1.39 2006/05/19 16:33:33 chrfranke Exp $
+
+# Notes on generating releases:
+# (1) update NEWS
+# (2) update CHANGELOG -- put in release number
+# (3) update release number in configure.in and smartmontools.spec
+# (4) update internal changelog in smartmontools.spec
+# (5) to test, set USECVS below to 0
+# (6) when satisfied, set USECVS below to 1
+
+USECVS=1
+
+KEYID=0x841ABAE8
+
+if [ -f /etc/redhat-release ]; then
+  RPM_BASE=/usr/src/redhat/
+else
+  RPM_BASE=/usr/src/rpm/
+fi
+SOURCES=$RPM_BASE/SOURCES/
+
+setup_cvs()
+{
+  CVS_SERVER=fakevalue
+  unset CVS_SERVER || echo "can't unset CVS_SERVER=$CVS_SERVER"
+  CVS_RSH=ssh
+  CVSROOT=:ext:ballen4705@smartmontools.cvs.sourceforge.net:/cvsroot/smartmontools
+}
+
+get_release()
+{
+  VERSION=`grep 'AC_INIT' configure.in | awk '{ print $2 }' | sed s/,//g`
+  RELEASE="RELEASE_${VERSION//\./_}"
+  echo "Version: $VERSION"
+  echo "Release: $RELEASE"
+}
+
+inc_release()
+{
+  MINOR=`echo $VERSION | cut -d. -f2`
+  MAJOR=`echo $VERSION | cut -d. -f1`
+  PERL_OLD=$MAJOR\\.$MINOR
+  ((MINOR++))
+  NEW_VERSION=$MAJOR.$MINOR
+  PERL_NEW=$MAJOR\\.$MINOR     
+  NEW_RELEASE="RELEASE_${NEW_VERSION//\./_}"
+  echo "New Version: $NEW_VERSION"
+  echo "New Release: $NEW_RELEASE"
+}
+
+# run automake/autoconf
+if [ -f Makefile ] ; then
+  make distcheck || exit 1
+  make clean
+  make distclean
+  rm -f Makefile configure
+fi
+
+smartmontools_release_date=`date -u +"%Y/%m/%d"`
+smartmontools_release_time=`date -u +"%T %Z"`
+cat configure.in  | sed "s|smartmontools_release_date=.*|smartmontools_release_date=${smartmontools_release_date}|" > configure.tmp
+cat configure.tmp | sed "s|smartmontools_release_time=.*|smartmontools_release_time=\"${smartmontools_release_time}\"|" > configure.in
+rm -f configure.tmp
+
+./autogen.sh
+
+get_release
+
+# tag CVS version
+if [ $USECVS -ne 0 ] ; then
+    setup_cvs
+    cvs commit -m "Release $VERSION $RELEASE"
+    cvs tag -d $RELEASE 
+    cvs tag $RELEASE
+fi
+
+# build .tar.gz
+rm -rf build
+mkdir build
+cd build
+../configure
+make distcheck || exit 1
+cd ..
+cp build/smartmontools-$VERSION.tar.gz $SOURCES
+
+# build rpm
+rpmbuild -ba --sign smartmontools.spec
+
+# remove source tarball
+rm -f $SOURCES/smartmontools-$VERSION.tar.gz
+
+# increase release number:
+inc_release
+if [ $USECVS -ne 0 ] ; then
+    perl -p -i.bak -e "s/$PERL_OLD/$PERL_NEW/" configure.in
+    perl -p -i.bak -e "s/Version:\t$PERL_OLD/Version:\t$PERL_NEW/" smartmontools.spec
+fi
+
+mv -f $RPM_BASE/RPMS/i386/smartmontools-$VERSION-*.i386.rpm . || mv -f $RPM_BASE/RPMS/x86_64/smartmontools-$VERSION-*.x86_64.rpm .
+mv -f $RPM_BASE/SRPMS/smartmontools-$VERSION-*.src.rpm .
+cp -f build/smartmontools-$VERSION.tar.gz .
+if [ "$KEYID" ]; then
+  gpg --default-key $KEYID --armor --detach-sign ./smartmontools-$VERSION.tar.gz
+fi
+
+# cleanup
+rm -rf autom4te.cache build/ config.h.in Makefile.in examplescripts/Makefile.in \
+       depcomp mkinstalldirs install-sh configure aclocal.m4 missing *.bak
diff --git a/examplescripts/Makefile.in b/examplescripts/Makefile.in
deleted file mode 100644 (file)
index 01ea4fa..0000000
+++ /dev/null
@@ -1,369 +0,0 @@
-# Makefile.in generated by automake 1.9.3 from Makefile.am.
-# @configure_input@
-
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004  Free Software Foundation, Inc.
-# This Makefile.in is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-@SET_MAKE@
-
-
-srcdir = @srcdir@
-top_srcdir = @top_srcdir@
-VPATH = @srcdir@
-pkgdatadir = $(datadir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-top_builddir = ..
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-INSTALL = @INSTALL@
-install_sh_DATA = $(install_sh) -c -m 644
-install_sh_PROGRAM = $(install_sh) -c
-install_sh_SCRIPT = $(install_sh) -c
-INSTALL_HEADER = $(INSTALL_DATA)
-transform = $(program_transform_name)
-NORMAL_INSTALL = :
-PRE_INSTALL = :
-POST_INSTALL = :
-NORMAL_UNINSTALL = :
-PRE_UNINSTALL = :
-POST_UNINSTALL = :
-build_triplet = @build@
-host_triplet = @host@
-subdir = examplescripts
-DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in
-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
-am__aclocal_m4_deps = $(top_srcdir)/configure.in
-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
-       $(ACLOCAL_M4)
-mkinstalldirs = $(install_sh) -d
-CONFIG_HEADER = $(top_builddir)/config.h
-CONFIG_CLEAN_FILES =
-am__installdirs = "$(DESTDIR)$(examplesdir)" \
-       "$(DESTDIR)$(examplesdir)"
-examplesSCRIPT_INSTALL = $(INSTALL_SCRIPT)
-SCRIPTS = $(examples_SCRIPTS)
-SOURCES =
-DIST_SOURCES =
-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
-am__vpath_adj = case $$p in \
-    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
-    *) f=$$p;; \
-  esac;
-am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
-examplesDATA_INSTALL = $(INSTALL_DATA)
-DATA = $(examples_DATA)
-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
-ACLOCAL = @ACLOCAL@
-AMDEP_FALSE = @AMDEP_FALSE@
-AMDEP_TRUE = @AMDEP_TRUE@
-AMTAR = @AMTAR@
-ASFLAGS = @ASFLAGS@
-AUTOCONF = @AUTOCONF@
-AUTOHEADER = @AUTOHEADER@
-AUTOMAKE = @AUTOMAKE@
-AWK = @AWK@
-CC = @CC@
-CCAS = @CCAS@
-CCASFLAGS = @CCASFLAGS@
-CCDEPMODE = @CCDEPMODE@
-CFLAGS = @CFLAGS@
-CPP = @CPP@
-CPPFLAGS = @CPPFLAGS@
-CYGPATH_W = @CYGPATH_W@
-DEFS = @DEFS@
-DEPDIR = @DEPDIR@
-ECHO_C = @ECHO_C@
-ECHO_N = @ECHO_N@
-ECHO_T = @ECHO_T@
-EGREP = @EGREP@
-EXEEXT = @EXEEXT@
-INSTALL_DATA = @INSTALL_DATA@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@
-INSTALL_SCRIPT = @INSTALL_SCRIPT@
-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
-LDFLAGS = @LDFLAGS@
-LIBOBJS = @LIBOBJS@
-LIBS = @LIBS@
-LTLIBOBJS = @LTLIBOBJS@
-MAINT = @MAINT@
-MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
-MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
-MAKEINFO = @MAKEINFO@
-OBJEXT = @OBJEXT@
-OS_DARWIN_FALSE = @OS_DARWIN_FALSE@
-OS_DARWIN_TRUE = @OS_DARWIN_TRUE@
-OS_SOLARIS_FALSE = @OS_SOLARIS_FALSE@
-OS_SOLARIS_TRUE = @OS_SOLARIS_TRUE@
-OS_WIN32_MINGW_FALSE = @OS_WIN32_MINGW_FALSE@
-OS_WIN32_MINGW_TRUE = @OS_WIN32_MINGW_TRUE@
-PACKAGE = @PACKAGE@
-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
-PACKAGE_NAME = @PACKAGE_NAME@
-PACKAGE_STRING = @PACKAGE_STRING@
-PACKAGE_TARNAME = @PACKAGE_TARNAME@
-PACKAGE_VERSION = @PACKAGE_VERSION@
-PATH_SEPARATOR = @PATH_SEPARATOR@
-SET_MAKE = @SET_MAKE@
-SHELL = @SHELL@
-SMARTD_SUFFIX_FALSE = @SMARTD_SUFFIX_FALSE@
-SMARTD_SUFFIX_TRUE = @SMARTD_SUFFIX_TRUE@
-STRIP = @STRIP@
-VERSION = @VERSION@
-ac_ct_CC = @ac_ct_CC@
-ac_ct_STRIP = @ac_ct_STRIP@
-am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
-am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
-am__include = @am__include@
-am__leading_dot = @am__leading_dot@
-am__quote = @am__quote@
-am__tar = @am__tar@
-am__untar = @am__untar@
-bindir = @bindir@
-build = @build@
-build_alias = @build_alias@
-build_cpu = @build_cpu@
-build_os = @build_os@
-build_vendor = @build_vendor@
-datadir = @datadir@
-docdir = @docdir@
-exampledir = @exampledir@
-exec_prefix = @exec_prefix@
-gcc_have_attr_packed = @gcc_have_attr_packed@
-host = @host@
-host_alias = @host_alias@
-host_cpu = @host_cpu@
-host_os = @host_os@
-host_vendor = @host_vendor@
-includedir = @includedir@
-infodir = @infodir@
-initddir = @initddir@
-install_sh = @install_sh@
-libc_have_working_snprintf = @libc_have_working_snprintf@
-libdir = @libdir@
-libexecdir = @libexecdir@
-localstatedir = @localstatedir@
-mandir = @mandir@
-mkdir_p = @mkdir_p@
-oldincludedir = @oldincludedir@
-os_deps = @os_deps@
-os_libs = @os_libs@
-prefix = @prefix@
-program_transform_name = @program_transform_name@
-releaseversion = @releaseversion@
-sbindir = @sbindir@
-sharedstatedir = @sharedstatedir@
-smartd_suffix = @smartd_suffix@
-smartmontools_release_date = @smartmontools_release_date@
-smartmontools_release_time = @smartmontools_release_time@
-sysconfdir = @sysconfdir@
-target_alias = @target_alias@
-examplesdir = $(exampledir)
-examples_DATA = README
-examples_SCRIPTS = Example1            \
-                       Example2        \
-                       Example3
-
-EXTRA_DIST = $(examples_SCRIPTS)
-all: all-am
-
-.SUFFIXES:
-$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
-       @for dep in $?; do \
-         case '$(am__configure_deps)' in \
-           *$$dep*) \
-             cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
-               && exit 0; \
-             exit 1;; \
-         esac; \
-       done; \
-       echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign  examplescripts/Makefile'; \
-       cd $(top_srcdir) && \
-         $(AUTOMAKE) --foreign  examplescripts/Makefile
-.PRECIOUS: Makefile
-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
-       @case '$?' in \
-         *config.status*) \
-           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
-         *) \
-           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
-           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
-       esac;
-
-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
-       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-
-$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
-       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
-       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
-install-examplesSCRIPTS: $(examples_SCRIPTS)
-       @$(NORMAL_INSTALL)
-       test -z "$(examplesdir)" || $(mkdir_p) "$(DESTDIR)$(examplesdir)"
-       @list='$(examples_SCRIPTS)'; for p in $$list; do \
-         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
-         if test -f $$d$$p; then \
-           f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
-           echo " $(examplesSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(examplesdir)/$$f'"; \
-           $(examplesSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(examplesdir)/$$f"; \
-         else :; fi; \
-       done
-
-uninstall-examplesSCRIPTS:
-       @$(NORMAL_UNINSTALL)
-       @list='$(examples_SCRIPTS)'; for p in $$list; do \
-         f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
-         echo " rm -f '$(DESTDIR)$(examplesdir)/$$f'"; \
-         rm -f "$(DESTDIR)$(examplesdir)/$$f"; \
-       done
-uninstall-info-am:
-install-examplesDATA: $(examples_DATA)
-       @$(NORMAL_INSTALL)
-       test -z "$(examplesdir)" || $(mkdir_p) "$(DESTDIR)$(examplesdir)"
-       @list='$(examples_DATA)'; for p in $$list; do \
-         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
-         f=$(am__strip_dir) \
-         echo " $(examplesDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(examplesdir)/$$f'"; \
-         $(examplesDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(examplesdir)/$$f"; \
-       done
-
-uninstall-examplesDATA:
-       @$(NORMAL_UNINSTALL)
-       @list='$(examples_DATA)'; for p in $$list; do \
-         f=$(am__strip_dir) \
-         echo " rm -f '$(DESTDIR)$(examplesdir)/$$f'"; \
-         rm -f "$(DESTDIR)$(examplesdir)/$$f"; \
-       done
-tags: TAGS
-TAGS:
-
-ctags: CTAGS
-CTAGS:
-
-
-distdir: $(DISTFILES)
-       @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
-       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
-       list='$(DISTFILES)'; for file in $$list; do \
-         case $$file in \
-           $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
-           $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
-         esac; \
-         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
-         dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
-         if test "$$dir" != "$$file" && test "$$dir" != "."; then \
-           dir="/$$dir"; \
-           $(mkdir_p) "$(distdir)$$dir"; \
-         else \
-           dir=''; \
-         fi; \
-         if test -d $$d/$$file; then \
-           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
-             cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
-           fi; \
-           cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
-         else \
-           test -f $(distdir)/$$file \
-           || cp -p $$d/$$file $(distdir)/$$file \
-           || exit 1; \
-         fi; \
-       done
-check-am: all-am
-check: check-am
-all-am: Makefile $(SCRIPTS) $(DATA)
-installdirs:
-       for dir in "$(DESTDIR)$(examplesdir)" "$(DESTDIR)$(examplesdir)"; do \
-         test -z "$$dir" || $(mkdir_p) "$$dir"; \
-       done
-install: install-am
-install-exec: install-exec-am
-install-data: install-data-am
-uninstall: uninstall-am
-
-install-am: all-am
-       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
-
-installcheck: installcheck-am
-install-strip:
-       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
-         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
-         `test -z '$(STRIP)' || \
-           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
-mostlyclean-generic:
-
-clean-generic:
-
-distclean-generic:
-       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-
-maintainer-clean-generic:
-       @echo "This command is intended for maintainers to use"
-       @echo "it deletes files that may require special tools to rebuild."
-clean: clean-am
-
-clean-am: clean-generic mostlyclean-am
-
-distclean: distclean-am
-       -rm -f Makefile
-distclean-am: clean-am distclean-generic
-
-dvi: dvi-am
-
-dvi-am:
-
-html: html-am
-
-info: info-am
-
-info-am:
-
-install-data-am: install-examplesDATA install-examplesSCRIPTS
-
-install-exec-am:
-
-install-info: install-info-am
-
-install-man:
-
-installcheck-am:
-
-maintainer-clean: maintainer-clean-am
-       -rm -f Makefile
-maintainer-clean-am: distclean-am maintainer-clean-generic
-
-mostlyclean: mostlyclean-am
-
-mostlyclean-am: mostlyclean-generic
-
-pdf: pdf-am
-
-pdf-am:
-
-ps: ps-am
-
-ps-am:
-
-uninstall-am: uninstall-examplesDATA uninstall-examplesSCRIPTS \
-       uninstall-info-am
-
-.PHONY: all all-am check check-am clean clean-generic distclean \
-       distclean-generic distdir dvi dvi-am html html-am info info-am \
-       install install-am install-data install-data-am \
-       install-examplesDATA install-examplesSCRIPTS install-exec \
-       install-exec-am install-info install-info-am install-man \
-       install-strip installcheck installcheck-am installdirs \
-       maintainer-clean maintainer-clean-generic mostlyclean \
-       mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am \
-       uninstall-examplesDATA uninstall-examplesSCRIPTS \
-       uninstall-info-am
-
-# Tell versions [3.59,3.63) of GNU make to not export all variables.
-# Otherwise a system limit (for SysV at least) may be exceeded.
-.NOEXPORT:
index 5eabed73118cada46515997885f2d77993e81ad5..2a53aa33d82efd8b61df981a283dfb441539d264 100644 (file)
--- a/extern.h
+++ b/extern.h
@@ -25,7 +25,7 @@
 #ifndef EXTERN_H_
 #define EXTERN_H_
 
-#define EXTERN_H_CVSID "$Id: extern.h,v 1.41 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define EXTERN_H_CVSID "$Id: extern.h,v 1.47 2006/09/15 08:03:52 sxzzsf Exp $\n"
 
 // Possible values for fixfirmwarebug.  If use has NOT specified -F at
 // all, then value is 0.
@@ -50,6 +50,8 @@ typedef struct smartmonctrl_s {
   // turn off scan after selective self-test, 2: turn on scan after
   // selective self-test.
   unsigned char scanafterselect;
+  // skip check, if disk in idle or standby mode
+  unsigned char powermode;
   unsigned char driveinfo;
   unsigned char checksmart;
   unsigned char smartvendorattrib;
@@ -58,6 +60,7 @@ typedef struct smartmonctrl_s {
   unsigned char smartselftestlog;
   unsigned char selectivetestlog;
   unsigned char smarterrorlog;
+  unsigned char smartbackgroundlog;
   unsigned char smartdisable;
   unsigned char smartenable; 
   unsigned char smartstatus;
@@ -84,10 +87,15 @@ typedef struct smartmonctrl_s {
   unsigned char reportataioctl;
   unsigned char reportscsiioctl;
   unsigned char fixfirmwarebug;
+  unsigned char satpassthrulen;
+  // Controller type (device type) has been specified explicitly
+  unsigned char controller_explicit;
   // 3Ware controller type, but also extensible to other contoller types
   unsigned char controller_type;
   // For 3Ware controllers, nonzero value is 1 plus the disk number
   unsigned char controller_port;
+  // combined controller/channle/pmport for highpoint rocketraid controller
+  unsigned char hpt_data[3];
   unsigned char ignorepresets;
   unsigned char showpresets;
   // The i'th entry in this array will modify the printed meaning of
diff --git a/install-sh b/install-sh
deleted file mode 100755 (executable)
index 0b65ee8..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-#!/bin/sh
-# install - install a program, script, or datafile
-
-scriptversion=2004-10-22.00
-
-# This originates from X11R5 (mit/util/scripts/install.sh), which was
-# later released in X11R6 (xc/config/util/install.sh) with the
-# following copyright and license.
-#
-# Copyright (C) 1994 X Consortium
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to
-# deal in the Software without restriction, including without limitation the
-# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
-# sell copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
-# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
-# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
-# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-#
-# Except as contained in this notice, the name of the X Consortium shall not
-# be used in advertising or otherwise to promote the sale, use or other deal-
-# ings in this Software without prior written authorization from the X Consor-
-# tium.
-#
-#
-# FSF changes to this file are in the public domain.
-#
-# 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}"
-
-chmodcmd="$chmodprog 0755"
-chowncmd=
-chgrpcmd=
-stripcmd=
-rmcmd="$rmprog -f"
-mvcmd="$mvprog"
-src=
-dst=
-dir_arg=
-dstarg=
-no_target_directory=
-
-usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
-   or: $0 [OPTION]... SRCFILES... DIRECTORY
-   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
-   or: $0 [OPTION]... -d DIRECTORIES...
-
-In the 1st form, copy SRCFILE to DSTFILE.
-In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
-In the 4th, create DIRECTORIES.
-
-Options:
--c         (ignored)
--d         create directories instead of installing files.
--g GROUP   $chgrpprog installed files to GROUP.
--m MODE    $chmodprog installed files to MODE.
--o USER    $chownprog installed files to USER.
--s         $stripprog installed files.
--t DIRECTORY  install into DIRECTORY.
--T         report an error if DSTFILE is a directory.
---help     display this help and exit.
---version  display version info and exit.
-
-Environment variables override the default commands:
-  CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
-"
-
-while test -n "$1"; do
-  case $1 in
-    -c) shift
-        continue;;
-
-    -d) dir_arg=true
-        shift
-        continue;;
-
-    -g) chgrpcmd="$chgrpprog $2"
-        shift
-        shift
-        continue;;
-
-    --help) echo "$usage"; exit 0;;
-
-    -m) chmodcmd="$chmodprog $2"
-        shift
-        shift
-        continue;;
-
-    -o) chowncmd="$chownprog $2"
-        shift
-        shift
-        continue;;
-
-    -s) stripcmd=$stripprog
-        shift
-        continue;;
-
-    -t) dstarg=$2
-       shift
-       shift
-       continue;;
-
-    -T) no_target_directory=true
-       shift
-       continue;;
-
-    --version) echo "$0 $scriptversion"; exit 0;;
-
-    *)  # When -d is used, all remaining arguments are directories to create.
-       # When -t is used, the destination is already specified.
-       test -n "$dir_arg$dstarg" && break
-        # Otherwise, the last argument is the destination.  Remove it from $@.
-       for arg
-       do
-          if test -n "$dstarg"; then
-           # $@ is not empty: it contains at least $arg.
-           set fnord "$@" "$dstarg"
-           shift # fnord
-         fi
-         shift # arg
-         dstarg=$arg
-       done
-       break;;
-  esac
-done
-
-if test -z "$1"; then
-  if test -z "$dir_arg"; then
-    echo "$0: no input file specified." >&2
-    exit 1
-  fi
-  # It's OK to call `install-sh -d' without argument.
-  # This can happen when creating conditional directories.
-  exit 0
-fi
-
-for src
-do
-  # Protect names starting with `-'.
-  case $src in
-    -*) src=./$src ;;
-  esac
-
-  if test -n "$dir_arg"; then
-    dst=$src
-    src=
-
-    if test -d "$dst"; then
-      mkdircmd=:
-      chmodcmd=
-    else
-      mkdircmd=$mkdirprog
-    fi
-  else
-    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
-    # might cause directories to be created, which would be especially bad
-    # if $src (and thus $dsttmp) contains '*'.
-    if test ! -f "$src" && test ! -d "$src"; then
-      echo "$0: $src does not exist." >&2
-      exit 1
-    fi
-
-    if test -z "$dstarg"; then
-      echo "$0: no destination specified." >&2
-      exit 1
-    fi
-
-    dst=$dstarg
-    # Protect names starting with `-'.
-    case $dst in
-      -*) dst=./$dst ;;
-    esac
-
-    # If destination is a directory, append the input filename; won't work
-    # if double slashes aren't ignored.
-    if test -d "$dst"; then
-      if test -n "$no_target_directory"; then
-       echo "$0: $dstarg: Is a directory" >&2
-       exit 1
-      fi
-      dst=$dst/`basename "$src"`
-    fi
-  fi
-
-  # This sed command emulates the dirname command.
-  dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'`
-
-  # Make sure that the destination directory exists.
-
-  # Skip lots of stat calls in the usual case.
-  if test ! -d "$dstdir"; then
-    defaultIFS='
-        '
-    IFS="${IFS-$defaultIFS}"
-
-    oIFS=$IFS
-    # Some sh's can't handle IFS=/ for some reason.
-    IFS='%'
-    set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
-    shift
-    IFS=$oIFS
-
-    pathcomp=
-
-    while test $# -ne 0 ; do
-      pathcomp=$pathcomp$1
-      shift
-      if test ! -d "$pathcomp"; then
-        $mkdirprog "$pathcomp"
-       # mkdir can fail with a `File exist' error in case several
-       # install-sh are creating the directory concurrently.  This
-       # is OK.
-       test -d "$pathcomp" || exit
-      fi
-      pathcomp=$pathcomp/
-    done
-  fi
-
-  if test -n "$dir_arg"; then
-    $doit $mkdircmd "$dst" \
-      && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \
-      && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \
-      && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \
-      && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }
-
-  else
-    dstfile=`basename "$dst"`
-
-    # Make a couple of temp file names in the proper directory.
-    dsttmp=$dstdir/_inst.$$_
-    rmtmp=$dstdir/_rm.$$_
-
-    # Trap to clean up those temp files at exit.
-    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
-    trap '(exit $?); exit' 1 2 13 15
-
-    # Copy the file name to the temp name.
-    $doit $cpprog "$src" "$dsttmp" &&
-
-    # 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 $cpprog $src $dsttmp" command.
-    #
-    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
-      && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
-      && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
-      && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } &&
-
-    # Now rename the file to the real destination.
-    { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \
-      || {
-          # The rename failed, perhaps because mv can't rename something else
-          # to itself, or perhaps because mv is so ancient that it does not
-          # support -f.
-
-          # Now remove or move aside any old file at destination location.
-          # We try this two ways since rm can't unlink itself on some
-          # systems and the destination file might be busy for other
-          # reasons.  In this case, the final cleanup might fail but the new
-          # file should still install successfully.
-          {
-            if test -f "$dstdir/$dstfile"; then
-              $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \
-              || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \
-              || {
-                echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
-                (exit 1); exit
-              }
-            else
-              :
-            fi
-          } &&
-
-          # Now rename the file to the real destination.
-          $doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
-        }
-    }
-  fi || { (exit 1); exit; }
-done
-
-# The final little trick to "correctly" pass the exit status to the exit trap.
-{
-  (exit 0); exit
-}
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-end: "$"
-# End:
diff --git a/knowndrives.c b/knowndrives.c
deleted file mode 100644 (file)
index 9494300..0000000
+++ /dev/null
@@ -1,1258 +0,0 @@
-/*
- * knowndrives.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- * Address of support mailing list: smartmontools-support@lists.sourceforge.net
- *
- * Copyright (C) 2003-6 Philip Williams, Bruce Allen
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include "config.h"
-#include "int64.h"
-#include <stdio.h>
-#include "atacmds.h"
-#include "ataprint.h"
-#include "extern.h"
-#include "knowndrives.h"
-#include "utility.h" // includes <regex.h>
-
-const char *knowndrives_c_cvsid="$Id: knowndrives.c,v 1.139 2006/04/05 19:50:07 chrfranke Exp $"
-ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID UTILITY_H_CVSID;
-
-#define MODEL_STRING_LENGTH                         40
-#define FIRMWARE_STRING_LENGTH                       8
-#define TABLEPRINTWIDTH                             19
-
-// See vendorattributeargs[] array in atacmds.c for definitions.
-#define PRESET_9_MINUTES                   {   9,  1 }
-#define PRESET_9_TEMP                      {   9,  2 }
-#define PRESET_9_SECONDS                   {   9,  3 }
-#define PRESET_9_HALFMINUTES               {   9,  4 }
-#define PRESET_192_EMERGENCYRETRACTCYCLECT { 192,  1 }
-#define PRESET_193_LOADUNLOAD              { 193,  1 }
-#define PRESET_194_10XCELSIUS              { 194,  1 }
-#define PRESET_194_UNKNOWN                 { 194,  2 }
-#define PRESET_198_OFFLINESCANUNCSECTORCT  { 198,  1 }
-#define PRESET_200_WRITEERRORCOUNT         { 200,  1 }
-#define PRESET_201_DETECTEDTACOUNT         { 201,  1 }         
-#define PRESET_220_TEMP                    { 220,  1 }
-
-/* Arrays of preset vendor-specific attribute options for use in
- * knowndrives[]. */
-
-extern int64_t bytes;
-
-// to hold onto exit code for atexit routine
-extern int exitstatus;
-
-// These three are common to several models.
-const unsigned char vendoropts_9_minutes[][2] = {
-  PRESET_9_MINUTES,
-  {0,0}
-};
-const unsigned char vendoropts_9_halfminutes[][2] = {
-  PRESET_9_HALFMINUTES,
-  {0,0}
-};
-const unsigned char vendoropts_9_seconds[][2] = {
-  PRESET_9_SECONDS,
-  {0,0}
-};
-
-const unsigned char vendoropts_Maxtor_4D080H4[][2] = {
-  PRESET_9_MINUTES,
-  PRESET_194_UNKNOWN,
-  {0,0}
-};
-
-const unsigned char vendoropts_Fujitsu_MHS2020AT[][2] = {
-  PRESET_9_SECONDS,
-  PRESET_192_EMERGENCYRETRACTCYCLECT,
-  PRESET_198_OFFLINESCANUNCSECTORCT,
-  PRESET_200_WRITEERRORCOUNT,
-  PRESET_201_DETECTEDTACOUNT,
-  {0,0}
-};
-
-const unsigned char vendoropts_Fujitsu_MHR2040AT[][2] = {
-  PRESET_9_SECONDS,
-  PRESET_192_EMERGENCYRETRACTCYCLECT,
-  PRESET_198_OFFLINESCANUNCSECTORCT,
-  PRESET_200_WRITEERRORCOUNT,
-  {0,0}
-};
-
-const unsigned char vendoropts_Samsung_SV4012H[][2] = {
-  PRESET_9_HALFMINUTES,
-  {0,0}
-};
-
-const unsigned char vendoropts_Samsung_SV1204H[][2] = {
-  PRESET_9_HALFMINUTES,
-  PRESET_194_10XCELSIUS,
-  {0,0}
-};
-
-const unsigned char vendoropts_Hitachi_DK23XX[][2] = {
-  PRESET_9_MINUTES,
-  PRESET_193_LOADUNLOAD,
-  {0,0}
-};
-
-const char same_as_minus_F[]="Fixes byte order in some SMART data (same as -F samsung)";
-const char same_as_minus_F2[]="Fixes byte order in some SMART data (same as -F samsung2)";
-
-const char may_need_minus_F_disabled[] ="May need -F samsung disabled; see manual for details.";
-const char may_need_minus_F2_disabled[]="May need -F samsung2 disabled; see manual for details.";
-const char may_need_minus_F2_enabled[] ="May need -F samsung2 enabled; see manual for details.";
-const char may_need_minus_F_enabled[]  ="May need -F samsung or -F samsung2 enabled; see manual for details.";
-
-/* Special-purpose functions for use in knowndrives[]. */
-void specialpurpose_reverse_samsung(smartmonctrl *con)
-{
-  if (con->fixfirmwarebug==FIX_NOTSPECIFIED)
-    con->fixfirmwarebug = FIX_SAMSUNG;
-}
-void specialpurpose_reverse_samsung2(smartmonctrl *con)
-{
-  if (con->fixfirmwarebug==FIX_NOTSPECIFIED)
-    con->fixfirmwarebug = FIX_SAMSUNG2;
-}
-
-/* Table of settings for known drives terminated by an element containing all
- * zeros.  The drivesettings structure is described in knowndrives.h.  Note
- * that lookupdrive() will search knowndrives[] from the start to end or
- * until it finds the first match, so the order in knowndrives[] is important
- * for distinct entries that could match the same drive. */
-
-// Note that the table just below uses EXTENDED REGULAR EXPRESSIONS.
-// A good on-line reference for these is:
-// http://www.zeus.com/extra/docsystem/docroot/apps/web/docs/modules/access/regex.html
-
-const drivesettings knowndrives[] = {
-  { "IBM Deskstar 60GXP series",  // ER60A46A firmware
-    "(IBM-|Hitachi )?IC35L0[12346]0AVER07",
-    "^ER60A46A$",
-    NULL, NULL, NULL, NULL
-  },
-  { "IBM Deskstar 60GXP series",  // All other firmware
-    "(IBM-|Hitachi )?IC35L0[12346]0AVER07",
-    ".*",
-    "IBM Deskstar 60GXP drives may need upgraded SMART firmware.\n"
-    "Please see http://www.geocities.com/dtla_update/index.html#rel and\n"
-    "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n"
-    "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215",
-    NULL, NULL, NULL
-  },
-  { "IBM Deskstar 40GV & 75GXP series (A5AA/A6AA firmware)",
-    "(IBM-)?DTLA-30[57]0[123467][05]",
-    "^T[WX][123468AG][OF]A[56]AA$",
-    NULL, NULL, NULL, NULL
-  },
-  { "IBM Deskstar 40GV & 75GXP series (all other firmware)",
-    "(IBM-)?DTLA-30[57]0[123467][05]",
-    ".*",
-    "IBM Deskstar 40GV and 75GXP drives may need upgraded SMART firmware.\n"
-    "Please see http://www.geocities.com/dtla_update/ and\n"
-    "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n"
-    "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215",
-    NULL, NULL, NULL
-  },
-  { NULL, // ExcelStor J240, J340, J360, J680, and J880
-    "^ExcelStor Technology J(24|34|36|68|88)0$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // Fujitsu M1623TAU
-    "^FUJITSU M1623TAU$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { "Fujitsu MHG and MHH series",
-    "^FUJITSU MH(G2102|H20(64|48|32))AT$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { "Fujitsu MHJ and MHK series",
-    "^FUJITSU MH[JK]....ATU?$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { "Fujitsu MPB series",
-    "^FUJITSU MPB....ATU?$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { "Fujitsu MPD and MPE series",
-    "^FUJITSU MP[DE]....A[HTE]$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { "Fujitsu MPF series",
-    "^FUJITSU MPF3(102A[HT]|153A[HT]|204A[HT])$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { "Fujitsu MPG series",
-    "^FUJITSU MPG3(102A(H|T  E)|153AH|204A(H|[HT]  E)|307A(H  E|T)|409A[HT]  E)$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { "Fujitsu MPC series",
-    "^FUJITSU MPC3(032AT|043AT|045AH|064A[HT]|084AT|096AT|102AT)$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { NULL, // Fujitsu MHN2300AT
-    "^FUJITSU MHN2300AT$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { NULL, // Fujitsu MHR2040AT
-    "^FUJITSU MHR2040AT$",
-    ".*",    // Tested on 40BA
-    NULL,
-    vendoropts_Fujitsu_MHR2040AT,
-    NULL, NULL
-  },
-  { NULL, // Fujitsu MHR2020AT
-    "^FUJITSU MHR2020AT$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { "Fujitsu MHSxxxxAT family",
-    "^FUJITSU MHS20[6432]0AT(  .)?$",
-    ".*",
-    NULL,
-    vendoropts_Fujitsu_MHS2020AT,
-    NULL, NULL
-  },
-  { NULL, // Fujitsu MHL2300AT, MHM2200AT, MHM2100AT, MHM2150AT
-    "^FUJITSU MH(L230|M2(20|10|15))0AT$",
-    ".*",
-    "This drive's firmware has a harmless Drive Identity Structure\n"
-      "checksum error bug.",
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { "Fujitsu MHT2xxxAT/MHU2100AT series",
-    "^FUJITSU MH(T20[23468]0AT( PL)?|U2100AT)$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { "Fujitsu MHTxxxxAH family",
-    "^FUJITSU MHT20[468]0AH$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { NULL, // Samsung SV4012H (known firmware)
-    "^SAMSUNG SV4012H$",
-    "^RM100-08$",
-    NULL,
-    vendoropts_Samsung_SV4012H,
-    specialpurpose_reverse_samsung,
-    same_as_minus_F
-  },
-  { NULL, // Samsung SV4012H (all other firmware)
-    "^SAMSUNG SV4012H$",
-    ".*",
-    may_need_minus_F_disabled,
-    vendoropts_Samsung_SV4012H,
-    specialpurpose_reverse_samsung,
-    same_as_minus_F
-  },
-  { NULL, // Samsung SV0412H (known firmware)
-    "^SAMSUNG SV0412H$",
-    "^SK100-01$",
-    NULL,
-    vendoropts_Samsung_SV1204H,
-    specialpurpose_reverse_samsung,
-    same_as_minus_F
-  },
-  { NULL, // Samsung SV0412H (all other firmware)
-    "^SAMSUNG SV0412H$",
-    ".*",
-    may_need_minus_F_disabled,
-    vendoropts_Samsung_SV1204H,
-    specialpurpose_reverse_samsung,
-    same_as_minus_F
-  },
-  { NULL, // Samsung SV1204H (known firmware)
-    "^SAMSUNG SV1204H$",
-    "^RK100-1[3-5]$",
-    NULL,
-    vendoropts_Samsung_SV1204H,
-    specialpurpose_reverse_samsung,
-    same_as_minus_F
-  },
-  { NULL, // Samsung SV1204H (all other firmware)
-    "^SAMSUNG SV1204H$",
-    ".*",
-    may_need_minus_F_disabled,
-    vendoropts_Samsung_SV1204H,
-    specialpurpose_reverse_samsung,
-    same_as_minus_F
-  },
-  { NULL, // SAMSUNG SV0322A tested with FW JK200-35
-    "^SAMSUNG SV0322A$",
-    ".*",
-    NULL,
-    NULL,
-    NULL,
-    NULL
-  },
-  { NULL, // SAMSUNG SP40A2H with RR100-07 firmware
-    "^SAMSUNG SP40A2H$",
-    "^RR100-07$",
-    NULL,
-    vendoropts_9_halfminutes,
-    specialpurpose_reverse_samsung,
-    same_as_minus_F
-  },
-  { 
-    NULL, // Any other Samsung disk with *-23 *-24 firmware
-    // SAMSUNG SP1213N (TL100-23 firmware)
-    // SAMSUNG SP0802N (TK100-23 firmware)
-    // Samsung SP1604N, tested with FW TM100-23 and TM100-24
-    "^SAMSUNG .*$",
-    ".*-2[34]$",
-    NULL,
-    vendoropts_Samsung_SV4012H,
-    specialpurpose_reverse_samsung2,
-    same_as_minus_F2
-  },
-  { NULL, // All Samsung drives with '.*-25' firmware
-    "^SAMSUNG.*",
-    ".*-25$",
-    may_need_minus_F2_disabled,
-    vendoropts_Samsung_SV4012H,
-    specialpurpose_reverse_samsung2,
-    same_as_minus_F2
-  },
-  { NULL, // All Samsung drives with '.*-26 or later (currently to -39)' firmware
-    "^SAMSUNG.*",
-    ".*-(2[6789]|3[0-9])$",
-    NULL,
-    vendoropts_Samsung_SV4012H,
-    NULL,
-    NULL
-  },
-  { NULL, // Samsung ALL OTHER DRIVES
-    "^SAMSUNG.*",
-    ".*",
-    may_need_minus_F_enabled,
-    NULL, NULL, NULL
-  },
-  { "Maxtor Fireball 541DX family",
-    "^Maxtor 2B0(0[468]|1[05]|20)H1$",
-    ".*",
-    NULL,
-    vendoropts_Maxtor_4D080H4,
-    NULL, NULL
-  },
-  { "Maxtor Fireball 3 family",
-    "^Maxtor 2F0[234]0[JL]0$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 2160 Ultra ATA family",
-    "^Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 2880 Ultra ATA family",
-    "^Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 3400 Ultra ATA family",
-    "^Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax D540X-4G family",
-    "^Maxtor 4G(120J6|160J[68])$",
-    ".*",
-    NULL,
-    vendoropts_Maxtor_4D080H4,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax D540X-4K family",
-    "^MAXTOR 4K(020H1|040H2|060H3|080H4)$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Maxtor DiamondMax Plus D740X family",
-    "^MAXTOR 6L0(20[JL]1|40[JL]2|60[JL]3|80[JL]4)$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Maxtor DiamondMax Plus 5120 Ultra ATA 33 family",
-    "^Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax Plus 6800 Ultra ATA 66 family",
-    "^Maxtor 9(2732U8|2390U7|2049U6|1707U5|1366U4|1024U3|0845U3|0683U2)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax D540X-4D",
-    "^Maxtor 4D0(20H1|40H2|60H3|80H4)$",
-    ".*",
-    NULL,
-    vendoropts_Maxtor_4D080H4,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 16 family",
-    "^Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 4320 family",
-    "^Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D4|90432D2)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 17 VL family",
-    "^Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 20 VL family",
-    "^Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax VL 30 family",
-    "^Maxtor (33073U4|32049U3|31536U2|30768U1)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 36 family",
-    "^Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 40 ATA 66 series",
-    "^Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax Plus 40 series (Ultra ATA 66 and Ultra ATA 100)",
-    "^Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 40 VL Ultra ATA 100 series",
-    "^Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax Plus 45 Ulta ATA 100 family",
-    "^Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax Plus 60 family",
-    "^Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 80 family",
-    "^Maxtor (98196H8|96147H6)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 536DX family",
-    "^Maxtor 4W(100H6|080H6|060H4|040H3|030H2)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax Plus 8 family",
-    "^Maxtor 6E0[234]0L0$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax 10 family",
-    "^Maxtor 6(B(30|25|20|16|12|08)0[MPRS]|L(100P|120[MP]|160M|200[MPRS]|250[RS]|300[RS]))0$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor DiamondMax Plus 9 family",
-    "^Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor MaXLine Plus II",
-    "^Maxtor 7Y250[PM]0$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { "Maxtor MaXLine II family",
-    "^Maxtor [45]A(25|30|32)0[JN]0$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
-  },
-  { NULL, // HITACHI_DK14FA-20B
-    "^HITACHI_DK14FA-20B$",
-    ".*",
-    NULL,
-    vendoropts_Hitachi_DK23XX,
-    NULL, NULL
-  },
-  { "HITACHI Travelstar DK23XX/DK23XXB series",
-    "^HITACHI_DK23..-..B?$",
-    ".*",
-    NULL,
-    vendoropts_Hitachi_DK23XX,
-    NULL, NULL
-  },
-  { "Hitachi Endurastar J4K20/N4K20 (formerly DK23FA-20J)",
-    "^(HITACHI_DK23FA-20J|HTA422020F9AT[JN]0)$",
-    ".*",
-    NULL,
-    vendoropts_Hitachi_DK23XX,
-    NULL, NULL
-  },
-  { "IBM Deskstar 14GXP and 16GP series",
-    "^IBM-DTTA-3(7101|7129|7144|5032|5043|5064|5084|5101|5129|5168)0$",
-    ".*",
-    NULL, NULL, NULL, NULL 
-  },
-  { "IBM Deskstar 25GP and 22GXP family",
-    "^IBM-DJNA-3(5(101|152|203|250)|7(091|135|180|220))0$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "IBM Travelstar 25GS, 18GT, and 12GN family",
-    "^IBM-DARA-2(25|18|15|12|09|06)000$",
-    ".*",
-    NULL, NULL, NULL, NULL 
-  },
-  { "IBM Travelstar 48GH, 30GN, and 15GN family",
-    "^(IBM-|Hitachi )?IC25(T048ATDA05|N0(30|20|15|12|10|07|06|05)ATDA04)-.$",
-    ".*",
-    NULL, NULL, NULL, NULL 
-  },
-  { "IBM Travelstar 32GH, 30GT, and 20GN family",
-    "^IBM-DJSA-2(32|30|20|10|05)$",
-    ".*",
-    NULL, NULL, NULL, NULL 
-  },
-  { "IBM Deskstar 37GP and 34GXP family",
-    "^IBM-DPTA-3(5(375|300|225|150)|7(342|273|205|136))0$",
-    ".*",
-    NULL, NULL, NULL, NULL 
-  },
-  { "IBM/Hitachi Travelstar 60GH and 40GN family",
-    "^(IBM-|Hitachi )?IC25(T060ATC[SX]05|N0[4321]0ATC[SX]04)-.$",
-    ".*",
-    NULL, NULL, NULL, NULL 
-  },
-  { "IBM/Hitachi Travelstar 40GNX family",
-    "^(IBM-|Hitachi )?IC25N0[42]0ATC[SX]05-.$",
-    ".*",
-    NULL, NULL, NULL, NULL 
-  },
-  { "Hitachi Travelstar 80GN family",
-    "^(Hitachi )?IC25N0[23468]0ATMR04-.$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Hitachi Travelstar 5K80 family",
-    "^HTS5480[8642]0M9AT00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
-  },
-  { "Hitachi Travelstar 5K100 series",
-    "^HTS5410[1864]0G9(AT|SA)00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
-  },
-  { "Hitachi Travelstar 7K60",
-    "^HTS726060M9AT00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
-  },
-  { "Hitachi Travelstar E7K60 family",
-    "^HTE7260[46]0M9AT00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
-  },
-  { "IBM/Hitachi Deskstar 120GXP family",
-    "^(IBM-)?IC35L((020|040|060|080|120)AVVA|0[24]0AVVN)07-[01]$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "IBM/Hitachi Deskstar GXP-180 family",
-    "^(IBM-)?IC35L(030|060|090|120|180)AVV207-[01]$",
-    ".*", 
-    NULL, NULL, NULL, NULL 
-  },
-  { "IBM Travelstar 14GS",
-    "^IBM-DCYA-214000$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "IBM Travelstar 4LP",
-    "^IBM-DTNA-2(180|216)0$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Hitachi Deskstar 7K80 series",
-    "^(Hitachi )?HDS7280([48]0PLAT20|(40)?PLA320)$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Hitachi Deskstar 7K250 series",
-    "^(Hitachi )?HDS7225((40|80|12|16)VLAT20|(12|16|25)VLAT80|(80|12|16|25)VLSA80)$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Hitachi Deskstar 7K400 series",
-    "^(Hitachi )?HDS724040KL(AT|SA)80$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // TOSHIBA MK4025GAS
-    "^TOSHIBA MK4025GAS$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Toshiba 2.5\" HDD series", // TOSHIBA MK6021GAS [Bruce -- use for testing on laptop]
-    "^TOSHIBA MK6021GAS$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // TOSHIBA MK6022GAX
-    "^TOSHIBA MK6022GAX$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // TOSHIBA MK4019GAX/MK4019GAXB
-    "^TOSHIBA MK4019GAXB?$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // TOSHIBA MK6409MAV
-    "^TOSHIBA MK6409MAV$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // TOS MK3019GAXB SUN30G
-    "^TOS MK3019GAXB SUN30G$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // TOSHIBA MK2016GAP, MK2017GAP, MK2018GAP, MK2018GAS, MK2023GAS
-    "^TOSHIBA MK20(1[678]GAP|(18|23)GAS)$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // TOSHIBA MK4018GAS, MK4018GAP
-    "^TOSHIBA MK4018GA[SP]$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // TOSHIBA MK3017GAP
-    "^TOSHIBA MK3017GAP$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // TOSHIBA MK8026GAX
-    "^TOSHIBA MK8026GAX$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Momentus family",
-    "^ST9(20|28|40|48)11A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Momentus 4200.2 Series",
-    "^ST9(100822|808210|60821|50212|402113|30219)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Momentus 5400.2 series",
-    "^ST9(100823|808211|60822|408114|308110)A$", 
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Medalist 8641 family",
-    "^ST3(2110|3221|4312|6531|8641)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate U Series X family",
-    "^ST3(10014A(CE)?|20014A)$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate U Series 6 family",
-    "^ST3(8002|6002|4081|3061|2041)0A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate U Series 5 family",
-    "^ST3(40823|30621|20413|15311|10211)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate U4 family",
-    "^ST3(2112|4311|6421|8421)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate U8 family",
-    "^ST3(8410|4313|17221|13021)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate U10 family",
-    "^ST3(20423|15323|10212)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Barracuda ATA II family",
-    "^ST3(3063|2042|1532|1021)0A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Barracuda ATA III family",
-    "^ST3(40824|30620|20414|15310|10215)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Barracuda ATA IV family",
-    "^ST3(20011|30011|40016|60021|80021)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Barracuda ATA V family",
-    "^ST3(12002(3A|4A|9A|3AS)|800(23A|15A|23AS)|60(015A|210A)|40017A)$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Barracuda 5400.1",
-    "^ST340015A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Barracuda 7200.7 and 7200.7 Plus family",
-    "^ST3(200021A|200822AS?|16002[13]AS?|12002[26]AS?|1[26]0827AS|8001[13]AS?|80817AS|60014A|40014AS?)$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Barracuda 7200.8 family",
-    "^ST3(400832|300831|250823|200826)AS?$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Medalist 17240, 13030, 10231, 8420, and 4310",
-    "^ST3(17240|13030|10231|8420|4310)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Medalist 17242, 13032, 10232, 8422, and 4312",
-    "^ST3(1724|1303|1023|842|431)2A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Western Digital Protege",
-  /* Western Digital drives with this comment all appear to use Attribute 9 in
-   * a  non-standard manner.  These entries may need to be updated when it
-   * is understood exactly how Attribute 9 should be interpreted.
-   * UPDATE: this is probably explained by the WD firmware bug described in the
-   * smartmontools FAQ */
-    "^WDC WD([2468]00E|1[26]00A)B-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Western Digital Caviar family",
-  /* Western Digital drives with this comment all appear to use Attribute 9 in
-   * a  non-standard manner.  These entries may need to be updated when it
-   * is understood exactly how Attribute 9 should be interpreted.
-   * UPDATE: this is probably explained by the WD firmware bug described in the
-   * smartmontools FAQ */
-    "^WDC WD(2|3|4|6|8|10|12|16|18|20|25)00BB-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Western Digital Caviar WDxxxAB series",
-  /* Western Digital drives with this comment all appear to use Attribute 9 in
-   * a  non-standard manner.  These entries may need to be updated when it
-   * is understood exactly how Attribute 9 should be interpreted.
-   * UPDATE: this is probably explained by the WD firmware bug described in the
-   * smartmontools FAQ */
-    "^WDC WD(3|4|6)00AB-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Western Digital Caviar WDxxxAA series",
-  /* Western Digital drives with this comment all appear to use Attribute 9 in
-   * a  non-standard manner.  These entries may need to be updated when it
-   * is understood exactly how Attribute 9 should be interpreted.
-   * UPDATE: this is probably explained by the WD firmware bug described in the
-   * smartmontools FAQ */
-    "^WDC WD...?AA(-.*)?$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Western Digital Caviar WDxxxBA series",
-  /* Western Digital drives with this comment all appear to use Attribute 9 in
-   * a  non-standard manner.  These entries may need to be updated when it
-   * is understood exactly how Attribute 9 should be interpreted.
-   * UPDATE: this is probably explained by the WD firmware bug described in the
-   * smartmontools FAQ */
-    "^WDC WD...BA$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // Western Digital Caviar AC12500, AC14300, AC23200, AC24300, AC25100,
-          // AC36400, AC38400
-    "^WDC AC(125|143|232|243|251|364|384)00.?",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Western Digital Caviar SE family",
-  /* Western Digital drives with this comment all appear to use Attribute 9 in
-   * a  non-standard manner.  These entries may need to be updated when it
-   * is understood exactly how Attribute 9 should be interpreted.
-   * UPDATE: this is probably explained by the WD firmware bug described in the
-   * smartmontools FAQ */
-    "^WDC WD((4|6|8|10|12|16|18|20|25|30|32)00JB|(12|20|25)00PB)-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Western Digital Caviar SE (Serial ATA) family",
-    "^WDC WD(4|8|12|16|20|25)00JD-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Western Digital Caviar RE Serial ATA series",
-    "^WDC WD((12|16|25|32)00SD|4000YR)-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Western Digital Raptor family",
-    "^WDC WD(360|740)GD",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL,  // QUANTUM BIGFOOT TS10.0A
-    "^QUANTUM BIGFOOT TS10.0A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // QUANTUM FIREBALLlct15 20 and QUANTUM FIREBALLlct15 30
-    "^QUANTUM FIREBALLlct15 [23]0$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "QUANTUM FIREBALLlct20 series",
-    "^QUANTUM FIREBALLlct20 [234]0$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // QUANTUM FIREBALL CX10.2A
-    "^QUANTUM FIREBALL CX10.2A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Quantum Fireball Plus LM series",
-    "^QUANTUM FIREBALLP LM(10.2|15|20.5|30)$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Quantum Fireball CR series",
-    "^QUANTUM FIREBALL CR(4.3|8.4)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // QUANTUM FIREBALLP AS10.2, AS20.5, and AS40.0
-    "^QUANTUM FIREBALLP AS(10.2|20.5|40.0)$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // QUANTUM FIREBALL EX6.4A
-    "^QUANTUM FIREBALL EX6.4A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // QUANTUM FIREBALL ST3.2A
-    "^QUANTUM FIREBALL ST3.2A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // QUANTUM FIREBALL EX3.2A
-    "^QUANTUM FIREBALL EX3.2A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // QUANTUM FIREBALLP KX27.3
-    "^QUANTUM FIREBALLP KX27.3$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // QUANTUM FIREBALLP KA10.1
-    "^QUANTUM FIREBALLP KA10.1$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  /*------------------------------------------------------------
-   *  End of table.  Do not add entries below this marker.
-   *------------------------------------------------------------ */
-  {NULL, NULL, NULL, NULL, NULL, NULL, NULL}
-};
-
-// Searches knowndrives[] for a drive with the given model number and firmware
-// string.  If either the drive's model or firmware strings are not set by the
-// manufacturer then values of NULL may be used.  Returns the index of the
-// first match in knowndrives[] or -1 if no match if found.
-int lookupdrive(const char *model, const char *firmware)
-{
-  regex_t regex;
-  int i, index;
-  const char *empty = "";
-
-  model = model ? model : empty;
-  firmware = firmware ? firmware : empty;
-
-  for (i = 0, index = -1; index == -1 && knowndrives[i].modelregexp; i++) {
-    // Attempt to compile regular expression.
-    if (compileregex(&regex, knowndrives[i].modelregexp, REG_EXTENDED))
-      goto CONTINUE;
-
-    // Check whether model matches the regular expression in knowndrives[i].
-    if (!regexec(&regex, model, 0, NULL, 0)) {
-      // model matches, now check firmware.
-      if (!knowndrives[i].firmwareregexp)
-        // The firmware regular expression in knowndrives[i] is NULL, which is
-        // considered a match.
-        index = i;
-      else {
-        // Compare firmware against the regular expression in knowndrives[i].
-        regfree(&regex);  // Recycle regex.
-        if (compileregex(&regex, knowndrives[i].firmwareregexp, REG_EXTENDED))
-          goto CONTINUE;
-        if (!regexec(&regex, firmware, 0, NULL, 0))
-          index = i;
-      }
-    }
-  CONTINUE:
-    regfree(&regex);
-  }
-
-  return index;
-}
-
-
-// Shows all presets for drives in knowndrives[].
-void showonepreset(const drivesettings *drivetable){
-  
-  const unsigned char (* presets)[2] = drivetable->vendoropts;
-  int first_preset = 1;
-  
-  // Basic error check
-  if (!drivetable || !drivetable->modelregexp){
-    pout("Null known drive table pointer. Please report\n"
-         "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n");
-    return;
-  }
-  
-  // print model and firmware regular expressions
-  pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL REGEXP:", drivetable->modelregexp);
-  pout("%-*s %s\n", TABLEPRINTWIDTH, "FIRMWARE REGEXP:", drivetable->firmwareregexp ?
-       drivetable->firmwareregexp : "");
-  pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", drivetable->modelfamily ?
-       drivetable->modelfamily : "");
-  
-  // if there are any presets, then show them
-  if (presets && (*presets)[0]) while (1) {
-    char out[256];
-    const int attr = (*presets)[0], val  = (*presets)[1];
-    unsigned char fakearray[MAX_ATTRIBUTE_NUM];
-
-    // if we are at the end of the attribute list, break out
-    if (!attr)  
-      break;
-    
-    // This is a hack. ataPrintSmartAttribName() needs a pointer to an
-    // "array" to dereference, so we provide such a pointer.
-    fakearray[attr]=val;
-    ataPrintSmartAttribName(out, attr, fakearray);
-
-    // Use leading zeros instead of spaces so that everything lines up.
-    out[0] = (out[0] == ' ') ? '0' : out[0];
-    out[1] = (out[1] == ' ') ? '0' : out[1];
-    pout("%-*s %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "", out);
-    first_preset = 0;
-    presets++;
-  }
-  else
-    pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required.");
-
-  
-  // Is a special purpose function defined?  If so, describe it
-  if (drivetable->specialpurpose){
-    pout("%-*s ", TABLEPRINTWIDTH, "OTHER PRESETS:");
-    pout("%s\n", drivetable->functiondesc ?
-         drivetable->functiondesc : "A special purpose function "
-         "is defined for this drive"); 
-  }
-  
-  // Print any special warnings
-  if (drivetable->warningmsg){
-    pout("%-*s ", TABLEPRINTWIDTH, "WARNINGS:");
-    pout("%s\n", drivetable->warningmsg);
-  }
-  
-  return;
-}
-
-// Shows all presets for drives in knowndrives[].
-// Returns <0 on syntax error in regular expressions.
-int showallpresets(void){
-  int i;
-  int rc = 0;
-  regex_t regex;
-
-  // loop over all entries in the knowndrives[] table, printing them
-  // out in a nice format
-  for (i=0; knowndrives[i].modelregexp; i++){
-    showonepreset(&knowndrives[i]);
-    pout("\n");
-  }
-
-  // Check all regular expressions
-  for (i=0; knowndrives[i].modelregexp; i++){
-    if (compileregex(&regex, knowndrives[i].modelregexp, REG_EXTENDED))
-      rc = -1;
-    if (knowndrives[i].firmwareregexp) {
-      if (compileregex(&regex, knowndrives[i].firmwareregexp, REG_EXTENDED))
-        rc = -1;
-    }
-  }
-  pout("For information about adding a drive to the database see the FAQ on the\n");
-  pout("smartmontools home page: " PACKAGE_HOMEPAGE "\n");
-  return rc;
-}
-
-// Shows all matching presets for a drive in knowndrives[].
-// Returns # matching entries.
-int showmatchingpresets(const char *model, const char *firmware){
-  int i;
-  int cnt = 0;
-  const char * firmwaremsg = (firmware ? firmware : "(any)");
-  regex_t regex;
-
-  for (i=0; knowndrives[i].modelregexp; i++){
-    if (i > 0)
-      regfree(&regex);
-    if (compileregex(&regex, knowndrives[i].modelregexp, REG_EXTENDED))
-      continue;
-    if (regexec(&regex, model, 0, NULL, 0))
-      continue;
-    if (firmware && knowndrives[i].firmwareregexp) {
-      regfree(&regex);
-      if (compileregex(&regex, knowndrives[i].firmwareregexp, REG_EXTENDED))
-        continue;
-      if (regexec(&regex, firmware, 0, NULL, 0))
-        continue;
-    }
-    if (++cnt == 1)
-      pout("Drive found in smartmontools Database.  Drive identity strings:\n"
-           "%-*s %s\n"
-           "%-*s %s\n"
-           "match smartmontools Drive Database entry:\n",
-           TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmwaremsg);
-    else if (cnt == 2)
-      pout("and match these additional entries:\n");
-    showonepreset(&knowndrives[i]);
-    pout("\n");
-  }
-  regfree(&regex);
-  if (cnt == 0)
-    pout("No presets are defined for this drive.  Its identity strings:\n"
-         "MODEL:    %s\n"
-         "FIRMWARE: %s\n"
-         "do not match any of the known regular expressions.\n",
-         model, firmwaremsg);
-  return cnt;
-}
-
-// Shows the presets (if any) that are available for the given drive.
-void showpresets(const struct ata_identify_device *drive){
-  int i;
-  char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
-
-  // get the drive's model/firmware strings
-  formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH);
-  formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH);
-  
-  // and search to see if they match values in the table
-  if ((i = lookupdrive(model, firmware)) < 0) {
-    // no matches found
-    pout("No presets are defined for this drive.  Its identity strings:\n"
-         "MODEL:    %s\n"
-         "FIRMWARE: %s\n"
-         "do not match any of the known regular expressions.\n"
-         "Use -P showall to list all known regular expressions.\n",
-         model, firmware);
-    return;
-  }
-  
-  // We found a matching drive.  Print out all information about it.
-  pout("Drive found in smartmontools Database.  Drive identity strings:\n"
-       "%-*s %s\n"
-       "%-*s %s\n"
-       "match smartmontools Drive Database entry:\n",
-       TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmware);
-  showonepreset(&knowndrives[i]);
-  return;
-}
-
-// Sets preset vendor attribute options in opts by finding the entry
-// (if any) for the given drive in knowndrives[].  Values that have
-// already been set in opts will not be changed.  Returns <0 if drive
-// not recognized else index >=0 into drive database.
-int applypresets(const struct ata_identify_device *drive, unsigned char **optsptr,
-                smartmonctrl *con) {
-  int i;
-  unsigned char *opts;
-  char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
-  
-  if (*optsptr==NULL)
-    bytes+=MAX_ATTRIBUTE_NUM;
-  
-  if (*optsptr==NULL && !(*optsptr=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM,1))){
-    pout("Unable to allocate memory in applypresets()");
-    bytes-=MAX_ATTRIBUTE_NUM;
-    EXIT(1);
-  }
-  
-  opts=*optsptr;
-  
-  // get the drive's model/firmware strings
-  formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH);
-  formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH);
-  
-  // Look up the drive in knowndrives[].
-  if ((i = lookupdrive(model, firmware)) >= 0) {
-    
-    // if vendoropts is non-NULL then Attribute interpretation presets
-    if (knowndrives[i].vendoropts) {
-      const unsigned char (* presets)[2];
-      
-      // For each attribute in list of attribute/val pairs...
-      presets = knowndrives[i].vendoropts;
-      while (1) {
-       const int attr = (*presets)[0];
-       const int val  = (*presets)[1];
-       
-       if (!attr)  
-         break;
-       
-       // ... set attribute if user hasn't already done so.
-       if (!opts[attr])
-         opts[attr] = val;
-       presets++;
-      }
-    }
-    
-    // If a special-purpose function is defined for this drive then
-    // call it. Note that if command line arguments or Directives
-    // over-ride this choice, then the specialpurpose function that is
-    // called must deal with this.
-    if (knowndrives[i].specialpurpose)
-      (*knowndrives[i].specialpurpose)(con);
-  }
-  
-  // return <0 if drive wasn't recognized, or index>=0 into database
-  // if it was
-  return i;
-}
diff --git a/knowndrives.cpp b/knowndrives.cpp
new file mode 100644 (file)
index 0000000..9fea5cd
--- /dev/null
@@ -0,0 +1,1217 @@
+/*
+ * knowndrives.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ * Address of support mailing list: smartmontools-support@lists.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Philip Williams, Bruce Allen
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include <stdio.h>
+#include "atacmds.h"
+#include "ataprint.h"
+#include "extern.h"
+#include "knowndrives.h"
+#include "utility.h" // includes <regex.h>
+
+const char *knowndrives_c_cvsid="$Id: knowndrives.cpp,v 1.144 2006/09/24 16:32:25 chrfranke Exp $"
+ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID UTILITY_H_CVSID;
+
+#define MODEL_STRING_LENGTH                         40
+#define FIRMWARE_STRING_LENGTH                       8
+#define TABLEPRINTWIDTH                             19
+
+// See vendorattributeargs[] array in atacmds.cpp for definitions.
+#define PRESET_9_MINUTES                   {   9,  1 }
+#define PRESET_9_TEMP                      {   9,  2 }
+#define PRESET_9_SECONDS                   {   9,  3 }
+#define PRESET_9_HALFMINUTES               {   9,  4 }
+#define PRESET_192_EMERGENCYRETRACTCYCLECT { 192,  1 }
+#define PRESET_193_LOADUNLOAD              { 193,  1 }
+#define PRESET_194_10XCELSIUS              { 194,  1 }
+#define PRESET_194_UNKNOWN                 { 194,  2 }
+#define PRESET_198_OFFLINESCANUNCSECTORCT  { 198,  1 }
+#define PRESET_200_WRITEERRORCOUNT         { 200,  1 }
+#define PRESET_201_DETECTEDTACOUNT         { 201,  1 }         
+#define PRESET_220_TEMP                    { 220,  1 }
+
+/* Arrays of preset vendor-specific attribute options for use in
+ * knowndrives[]. */
+
+extern int64_t bytes;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// These three are common to several models.
+const unsigned char vendoropts_9_minutes[][2] = {
+  PRESET_9_MINUTES,
+  {0,0}
+};
+const unsigned char vendoropts_9_halfminutes[][2] = {
+  PRESET_9_HALFMINUTES,
+  {0,0}
+};
+const unsigned char vendoropts_9_seconds[][2] = {
+  PRESET_9_SECONDS,
+  {0,0}
+};
+
+const unsigned char vendoropts_Maxtor_4D080H4[][2] = {
+  PRESET_9_MINUTES,
+  PRESET_194_UNKNOWN,
+  {0,0}
+};
+
+const unsigned char vendoropts_Fujitsu_MHS2020AT[][2] = {
+  PRESET_9_SECONDS,
+  PRESET_192_EMERGENCYRETRACTCYCLECT,
+  PRESET_198_OFFLINESCANUNCSECTORCT,
+  PRESET_200_WRITEERRORCOUNT,
+  PRESET_201_DETECTEDTACOUNT,
+  {0,0}
+};
+
+const unsigned char vendoropts_Fujitsu_MHR2040AT[][2] = {
+  PRESET_9_SECONDS,
+  PRESET_192_EMERGENCYRETRACTCYCLECT,
+  PRESET_198_OFFLINESCANUNCSECTORCT,
+  PRESET_200_WRITEERRORCOUNT,
+  {0,0}
+};
+
+const unsigned char vendoropts_Samsung_SV4012H[][2] = {
+  PRESET_9_HALFMINUTES,
+  {0,0}
+};
+
+const unsigned char vendoropts_Samsung_SV1204H[][2] = {
+  PRESET_9_HALFMINUTES,
+  PRESET_194_10XCELSIUS,
+  {0,0}
+};
+
+const unsigned char vendoropts_Hitachi_DK23XX[][2] = {
+  PRESET_9_MINUTES,
+  PRESET_193_LOADUNLOAD,
+  {0,0}
+};
+
+const char same_as_minus_F[]="Fixes byte order in some SMART data (same as -F samsung)";
+const char same_as_minus_F2[]="Fixes byte order in some SMART data (same as -F samsung2)";
+
+const char may_need_minus_F_disabled[] ="May need -F samsung disabled; see manual for details.";
+const char may_need_minus_F2_disabled[]="May need -F samsung2 disabled; see manual for details.";
+const char may_need_minus_F2_enabled[] ="May need -F samsung2 enabled; see manual for details.";
+const char may_need_minus_F_enabled[]  ="May need -F samsung or -F samsung2 enabled; see manual for details.";
+
+/* Special-purpose functions for use in knowndrives[]. */
+void specialpurpose_reverse_samsung(smartmonctrl *con)
+{
+  if (con->fixfirmwarebug==FIX_NOTSPECIFIED)
+    con->fixfirmwarebug = FIX_SAMSUNG;
+}
+void specialpurpose_reverse_samsung2(smartmonctrl *con)
+{
+  if (con->fixfirmwarebug==FIX_NOTSPECIFIED)
+    con->fixfirmwarebug = FIX_SAMSUNG2;
+}
+
+/* Table of settings for known drives terminated by an element containing all
+ * zeros.  The drivesettings structure is described in knowndrives.h.  Note
+ * that lookupdrive() will search knowndrives[] from the start to end or
+ * until it finds the first match, so the order in knowndrives[] is important
+ * for distinct entries that could match the same drive. */
+
+// Note that the table just below uses EXTENDED REGULAR EXPRESSIONS.
+// A good on-line reference for these is:
+// http://www.zeus.com/extra/docsystem/docroot/apps/web/docs/modules/access/regex.html
+
+const drivesettings knowndrives[] = {
+  { "IBM Deskstar 60GXP series",  // ER60A46A firmware
+    "(IBM-|Hitachi )?IC35L0[12346]0AVER07",
+    "^ER60A46A$",
+    NULL, NULL, NULL, NULL
+  },
+  { "IBM Deskstar 60GXP series",  // All other firmware
+    "(IBM-|Hitachi )?IC35L0[12346]0AVER07",
+    ".*",
+    "IBM Deskstar 60GXP drives may need upgraded SMART firmware.\n"
+    "Please see http://www.geocities.com/dtla_update/index.html#rel and\n"
+    "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n"
+    "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215",
+    NULL, NULL, NULL
+  },
+  { "IBM Deskstar 40GV & 75GXP series (A5AA/A6AA firmware)",
+    "(IBM-)?DTLA-30[57]0[123467][05]",
+    "^T[WX][123468AG][OF]A[56]AA$",
+    NULL, NULL, NULL, NULL
+  },
+  { "IBM Deskstar 40GV & 75GXP series (all other firmware)",
+    "(IBM-)?DTLA-30[57]0[123467][05]",
+    ".*",
+    "IBM Deskstar 40GV and 75GXP drives may need upgraded SMART firmware.\n"
+    "Please see http://www.geocities.com/dtla_update/ and\n"
+    "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n"
+    "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215",
+    NULL, NULL, NULL
+  },
+  { NULL, // ExcelStor J240, J340, J360, J680, and J880
+    "^ExcelStor Technology J(24|34|36|68|88)0$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // Fujitsu M1623TAU
+    "^FUJITSU M1623TAU$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MHG..MHK, MHT, MHU series",
+    "^FUJITSU MH[GHJKTU]2...ATU?",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { NULL, // Fujitsu MHN2300AT
+    "^FUJITSU MHN2300AT$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { NULL, // Fujitsu MHR2040AT
+    "^FUJITSU MHR2040AT$",
+    ".*",    // Tested on 40BA
+    NULL,
+    vendoropts_Fujitsu_MHR2040AT,
+    NULL, NULL
+  },
+  { NULL, // Fujitsu MHR2020AT
+    "^FUJITSU MHR2020AT$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MHSxxxxAT family",
+    "^FUJITSU MHS20[6432]0AT(  .)?$",
+    ".*",
+    NULL,
+    vendoropts_Fujitsu_MHS2020AT,
+    NULL, NULL
+  },
+  { NULL, // Fujitsu MHL2300AT, MHM2200AT, MHM2100AT, MHM2150AT
+    "^FUJITSU MH(L230|M2(20|10|15))0AT$",
+    ".*",
+    "This drive's firmware has a harmless Drive Identity Structure\n"
+      "checksum error bug.",
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MPA..MPG series",
+    "^FUJITSU MP[A-G]3...A[HTEV]U?",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { NULL, // Samsung SV4012H (known firmware)
+    "^SAMSUNG SV4012H$",
+    "^RM100-08$",
+    NULL,
+    vendoropts_Samsung_SV4012H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // Samsung SV4012H (all other firmware)
+    "^SAMSUNG SV4012H$",
+    ".*",
+    may_need_minus_F_disabled,
+    vendoropts_Samsung_SV4012H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // Samsung SV0412H (known firmware)
+    "^SAMSUNG SV0412H$",
+    "^SK100-01$",
+    NULL,
+    vendoropts_Samsung_SV1204H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // Samsung SV0412H (all other firmware)
+    "^SAMSUNG SV0412H$",
+    ".*",
+    may_need_minus_F_disabled,
+    vendoropts_Samsung_SV1204H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // Samsung SV1204H (known firmware)
+    "^SAMSUNG SV1204H$",
+    "^RK100-1[3-5]$",
+    NULL,
+    vendoropts_Samsung_SV1204H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // Samsung SV1204H (all other firmware)
+    "^SAMSUNG SV1204H$",
+    ".*",
+    may_need_minus_F_disabled,
+    vendoropts_Samsung_SV1204H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // SAMSUNG SV0322A tested with FW JK200-35
+    "^SAMSUNG SV0322A$",
+    ".*",
+    NULL,
+    NULL,
+    NULL,
+    NULL
+  },
+  { NULL, // SAMSUNG SP40A2H with RR100-07 firmware
+    "^SAMSUNG SP40A2H$",
+    "^RR100-07$",
+    NULL,
+    vendoropts_9_halfminutes,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { "SAMSUNG SpinPoint T133 series", // tested with HD300LJ/ZT100-12, HD400LJ/ZZ100-14, HD401LJ/ZZ100-15
+    "^SAMSUNG HD[34][02][01]L[DJ]$",
+    ".*",
+    NULL,
+    NULL,
+    NULL,
+    NULL
+  },
+  { 
+    NULL, // Any other Samsung disk with *-23 *-24 firmware
+    // SAMSUNG SP1213N (TL100-23 firmware)
+    // SAMSUNG SP0802N (TK100-23 firmware)
+    // Samsung SP1604N, tested with FW TM100-23 and TM100-24
+    "^SAMSUNG .*$",
+    ".*-2[34]$",
+    NULL,
+    vendoropts_Samsung_SV4012H,
+    specialpurpose_reverse_samsung2,
+    same_as_minus_F2
+  },
+  { NULL, // All Samsung drives with '.*-25' firmware
+    "^SAMSUNG.*",
+    ".*-25$",
+    may_need_minus_F2_disabled,
+    vendoropts_Samsung_SV4012H,
+    specialpurpose_reverse_samsung2,
+    same_as_minus_F2
+  },
+  { NULL, // All Samsung drives with '.*-26 or later (currently to -39)' firmware
+    "^SAMSUNG.*",
+    ".*-(2[6789]|3[0-9])$",
+    NULL,
+    vendoropts_Samsung_SV4012H,
+    NULL,
+    NULL
+  },
+  { NULL, // Samsung ALL OTHER DRIVES
+    "^SAMSUNG.*",
+    ".*",
+    may_need_minus_F_enabled,
+    NULL, NULL, NULL
+  },
+  { "Maxtor Fireball 541DX family",
+    "^Maxtor 2B0(0[468]|1[05]|20)H1$",
+    ".*",
+    NULL,
+    vendoropts_Maxtor_4D080H4,
+    NULL, NULL
+  },
+  { "Maxtor Fireball 3 family",
+    "^Maxtor 2F0[234]0[JL]0$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 2160 Ultra ATA family",
+    "^Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 2880 Ultra ATA family",
+    "^Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 3400 Ultra ATA family",
+    "^Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax D540X-4G family",
+    "^Maxtor 4G(120J6|160J[68])$",
+    ".*",
+    NULL,
+    vendoropts_Maxtor_4D080H4,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax D540X-4K family",
+    "^MAXTOR 4K(020H1|040H2|060H3|080H4)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus D740X family",
+    "^MAXTOR 6L0(20[JL]1|40[JL]2|60[JL]3|80[JL]4)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 5120 Ultra ATA 33 family",
+    "^Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 6800 Ultra ATA 66 family",
+    "^Maxtor 9(2732U8|2390U7|2049U6|1707U5|1366U4|1024U3|0845U3|0683U2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax D540X-4D",
+    "^Maxtor 4D0(20H1|40H2|60H3|80H4)$",
+    ".*",
+    NULL,
+    vendoropts_Maxtor_4D080H4,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 16 family",
+    "^Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 4320 family",
+    "^Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D4|90432D2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 17 VL family",
+    "^Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 20 VL family",
+    "^Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax VL 30 family",
+    "^Maxtor (33073U4|32049U3|31536U2|30768U1)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 36 family",
+    "^Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 40 ATA 66 series",
+    "^Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 40 series (Ultra ATA 66 and Ultra ATA 100)",
+    "^Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 40 VL Ultra ATA 100 series",
+    "^Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 45 Ulta ATA 100 family",
+    "^Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 60 family",
+    "^Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 80 family",
+    "^Maxtor (98196H8|96147H6)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 536DX family",
+    "^Maxtor 4W(100H6|080H6|060H4|040H3|030H2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 8 family",
+    "^Maxtor 6E0[234]0L0$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 10 family",
+    "^Maxtor 6(B(30|25|20|16|12|08)0[MPRS]|L(100P|120[MP]|160M|200[MPRS]|250[RS]|300[RS]))0$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 9 family",
+    "^Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor MaXLine Plus II",
+    "^Maxtor 7Y250[PM]0$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor MaXLine II family",
+    "^Maxtor [45]A(25|30|32)0[JN]0$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { NULL, // HITACHI_DK14FA-20B
+    "^HITACHI_DK14FA-20B$",
+    ".*",
+    NULL,
+    vendoropts_Hitachi_DK23XX,
+    NULL, NULL
+  },
+  { "HITACHI Travelstar DK23XX/DK23XXB series",
+    "^HITACHI_DK23..-..B?$",
+    ".*",
+    NULL,
+    vendoropts_Hitachi_DK23XX,
+    NULL, NULL
+  },
+  { "Hitachi Endurastar J4K20/N4K20 (formerly DK23FA-20J)",
+    "^(HITACHI_DK23FA-20J|HTA422020F9AT[JN]0)$",
+    ".*",
+    NULL,
+    vendoropts_Hitachi_DK23XX,
+    NULL, NULL
+  },
+  { "IBM Deskstar 14GXP and 16GP series",
+    "^IBM-DTTA-3(7101|7129|7144|5032|5043|5064|5084|5101|5129|5168)0$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM Deskstar 25GP and 22GXP family",
+    "^IBM-DJNA-3(5(101|152|203|250)|7(091|135|180|220))0$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "IBM Travelstar 25GS, 18GT, and 12GN family",
+    "^IBM-DARA-2(25|18|15|12|09|06)000$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM Travelstar 48GH, 30GN, and 15GN family",
+    "^(IBM-|Hitachi )?IC25(T048ATDA05|N0(30|20|15|12|10|07|06|05)ATDA04)-.$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM Travelstar 32GH, 30GT, and 20GN family",
+    "^IBM-DJSA-2(32|30|20|10|05)$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM Deskstar 37GP and 34GXP family",
+    "^IBM-DPTA-3(5(375|300|225|150)|7(342|273|205|136))0$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM/Hitachi Travelstar 60GH and 40GN family",
+    "^(IBM-|Hitachi )?IC25(T060ATC[SX]05|N0[4321]0ATC[SX]04)-.$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM/Hitachi Travelstar 40GNX family",
+    "^(IBM-|Hitachi )?IC25N0[42]0ATC[SX]05-.$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "Hitachi Travelstar 80GN family",
+    "^(Hitachi )?IC25N0[23468]0ATMR04-.$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Hitachi Travelstar 5K80 family",
+    "^HTS5480[8642]0M9AT00$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "Hitachi Travelstar 5K100 series",
+    "^HTS5410[1864]0G9(AT|SA)00$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "Hitachi Travelstar 7K60",
+    "^HTS726060M9AT00$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "Hitachi Travelstar E7K60 family",
+    "^HTE7260[46]0M9AT00$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM/Hitachi Deskstar 120GXP family",
+    "^(IBM-)?IC35L((020|040|060|080|120)AVVA|0[24]0AVVN)07-[01]$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "IBM/Hitachi Deskstar GXP-180 family",
+    "^(IBM-)?IC35L(030|060|090|120|180)AVV207-[01]$",
+    ".*", 
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM Travelstar 14GS",
+    "^IBM-DCYA-214000$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "IBM Travelstar 4LP",
+    "^IBM-DTNA-2(180|216)0$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Hitachi Deskstar 7K80 series",
+    "^(Hitachi )?HDS7280([48]0PLAT20|(40)?PLA320)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Hitachi Deskstar 7K250 series",
+    "^(Hitachi )?HDS7225((40|80|12|16)VLAT20|(12|16|25)VLAT80|(80|12|16|25)VLSA80)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Hitachi Deskstar 7K400 series",
+    "^(Hitachi )?HDS724040KL(AT|SA)80$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK4025GAS
+    "^TOSHIBA MK4025GAS$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Toshiba 2.5\" HDD series", // TOSHIBA MK6021GAS [Bruce -- use for testing on laptop]
+    "^TOSHIBA MK6021GAS$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK6022GAX
+    "^TOSHIBA MK6022GAX$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK4019GAX/MK4019GAXB
+    "^TOSHIBA MK4019GAXB?$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK6409MAV
+    "^TOSHIBA MK6409MAV$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOS MK3019GAXB SUN30G
+    "^TOS MK3019GAXB SUN30G$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK2016GAP, MK2017GAP, MK2018GAP, MK2018GAS, MK2023GAS
+    "^TOSHIBA MK20(1[678]GAP|(18|23)GAS)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK4018GAS, MK4018GAP
+    "^TOSHIBA MK4018GA[SP]$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK3017GAP
+    "^TOSHIBA MK3017GAP$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK8026GAX
+    "^TOSHIBA MK8026GAX$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Momentus family",
+    "^ST9(20|28|40|48)11A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Momentus 4200.2 Series",
+    "^ST9(100822|808210|60821|50212|402113|30219)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Momentus 5400.2 series",
+    "^ST9(9808211|960822|808211|408114|308110|120821|10082[34]|98823|96812|94813|93811|60822)AS?$", 
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Medalist 8641 family",
+    "^ST3(2110|3221|4312|6531|8641)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U Series X family",
+    "^ST3(10014A(CE)?|20014A)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U Series 6 family",
+    "^ST3(8002|6002|4081|3061|2041)0A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U Series 5 family",
+    "^ST3(40823|30621|20413|15311|10211)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U4 family",
+    "^ST3(2112|4311|6421|8421)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U8 family",
+    "^ST3(8410|4313|17221|13021)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U10 family",
+    "^ST3(20423|15323|10212)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda ATA II family",
+    "^ST3(3063|2042|1532|1021)0A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda ATA III family",
+    "^ST3(40824|30620|20414|15310|10215)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda ATA IV family",
+    "^ST3(20011|30011|40016|60021|80021)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda ATA V family",
+    "^ST3(12002(3A|4A|9A|3AS)|800(23A|15A|23AS)|60(015A|210A)|40017A)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda 5400.1",
+    "^ST340015A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda 7200.7 and 7200.7 Plus family",
+    "^ST3(200021A|200822AS?|16002[13]AS?|12002[26]AS?|1[26]0827AS|8001[13]AS?|80817AS|60014A|40014AS?)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda 7200.8 family",
+    "^ST3(400832|300831|250823|200826)AS?$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Medalist 17240, 13030, 10231, 8420, and 4310",
+    "^ST3(17240|13030|10231|8420|4310)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Medalist 17242, 13032, 10232, 8422, and 4312",
+    "^ST3(1724|1303|1023|842|431)2A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Protege",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD([2468]00E|1[26]00A)B-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar family",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD(2|3|4|6|8|10|12|16|18|20|25)00BB-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar WDxxxAB series",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD(3|4|6)00AB-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar WDxxxAA series",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD...?AA(-.*)?$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar WDxxxBA series",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD...BA$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // Western Digital Caviar AC12500, AC14300, AC23200, AC24300, AC25100,
+          // AC36400, AC38400
+    "^WDC AC(125|143|232|243|251|364|384)00.?",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar SE family",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD((4|6|8|10|12|16|18|20|25|30|32)00JB|(12|20|25)00PB)-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar SE (Serial ATA) family",
+    "^WDC WD(4|8|12|16|20|25)00JD-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar RE Serial ATA series",
+    "^WDC WD((12|16|25|32)00SD|4000YR)-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Raptor family",
+    "^WDC WD(360|740)GD",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL,  // QUANTUM BIGFOOT TS10.0A
+    "^QUANTUM BIGFOOT TS10.0A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALLlct15 20 and QUANTUM FIREBALLlct15 30
+    "^QUANTUM FIREBALLlct15 [23]0$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "QUANTUM FIREBALLlct20 series",
+    "^QUANTUM FIREBALLlct20 [234]0$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALL CX10.2A
+    "^QUANTUM FIREBALL CX10.2A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Quantum Fireball Plus LM series",
+    "^QUANTUM FIREBALLP LM(10.2|15|20.5|30)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Quantum Fireball CR series",
+    "^QUANTUM FIREBALL CR(4.3|8.4)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALLP AS10.2, AS20.5, and AS40.0
+    "^QUANTUM FIREBALLP AS(10.2|20.5|40.0)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALL EX6.4A
+    "^QUANTUM FIREBALL EX6.4A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALL ST3.2A
+    "^QUANTUM FIREBALL ST3.2A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALL EX3.2A
+    "^QUANTUM FIREBALL EX3.2A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALLP KX27.3
+    "^QUANTUM FIREBALLP KX27.3$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALLP KA10.1
+    "^QUANTUM FIREBALLP KA10.1$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  /*------------------------------------------------------------
+   *  End of table.  Do not add entries below this marker.
+   *------------------------------------------------------------ */
+  {NULL, NULL, NULL, NULL, NULL, NULL, NULL}
+};
+
+// Searches knowndrives[] for a drive with the given model number and firmware
+// string.  If either the drive's model or firmware strings are not set by the
+// manufacturer then values of NULL may be used.  Returns the index of the
+// first match in knowndrives[] or -1 if no match if found.
+int lookupdrive(const char *model, const char *firmware)
+{
+  regex_t regex;
+  int i, index;
+  const char *empty = "";
+
+  model = model ? model : empty;
+  firmware = firmware ? firmware : empty;
+
+  for (i = 0, index = -1; index == -1 && knowndrives[i].modelregexp; i++) {
+    // Attempt to compile regular expression.
+    if (compileregex(&regex, knowndrives[i].modelregexp, REG_EXTENDED))
+      goto CONTINUE;
+
+    // Check whether model matches the regular expression in knowndrives[i].
+    if (!regexec(&regex, model, 0, NULL, 0)) {
+      // model matches, now check firmware.
+      if (!knowndrives[i].firmwareregexp)
+        // The firmware regular expression in knowndrives[i] is NULL, which is
+        // considered a match.
+        index = i;
+      else {
+        // Compare firmware against the regular expression in knowndrives[i].
+        regfree(&regex);  // Recycle regex.
+        if (compileregex(&regex, knowndrives[i].firmwareregexp, REG_EXTENDED))
+          goto CONTINUE;
+        if (!regexec(&regex, firmware, 0, NULL, 0))
+          index = i;
+      }
+    }
+  CONTINUE:
+    regfree(&regex);
+  }
+
+  return index;
+}
+
+
+// Shows all presets for drives in knowndrives[].
+void showonepreset(const drivesettings *drivetable){
+  
+  const unsigned char (* presets)[2] = drivetable->vendoropts;
+  int first_preset = 1;
+  
+  // Basic error check
+  if (!drivetable || !drivetable->modelregexp){
+    pout("Null known drive table pointer. Please report\n"
+         "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n");
+    return;
+  }
+  
+  // print model and firmware regular expressions
+  pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL REGEXP:", drivetable->modelregexp);
+  pout("%-*s %s\n", TABLEPRINTWIDTH, "FIRMWARE REGEXP:", drivetable->firmwareregexp ?
+       drivetable->firmwareregexp : "");
+  pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", drivetable->modelfamily ?
+       drivetable->modelfamily : "");
+  
+  // if there are any presets, then show them
+  if (presets && (*presets)[0]) while (1) {
+    char out[256];
+    const int attr = (*presets)[0], val  = (*presets)[1];
+    unsigned char fakearray[MAX_ATTRIBUTE_NUM];
+
+    // if we are at the end of the attribute list, break out
+    if (!attr)  
+      break;
+    
+    // This is a hack. ataPrintSmartAttribName() needs a pointer to an
+    // "array" to dereference, so we provide such a pointer.
+    fakearray[attr]=val;
+    ataPrintSmartAttribName(out, attr, fakearray);
+
+    // Use leading zeros instead of spaces so that everything lines up.
+    out[0] = (out[0] == ' ') ? '0' : out[0];
+    out[1] = (out[1] == ' ') ? '0' : out[1];
+    pout("%-*s %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "", out);
+    first_preset = 0;
+    presets++;
+  }
+  else
+    pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required.");
+
+  
+  // Is a special purpose function defined?  If so, describe it
+  if (drivetable->specialpurpose){
+    pout("%-*s ", TABLEPRINTWIDTH, "OTHER PRESETS:");
+    pout("%s\n", drivetable->functiondesc ?
+         drivetable->functiondesc : "A special purpose function "
+         "is defined for this drive"); 
+  }
+  
+  // Print any special warnings
+  if (drivetable->warningmsg){
+    pout("%-*s ", TABLEPRINTWIDTH, "WARNINGS:");
+    pout("%s\n", drivetable->warningmsg);
+  }
+  
+  return;
+}
+
+// Shows all presets for drives in knowndrives[].
+// Returns <0 on syntax error in regular expressions.
+int showallpresets(void){
+  int i;
+  int rc = 0;
+  regex_t regex;
+
+  // loop over all entries in the knowndrives[] table, printing them
+  // out in a nice format
+  for (i=0; knowndrives[i].modelregexp; i++){
+    showonepreset(&knowndrives[i]);
+    pout("\n");
+  }
+
+  // Check all regular expressions
+  for (i=0; knowndrives[i].modelregexp; i++){
+    if (compileregex(&regex, knowndrives[i].modelregexp, REG_EXTENDED))
+      rc = -1;
+    if (knowndrives[i].firmwareregexp) {
+      if (compileregex(&regex, knowndrives[i].firmwareregexp, REG_EXTENDED))
+        rc = -1;
+    }
+  }
+  pout("For information about adding a drive to the database see the FAQ on the\n");
+  pout("smartmontools home page: " PACKAGE_HOMEPAGE "\n");
+  return rc;
+}
+
+// Shows all matching presets for a drive in knowndrives[].
+// Returns # matching entries.
+int showmatchingpresets(const char *model, const char *firmware){
+  int i;
+  int cnt = 0;
+  const char * firmwaremsg = (firmware ? firmware : "(any)");
+  regex_t regex;
+
+  for (i=0; knowndrives[i].modelregexp; i++){
+    if (i > 0)
+      regfree(&regex);
+    if (compileregex(&regex, knowndrives[i].modelregexp, REG_EXTENDED))
+      continue;
+    if (regexec(&regex, model, 0, NULL, 0))
+      continue;
+    if (firmware && knowndrives[i].firmwareregexp) {
+      regfree(&regex);
+      if (compileregex(&regex, knowndrives[i].firmwareregexp, REG_EXTENDED))
+        continue;
+      if (regexec(&regex, firmware, 0, NULL, 0))
+        continue;
+    }
+    if (++cnt == 1)
+      pout("Drive found in smartmontools Database.  Drive identity strings:\n"
+           "%-*s %s\n"
+           "%-*s %s\n"
+           "match smartmontools Drive Database entry:\n",
+           TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmwaremsg);
+    else if (cnt == 2)
+      pout("and match these additional entries:\n");
+    showonepreset(&knowndrives[i]);
+    pout("\n");
+  }
+  regfree(&regex);
+  if (cnt == 0)
+    pout("No presets are defined for this drive.  Its identity strings:\n"
+         "MODEL:    %s\n"
+         "FIRMWARE: %s\n"
+         "do not match any of the known regular expressions.\n",
+         model, firmwaremsg);
+  return cnt;
+}
+
+// Shows the presets (if any) that are available for the given drive.
+void showpresets(const struct ata_identify_device *drive){
+  int i;
+  char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
+
+  // get the drive's model/firmware strings
+  formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH);
+  formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH);
+  
+  // and search to see if they match values in the table
+  if ((i = lookupdrive(model, firmware)) < 0) {
+    // no matches found
+    pout("No presets are defined for this drive.  Its identity strings:\n"
+         "MODEL:    %s\n"
+         "FIRMWARE: %s\n"
+         "do not match any of the known regular expressions.\n"
+         "Use -P showall to list all known regular expressions.\n",
+         model, firmware);
+    return;
+  }
+  
+  // We found a matching drive.  Print out all information about it.
+  pout("Drive found in smartmontools Database.  Drive identity strings:\n"
+       "%-*s %s\n"
+       "%-*s %s\n"
+       "match smartmontools Drive Database entry:\n",
+       TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmware);
+  showonepreset(&knowndrives[i]);
+  return;
+}
+
+// Sets preset vendor attribute options in opts by finding the entry
+// (if any) for the given drive in knowndrives[].  Values that have
+// already been set in opts will not be changed.  Returns <0 if drive
+// not recognized else index >=0 into drive database.
+int applypresets(const struct ata_identify_device *drive, unsigned char **optsptr,
+                smartmonctrl *con) {
+  int i;
+  unsigned char *opts;
+  char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
+  
+  if (*optsptr==NULL)
+    bytes+=MAX_ATTRIBUTE_NUM;
+  
+  if (*optsptr==NULL && !(*optsptr=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM,1))){
+    pout("Unable to allocate memory in applypresets()");
+    bytes-=MAX_ATTRIBUTE_NUM;
+    EXIT(1);
+  }
+  
+  opts=*optsptr;
+  
+  // get the drive's model/firmware strings
+  formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH);
+  formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH);
+  
+  // Look up the drive in knowndrives[].
+  if ((i = lookupdrive(model, firmware)) >= 0) {
+    
+    // if vendoropts is non-NULL then Attribute interpretation presets
+    if (knowndrives[i].vendoropts) {
+      const unsigned char (* presets)[2];
+      
+      // For each attribute in list of attribute/val pairs...
+      presets = knowndrives[i].vendoropts;
+      while (1) {
+       const int attr = (*presets)[0];
+       const int val  = (*presets)[1];
+       
+       if (!attr)  
+         break;
+       
+       // ... set attribute if user hasn't already done so.
+       if (!opts[attr])
+         opts[attr] = val;
+       presets++;
+      }
+    }
+    
+    // If a special-purpose function is defined for this drive then
+    // call it. Note that if command line arguments or Directives
+    // over-ride this choice, then the specialpurpose function that is
+    // called must deal with this.
+    if (knowndrives[i].specialpurpose)
+      (*knowndrives[i].specialpurpose)(con);
+  }
+  
+  // return <0 if drive wasn't recognized, or index>=0 into database
+  // if it was
+  return i;
+}
index 1d1e7511d99d65df8dcd8f438fefe518d1efd5e5..d669071bcbabfa5fe66e5acb9585912aaabfd608 100644 (file)
@@ -20,7 +20,7 @@
 #ifndef KNOWNDRIVES_H_
 #define KNOWNDRIVES_H_
 
-#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h,v 1.16 2006/04/05 19:50:07 chrfranke Exp $\n"
+#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h,v 1.17 2006/08/09 20:40:19 chrfranke Exp $\n"
 
 /* Structure used to store settings for specific drives in knowndrives[]. The
  * elements are used in the following ways:
  *                  make the output more informative.
  */
 typedef struct drivesettings_s {
-  const char * const modelfamily;
-  const char * const modelregexp;
-  const char * const firmwareregexp;
-  const char * const warningmsg;
-  const unsigned char (* const vendoropts)[2];
-  void (* const specialpurpose)(smartmonctrl *);
-  const char * const functiondesc;
+  const char * modelfamily;
+  const char * modelregexp;
+  const char * firmwareregexp;
+  const char * warningmsg;
+  const unsigned char (* vendoropts)[2];
+  void (* specialpurpose)(smartmonctrl *);
+  const char * functiondesc;
 } drivesettings;
 
 /* Table of settings for known drives.  Defined in knowndrives.c. */
diff --git a/missing b/missing
deleted file mode 100755 (executable)
index 64b5f90..0000000
--- a/missing
+++ /dev/null
@@ -1,353 +0,0 @@
-#! /bin/sh
-# Common stub for a few missing GNU programs while installing.
-
-scriptversion=2004-09-07.08
-
-# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004
-#   Free Software Foundation, Inc.
-# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
-
-# 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, 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.
-
-if test $# -eq 0; then
-  echo 1>&2 "Try \`$0 --help' for more information"
-  exit 1
-fi
-
-run=:
-
-# In the cases where this matters, `missing' is being run in the
-# srcdir already.
-if test -f configure.ac; then
-  configure_ac=configure.ac
-else
-  configure_ac=configure.in
-fi
-
-msg="missing on your system"
-
-case "$1" in
---run)
-  # Try to run requested program, and just exit if it succeeds.
-  run=
-  shift
-  "$@" && exit 0
-  # Exit code 63 means version mismatch.  This often happens
-  # when the user try to use an ancient version of a tool on
-  # a file that requires a minimum version.  In this case we
-  # we should proceed has if the program had been absent, or
-  # if --run hadn't been passed.
-  if test $? = 63; then
-    run=:
-    msg="probably too old"
-  fi
-  ;;
-
-  -h|--h|--he|--hel|--help)
-    echo "\
-$0 [OPTION]... PROGRAM [ARGUMENT]...
-
-Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
-error status if there is no known handling for PROGRAM.
-
-Options:
-  -h, --help      display this help and exit
-  -v, --version   output version information and exit
-  --run           try to run the given command, and emulate it if it fails
-
-Supported PROGRAM values:
-  aclocal      touch file \`aclocal.m4'
-  autoconf     touch file \`configure'
-  autoheader   touch file \`config.h.in'
-  automake     touch all \`Makefile.in' files
-  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
-  flex         create \`lex.yy.c', if possible, from existing .c
-  help2man     touch the output file
-  lex          create \`lex.yy.c', if possible, from existing .c
-  makeinfo     touch the output file
-  tar          try tar, gnutar, gtar, then tar without non-portable flags
-  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]
-
-Send bug reports to <bug-automake@gnu.org>."
-    exit 0
-    ;;
-
-  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
-    echo "missing $scriptversion (GNU Automake)"
-    exit 0
-    ;;
-
-  -*)
-    echo 1>&2 "$0: Unknown \`$1' option"
-    echo 1>&2 "Try \`$0 --help' for more information"
-    exit 1
-    ;;
-
-esac
-
-# Now exit if we have it, but it failed.  Also exit now if we
-# don't have it and --version was passed (most likely to detect
-# the program).
-case "$1" in
-  lex|yacc)
-    # Not GNU programs, they don't have --version.
-    ;;
-
-  tar)
-    if test -n "$run"; then
-       echo 1>&2 "ERROR: \`tar' requires --run"
-       exit 1
-    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
-       exit 1
-    fi
-    ;;
-
-  *)
-    if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
-       # We have it, but it failed.
-       exit 1
-    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
-       # Could not run --version or --help.  This is probably someone
-       # running `$TOOL --version' or `$TOOL --help' to check whether
-       # $TOOL exists and not knowing $TOOL uses missing.
-       exit 1
-    fi
-    ;;
-esac
-
-# If it does not exist, or fails to run (possibly an outdated version),
-# try to emulate it.
-case "$1" in
-  aclocal*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
-         to install the \`Automake' and \`Perl' packages.  Grab them from
-         any GNU archive site."
-    touch aclocal.m4
-    ;;
-
-  autoconf)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`${configure_ac}'.  You might want to install the
-         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
-         archive site."
-    touch configure
-    ;;
-
-  autoheader)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
-         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
-         from any GNU archive site."
-    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
-    test -z "$files" && files="config.h"
-    touch_files=
-    for f in $files; do
-      case "$f" in
-      *:*) touch_files="$touch_files "`echo "$f" |
-                                      sed -e 's/^[^:]*://' -e 's/:.*//'`;;
-      *) touch_files="$touch_files $f.in";;
-      esac
-    done
-    touch $touch_files
-    ;;
-
-  automake*)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
-         You might want to install the \`Automake' and \`Perl' packages.
-         Grab them from any GNU archive site."
-    find . -type f -name Makefile.am -print |
-          sed 's/\.am$/.in/' |
-          while read f; do touch "$f"; done
-    ;;
-
-  autom4te)
-    echo 1>&2 "\
-WARNING: \`$1' is needed, but is $msg.
-         You might have modified some files without having the
-         proper tools for further handling them.
-         You can get \`$1' as part of \`Autoconf' from any GNU
-         archive site."
-
-    file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
-    test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
-    if test -f "$file"; then
-       touch $file
-    else
-       test -z "$file" || exec >$file
-       echo "#! /bin/sh"
-       echo "# Created by GNU Automake missing as a replacement of"
-       echo "#  $ $@"
-       echo "exit 0"
-       chmod +x $file
-       exit 1
-    fi
-    ;;
-
-  bison|yacc)
-    echo 1>&2 "\
-WARNING: \`$1' $msg.  You should only need it if
-         you modified a \`.y' file.  You may need the \`Bison' package
-         in order for those modifications to take effect.  You can get
-         \`Bison' from any GNU archive site."
-    rm -f y.tab.c y.tab.h
-    if [ $# -ne 1 ]; then
-        eval LASTARG="\${$#}"
-       case "$LASTARG" in
-       *.y)
-           SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
-           if [ -f "$SRCFILE" ]; then
-                cp "$SRCFILE" y.tab.c
-           fi
-           SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
-           if [ -f "$SRCFILE" ]; then
-                cp "$SRCFILE" y.tab.h
-           fi
-         ;;
-       esac
-    fi
-    if [ ! -f y.tab.h ]; then
-       echo >y.tab.h
-    fi
-    if [ ! -f y.tab.c ]; then
-       echo 'main() { return 0; }' >y.tab.c
-    fi
-    ;;
-
-  lex|flex)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified a \`.l' file.  You may need the \`Flex' package
-         in order for those modifications to take effect.  You can get
-         \`Flex' from any GNU archive site."
-    rm -f lex.yy.c
-    if [ $# -ne 1 ]; then
-        eval LASTARG="\${$#}"
-       case "$LASTARG" in
-       *.l)
-           SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
-           if [ -f "$SRCFILE" ]; then
-                cp "$SRCFILE" lex.yy.c
-           fi
-         ;;
-       esac
-    fi
-    if [ ! -f lex.yy.c ]; then
-       echo 'main() { return 0; }' >lex.yy.c
-    fi
-    ;;
-
-  help2man)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-        you modified a dependency of a manual page.  You may need the
-        \`Help2man' package in order for those modifications to take
-        effect.  You can get \`Help2man' from any GNU archive site."
-
-    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
-    if test -z "$file"; then
-       file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
-    fi
-    if [ -f "$file" ]; then
-       touch $file
-    else
-       test -z "$file" || exec >$file
-       echo ".ab help2man is required to generate this page"
-       exit 1
-    fi
-    ;;
-
-  makeinfo)
-    echo 1>&2 "\
-WARNING: \`$1' is $msg.  You should only need it if
-         you modified a \`.texi' or \`.texinfo' file, or any other file
-         indirectly affecting the aspect of the manual.  The spurious
-         call might also be the consequence of using a buggy \`make' (AIX,
-         DU, IRIX).  You might want to install the \`Texinfo' package or
-         the \`GNU make' package.  Grab either from any GNU archive site."
-    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
-    if test -z "$file"; then
-      file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
-      file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
-    fi
-    touch $file
-    ;;
-
-  tar)
-    shift
-
-    # We have already tried tar in the generic part.
-    # Look for gnutar/gtar before invocation to avoid ugly error
-    # messages.
-    if (gnutar --version > /dev/null 2>&1); then
-       gnutar "$@" && exit 0
-    fi
-    if (gtar --version > /dev/null 2>&1); then
-       gtar "$@" && exit 0
-    fi
-    firstarg="$1"
-    if shift; then
-       case "$firstarg" in
-       *o*)
-           firstarg=`echo "$firstarg" | sed s/o//`
-           tar "$firstarg" "$@" && exit 0
-           ;;
-       esac
-       case "$firstarg" in
-       *h*)
-           firstarg=`echo "$firstarg" | sed s/h//`
-           tar "$firstarg" "$@" && exit 0
-           ;;
-       esac
-    fi
-
-    echo 1>&2 "\
-WARNING: I can't seem to be able to run \`tar' with the given arguments.
-         You may want to install GNU tar or Free paxutils, or check the
-         command line arguments."
-    exit 1
-    ;;
-
-  *)
-    echo 1>&2 "\
-WARNING: \`$1' is needed, and is $msg.
-         You might have modified some files without having the
-         proper tools for further handling them.  Check the \`README' file,
-         it often tells you about the needed prerequisites for installing
-         this package.  You may also peek at any GNU archive site, in case
-         some other package would contain this missing \`$1' program."
-    exit 1
-    ;;
-esac
-
-exit 0
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "scriptversion="
-# time-stamp-format: "%:y-%02m-%02d.%02H"
-# time-stamp-end: "$"
-# End:
diff --git a/os_darwin.c b/os_darwin.c
deleted file mode 100644 (file)
index d733290..0000000
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * os_darwin.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2004-6 Geoffrey Keating <geoffk@geoffk.org>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <stdbool.h>
-#include <errno.h>
-#include <mach/mach.h>
-#include <mach/mach_error.h>
-#include <mach/mach_init.h>
-#include <IOKit/IOCFPlugIn.h>
-#include <IOKit/IOKitLib.h>
-#include <IOKit/IOReturn.h>
-#include <IOKit/IOBSD.h>
-#include <IOKit/storage/ata/IOATAStorageDefines.h>
-#include <IOKit/storage/ata/ATASMARTLib.h>
-#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
-#include <IOKit/storage/IOMedia.h>
-#include <CoreFoundation/CoreFoundation.h>
-
-  // No, I don't know why there isn't a header for this.
-#define kIOATABlockStorageDeviceClass   "IOATABlockStorageDevice"
-
-#include "config.h"
-#include "int64.h"
-#include "atacmds.h"
-#include "scsicmds.h"
-#include "utility.h"
-
-#include "os_darwin.h"
-
-// Needed by '-V' option (CVS versioning) of smartd/smartctl
-const char *os_XXXX_c_cvsid="$Id: os_darwin.c,v 1.13 2006/04/12 14:54:28 ballen4705 Exp $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
-
-// Print examples for smartctl.
-void print_smartctl_examples(){
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
-  printf(
-         "  smartctl -a disk0                            (Prints all SMART information)\n\n"
-         "  smartctl -t long /dev/disk0              (Executes extended disk self-test)\n\n"
-#ifdef HAVE_GETOPT_LONG
-         "  smartctl --smart=on --saveauto=on /dev/rdisk0 (Enables SMART on first disk)\n\n"
-         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/disk0\n"
-         "                                        (Prints Self-Test & Attribute errors)\n\n"
-#else
-         "  smartctl -s on -S on /dev/rdisk0              (Enables SMART on first disk)\n\n"
-         "  smartctl -A -l selftest -q errorsonly /dev/disk0\n"
-         "                                        (Prints Self-Test & Attribute errors)\n\n"
-#endif
-         "  smartctl -a IOService:/MacRISC2PE/pci@f4000000/AppleMacRiscPCI/ata-6@D/AppleKauaiATA/ATADeviceNub@0/IOATABlockStorageDriver/IOATABlockStorageDevice\n"
-         "                                                 (You can use IOService: ...)\n\n"
-         "  smartctl -c IODeviceTree:/pci@f4000000/ata-6@D/@0:0\n"
-         "                                                       (... Or IODeviceTree:)\n"
-         );
-  return;
-}
-
-// tries to guess device type given the name (a path).  See utility.h
-// for return values.
-int guess_device_type (const char* dev_name) {
-  // Only ATA is supported right now, so that's what it'd better be.
-  dev_name = dev_name;  // suppress unused warning.
-  return CONTROLLER_ATA;
-}
-
-// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
-// smartd.  Returns number N of devices, or -1 if out of
-// memory. Allocates N+1 arrays: one of N pointers (devlist); the
-// other N arrays each contain null-terminated character strings.  In
-// the case N==0, no arrays are allocated because the array of 0
-// pointers has zero length, equivalent to calling malloc(0).
-int make_device_names (char*** devlist, const char* name) {
-  IOReturn err;
-  io_iterator_t i;
-  io_object_t device;
-  int result;
-  int index;
-  const char * cls;
-
-  if (strcmp (name, "ATA") == 0)
-    cls = kIOATABlockStorageDeviceClass;
-  else  // only ATA supported right now.
-    return 0;
-
-  err = IOServiceGetMatchingServices (kIOMasterPortDefault,
-                                     IOServiceMatching (cls),
-                                     &i);
-  if (err != kIOReturnSuccess)
-    return -1;
-
-  // Count the devices.
-  for (result = 0; (device = IOIteratorNext (i)) != MACH_PORT_NULL; result++)
-    IOObjectRelease (device);
-
-  // Create an array of service names.
-  IOIteratorReset (i);
-  *devlist = Calloc (result, sizeof (char *));
-  if (! *devlist)
-    goto error;
-  for (index = 0; (device = IOIteratorNext (i)) != MACH_PORT_NULL; index++)
-    {
-      io_string_t devName;
-      IORegistryEntryGetPath(device, kIOServicePlane, devName);
-      IOObjectRelease (device);
-
-      (*devlist)[index] = CustomStrDup (devName, true, __LINE__, __FILE__);
-      if (! (*devlist)[index])
-       goto error;
-    }
-  IOObjectRelease (i);
-
-  return result;
-
- error:
-  IOObjectRelease (i);
-  if (*devlist)
-    {
-      for (index = 0; index < result; index++)
-       if ((*devlist)[index])
-         FreeNonZero ((*devlist)[index], 0, __LINE__, __FILE__);
-      FreeNonZero (*devlist, result * sizeof (char *), __LINE__, __FILE__);
-    }
-  return -1;
-}
-
-// Information that we keep about each device.
-
-static struct {
-  io_object_t ioob;
-  bool hassmart;
-  IOCFPlugInInterface **plugin;
-  IOATASMARTInterface **smartIf;
-} devices[20];
-
-// Like open().  Return non-negative integer handle, only used by the
-// functions below.  type=="ATA" or "SCSI".  The return value is
-// an index into the devices[] array.  If the device can't be opened,
-// sets errno and returns -1.
-// Acceptable device names are:
-// /dev/disk*
-// /dev/rdisk*
-// disk*
-// IOService:*
-// IODeviceTree:*
-int deviceopen(const char *pathname, char *type){
-  size_t devnum;
-  const char *devname;
-  io_object_t disk;
-  
-  if (strcmp (type, "ATA") != 0)
-    {
-      errno = EINVAL;
-      return -1;
-    }
-  
-  // Find a free device number.
-  for (devnum = 0; devnum < sizeof (devices) / sizeof (devices[0]); devnum++)
-    if (! devices[devnum].ioob)
-      break;
-  if (devnum == sizeof (devices) / sizeof (devices[0]))
-    {
-      errno = EMFILE;
-      return -1;
-    }
-  
-  devname = NULL;
-  if (strncmp (pathname, "/dev/rdisk", 10) == 0)
-    devname = pathname + 6;
-  else if (strncmp (pathname, "/dev/disk", 9) == 0)
-    devname = pathname + 5;
-  else if (strncmp (pathname, "disk", 4) == 0)
-    // allow user to just say 'disk0'
-    devname = pathname;
-
-  // Find the device.
-  if (devname)
-    {
-      CFMutableDictionaryRef matcher;
-      matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname);
-      disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher);
-    }
-  else
-    {
-      disk = IORegistryEntryFromPath (kIOMasterPortDefault, pathname);
-    }
-
-  if (! disk)
-    {
-      errno = ENOENT;
-      return -1;
-    }
-  
-  // Find the ATA block storage driver that is the parent of this device
-  while (! IOObjectConformsTo (disk, kIOATABlockStorageDeviceClass))
-    {
-      IOReturn err;
-      io_object_t notdisk = disk;
-
-      err = IORegistryEntryGetParentEntry (notdisk, kIOServicePlane, &disk);
-      if (err != kIOReturnSuccess || ! disk)
-       {
-         errno = ENODEV;
-         IOObjectRelease (notdisk);
-         return -1;
-       }
-    }
-
-  devices[devnum].ioob = disk;
-  
-  {
-    CFDictionaryRef diskChars = NULL;
-    CFNumberRef diskFeatures = NULL;
-    UInt32 ataFeatures;
-
-    // Determine whether the drive actually supports SMART.
-    if ((diskChars = IORegistryEntryCreateCFProperty (disk, 
-                             CFSTR (kIOPropertyDeviceCharacteristicsKey),
-                                                     kCFAllocatorDefault,
-                                                     kNilOptions)) != NULL
-       && CFDictionaryGetValueIfPresent (diskChars, CFSTR ("ATA Features"),
-                                         (const void **)&diskFeatures)
-       && CFNumberGetValue (diskFeatures, kCFNumberLongType, &ataFeatures)
-       && (ataFeatures & kIOATAFeatureSMART))
-      devices[devnum].hassmart = true;
-    else
-      devices[devnum].hassmart = false;
-    if (diskChars)
-      CFRelease (diskChars);
-  }
-  
-  {
-    SInt32 dummy;
-  
-    devices[devnum].plugin = NULL;
-    devices[devnum].smartIf = NULL;
-
-    // Create an interface to the ATA SMART library.
-    if (devices[devnum].hassmart
-       && IOCreatePlugInInterfaceForService (disk,
-                                             kIOATASMARTUserClientTypeID,
-                                             kIOCFPlugInInterfaceID,
-                                             &devices[devnum].plugin,
-                                             &dummy) == kIOReturnSuccess)
-      (*devices[devnum].plugin)->QueryInterface
-       (devices[devnum].plugin,
-        CFUUIDGetUUIDBytes ( kIOATASMARTInterfaceID),
-        (LPVOID) &devices[devnum].smartIf);
-  }
-  
-  return devnum;
-}
-
-// Like close().  Acts only on integer handles returned by
-// deviceopen() above.
-int deviceclose(int fd){
-  if (devices[fd].smartIf)
-    (*devices[fd].smartIf)->Release (devices[fd].smartIf);
-  if (devices[fd].plugin)
-    IODestroyPlugInInterface (devices[fd].plugin);
-  IOObjectRelease (devices[fd].ioob);
-  devices[fd].ioob = MACH_PORT_NULL;
-  return 0;
-}
-
-// Interface to ATA devices.  See os_linux.c for the cannonical example.
-// DETAILED DESCRIPTION OF ARGUMENTS
-//   device: is the integer handle provided by deviceopen()
-//   command: defines the different operations, see atacmds.h
-//   select: additional input data IF NEEDED (which log, which type of
-//           self-test).
-//   data:   location to write output data, IF NEEDED (1 or 512 bytes).
-//   Note: not all commands use all arguments.
-// RETURN VALUES (for all commands BUT command==STATUS_CHECK)
-//  -1 if the command failed
-//   0 if the command succeeded,
-// RETURN VALUES if command==STATUS_CHECK
-//  -1 if the command failed OR the disk SMART status can't be determined
-//   0 if the command succeeded and disk SMART status is "OK"
-//   1 if the command succeeded and disk SMART status is "FAILING"
-
-// Things that aren't available in the Darwin interfaces:
-// - Tests other than short and extended (in particular, can't run
-//   an immediate offline test)
-// - Captive-mode tests, aborting tests
-// - ability to switch automatic offline testing on or off
-
-// Note that some versions of Darwin, at least 7H63 and earlier,
-// have a buggy library that treats the boolean value in
-// SMARTEnableDisableOperations, SMARTEnableDisableAutosave, and
-// SMARTExecuteOffLineImmediate as always being true.
-int marvell_command_interface(int fd, smart_command_set command,
-                     int select, char *data)
-{ return -1; }
-int
-ata_command_interface(int fd, smart_command_set command,
-                     int select, char *data)
-{
-  IOATASMARTInterface **ifp = devices[fd].smartIf;
-  IOATASMARTInterface *smartIf;
-  IOReturn err;
-  
-  if (! ifp)
-    return -1;
-  smartIf = *ifp;
-
-  switch (command)
-    {
-    case STATUS:
-      return 0;
-    case STATUS_CHECK:
-      {
-       Boolean is_failing;
-       err = smartIf->SMARTReturnStatus (ifp, &is_failing);
-       if (err == kIOReturnSuccess && is_failing)
-         return 1;
-       break;
-      }
-    case ENABLE:
-    case DISABLE:
-      err = smartIf->SMARTEnableDisableOperations (ifp, command == ENABLE);
-      break;
-    case AUTOSAVE:
-      err = smartIf->SMARTEnableDisableAutosave (ifp, select != 0);
-      break;
-    case IMMEDIATE_OFFLINE:
-      if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST)
-       {
-         errno = EINVAL;
-         return -1;
-       }
-      err = smartIf->SMARTExecuteOffLineImmediate (ifp, 
-                                                  select == EXTEND_SELF_TEST);
-      break;
-    case READ_VALUES:
-      err = smartIf->SMARTReadData (ifp, (ATASMARTData *)data);
-      break;
-    case READ_THRESHOLDS:
-      err = smartIf->SMARTReadDataThresholds (ifp, 
-                                             (ATASMARTDataThresholds *)data);
-      break;
-    case READ_LOG:
-      err = smartIf->SMARTReadLogAtAddress (ifp, select, data, 512);
-      break;
-    case WRITE_LOG:
-      err = smartIf->SMARTWriteLogAtAddress (ifp, select, data, 512);
-      break;
-    case IDENTIFY:
-      {
-       UInt32 dummy;
-       err = smartIf->GetATAIdentifyData (ifp, data, 512, &dummy);
-       if (err == kIOReturnSuccess && isbigendian())
-         {
-           int i;
-           /* The system has already byte-swapped, undo it.  */
-           for (i = 0; i < 256; i+=2)
-             swap2 (data + i);
-         }
-      }
-      break;
-    case CHECK_POWER_MODE:
-      // The information is right there in the device registry, but how
-      // to get to it portably?
-    default:
-      errno = ENOTSUP;
-      return -1;
-    }
-  if (err == kIOReturnExclusiveAccess)
-    errno = EBUSY;
-  return err == kIOReturnSuccess ? 0 : -1;
-}
-
-// There's no special handling needed for hidden devices, the kernel
-// must deal with them.
-int escalade_command_interface(int fd, int escalade_port, int escalade_type,
-                              smart_command_set command, int select,
-                              char *data)
-{
-  fd = fd;
-  escalade_port = escalade_port;
-  escalade_type = escalade_type;
-  command = command;
-  select = select;
-  data = data;
-  return -1;
-}
-
-// Interface to SCSI devices.  See os_linux.c
-int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) {
-  return -ENOSYS;
-}
diff --git a/os_darwin.cpp b/os_darwin.cpp
new file mode 100644 (file)
index 0000000..867f804
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * os_darwin.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Geoffrey Keating <geoffk@geoffk.org>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdbool.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <mach/mach_init.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOReturn.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/storage/IOBlockStorageDevice.h>
+#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/storage/ata/IOATAStorageDefines.h>
+#include <IOKit/storage/ata/ATASMARTLib.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+  // No, I don't know why there isn't a header for this.
+#define kIOATABlockStorageDeviceClass   "IOATABlockStorageDevice"
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+#include "os_darwin.h"
+
+// Needed by '-V' option (CVS versioning) of smartd/smartctl
+const char *os_XXXX_c_cvsid="$Id: os_darwin.cpp,v 1.18 2006/09/20 16:17:31 shattered Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+// Print examples for smartctl.
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+  printf(
+         "  smartctl -a disk0                            (Prints all SMART information)\n\n"
+         "  smartctl -t long /dev/disk0              (Executes extended disk self-test)\n\n"
+#ifdef HAVE_GETOPT_LONG
+         "  smartctl --smart=on --saveauto=on /dev/rdisk0 (Enables SMART on first disk)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/disk0\n"
+         "                                        (Prints Self-Test & Attribute errors)\n\n"
+#else
+         "  smartctl -s on -S on /dev/rdisk0              (Enables SMART on first disk)\n\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/disk0\n"
+         "                                        (Prints Self-Test & Attribute errors)\n\n"
+#endif
+         "  smartctl -a IOService:/MacRISC2PE/pci@f4000000/AppleMacRiscPCI/ata-6@D/AppleKauaiATA/ATADeviceNub@0/IOATABlockStorageDriver/IOATABlockStorageDevice\n"
+         "                                                 (You can use IOService: ...)\n\n"
+         "  smartctl -c IODeviceTree:/pci@f4000000/ata-6@D/@0:0\n"
+         "                                                       (... Or IODeviceTree:)\n"
+         );
+  return;
+}
+
+// tries to guess device type given the name (a path).  See utility.h
+// for return values.
+int guess_device_type (const char* dev_name) {
+  // Only ATA is supported right now, so that's what it'd better be.
+  dev_name = dev_name;  // suppress unused warning.
+  return CONTROLLER_ATA;
+}
+
+// Determine whether 'dev' is a SMART-capable device.
+static bool is_smart_capable (io_object_t dev) {
+  CFTypeRef smartCapableKey;
+  CFDictionaryRef diskChars;
+
+  // If the device has kIOPropertySMARTCapableKey, then it's capable,
+  // no matter what it looks like.
+  smartCapableKey = IORegistryEntryCreateCFProperty
+    (dev, CFSTR (kIOPropertySMARTCapableKey),
+     kCFAllocatorDefault, 0);
+  if (smartCapableKey)
+    {
+      CFRelease (smartCapableKey);
+      return true;
+    }
+
+  // If it's an kIOATABlockStorageDeviceClass then we're successful
+  // only if its ATA features indicate it supports SMART.
+  if (IOObjectConformsTo (dev, kIOATABlockStorageDeviceClass)
+      && (diskChars = (CFDictionaryRef)IORegistryEntryCreateCFProperty                                                                                                           
+         (dev, CFSTR (kIOPropertyDeviceCharacteristicsKey),
+          kCFAllocatorDefault, kNilOptions)) != NULL)
+    {
+      CFNumberRef diskFeatures = NULL;
+      UInt32 ataFeatures = 0;
+
+      if (CFDictionaryGetValueIfPresent (diskChars, CFSTR ("ATA Features"),
+                                        (const void **)&diskFeatures))
+       CFNumberGetValue (diskFeatures, kCFNumberLongType,
+                         &ataFeatures);
+      CFRelease (diskChars);
+      if (diskFeatures)
+       CFRelease (diskFeatures);
+      
+      return (ataFeatures & kIOATAFeatureSMART) != 0;
+    }
+  return false;
+}
+
+
+// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
+// smartd.  Returns number N of devices, or -1 if out of
+// memory. Allocates N+1 arrays: one of N pointers (devlist); the
+// other N arrays each contain null-terminated character strings.  In
+// the case N==0, no arrays are allocated because the array of 0
+// pointers has zero length, equivalent to calling malloc(0).
+int make_device_names (char*** devlist, const char* name) {
+  IOReturn err;
+  io_iterator_t i;
+  io_object_t device = MACH_PORT_NULL;
+  int result;
+  int index;
+
+  // We treat all devices as ATA so long as they support SMARTLib.
+  if (strcmp (name, "ATA") != 0)
+    return 0;
+
+  err = IOServiceGetMatchingServices 
+    (kIOMasterPortDefault, IOServiceMatching (kIOBlockStorageDeviceClass), &i);
+  if (err != kIOReturnSuccess)
+    return -1;
+
+  // Count the devices.
+  result = 0;
+  while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
+    if (is_smart_capable (device))
+      result++;
+    IOObjectRelease (device);
+  }
+
+  // Create an array of service names.
+  IOIteratorReset (i);
+  *devlist = (char**)Calloc (result, sizeof (char *)); 
+  if (! *devlist)
+    goto error;
+  index = 0;
+  while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
+    if (is_smart_capable (device))
+      {
+       io_string_t devName;
+       IORegistryEntryGetPath(device, kIOServicePlane, devName);
+       (*devlist)[index] = CustomStrDup (devName, true, __LINE__, __FILE__);
+       if (! (*devlist)[index])
+         goto error;
+       index++;
+      }
+    IOObjectRelease (device);
+  }
+
+  IOObjectRelease (i);
+  return result;
+
+ error:
+  if (device != MACH_PORT_NULL)
+    IOObjectRelease (device);
+  IOObjectRelease (i);
+  if (*devlist)
+    {
+      for (index = 0; index < result; index++)
+       if ((*devlist)[index])
+         FreeNonZero ((*devlist)[index], 0, __LINE__, __FILE__);
+      FreeNonZero (*devlist, result * sizeof (char *), __LINE__, __FILE__);
+    }
+  return -1;
+}
+
+// Information that we keep about each device.
+
+static struct {
+  io_object_t ioob;
+  IOCFPlugInInterface **plugin;
+  IOATASMARTInterface **smartIf;
+} devices[20];
+
+// Like open().  Return non-negative integer handle, only used by the
+// functions below.  type=="ATA" or "SCSI".  The return value is
+// an index into the devices[] array.  If the device can't be opened,
+// sets errno and returns -1.
+// Acceptable device names are:
+// /dev/disk*
+// /dev/rdisk*
+// disk*
+// IOService:*
+// IODeviceTree:*
+int deviceopen(const char *pathname, char *type){
+  size_t devnum;
+  const char *devname;
+  io_object_t disk;
+  
+  if (strcmp (type, "ATA") != 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+  
+  // Find a free device number.
+  for (devnum = 0; devnum < sizeof (devices) / sizeof (devices[0]); devnum++)
+    if (! devices[devnum].ioob)
+      break;
+  if (devnum == sizeof (devices) / sizeof (devices[0]))
+    {
+      errno = EMFILE;
+      return -1;
+    }
+  
+  devname = NULL;
+  if (strncmp (pathname, "/dev/rdisk", 10) == 0)
+    devname = pathname + 6;
+  else if (strncmp (pathname, "/dev/disk", 9) == 0)
+    devname = pathname + 5;
+  else if (strncmp (pathname, "disk", 4) == 0)
+    // allow user to just say 'disk0'
+    devname = pathname;
+
+  // Find the device.
+  if (devname)
+    {
+      CFMutableDictionaryRef matcher;
+      matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname);
+      disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher);
+    }
+  else
+    {
+      disk = IORegistryEntryFromPath (kIOMasterPortDefault, pathname);
+    }
+
+  if (! disk)
+    {
+      errno = ENOENT;
+      return -1;
+    }
+  
+  // Find a SMART-capable driver which is a parent of this device.
+  while (! is_smart_capable (disk))
+    {
+      IOReturn err;
+      io_object_t prevdisk = disk;
+
+      // Find this device's parent and try again.
+      err = IORegistryEntryGetParentEntry (disk, kIOServicePlane, &disk);
+      if (err != kIOReturnSuccess || ! disk)
+       {
+         errno = ENODEV;
+         IOObjectRelease (prevdisk);
+         return -1;
+       }
+    }
+  
+  devices[devnum].ioob = disk;
+
+  {
+    SInt32 dummy;
+  
+    devices[devnum].plugin = NULL;
+    devices[devnum].smartIf = NULL;
+
+    // Create an interface to the ATA SMART library.
+    if (IOCreatePlugInInterfaceForService (disk,
+                                          kIOATASMARTUserClientTypeID,
+                                          kIOCFPlugInInterfaceID,
+                                          &devices[devnum].plugin,
+                                          &dummy) == kIOReturnSuccess)
+      (*devices[devnum].plugin)->QueryInterface
+       (devices[devnum].plugin,
+        CFUUIDGetUUIDBytes ( kIOATASMARTInterfaceID),
+         (void **)&devices[devnum].smartIf);
+  }
+  
+  return devnum;
+}
+
+// Like close().  Acts only on integer handles returned by
+// deviceopen() above.
+int deviceclose(int fd){
+  if (devices[fd].smartIf)
+    (*devices[fd].smartIf)->Release (devices[fd].smartIf);
+  if (devices[fd].plugin)
+    IODestroyPlugInInterface (devices[fd].plugin);
+  IOObjectRelease (devices[fd].ioob);
+  devices[fd].ioob = MACH_PORT_NULL;
+  return 0;
+}
+
+// Interface to ATA devices.  See os_linux.cpp for the cannonical example.
+// DETAILED DESCRIPTION OF ARGUMENTS
+//   device: is the integer handle provided by deviceopen()
+//   command: defines the different operations, see atacmds.h
+//   select: additional input data IF NEEDED (which log, which type of
+//           self-test).
+//   data:   location to write output data, IF NEEDED (1 or 512 bytes).
+//   Note: not all commands use all arguments.
+// RETURN VALUES (for all commands BUT command==STATUS_CHECK)
+//  -1 if the command failed
+//   0 if the command succeeded,
+// RETURN VALUES if command==STATUS_CHECK
+//  -1 if the command failed OR the disk SMART status can't be determined
+//   0 if the command succeeded and disk SMART status is "OK"
+//   1 if the command succeeded and disk SMART status is "FAILING"
+
+// Things that aren't available in the Darwin interfaces:
+// - Tests other than short and extended (in particular, can't run
+//   an immediate offline test)
+// - Captive-mode tests, aborting tests
+// - ability to switch automatic offline testing on or off
+
+// Note that some versions of Darwin, at least 7H63 and earlier,
+// have a buggy library that treats the boolean value in
+// SMARTEnableDisableOperations, SMARTEnableDisableAutosave, and
+// SMARTExecuteOffLineImmediate as always being true.
+int
+ata_command_interface(int fd, smart_command_set command,
+                     int select, char *data)
+{
+  IOATASMARTInterface **ifp = devices[fd].smartIf;
+  IOATASMARTInterface *smartIf;
+  IOReturn err;
+  int timeoutCount = 2;
+  
+  if (! ifp)
+    return -1;
+  smartIf = *ifp;
+
+  do {
+    switch (command)
+      {
+      case STATUS:
+       return 0;
+      case STATUS_CHECK:
+       {
+         Boolean is_failing;
+         err = smartIf->SMARTReturnStatus (ifp, &is_failing);
+         if (err == kIOReturnSuccess && is_failing)
+           return 1;
+         break;
+       }
+      case ENABLE:
+      case DISABLE:
+       err = smartIf->SMARTEnableDisableOperations (ifp, command == ENABLE);
+       break;
+      case AUTOSAVE:
+       err = smartIf->SMARTEnableDisableAutosave (ifp, select != 0);
+       break;
+      case IMMEDIATE_OFFLINE:
+       if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST)
+         {
+           errno = EINVAL;
+           return -1;
+         }
+       err = smartIf->SMARTExecuteOffLineImmediate (ifp, 
+                                                    select == EXTEND_SELF_TEST);
+       break;
+      case READ_VALUES:
+       err = smartIf->SMARTReadData (ifp, (ATASMARTData *)data);
+       break;
+      case READ_THRESHOLDS:
+       err = smartIf->SMARTReadDataThresholds (ifp, 
+                                               (ATASMARTDataThresholds *)data);
+       break;
+      case READ_LOG:
+       err = smartIf->SMARTReadLogAtAddress (ifp, select, data, 512);
+       break;
+      case WRITE_LOG:
+       err = smartIf->SMARTWriteLogAtAddress (ifp, select, data, 512);
+       break;
+      case IDENTIFY:
+       {
+         UInt32 dummy;
+         err = smartIf->GetATAIdentifyData (ifp, data, 512, &dummy);
+         if (err != kIOReturnSuccess)
+           printf ("identify failed: %d\n", (int) err);
+         if (err == kIOReturnSuccess && isbigendian())
+           {
+             int i;
+             /* The system has already byte-swapped, undo it.  */
+             for (i = 0; i < 256; i+=2)
+               swap2 (data + i);
+           }
+       }
+       break;
+      case CHECK_POWER_MODE:
+       // The information is right there in the device registry, but how
+       // to get to it portably?
+      default:
+       errno = ENOTSUP;
+       return -1;
+      }
+    /* This bit is a bit strange.  Apparently, when the drive is spun
+       down, the intended behaviour of these calls is that they fail,
+       return kIOReturnTimeout and then power the drive up.  So if
+       you get a timeout, you have to try again to get the actual
+       command run, but the drive is already powering up so you can't
+       use this for CHECK_POWER_MODE.  */
+  } while (err == kIOReturnTimeout && timeoutCount-- > 0);
+  if (err == kIOReturnExclusiveAccess)
+    errno = EBUSY;
+  return err == kIOReturnSuccess ? 0 : -1;
+}
+
+// There's no special handling needed for hidden devices, the kernel
+// must deal with them.
+int escalade_command_interface(int fd, int escalade_port, int escalade_type,
+                              smart_command_set command, int select,
+                              char *data)
+{
+  fd = fd;
+  escalade_port = escalade_port;
+  escalade_type = escalade_type;
+  command = command;
+  select = select;
+  data = data;
+  return -1;
+}
+
+int marvell_command_interface(int fd, smart_command_set command,
+                     int select, char *data)
+{ 
+  fd = fd;
+  command = command;
+  select = select;
+  data = data;
+  return -1;
+}
+
+int highpoint_command_interface(int fd, smart_command_set command, int select, char *data)
+{
+  fd = fd;
+  command = command;
+  select = select;
+  data = data;
+  return -1;
+}
+
+// Interface to SCSI devices.  See os_linux.c
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) {
+  fd = fd;
+  iop = iop;
+  report = report;
+  return -ENOSYS;
+}
diff --git a/os_freebsd.c b/os_freebsd.c
deleted file mode 100644 (file)
index 19bbef1..0000000
+++ /dev/null
@@ -1,1069 +0,0 @@
-/*
- * os_freebsd.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2003-6 Eduard Martinescu <smartmontools-support@lists.sourceforge.net>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <err.h>
-#include <camlib.h>
-#include <cam/scsi/scsi_message.h>
-#include <sys/ata.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <glob.h>
-#include <fcntl.h>
-#include <stddef.h>
-
-
-#include "config.h"
-#include "int64.h"
-#include "atacmds.h"
-#include "scsicmds.h"
-#include "utility.h"
-#include "os_freebsd.h"
-
-static const char *filenameandversion="$Id: os_freebsd.c,v 1.49 2006/04/12 14:54:28 ballen4705 Exp $";
-
-const char *os_XXXX_c_cvsid="$Id: os_freebsd.c,v 1.49 2006/04/12 14:54:28 ballen4705 Exp $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
-
-// to hold onto exit code for atexit routine
-extern int exitstatus;
-
-// Private table of open devices: guaranteed zero on startup since
-// part of static data.
-struct freebsd_dev_channel *devicetable[FREEBSD_MAXDEV];
-
-// forward declaration
-static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *ch);
-
-// print examples for smartctl
-void print_smartctl_examples(){
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
-         "  smartctl -a /dev/ad0                       (Prints all SMART information)\n\n"
-         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/ad0\n"
-         "                                              (Enables SMART on first disk)\n\n"
-         "  smartctl -t long /dev/ad0              (Executes extended disk self-test)\n\n"
-         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/ad0\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-         "                                      (Prints Self-Test & Attribute errors)\n\n"
-         "  smartctl -a --device=3ware,2 /dev/twa0\n"
-         "  smartctl -a --device=3ware,2 /dev/twe0\n"
-         "                              (Prints all SMART information for ATA disk on\n"
-         "                                 third port of first 3ware RAID controller)\n"
-         );
-#else
-  printf(
-         "  smartctl -a /dev/ad0                       (Prints all SMART information)\n"
-         "  smartctl -s on -o on -S on /dev/ad0         (Enables SMART on first disk)\n"
-         "  smartctl -t long /dev/ad0              (Executes extended disk self-test)\n"
-         "  smartctl -A -l selftest -q errorsonly /dev/ad0\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-         "  smartctl -a -d 3ware,2 /dev/twa0\n"
-         "  smartctl -a -d 3ware,2 /dev/twe0\n"
-         );
-#endif
-  return;
-}
-
-// Like open().  Return positive integer handle, used by functions below only.  mode=="ATA" or "SCSI".
-int deviceopen (const char* dev, char* mode __unused) {
-  struct freebsd_dev_channel *fdchan;
-  int parse_ok, i;
-
-  // Search table for a free entry
-  for (i=0; i<FREEBSD_MAXDEV; i++)
-    if (!devicetable[i])
-      break;
-  
-  // If no free entry found, return error.  We have max allowed number
-  // of "file descriptors" already allocated.
-  if (i==FREEBSD_MAXDEV) {
-    errno=EMFILE;
-    return -1;
-  }
-
-  fdchan = calloc(1,sizeof(struct freebsd_dev_channel));
-  if (fdchan == NULL) {
-    // errno already set by call to malloc()
-    return -1;
-  }
-
-  parse_ok = parse_ata_chan_dev (dev,fdchan);
-  if (parse_ok == CONTROLLER_UNKNOWN) {
-    free(fdchan);
-    errno = ENOTTY;
-    return -1; // can't handle what we don't know
-  }
-
-  if (parse_ok == CONTROLLER_ATA) {
-#ifdef IOCATAREQUEST
-    if ((fdchan->device = open(dev,O_RDONLY))<0) {
-#else
-    if ((fdchan->atacommand = open("/dev/ata",O_RDWR))<0) {
-#endif
-      int myerror = errno;      //preserve across free call
-      free (fdchan);
-      errno = myerror;
-      return -1;
-    }
-  }
-
-  if (parse_ok == CONTROLLER_3WARE_678K_CHAR) {
-    char buf[512];
-    sprintf(buf,"/dev/twe%d",fdchan->device);
-#ifdef IOCATAREQUEST
-    if ((fdchan->device = open(buf,O_RDWR))<0) {
-#else
-    if ((fdchan->atacommand = open(buf,O_RDWR))<0) {
-#endif
-      int myerror = errno; // preserver across free call
-      free(fdchan);
-      errno=myerror;
-      return -1;
-    }
-  }
-
-  if (parse_ok == CONTROLLER_3WARE_9000_CHAR) {
-    char buf[512];
-    sprintf(buf,"/dev/twa%d",fdchan->device);
-#ifdef IOCATAREQUEST
-    if ((fdchan->device = open(buf,O_RDWR))<0) {
-#else
-    if ((fdchan->atacommand = open(buf,O_RDWR))<0) {
-#endif
-      int myerror = errno; // preserver across free call
-      free(fdchan);
-      errno=myerror;
-      return -1;
-    }
-  }
-
-  if (parse_ok == CONTROLLER_SCSI) {
-    // this is really a NO-OP, as the parse takes care
-    // of filling in correct details
-  }
-  
-  // return pointer to "file descriptor" table entry, properly offset.
-  devicetable[i]=fdchan;
-  return i+FREEBSD_FDOFFSET;
-}
-
-// Returns 1 if device not available/open/found else 0.  Also shifts fd into valid range.
-static int isnotopen(int *fd, struct freebsd_dev_channel** fdchan) {
-  // put valid "file descriptor" into range 0...FREEBSD_MAXDEV-1
-  *fd -= FREEBSD_FDOFFSET;
-  
-  // check for validity of "file descriptor".
-  if (*fd<0 || *fd>=FREEBSD_MAXDEV || !((*fdchan)=devicetable[*fd])) {
-    errno = ENODEV;
-    return 1;
-  }
-  
-  return 0;
-}
-
-// Like close().  Acts on handles returned by above function.
-int deviceclose (int fd) {
-  struct freebsd_dev_channel *fdchan;
-  int failed = 0;
-
-  // check for valid file descriptor
-  if (isnotopen(&fd, &fdchan))
-    return -1;
-  
-
-  // did we allocate a SCSI device name?
-  if (fdchan->devname)
-    free(fdchan->devname);
-  
-  // close device, if open
-#ifdef IOCATAREQUEST
-  if (fdchan->device)
-    failed=close(fdchan->device);
-#else
-  if (fdchan->atacommand)
-    failed=close(fdchan->atacommand);
-#endif
-
-  if (fdchan->scsicontrol)
-    failed=close(fdchan->scsicontrol);
-  
-  // if close succeeded, then remove from device list
-  // Eduard, should we also remove it from list if close() fails?  I'm
-  // not sure. Here I only remove it from list if close() worked.
-  if (!failed) {
-    free(fdchan);
-    devicetable[fd]=NULL;
-  }
-  
-  return failed;
-}
-
-#define NO_RETURN 0
-#define BAD_SMART 1
-#define NO_DISK_3WARE 2
-#define BAD_KERNEL 3
-#define MAX_MSG 3
-
-// Utility function for printing warnings
-void printwarning(int msgNo, const char* extra) {
-  static int printed[] = {0,0,0,0};
-  static const char* message[]={
-    "The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n can not be retrieved with this version of ATAng, please do not rely on this value\nYou should update to at least 5.2\n",
-    
-    "Error SMART Status command failed\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
-    
-    "You must specify a DISK # for 3ware drives with -d 3ware,<n> where <n> begins with 1 for first disk drive\n",
-    
-    "ATA support is not provided for this kernel version. Please ugrade to a recent 5-CURRENT kernel (post 09/01/2003 or so)\n"
-  };
-
-  if (msgNo >= 0 && msgNo <= MAX_MSG) {
-    if (!printed[msgNo]) {
-      printed[msgNo] = 1;
-      pout("%s", message[msgNo]);
-      if (extra)
-        pout("%s",extra);
-    }
-  }
-  return;
-}
-
-
-// Interface to ATA devices.  See os_linux.c
-int marvell_command_interface(int fd __unused, smart_command_set command __unused, int select __unused, char *data __unused) {
-       return -1;
-}
-
-int ata_command_interface(int fd, smart_command_set command, int select, char *data) {
-#if !defined(ATAREQUEST) && !defined(IOCATAREQUEST)
-  // sorry, but without ATAng, we can't do anything here
-  printwarning(BAD_KERNEL,NULL);
-  errno = ENOSYS;
-  return -1;
-#else
-  struct freebsd_dev_channel* con;
-  int retval, copydata=0;
-#ifdef IOCATAREQUEST
-  struct ata_ioc_request request;
-#else
-  struct ata_cmd iocmd;
-#endif
-  unsigned char buff[512];
-
-  // check that "file descriptor" is valid
-  if (isnotopen(&fd,&con))
-      return -1;
-
-  bzero(buff,512);
-
-#ifdef IOCATAREQUEST
-  bzero(&request,sizeof(struct ata_ioc_request));
-#else
-  bzero(&iocmd,sizeof(struct ata_cmd));
-#endif
-  bzero(buff,512);
-
-#ifndef IOCATAREQUEST
-  iocmd.cmd=ATAREQUEST;
-  iocmd.channel=con->channel;
-  iocmd.device=con->device;
-#define request iocmd.u.request
-#endif
-
-  request.u.ata.command=ATA_SMART_CMD;
-  request.timeout=600;
-  switch (command){
-  case READ_VALUES:
-    request.u.ata.feature=ATA_SMART_READ_VALUES;
-    request.u.ata.lba=0xc24f<<8;
-    request.flags=ATA_CMD_READ;
-    request.data=buff;
-    request.count=512;
-    copydata=1;
-    break;
-  case READ_THRESHOLDS:
-    request.u.ata.feature=ATA_SMART_READ_THRESHOLDS;
-    request.u.ata.count=1;
-    request.u.ata.lba=1|(0xc24f<<8);
-    request.flags=ATA_CMD_READ;
-    request.data=buff;
-    request.count=512;
-    copydata=1;
-    break;
-  case READ_LOG:
-    request.u.ata.feature=ATA_SMART_READ_LOG_SECTOR;
-    request.u.ata.lba=select|(0xc24f<<8);
-    request.u.ata.count=1;
-    request.flags=ATA_CMD_READ;
-    request.data=buff;
-    request.count=512;
-    copydata=1;
-    break;
-  case IDENTIFY:
-    request.u.ata.command=ATA_IDENTIFY_DEVICE;
-    request.flags=ATA_CMD_READ;
-    request.data=buff;
-    request.count=512;
-    copydata=1;
-    break;
-  case PIDENTIFY:
-    request.u.ata.command=ATA_IDENTIFY_PACKET_DEVICE;
-    request.flags=ATA_CMD_READ;
-    request.data=buff;
-    request.count=512;
-    copydata=1;
-    break;
-  case ENABLE:
-    request.u.ata.feature=ATA_SMART_ENABLE;
-    request.u.ata.lba=0xc24f<<8;
-    request.flags=ATA_CMD_CONTROL;
-    break;
-  case DISABLE:
-    request.u.ata.feature=ATA_SMART_DISABLE;
-    request.u.ata.lba=0xc24f<<8;
-    request.flags=ATA_CMD_CONTROL;
-    break;
-  case AUTO_OFFLINE:
-    // NOTE: According to ATAPI 4 and UP, this command is obsolete
-    request.u.ata.feature=ATA_SMART_AUTO_OFFLINE;
-    request.u.ata.lba=select|(0xc24f<<8);
-    request.flags=ATA_CMD_CONTROL;
-    break;
-  case AUTOSAVE:
-    request.u.ata.feature=ATA_SMART_AUTOSAVE;
-    request.u.ata.count=0xf1;  // to enable autosave
-    request.u.ata.lba=0xc24f<<8;
-    request.flags=ATA_CMD_CONTROL;
-    break;
-  case IMMEDIATE_OFFLINE:
-    request.u.ata.feature=ATA_SMART_IMMEDIATE_OFFLINE;
-    request.u.ata.lba = select|(0xc24f<<8); // put test in sector
-    request.flags=ATA_CMD_CONTROL;
-    break;
-  case STATUS_CHECK: // same command, no HDIO in FreeBSD
-  case STATUS:
-    // this command only says if SMART is working.  It could be
-    // replaced with STATUS_CHECK below.
-    request.u.ata.feature=ATA_SMART_STATUS;
-    request.u.ata.lba=0xc24f<<8;
-    request.flags=ATA_CMD_CONTROL;
-    break;
-  default:
-    pout("Unrecognized command %d in ata_command_interface()\n"
-         "Please contact " PACKAGE_BUGREPORT "\n", command);
-    errno=ENOSYS;
-    return -1;
-  }
-  
-  if (command==STATUS_CHECK){
-    unsigned const char normal_lo=0x4f, normal_hi=0xc2;
-    unsigned const char failed_lo=0xf4, failed_hi=0x2c;
-    unsigned char low,high;
-    
-#ifdef IOCATAREQUEST
-    if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error)
-#else
-    if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error)
-#endif
-      return -1;
-
-#if __FreeBSD_version < 502000
-    printwarning(NO_RETURN,NULL);
-#endif
-
-    high = (request.u.ata.lba >> 16) & 0xff;
-    low = (request.u.ata.lba >> 8) & 0xff;
-    
-    // Cyl low and Cyl high unchanged means "Good SMART status"
-    if (low==normal_lo && high==normal_hi)
-      return 0;
-    
-    // These values mean "Bad SMART status"
-    if (low==failed_lo && high==failed_hi)
-      return 1;
-    
-    // We haven't gotten output that makes sense; print out some debugging info
-    char buf[512];
-    sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
-            (int)request.u.ata.command,
-            (int)request.u.ata.feature,
-            (int)request.u.ata.count,
-            (int)((request.u.ata.lba) & 0xff),
-            (int)((request.u.ata.lba>>8) & 0xff),
-            (int)((request.u.ata.lba>>16) & 0xff),
-            (int)request.error);
-    printwarning(BAD_SMART,buf);
-    return 0;   
-  }
-
-#ifdef IOCATAREQUEST
-  if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error)
-#else
-  if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error)
-#endif
-  {
-    return -1;
-  }
-  // 
-  if (copydata)
-    memcpy(data, buff, 512);
-  
-  return 0;
-#endif
-}
-
-
-// Interface to SCSI devices.  See os_linux.c
-int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
-{
-  struct freebsd_dev_channel* con = NULL;
-  struct cam_device* cam_dev = NULL;
-  union ccb *ccb;
-  
-  
-    if (report > 0) {
-        unsigned int k;
-        const unsigned char * ucp = iop->cmnd;
-        const char * np;
-
-        np = scsi_get_opcode_name(ucp[0]);
-        pout(" [%s: ", np ? np : "<unknown opcode>");
-        for (k = 0; k < iop->cmnd_len; ++k)
-            pout("%02x ", ucp[k]);
-        if ((report > 1) && 
-            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
-            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-
-            pout("]\n  Outgoing data, len=%d%s:\n", (int)iop->dxfer_len,
-                 (trunc ? " [only first 256 bytes shown]" : ""));
-            dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
-        }
-        else
-            pout("]");
-    }
-
-  // check that "file descriptor" is valid
-  if (isnotopen(&fd,&con))
-      return -ENOTTY;
-
-
-  if (!(cam_dev = cam_open_spec_device(con->devname,con->unitnum,O_RDWR,NULL))) {
-    warnx("%s",cam_errbuf);
-    return -1;
-  }
-
-  if (!(ccb = cam_getccb(cam_dev))) {
-    warnx("error allocating ccb");
-    return -ENOMEM;
-  }
-
-  // clear out structure, except for header that was filled in for us
-  bzero(&(&ccb->ccb_h)[1],
-        sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
-
-  cam_fill_csio(&ccb->csio,
-                /*retrires*/ 1,
-                /*cbfcnp*/ NULL,
-                /* flags */ (iop->dxfer_dir == DXFER_NONE ? CAM_DIR_NONE :(iop->dxfer_dir == DXFER_FROM_DEVICE ? CAM_DIR_IN : CAM_DIR_OUT)),
-                /* tagaction */ MSG_SIMPLE_Q_TAG,
-                /* dataptr */ iop->dxferp,
-                /* datalen */ iop->dxfer_len,
-                /* senselen */ iop->max_sense_len,
-                /* cdblen */ iop->cmnd_len,
-                /* timout (converted to seconds) */ iop->timeout*1000);
-  memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len);
-
-  if (cam_send_ccb(cam_dev,ccb) < 0) {
-    warn("error sending SCSI ccb");
- #if __FreeBSD_version > 500000
-    cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr);
- #endif
-    cam_freeccb(ccb);
-    return -1;
-  }
-
-  if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
- #if __FreeBSD_version > 500000
-    cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr);
- #endif
-    cam_freeccb(ccb);
-    return -1;
-  }
-
-  if (iop->sensep) {
-    memcpy(iop->sensep,&(ccb->csio.sense_data),sizeof(struct scsi_sense_data));
-    iop->resp_sense_len = sizeof(struct scsi_sense_data);
-  }
-
-  iop->scsi_status = ccb->csio.scsi_status;
-
-  cam_freeccb(ccb);
-  
-  if (cam_dev)
-    cam_close_device(cam_dev);
-
-  if (report > 0) {
-    int trunc;
-
-    pout("  status=0\n");
-    trunc = (iop->dxfer_len > 256) ? 1 : 0;
-    
-    pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
-         (trunc ? " [only first 256 bytes shown]" : ""));
-    dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
-  }
-  return 0;
-}
-
-// Interface to ATA devices behind 3ware escalade RAID controller cards.  See os_linux.c
-
-#define BUFFER_LEN_678K_CHAR ( sizeof(struct twe_usercommand) ) // 520
-#define BUFFER_LEN_9000_CHAR ( sizeof(TW_OSLI_IOCTL_NO_DATA_BUF) + sizeof(TWE_Command) ) // 2048
-#define TW_IOCTL_BUFFER_SIZE ( MAX(BUFFER_LEN_678K_CHAR, BUFFER_LEN_9000_CHAR) )
-
-int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data) {
-  // to hold true file descriptor
-  struct freebsd_dev_channel* con;
-
-  // return value and buffer for ioctl()
-  int  ioctlreturn, readdata=0;
-  struct twe_usercommand* cmd_twe = NULL;
-  TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL;
-  TWE_Command_ATA* ata = NULL;
-
-  // Used by both the SCSI and char interfaces
-  char ioctl_buffer[TW_IOCTL_BUFFER_SIZE];
-
-  if (disknum < 0) {
-    printwarning(NO_DISK_3WARE,NULL);
-    return -1;
-  }
-
-  // check that "file descriptor" is valid
-  if (isnotopen(&fd,&con))
-      return -1;
-
-  memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE);
-
-  if (escalade_type==CONTROLLER_3WARE_9000_CHAR) {
-    cmd_twa = (TW_OSLI_IOCTL_NO_DATA_BUF*)ioctl_buffer;
-    cmd_twa->pdata = ((TW_OSLI_IOCTL_WITH_PAYLOAD*)cmd_twa)->payload.data_buf;
-    cmd_twa->driver_pkt.buffer_length = 512;
-    ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k;
-  } else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) {
-    cmd_twe = (struct twe_usercommand*)ioctl_buffer;
-    ata = &cmd_twe->tu_command.ata;
-  } else {
-    pout("Unrecognized escalade_type %d in freebsd_3ware_command_interface(disk %d)\n"
-         "Please contact " PACKAGE_BUGREPORT "\n", escalade_type, disknum);
-    errno=ENOSYS;
-    return -1;
-  }
-
-  ata->opcode = TWE_OP_ATA_PASSTHROUGH;
-
-  // Same for (almost) all commands - but some reset below
-  ata->request_id    = 0xFF;
-  ata->unit   = disknum;
-  ata->host_id = 0;
-  ata->status        = 0;           
-  ata->flags         = 0x1;
-  ata->drive_head    = 0x0;
-  ata->sector_num    = 0;
-
-  // All SMART commands use this CL/CH signature.  These are magic
-  // values from the ATA specifications.
-  ata->cylinder_lo   = 0x4F;
-  ata->cylinder_hi   = 0xC2;
-  
-  // SMART ATA COMMAND REGISTER value
-  ata->command       = ATA_SMART_CMD;
-  
-  // Is this a command that reads or returns 512 bytes?
-  // passthru->param values are:
-  // 0x0 - non data command without TFR write check,
-  // 0x8 - non data command with TFR write check,
-  // 0xD - data command that returns data to host from device
-  // 0xF - data command that writes data from host to device
-  // passthru->size values are 0x5 for non-data and 0x07 for data
-  if (command == READ_VALUES     ||
-      command == READ_THRESHOLDS ||
-      command == READ_LOG        ||
-      command == IDENTIFY        ||
-      command == WRITE_LOG ) {
-    readdata=1;
-    if (escalade_type==CONTROLLER_3WARE_678K_CHAR) {
-      cmd_twe->tu_data = data;
-      cmd_twe->tu_size = 512;
-    }
-    ata->sgl_offset = 0x5;
-    ata->size         = 0x5;
-    ata->param        = 0xD;
-    ata->sector_count = 0x1;
-    // For 64-bit to work correctly, up the size of the command packet
-    // in dwords by 1 to account for the 64-bit single sgl 'address'
-    // field. Note that this doesn't agree with the typedefs but it's
-    // right (agree with kernel driver behavior/typedefs).
-    //if (sizeof(long)==8)
-    //  ata->size++;
-  }
-  else {
-    // Non data command -- but doesn't use large sector 
-    // count register values.  
-    ata->sgl_offset = 0x0;
-    ata->size         = 0x5;
-    ata->param        = 0x8;
-    ata->sector_count = 0x0;
-  }
-  
-  // Now set ATA registers depending upon command
-  switch (command){
-  case CHECK_POWER_MODE:
-    ata->command     = ATA_CHECK_POWER_MODE;
-    ata->features    = 0;
-    ata->cylinder_lo = 0;
-    ata->cylinder_hi = 0;
-    break;
-  case READ_VALUES:
-    ata->features = ATA_SMART_READ_VALUES;
-    break;
-  case READ_THRESHOLDS:
-    ata->features = ATA_SMART_READ_THRESHOLDS;
-    break;
-  case READ_LOG:
-    ata->features = ATA_SMART_READ_LOG_SECTOR;
-    // log number to return
-    ata->sector_num  = select;
-    break;
-  case WRITE_LOG:
-    readdata=0;
-    ata->features     = ATA_SMART_WRITE_LOG_SECTOR;
-    ata->sector_count = 1;
-    ata->sector_num   = select;
-    ata->param        = 0xF;  // PIO data write
-    break;
-  case IDENTIFY:
-    // ATA IDENTIFY DEVICE
-    ata->command     = ATA_IDENTIFY_DEVICE;
-    ata->features    = 0;
-    ata->cylinder_lo = 0;
-    ata->cylinder_hi = 0;
-    break;
-  case PIDENTIFY:
-    // 3WARE controller can NOT have packet device internally
-    pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", disknum);
-    errno=ENODEV;
-    return -1;
-  case ENABLE:
-    ata->features = ATA_SMART_ENABLE;
-    break;
-  case DISABLE:
-    ata->features = ATA_SMART_DISABLE;
-    break;
-  case AUTO_OFFLINE:
-    ata->features     = ATA_SMART_AUTO_OFFLINE;
-    // Enable or disable?
-    ata->sector_count = select;
-    break;
-  case AUTOSAVE:
-    ata->features     = ATA_SMART_AUTOSAVE;
-    // Enable or disable?
-    ata->sector_count = select;
-    break;
-  case IMMEDIATE_OFFLINE:
-    ata->features    = ATA_SMART_IMMEDIATE_OFFLINE;
-    // What test type to run?
-    ata->sector_num  = select;
-    break;
-  case STATUS_CHECK:
-    ata->features = ATA_SMART_STATUS;
-    break;
-  case STATUS:
-    // This is JUST to see if SMART is enabled, by giving SMART status
-    // command. But it doesn't say if status was good, or failing.
-    // See below for the difference.
-    ata->features = ATA_SMART_STATUS;
-    break;
-  default:
-    pout("Unrecognized command %d in freebsd_3ware_command_interface(disk %d)\n"
-         "Please contact " PACKAGE_BUGREPORT "\n", command, disknum);
-    errno=ENOSYS;
-    return -1;
-  }
-
-  // Now send the command down through an ioctl()
-  if (escalade_type==CONTROLLER_3WARE_9000_CHAR) {
-#ifdef IOCATAREQUEST
-    ioctlreturn=ioctl(con->device,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa);
-#else
-    ioctlreturn=ioctl(con->atacommand,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa);
-#endif
-  } else {
-#ifdef IOCATAREQUEST
-    ioctlreturn=ioctl(con->device,TWEIO_COMMAND,cmd_twe);
-#else
-    ioctlreturn=ioctl(con->atacommand,TWEIO_COMMAND,cmd_twe);
-#endif
-  }
-
-  // Deal with the different error cases
-  if (ioctlreturn) {
-    if (!errno)
-      errno=EIO;
-    return -1;
-  }
-  
-  // See if the ATA command failed.  Now that we have returned from
-  // the ioctl() call, if passthru is valid, then:
-  // - ata->status contains the 3ware controller STATUS
-  // - ata->command contains the ATA STATUS register
-  // - ata->features contains the ATA ERROR register
-  //
-  // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS
-  // If bit 0 (error bit) is set, then ATA ERROR register is valid.
-  // While we *might* decode the ATA ERROR register, at the moment it
-  // doesn't make much sense: we don't care in detail why the error
-  // happened.
-  
-  if (ata->status || (ata->command & 0x21)) {
-    pout("Command failed, ata.status=(0x%2.2x), ata.command=(0x%2.2x), ata.flags=(0x%2.2x)\n",ata->status,ata->command,ata->flags);
-    errno=EIO;
-    return -1;
-  }
-  
-  // If this is a read data command, copy data to output buffer
-  if (readdata) {
-    if (escalade_type==CONTROLLER_3WARE_9000_CHAR)
-      memcpy(data, cmd_twa->pdata, 512);
-  }
-
-  // For STATUS_CHECK, we need to check register values
-  if (command==STATUS_CHECK) {
-    
-    // To find out if the SMART RETURN STATUS is good or failing, we
-    // need to examine the values of the Cylinder Low and Cylinder
-    // High Registers.
-    
-    unsigned short cyl_lo=ata->cylinder_lo;
-    unsigned short cyl_hi=ata->cylinder_hi;
-    
-    // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good.
-    if (cyl_lo==0x4F && cyl_hi==0xC2)
-      return 0;
-    
-    // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL
-    if (cyl_lo==0xF4 && cyl_hi==0x2C)
-      return 1;
-    
-      errno=EIO;
-      return -1;
-  }
-  
-  // copy sector count register (one byte!) to return data
-  if (command==CHECK_POWER_MODE)
-    *data=*(char *)&(ata->sector_count);
-  
-  // look for nonexistent devices/ports
-  if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) {
-    errno=ENODEV;
-    return -1;
-  }
-  
-  return 0;
-}
-
-static int get_tw_channel_unit (const char* name, int* unit, int* dev) {
-  const char *p;
-
-  /* device node sanity check */
-  for (p = name + 3; *p; p++)
-    if (*p < '0' || *p > '9')
-      return -1;
-  if (strlen(name) > 4 && *(name + 3) == '0')
-    return -1;
-
-  if (dev != NULL)
-    *dev=atoi(name + 3);
-
-  /* no need for unit number */
-  if (unit != NULL)
-    *unit=0;
-  return 0;
-}
-
-
-#ifndef IOCATAREQUEST
-static int get_ata_channel_unit ( const char* name, int* unit, int* dev) {
-#ifndef ATAREQUEST
-  *dev=0;
-  *unit=0;
-return 0;
-#else
-  // there is no direct correlation between name 'ad0, ad1, ...' and
-  // channel/unit number.  So we need to iterate through the possible
-  // channels and check each unit to see if we match names
-  struct ata_cmd iocmd;
-  int fd,maxunit;
-  
-  bzero(&iocmd, sizeof(struct ata_cmd));
-
-  if ((fd = open("/dev/ata", O_RDWR)) < 0)
-    return -errno;
-  
-  iocmd.cmd = ATAGMAXCHANNEL;
-  if (ioctl(fd, IOCATA, &iocmd) < 0) {
-    return -errno;
-    close(fd);
-  }
-  maxunit = iocmd.u.maxchan;
-  for (*unit = 0; *unit < maxunit; (*unit)++) {
-    iocmd.channel = *unit;
-    iocmd.device = -1;
-    iocmd.cmd = ATAGPARM;
-    if (ioctl(fd, IOCATA, &iocmd) < 0) {
-      close(fd);
-      return -errno;
-    }
-    if (iocmd.u.param.type[0] && !strcmp(name,iocmd.u.param.name[0])) {
-      *dev = 0;
-      break;
-    }
-    if (iocmd.u.param.type[1] && !strcmp(name,iocmd.u.param.name[1])) {
-      *dev = 1;
-      break;
-    }
-  }
-  close(fd);
-  if (*unit == maxunit)
-    return -1;
-  else
-    return 0;
-#endif
-}
-#endif
-
-// Guess device type (ata or scsi) based on device name (FreeBSD
-// specific) SCSI device name in FreeBSD can be sd, sr, scd, st, nst,
-// osst, nosst and sg.
-static const char * fbsd_dev_prefix = "/dev/";
-static const char * fbsd_dev_ata_disk_prefix = "ad";
-static const char * fbsd_dev_scsi_disk_plus = "da";
-static const char * fbsd_dev_scsi_tape1 = "sa";
-static const char * fbsd_dev_scsi_tape2 = "nsa";
-static const char * fbsd_dev_scsi_tape3 = "esa";
-static const char * fbsd_dev_twe_ctrl = "twe";
-static const char * fbsd_dev_twa_ctrl = "twa";
-
-static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *chan) {
-  int len;
-  int dev_prefix_len = strlen(fbsd_dev_prefix);
-  
-  // if dev_name null, or string length zero
-  if (!dev_name || !(len = strlen(dev_name)))
-    return CONTROLLER_UNKNOWN;
-  
-  // Remove the leading /dev/... if it's there
-  if (!strncmp(fbsd_dev_prefix, dev_name, dev_prefix_len)) {
-    if (len <= dev_prefix_len) 
-      // if nothing else in the string, unrecognized
-      return CONTROLLER_UNKNOWN;
-    // else advance pointer to following characters
-    dev_name += dev_prefix_len;
-  }
-  // form /dev/ad* or ad*
-  if (!strncmp(fbsd_dev_ata_disk_prefix, dev_name,
-               strlen(fbsd_dev_ata_disk_prefix))) {
-#ifndef IOCATAREQUEST
-    if (chan != NULL) {
-      if (get_ata_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) {
-        return CONTROLLER_UNKNOWN;
-      }
-    }
-#endif
-    return CONTROLLER_ATA;
-  }
-  
-  // form /dev/da* or da*
-  if (!strncmp(fbsd_dev_scsi_disk_plus, dev_name,
-               strlen(fbsd_dev_scsi_disk_plus)))
-    goto handlescsi;
-
-  // form /dev/sa* or sa*
-  if (!strncmp(fbsd_dev_scsi_tape1, dev_name,
-              strlen(fbsd_dev_scsi_tape1)))
-    goto handlescsi;
-
-  // form /dev/nsa* or nsa*
-  if (!strncmp(fbsd_dev_scsi_tape2, dev_name,
-              strlen(fbsd_dev_scsi_tape2)))
-    goto handlescsi;
-
-  // form /dev/esa* or esa*
-  if (!strncmp(fbsd_dev_scsi_tape3, dev_name,
-              strlen(fbsd_dev_scsi_tape3)))
-    goto handlescsi;
-  
-  if (!strncmp(fbsd_dev_twa_ctrl,dev_name,
-              strlen(fbsd_dev_twa_ctrl))) {
-    if (chan != NULL) {
-      if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) {
-       return CONTROLLER_UNKNOWN;
-      }
-    }
-    else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) {
-       return CONTROLLER_UNKNOWN;
-    }
-    return CONTROLLER_3WARE_9000_CHAR;
-  }
-
-  if (!strncmp(fbsd_dev_twe_ctrl,dev_name,
-              strlen(fbsd_dev_twe_ctrl))) {
-    if (chan != NULL) {
-      if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) {
-       return CONTROLLER_UNKNOWN;
-      }
-    }
-    else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) {
-       return CONTROLLER_UNKNOWN;
-    }
-    return CONTROLLER_3WARE_678K_CHAR;
-  }
-
-  // we failed to recognize any of the forms
-  return CONTROLLER_UNKNOWN;
-
- handlescsi:
-  if (chan != NULL) {
-    if (!(chan->devname = calloc(1,DEV_IDLEN+1)))
-      return CONTROLLER_UNKNOWN;
-    
-    if (cam_get_device(dev_name,chan->devname,DEV_IDLEN,&(chan->unitnum)) == -1)
-      return CONTROLLER_UNKNOWN;
-  }
-  return CONTROLLER_SCSI;
-  
-}
-
-int guess_device_type (const char* dev_name) {
-  return parse_ata_chan_dev(dev_name,NULL);
-}
-
-// global variable holding byte count of allocated memory
-extern long long bytes;
-
-// we are going to take advantage of the fact that FreeBSD's devfs will only
-// have device entries for devices that exist.  So if we get the equivilent of
-// ls /dev/ad?, we have all the ATA devices on the system
-//
-// If any errors occur, leave errno set as it was returned by the
-// system call, and return <0.
-
-// Return values:
-// -1 out of memory
-// -2 to -5 errors in glob
-
-int get_dev_names(char*** names, const char* prefix) {
-  int n = 0;
-  char** mp;
-  int retglob,lim;
-  glob_t globbuf;
-  int i;
-  char pattern1[128],pattern2[128];
-
-  bzero(&globbuf,sizeof(globbuf));
-  // in case of non-clean exit
-  *names=NULL;
-
-  // handle 0-99 possible devices, will still be limited by MAX_NUM_DEV
-  sprintf(pattern1,"/dev/%s[0-9]",prefix);
-  sprintf(pattern2,"/dev/%s[0-9][0-9]",prefix);
-  
-  // Use glob to look for any directory entries matching the patterns
-  // first call inits with first pattern match, second call appends
-  // to first list. Turn on NOCHECK for second call. This results in no
-  // error if no more matches found, however it does append the actual
-  // pattern to the list of paths....
-  if ((retglob=glob(pattern1, GLOB_ERR, NULL, &globbuf)) ||
-      (retglob=glob(pattern2, GLOB_ERR|GLOB_APPEND|GLOB_NOCHECK,NULL,&globbuf))) {
-     int retval = -1;
-    // glob failed
-    if (retglob==GLOB_NOSPACE)
-      pout("glob(3) ran out of memory matching patterns (%s),(%s)\n",
-           pattern1, pattern2);
-    else if (retglob==GLOB_ABORTED)
-      pout("glob(3) aborted matching patterns (%s),(%s)\n",
-           pattern1, pattern2);
-    else if (retglob==GLOB_NOMATCH) {
-      pout("glob(3) found no matches for patterns (%s),(%s)\n",
-           pattern1, pattern2);
-      retval = 0;
-    }
-    else if (retglob)
-      pout("Unexplained error in glob(3) of patterns (%s),(%s)\n",
-           pattern1, pattern2);
-    
-    //  Free memory and return
-    globfree(&globbuf);
-
-    return retval;
-  }
-
-  // did we find too many paths?
-  // did we find too many paths?
-  lim = globbuf.gl_pathc < MAX_NUM_DEV ? globbuf.gl_pathc : MAX_NUM_DEV;
-  if (lim < globbuf.gl_pathc)
-    pout("glob(3) found %d > MAX=%d devices matching patterns (%s),(%s): ignoring %d paths\n", 
-         globbuf.gl_pathc, MAX_NUM_DEV, pattern1,pattern2,
-         globbuf.gl_pathc-MAX_NUM_DEV);
-  
-  // allocate space for up to lim number of ATA devices
-  if (!(mp =  (char **)calloc(lim, sizeof(char*)))){
-    pout("Out of memory constructing scan device list\n");
-    return -1;
-  }
-
-  // now step through the list returned by glob.  No link checking needed
-  // in FreeBSD
-  for (i=0; i<globbuf.gl_pathc; i++){
-    // becuase of the NO_CHECK on second call to glob,
-    // the pattern itself will be added to path list..
-    // so ignore any paths that have the ']' from pattern
-    if (strchr(globbuf.gl_pathv[i],']') == NULL)
-      mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion);
-  }
-
-  globfree(&globbuf);
-  mp = realloc(mp,n*(sizeof(char*))); // shrink to correct size
-  bytes += (n)*(sizeof(char*)); // and set allocated byte count
-  *names=mp;
-  return n;
-}
-
-int make_device_names (char*** devlist, const char* name) {
-  if (!strcmp(name,"SCSI"))
-    return get_dev_names(devlist,"da");
-  else if (!strcmp(name,"ATA"))
-    return get_dev_names(devlist,"ad");
-  else
-    return 0;
-}
diff --git a/os_freebsd.cpp b/os_freebsd.cpp
new file mode 100644 (file)
index 0000000..43dc2cb
--- /dev/null
@@ -0,0 +1,1074 @@
+/*
+ * os_freebsd.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Eduard Martinescu <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <err.h>
+#include <camlib.h>
+#include <cam/scsi/scsi_message.h>
+#include <sys/ata.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <fcntl.h>
+#include <stddef.h>
+
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+#include "os_freebsd.h"
+
+static const char *filenameandversion="$Id: os_freebsd.cpp,v 1.51 2006/09/17 03:17:53 dpgilbert Exp $";
+
+const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp,v 1.51 2006/09/17 03:17:53 dpgilbert Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// Private table of open devices: guaranteed zero on startup since
+// part of static data.
+struct freebsd_dev_channel *devicetable[FREEBSD_MAXDEV];
+
+// forward declaration
+static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *ch);
+
+// print examples for smartctl
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+         "  smartctl -a /dev/ad0                       (Prints all SMART information)\n\n"
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/ad0\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl -t long /dev/ad0              (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/ad0\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "                                      (Prints Self-Test & Attribute errors)\n\n"
+         "  smartctl -a --device=3ware,2 /dev/twa0\n"
+         "  smartctl -a --device=3ware,2 /dev/twe0\n"
+         "                              (Prints all SMART information for ATA disk on\n"
+         "                                 third port of first 3ware RAID controller)\n"
+         );
+#else
+  printf(
+         "  smartctl -a /dev/ad0                       (Prints all SMART information)\n"
+         "  smartctl -s on -o on -S on /dev/ad0         (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/ad0              (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/ad0\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl -a -d 3ware,2 /dev/twa0\n"
+         "  smartctl -a -d 3ware,2 /dev/twe0\n"
+         );
+#endif
+  return;
+}
+
+// Like open().  Return positive integer handle, used by functions below only.  mode=="ATA" or "SCSI".
+int deviceopen (const char* dev, char* mode __unused) {
+  struct freebsd_dev_channel *fdchan;
+  int parse_ok, i;
+
+  // Search table for a free entry
+  for (i=0; i<FREEBSD_MAXDEV; i++)
+    if (!devicetable[i])
+      break;
+  
+  // If no free entry found, return error.  We have max allowed number
+  // of "file descriptors" already allocated.
+  if (i==FREEBSD_MAXDEV) {
+    errno=EMFILE;
+    return -1;
+  }
+
+  fdchan = (struct freebsd_dev_channel *)calloc(1,sizeof(struct freebsd_dev_channel));
+  if (fdchan == NULL) {
+    // errno already set by call to malloc()
+    return -1;
+  }
+
+  parse_ok = parse_ata_chan_dev (dev,fdchan);
+  if (parse_ok == CONTROLLER_UNKNOWN) {
+    free(fdchan);
+    errno = ENOTTY;
+    return -1; // can't handle what we don't know
+  }
+
+  if (parse_ok == CONTROLLER_ATA) {
+#ifdef IOCATAREQUEST
+    if ((fdchan->device = open(dev,O_RDONLY))<0) {
+#else
+    if ((fdchan->atacommand = open("/dev/ata",O_RDWR))<0) {
+#endif
+      int myerror = errno;      //preserve across free call
+      free (fdchan);
+      errno = myerror;
+      return -1;
+    }
+  }
+
+  if (parse_ok == CONTROLLER_3WARE_678K_CHAR) {
+    char buf[512];
+    sprintf(buf,"/dev/twe%d",fdchan->device);
+#ifdef IOCATAREQUEST
+    if ((fdchan->device = open(buf,O_RDWR))<0) {
+#else
+    if ((fdchan->atacommand = open(buf,O_RDWR))<0) {
+#endif
+      int myerror = errno; // preserver across free call
+      free(fdchan);
+      errno=myerror;
+      return -1;
+    }
+  }
+
+  if (parse_ok == CONTROLLER_3WARE_9000_CHAR) {
+    char buf[512];
+    sprintf(buf,"/dev/twa%d",fdchan->device);
+#ifdef IOCATAREQUEST
+    if ((fdchan->device = open(buf,O_RDWR))<0) {
+#else
+    if ((fdchan->atacommand = open(buf,O_RDWR))<0) {
+#endif
+      int myerror = errno; // preserver across free call
+      free(fdchan);
+      errno=myerror;
+      return -1;
+    }
+  }
+
+  if (parse_ok == CONTROLLER_SCSI) {
+    // this is really a NO-OP, as the parse takes care
+    // of filling in correct details
+  }
+  
+  // return pointer to "file descriptor" table entry, properly offset.
+  devicetable[i]=fdchan;
+  return i+FREEBSD_FDOFFSET;
+}
+
+// Returns 1 if device not available/open/found else 0.  Also shifts fd into valid range.
+static int isnotopen(int *fd, struct freebsd_dev_channel** fdchan) {
+  // put valid "file descriptor" into range 0...FREEBSD_MAXDEV-1
+  *fd -= FREEBSD_FDOFFSET;
+  
+  // check for validity of "file descriptor".
+  if (*fd<0 || *fd>=FREEBSD_MAXDEV || !((*fdchan)=devicetable[*fd])) {
+    errno = ENODEV;
+    return 1;
+  }
+  
+  return 0;
+}
+
+// Like close().  Acts on handles returned by above function.
+int deviceclose (int fd) {
+  struct freebsd_dev_channel *fdchan;
+  int failed = 0;
+
+  // check for valid file descriptor
+  if (isnotopen(&fd, &fdchan))
+    return -1;
+  
+
+  // did we allocate a SCSI device name?
+  if (fdchan->devname)
+    free(fdchan->devname);
+  
+  // close device, if open
+#ifdef IOCATAREQUEST
+  if (fdchan->device)
+    failed=close(fdchan->device);
+#else
+  if (fdchan->atacommand)
+    failed=close(fdchan->atacommand);
+#endif
+
+  if (fdchan->scsicontrol)
+    failed=close(fdchan->scsicontrol);
+  
+  // if close succeeded, then remove from device list
+  // Eduard, should we also remove it from list if close() fails?  I'm
+  // not sure. Here I only remove it from list if close() worked.
+  if (!failed) {
+    free(fdchan);
+    devicetable[fd]=NULL;
+  }
+  
+  return failed;
+}
+
+#define NO_RETURN 0
+#define BAD_SMART 1
+#define NO_DISK_3WARE 2
+#define BAD_KERNEL 3
+#define MAX_MSG 3
+
+// Utility function for printing warnings
+void printwarning(int msgNo, const char* extra) {
+  static int printed[] = {0,0,0,0};
+  static const char* message[]={
+    "The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n can not be retrieved with this version of ATAng, please do not rely on this value\nYou should update to at least 5.2\n",
+    
+    "Error SMART Status command failed\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
+    
+    "You must specify a DISK # for 3ware drives with -d 3ware,<n> where <n> begins with 1 for first disk drive\n",
+    
+    "ATA support is not provided for this kernel version. Please ugrade to a recent 5-CURRENT kernel (post 09/01/2003 or so)\n"
+  };
+
+  if (msgNo >= 0 && msgNo <= MAX_MSG) {
+    if (!printed[msgNo]) {
+      printed[msgNo] = 1;
+      pout("%s", message[msgNo]);
+      if (extra)
+        pout("%s",extra);
+    }
+  }
+  return;
+}
+
+
+// Interface to ATA devices.  See os_linux.c
+int marvell_command_interface(int fd __unused, smart_command_set command __unused, int select __unused, char *data __unused) {
+       return -1;
+}
+
+int highpoint_command_interface(int fd __unused, smart_command_set command __unused, int select __unused, char *data __unused)
+{
+  return -1;
+}
+
+int ata_command_interface(int fd, smart_command_set command, int select, char *data) {
+#if !defined(ATAREQUEST) && !defined(IOCATAREQUEST)
+  // sorry, but without ATAng, we can't do anything here
+  printwarning(BAD_KERNEL,NULL);
+  errno = ENOSYS;
+  return -1;
+#else
+  struct freebsd_dev_channel* con;
+  int retval, copydata=0;
+#ifdef IOCATAREQUEST
+  struct ata_ioc_request request;
+#else
+  struct ata_cmd iocmd;
+#endif
+  unsigned char buff[512];
+
+  // check that "file descriptor" is valid
+  if (isnotopen(&fd,&con))
+      return -1;
+
+  bzero(buff,512);
+
+#ifdef IOCATAREQUEST
+  bzero(&request,sizeof(struct ata_ioc_request));
+#else
+  bzero(&iocmd,sizeof(struct ata_cmd));
+#endif
+  bzero(buff,512);
+
+#ifndef IOCATAREQUEST
+  iocmd.cmd=ATAREQUEST;
+  iocmd.channel=con->channel;
+  iocmd.device=con->device;
+#define request iocmd.u.request
+#endif
+
+  request.u.ata.command=ATA_SMART_CMD;
+  request.timeout=600;
+  switch (command){
+  case READ_VALUES:
+    request.u.ata.feature=ATA_SMART_READ_VALUES;
+    request.u.ata.lba=0xc24f<<8;
+    request.flags=ATA_CMD_READ;
+    request.data=(char *)buff;
+    request.count=512;
+    copydata=1;
+    break;
+  case READ_THRESHOLDS:
+    request.u.ata.feature=ATA_SMART_READ_THRESHOLDS;
+    request.u.ata.count=1;
+    request.u.ata.lba=1|(0xc24f<<8);
+    request.flags=ATA_CMD_READ;
+    request.data=(char *)buff;
+    request.count=512;
+    copydata=1;
+    break;
+  case READ_LOG:
+    request.u.ata.feature=ATA_SMART_READ_LOG_SECTOR;
+    request.u.ata.lba=select|(0xc24f<<8);
+    request.u.ata.count=1;
+    request.flags=ATA_CMD_READ;
+    request.data=(char *)buff;
+    request.count=512;
+    copydata=1;
+    break;
+  case IDENTIFY:
+    request.u.ata.command=ATA_IDENTIFY_DEVICE;
+    request.flags=ATA_CMD_READ;
+    request.data=(char *)buff;
+    request.count=512;
+    copydata=1;
+    break;
+  case PIDENTIFY:
+    request.u.ata.command=ATA_IDENTIFY_PACKET_DEVICE;
+    request.flags=ATA_CMD_READ;
+    request.data=(char *)buff;
+    request.count=512;
+    copydata=1;
+    break;
+  case ENABLE:
+    request.u.ata.feature=ATA_SMART_ENABLE;
+    request.u.ata.lba=0xc24f<<8;
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  case DISABLE:
+    request.u.ata.feature=ATA_SMART_DISABLE;
+    request.u.ata.lba=0xc24f<<8;
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  case AUTO_OFFLINE:
+    // NOTE: According to ATAPI 4 and UP, this command is obsolete
+    request.u.ata.feature=ATA_SMART_AUTO_OFFLINE;
+    request.u.ata.lba=select|(0xc24f<<8);
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  case AUTOSAVE:
+    request.u.ata.feature=ATA_SMART_AUTOSAVE;
+    request.u.ata.count=0xf1;  // to enable autosave
+    request.u.ata.lba=0xc24f<<8;
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  case IMMEDIATE_OFFLINE:
+    request.u.ata.feature=ATA_SMART_IMMEDIATE_OFFLINE;
+    request.u.ata.lba = select|(0xc24f<<8); // put test in sector
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  case STATUS_CHECK: // same command, no HDIO in FreeBSD
+  case STATUS:
+    // this command only says if SMART is working.  It could be
+    // replaced with STATUS_CHECK below.
+    request.u.ata.feature=ATA_SMART_STATUS;
+    request.u.ata.lba=0xc24f<<8;
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  default:
+    pout("Unrecognized command %d in ata_command_interface()\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", command);
+    errno=ENOSYS;
+    return -1;
+  }
+  
+  if (command==STATUS_CHECK){
+    unsigned const char normal_lo=0x4f, normal_hi=0xc2;
+    unsigned const char failed_lo=0xf4, failed_hi=0x2c;
+    unsigned char low,high;
+    
+#ifdef IOCATAREQUEST
+    if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error)
+#else
+    if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error)
+#endif
+      return -1;
+
+#if __FreeBSD_version < 502000
+    printwarning(NO_RETURN,NULL);
+#endif
+
+    high = (request.u.ata.lba >> 16) & 0xff;
+    low = (request.u.ata.lba >> 8) & 0xff;
+    
+    // Cyl low and Cyl high unchanged means "Good SMART status"
+    if (low==normal_lo && high==normal_hi)
+      return 0;
+    
+    // These values mean "Bad SMART status"
+    if (low==failed_lo && high==failed_hi)
+      return 1;
+    
+    // We haven't gotten output that makes sense; print out some debugging info
+    char buf[512];
+    sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
+            (int)request.u.ata.command,
+            (int)request.u.ata.feature,
+            (int)request.u.ata.count,
+            (int)((request.u.ata.lba) & 0xff),
+            (int)((request.u.ata.lba>>8) & 0xff),
+            (int)((request.u.ata.lba>>16) & 0xff),
+            (int)request.error);
+    printwarning(BAD_SMART,buf);
+    return 0;   
+  }
+
+#ifdef IOCATAREQUEST
+  if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error)
+#else
+  if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error)
+#endif
+  {
+    return -1;
+  }
+  // 
+  if (copydata)
+    memcpy(data, buff, 512);
+  
+  return 0;
+#endif
+}
+
+
+// Interface to SCSI devices.  See os_linux.c
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
+{
+  struct freebsd_dev_channel* con = NULL;
+  struct cam_device* cam_dev = NULL;
+  union ccb *ccb;
+  
+  
+    if (report > 0) {
+        unsigned int k;
+        const unsigned char * ucp = iop->cmnd;
+        const char * np;
+
+        np = scsi_get_opcode_name(ucp[0]);
+        pout(" [%s: ", np ? np : "<unknown opcode>");
+        for (k = 0; k < iop->cmnd_len; ++k)
+            pout("%02x ", ucp[k]);
+        if ((report > 1) && 
+            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+            pout("]\n  Outgoing data, len=%d%s:\n", (int)iop->dxfer_len,
+                 (trunc ? " [only first 256 bytes shown]" : ""));
+            dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+        }
+        else
+            pout("]");
+    }
+
+  // check that "file descriptor" is valid
+  if (isnotopen(&fd,&con))
+      return -ENOTTY;
+
+
+  if (!(cam_dev = cam_open_spec_device(con->devname,con->unitnum,O_RDWR,NULL))) {
+    warnx("%s",cam_errbuf);
+    return -1;
+  }
+
+  if (!(ccb = cam_getccb(cam_dev))) {
+    warnx("error allocating ccb");
+    return -ENOMEM;
+  }
+
+  // clear out structure, except for header that was filled in for us
+  bzero(&(&ccb->ccb_h)[1],
+        sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+  cam_fill_csio(&ccb->csio,
+                /*retrires*/ 1,
+                /*cbfcnp*/ NULL,
+                /* flags */ (iop->dxfer_dir == DXFER_NONE ? CAM_DIR_NONE :(iop->dxfer_dir == DXFER_FROM_DEVICE ? CAM_DIR_IN : CAM_DIR_OUT)),
+                /* tagaction */ MSG_SIMPLE_Q_TAG,
+                /* dataptr */ iop->dxferp,
+                /* datalen */ iop->dxfer_len,
+                /* senselen */ iop->max_sense_len,
+                /* cdblen */ iop->cmnd_len,
+                /* timout (converted to seconds) */ iop->timeout*1000);
+  memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len);
+
+  if (cam_send_ccb(cam_dev,ccb) < 0) {
+    warn("error sending SCSI ccb");
+ #if __FreeBSD_version > 500000
+    cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr);
+ #endif
+    cam_freeccb(ccb);
+    return -1;
+  }
+
+  if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ #if __FreeBSD_version > 500000
+    cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr);
+ #endif
+    cam_freeccb(ccb);
+    return -1;
+  }
+
+  if (iop->sensep) {
+    memcpy(iop->sensep,&(ccb->csio.sense_data),sizeof(struct scsi_sense_data));
+    iop->resp_sense_len = sizeof(struct scsi_sense_data);
+  }
+
+  iop->scsi_status = ccb->csio.scsi_status;
+
+  cam_freeccb(ccb);
+  
+  if (cam_dev)
+    cam_close_device(cam_dev);
+
+  if (report > 0) {
+    int trunc;
+
+    pout("  status=0\n");
+    trunc = (iop->dxfer_len > 256) ? 1 : 0;
+    
+    pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+         (trunc ? " [only first 256 bytes shown]" : ""));
+    dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+  }
+  return 0;
+}
+
+// Interface to ATA devices behind 3ware escalade RAID controller cards.  See os_linux.c
+
+#define BUFFER_LEN_678K_CHAR ( sizeof(struct twe_usercommand) ) // 520
+#define BUFFER_LEN_9000_CHAR ( sizeof(TW_OSLI_IOCTL_NO_DATA_BUF) + sizeof(TWE_Command) ) // 2048
+#define TW_IOCTL_BUFFER_SIZE ( MAX(BUFFER_LEN_678K_CHAR, BUFFER_LEN_9000_CHAR) )
+
+int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data) {
+  // to hold true file descriptor
+  struct freebsd_dev_channel* con;
+
+  // return value and buffer for ioctl()
+  int  ioctlreturn, readdata=0;
+  struct twe_usercommand* cmd_twe = NULL;
+  TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL;
+  TWE_Command_ATA* ata = NULL;
+
+  // Used by both the SCSI and char interfaces
+  char ioctl_buffer[TW_IOCTL_BUFFER_SIZE];
+
+  if (disknum < 0) {
+    printwarning(NO_DISK_3WARE,NULL);
+    return -1;
+  }
+
+  // check that "file descriptor" is valid
+  if (isnotopen(&fd,&con))
+      return -1;
+
+  memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE);
+
+  if (escalade_type==CONTROLLER_3WARE_9000_CHAR) {
+    cmd_twa = (TW_OSLI_IOCTL_NO_DATA_BUF*)ioctl_buffer;
+    cmd_twa->pdata = ((TW_OSLI_IOCTL_WITH_PAYLOAD*)cmd_twa)->payload.data_buf;
+    cmd_twa->driver_pkt.buffer_length = 512;
+    ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k;
+  } else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) {
+    cmd_twe = (struct twe_usercommand*)ioctl_buffer;
+    ata = &cmd_twe->tu_command.ata;
+  } else {
+    pout("Unrecognized escalade_type %d in freebsd_3ware_command_interface(disk %d)\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", escalade_type, disknum);
+    errno=ENOSYS;
+    return -1;
+  }
+
+  ata->opcode = TWE_OP_ATA_PASSTHROUGH;
+
+  // Same for (almost) all commands - but some reset below
+  ata->request_id    = 0xFF;
+  ata->unit   = disknum;
+  ata->host_id = 0;
+  ata->status        = 0;           
+  ata->flags         = 0x1;
+  ata->drive_head    = 0x0;
+  ata->sector_num    = 0;
+
+  // All SMART commands use this CL/CH signature.  These are magic
+  // values from the ATA specifications.
+  ata->cylinder_lo   = 0x4F;
+  ata->cylinder_hi   = 0xC2;
+  
+  // SMART ATA COMMAND REGISTER value
+  ata->command       = ATA_SMART_CMD;
+  
+  // Is this a command that reads or returns 512 bytes?
+  // passthru->param values are:
+  // 0x0 - non data command without TFR write check,
+  // 0x8 - non data command with TFR write check,
+  // 0xD - data command that returns data to host from device
+  // 0xF - data command that writes data from host to device
+  // passthru->size values are 0x5 for non-data and 0x07 for data
+  if (command == READ_VALUES     ||
+      command == READ_THRESHOLDS ||
+      command == READ_LOG        ||
+      command == IDENTIFY        ||
+      command == WRITE_LOG ) {
+    readdata=1;
+    if (escalade_type==CONTROLLER_3WARE_678K_CHAR) {
+      cmd_twe->tu_data = data;
+      cmd_twe->tu_size = 512;
+    }
+    ata->sgl_offset = 0x5;
+    ata->size         = 0x5;
+    ata->param        = 0xD;
+    ata->sector_count = 0x1;
+    // For 64-bit to work correctly, up the size of the command packet
+    // in dwords by 1 to account for the 64-bit single sgl 'address'
+    // field. Note that this doesn't agree with the typedefs but it's
+    // right (agree with kernel driver behavior/typedefs).
+    //if (sizeof(long)==8)
+    //  ata->size++;
+  }
+  else {
+    // Non data command -- but doesn't use large sector 
+    // count register values.  
+    ata->sgl_offset = 0x0;
+    ata->size         = 0x5;
+    ata->param        = 0x8;
+    ata->sector_count = 0x0;
+  }
+  
+  // Now set ATA registers depending upon command
+  switch (command){
+  case CHECK_POWER_MODE:
+    ata->command     = ATA_CHECK_POWER_MODE;
+    ata->features    = 0;
+    ata->cylinder_lo = 0;
+    ata->cylinder_hi = 0;
+    break;
+  case READ_VALUES:
+    ata->features = ATA_SMART_READ_VALUES;
+    break;
+  case READ_THRESHOLDS:
+    ata->features = ATA_SMART_READ_THRESHOLDS;
+    break;
+  case READ_LOG:
+    ata->features = ATA_SMART_READ_LOG_SECTOR;
+    // log number to return
+    ata->sector_num  = select;
+    break;
+  case WRITE_LOG:
+    readdata=0;
+    ata->features     = ATA_SMART_WRITE_LOG_SECTOR;
+    ata->sector_count = 1;
+    ata->sector_num   = select;
+    ata->param        = 0xF;  // PIO data write
+    break;
+  case IDENTIFY:
+    // ATA IDENTIFY DEVICE
+    ata->command     = ATA_IDENTIFY_DEVICE;
+    ata->features    = 0;
+    ata->cylinder_lo = 0;
+    ata->cylinder_hi = 0;
+    break;
+  case PIDENTIFY:
+    // 3WARE controller can NOT have packet device internally
+    pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", disknum);
+    errno=ENODEV;
+    return -1;
+  case ENABLE:
+    ata->features = ATA_SMART_ENABLE;
+    break;
+  case DISABLE:
+    ata->features = ATA_SMART_DISABLE;
+    break;
+  case AUTO_OFFLINE:
+    ata->features     = ATA_SMART_AUTO_OFFLINE;
+    // Enable or disable?
+    ata->sector_count = select;
+    break;
+  case AUTOSAVE:
+    ata->features     = ATA_SMART_AUTOSAVE;
+    // Enable or disable?
+    ata->sector_count = select;
+    break;
+  case IMMEDIATE_OFFLINE:
+    ata->features    = ATA_SMART_IMMEDIATE_OFFLINE;
+    // What test type to run?
+    ata->sector_num  = select;
+    break;
+  case STATUS_CHECK:
+    ata->features = ATA_SMART_STATUS;
+    break;
+  case STATUS:
+    // This is JUST to see if SMART is enabled, by giving SMART status
+    // command. But it doesn't say if status was good, or failing.
+    // See below for the difference.
+    ata->features = ATA_SMART_STATUS;
+    break;
+  default:
+    pout("Unrecognized command %d in freebsd_3ware_command_interface(disk %d)\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", command, disknum);
+    errno=ENOSYS;
+    return -1;
+  }
+
+  // Now send the command down through an ioctl()
+  if (escalade_type==CONTROLLER_3WARE_9000_CHAR) {
+#ifdef IOCATAREQUEST
+    ioctlreturn=ioctl(con->device,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa);
+#else
+    ioctlreturn=ioctl(con->atacommand,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa);
+#endif
+  } else {
+#ifdef IOCATAREQUEST
+    ioctlreturn=ioctl(con->device,TWEIO_COMMAND,cmd_twe);
+#else
+    ioctlreturn=ioctl(con->atacommand,TWEIO_COMMAND,cmd_twe);
+#endif
+  }
+
+  // Deal with the different error cases
+  if (ioctlreturn) {
+    if (!errno)
+      errno=EIO;
+    return -1;
+  }
+  
+  // See if the ATA command failed.  Now that we have returned from
+  // the ioctl() call, if passthru is valid, then:
+  // - ata->status contains the 3ware controller STATUS
+  // - ata->command contains the ATA STATUS register
+  // - ata->features contains the ATA ERROR register
+  //
+  // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS
+  // If bit 0 (error bit) is set, then ATA ERROR register is valid.
+  // While we *might* decode the ATA ERROR register, at the moment it
+  // doesn't make much sense: we don't care in detail why the error
+  // happened.
+  
+  if (ata->status || (ata->command & 0x21)) {
+    pout("Command failed, ata.status=(0x%2.2x), ata.command=(0x%2.2x), ata.flags=(0x%2.2x)\n",ata->status,ata->command,ata->flags);
+    errno=EIO;
+    return -1;
+  }
+  
+  // If this is a read data command, copy data to output buffer
+  if (readdata) {
+    if (escalade_type==CONTROLLER_3WARE_9000_CHAR)
+      memcpy(data, cmd_twa->pdata, 512);
+  }
+
+  // For STATUS_CHECK, we need to check register values
+  if (command==STATUS_CHECK) {
+    
+    // To find out if the SMART RETURN STATUS is good or failing, we
+    // need to examine the values of the Cylinder Low and Cylinder
+    // High Registers.
+    
+    unsigned short cyl_lo=ata->cylinder_lo;
+    unsigned short cyl_hi=ata->cylinder_hi;
+    
+    // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good.
+    if (cyl_lo==0x4F && cyl_hi==0xC2)
+      return 0;
+    
+    // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL
+    if (cyl_lo==0xF4 && cyl_hi==0x2C)
+      return 1;
+    
+      errno=EIO;
+      return -1;
+  }
+  
+  // copy sector count register (one byte!) to return data
+  if (command==CHECK_POWER_MODE)
+    *data=*(char *)&(ata->sector_count);
+  
+  // look for nonexistent devices/ports
+  if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) {
+    errno=ENODEV;
+    return -1;
+  }
+  
+  return 0;
+}
+
+static int get_tw_channel_unit (const char* name, int* unit, int* dev) {
+  const char *p;
+
+  /* device node sanity check */
+  for (p = name + 3; *p; p++)
+    if (*p < '0' || *p > '9')
+      return -1;
+  if (strlen(name) > 4 && *(name + 3) == '0')
+    return -1;
+
+  if (dev != NULL)
+    *dev=atoi(name + 3);
+
+  /* no need for unit number */
+  if (unit != NULL)
+    *unit=0;
+  return 0;
+}
+
+
+#ifndef IOCATAREQUEST
+static int get_ata_channel_unit ( const char* name, int* unit, int* dev) {
+#ifndef ATAREQUEST
+  *dev=0;
+  *unit=0;
+return 0;
+#else
+  // there is no direct correlation between name 'ad0, ad1, ...' and
+  // channel/unit number.  So we need to iterate through the possible
+  // channels and check each unit to see if we match names
+  struct ata_cmd iocmd;
+  int fd,maxunit;
+  
+  bzero(&iocmd, sizeof(struct ata_cmd));
+
+  if ((fd = open("/dev/ata", O_RDWR)) < 0)
+    return -errno;
+  
+  iocmd.cmd = ATAGMAXCHANNEL;
+  if (ioctl(fd, IOCATA, &iocmd) < 0) {
+    return -errno;
+    close(fd);
+  }
+  maxunit = iocmd.u.maxchan;
+  for (*unit = 0; *unit < maxunit; (*unit)++) {
+    iocmd.channel = *unit;
+    iocmd.device = -1;
+    iocmd.cmd = ATAGPARM;
+    if (ioctl(fd, IOCATA, &iocmd) < 0) {
+      close(fd);
+      return -errno;
+    }
+    if (iocmd.u.param.type[0] && !strcmp(name,iocmd.u.param.name[0])) {
+      *dev = 0;
+      break;
+    }
+    if (iocmd.u.param.type[1] && !strcmp(name,iocmd.u.param.name[1])) {
+      *dev = 1;
+      break;
+    }
+  }
+  close(fd);
+  if (*unit == maxunit)
+    return -1;
+  else
+    return 0;
+#endif
+}
+#endif
+
+// Guess device type (ata or scsi) based on device name (FreeBSD
+// specific) SCSI device name in FreeBSD can be sd, sr, scd, st, nst,
+// osst, nosst and sg.
+static const char * fbsd_dev_prefix = "/dev/";
+static const char * fbsd_dev_ata_disk_prefix = "ad";
+static const char * fbsd_dev_scsi_disk_plus = "da";
+static const char * fbsd_dev_scsi_tape1 = "sa";
+static const char * fbsd_dev_scsi_tape2 = "nsa";
+static const char * fbsd_dev_scsi_tape3 = "esa";
+static const char * fbsd_dev_twe_ctrl = "twe";
+static const char * fbsd_dev_twa_ctrl = "twa";
+
+static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *chan) {
+  int len;
+  int dev_prefix_len = strlen(fbsd_dev_prefix);
+  
+  // if dev_name null, or string length zero
+  if (!dev_name || !(len = strlen(dev_name)))
+    return CONTROLLER_UNKNOWN;
+  
+  // Remove the leading /dev/... if it's there
+  if (!strncmp(fbsd_dev_prefix, dev_name, dev_prefix_len)) {
+    if (len <= dev_prefix_len) 
+      // if nothing else in the string, unrecognized
+      return CONTROLLER_UNKNOWN;
+    // else advance pointer to following characters
+    dev_name += dev_prefix_len;
+  }
+  // form /dev/ad* or ad*
+  if (!strncmp(fbsd_dev_ata_disk_prefix, dev_name,
+               strlen(fbsd_dev_ata_disk_prefix))) {
+#ifndef IOCATAREQUEST
+    if (chan != NULL) {
+      if (get_ata_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) {
+        return CONTROLLER_UNKNOWN;
+      }
+    }
+#endif
+    return CONTROLLER_ATA;
+  }
+  
+  // form /dev/da* or da*
+  if (!strncmp(fbsd_dev_scsi_disk_plus, dev_name,
+               strlen(fbsd_dev_scsi_disk_plus)))
+    goto handlescsi;
+
+  // form /dev/sa* or sa*
+  if (!strncmp(fbsd_dev_scsi_tape1, dev_name,
+              strlen(fbsd_dev_scsi_tape1)))
+    goto handlescsi;
+
+  // form /dev/nsa* or nsa*
+  if (!strncmp(fbsd_dev_scsi_tape2, dev_name,
+              strlen(fbsd_dev_scsi_tape2)))
+    goto handlescsi;
+
+  // form /dev/esa* or esa*
+  if (!strncmp(fbsd_dev_scsi_tape3, dev_name,
+              strlen(fbsd_dev_scsi_tape3)))
+    goto handlescsi;
+  
+  if (!strncmp(fbsd_dev_twa_ctrl,dev_name,
+              strlen(fbsd_dev_twa_ctrl))) {
+    if (chan != NULL) {
+      if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) {
+       return CONTROLLER_UNKNOWN;
+      }
+    }
+    else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) {
+       return CONTROLLER_UNKNOWN;
+    }
+    return CONTROLLER_3WARE_9000_CHAR;
+  }
+
+  if (!strncmp(fbsd_dev_twe_ctrl,dev_name,
+              strlen(fbsd_dev_twe_ctrl))) {
+    if (chan != NULL) {
+      if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) {
+       return CONTROLLER_UNKNOWN;
+      }
+    }
+    else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) {
+       return CONTROLLER_UNKNOWN;
+    }
+    return CONTROLLER_3WARE_678K_CHAR;
+  }
+
+  // we failed to recognize any of the forms
+  return CONTROLLER_UNKNOWN;
+
+ handlescsi:
+  if (chan != NULL) {
+    if (!(chan->devname = (char *)calloc(1,DEV_IDLEN+1)))
+      return CONTROLLER_UNKNOWN;
+    
+    if (cam_get_device(dev_name,chan->devname,DEV_IDLEN,&(chan->unitnum)) == -1)
+      return CONTROLLER_UNKNOWN;
+  }
+  return CONTROLLER_SCSI;
+  
+}
+
+int guess_device_type (const char* dev_name) {
+  return parse_ata_chan_dev(dev_name,NULL);
+}
+
+// global variable holding byte count of allocated memory
+extern long long bytes;
+
+// we are going to take advantage of the fact that FreeBSD's devfs will only
+// have device entries for devices that exist.  So if we get the equivilent of
+// ls /dev/ad?, we have all the ATA devices on the system
+//
+// If any errors occur, leave errno set as it was returned by the
+// system call, and return <0.
+
+// Return values:
+// -1 out of memory
+// -2 to -5 errors in glob
+
+int get_dev_names(char*** names, const char* prefix) {
+  int n = 0;
+  char** mp;
+  int retglob,lim;
+  glob_t globbuf;
+  int i;
+  char pattern1[128],pattern2[128];
+
+  bzero(&globbuf,sizeof(globbuf));
+  // in case of non-clean exit
+  *names=NULL;
+
+  // handle 0-99 possible devices, will still be limited by MAX_NUM_DEV
+  sprintf(pattern1,"/dev/%s[0-9]",prefix);
+  sprintf(pattern2,"/dev/%s[0-9][0-9]",prefix);
+  
+  // Use glob to look for any directory entries matching the patterns
+  // first call inits with first pattern match, second call appends
+  // to first list. Turn on NOCHECK for second call. This results in no
+  // error if no more matches found, however it does append the actual
+  // pattern to the list of paths....
+  if ((retglob=glob(pattern1, GLOB_ERR, NULL, &globbuf)) ||
+      (retglob=glob(pattern2, GLOB_ERR|GLOB_APPEND|GLOB_NOCHECK,NULL,&globbuf))) {
+     int retval = -1;
+    // glob failed
+    if (retglob==GLOB_NOSPACE)
+      pout("glob(3) ran out of memory matching patterns (%s),(%s)\n",
+           pattern1, pattern2);
+    else if (retglob==GLOB_ABORTED)
+      pout("glob(3) aborted matching patterns (%s),(%s)\n",
+           pattern1, pattern2);
+    else if (retglob==GLOB_NOMATCH) {
+      pout("glob(3) found no matches for patterns (%s),(%s)\n",
+           pattern1, pattern2);
+      retval = 0;
+    }
+    else if (retglob)
+      pout("Unexplained error in glob(3) of patterns (%s),(%s)\n",
+           pattern1, pattern2);
+    
+    //  Free memory and return
+    globfree(&globbuf);
+
+    return retval;
+  }
+
+  // did we find too many paths?
+  // did we find too many paths?
+  lim = globbuf.gl_pathc < MAX_NUM_DEV ? globbuf.gl_pathc : MAX_NUM_DEV;
+  if (lim < globbuf.gl_pathc)
+    pout("glob(3) found %d > MAX=%d devices matching patterns (%s),(%s): ignoring %d paths\n", 
+         globbuf.gl_pathc, MAX_NUM_DEV, pattern1,pattern2,
+         globbuf.gl_pathc-MAX_NUM_DEV);
+  
+  // allocate space for up to lim number of ATA devices
+  if (!(mp =  (char **)calloc(lim, sizeof(char*)))){
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+
+  // now step through the list returned by glob.  No link checking needed
+  // in FreeBSD
+  for (i=0; i<globbuf.gl_pathc; i++){
+    // becuase of the NO_CHECK on second call to glob,
+    // the pattern itself will be added to path list..
+    // so ignore any paths that have the ']' from pattern
+    if (strchr(globbuf.gl_pathv[i],']') == NULL)
+      mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion);
+  }
+
+  globfree(&globbuf);
+  mp = (char **)realloc(mp,n*(sizeof(char*))); // shrink to correct size
+  bytes += (n)*(sizeof(char*)); // and set allocated byte count
+  *names=mp;
+  return n;
+}
+
+int make_device_names (char*** devlist, const char* name) {
+  if (!strcmp(name,"SCSI"))
+    return get_dev_names(devlist,"da");
+  else if (!strcmp(name,"ATA"))
+    return get_dev_names(devlist,"ad");
+  else
+    return 0;
+}
diff --git a/os_generic.c b/os_generic.c
deleted file mode 100644 (file)
index 3c5b5c4..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * os_generic.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) YEAR YOUR_NAME <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2003-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/*  PORTING NOTES AND COMMENTS
-
-    To port smartmontools to the OS of your choice, please:
-
- [0] Contact smartmontools-support@lists.sourceforge.net to check
-     that it's not already been done.
-
- [1] Make copies of os_generic.[hc] called os_myOS.[hc].
-
- [2] Modify configure.in so that case "${host}" includes myOS.
-
- [3] Verify that ./autogen.sh && ./configure && make compiles the
-     code.  If not, fix any compilation problems.  If your OS lacks
-     some function that is used elsewhere in the code, then add a
-     AC_CHECK_FUNCS([missingfunction]) line to configure.in, and
-     surround uses of the function with:
-     #ifdef HAVE_MISSINGFUNCTION
-     ... 
-     #endif
-     where the macro HAVE_MISSINGFUNCTION is (or is not) defined in
-     config.h.
-
- [4] Provide the functions defined in this file by fleshing out the
-     skeletons below.  You can entirely eliminate the function
-     'unsupported()'.
-
- [5] Contact smartmontools-support@lists.sourceforge.net to see
-     about checking your code into the smartmontools CVS archive.
-*/
-
-/*
- Developer's note: for testing this file, use an unsupported system,
- for example: ./configure --build=rs6000-ibm-aix && make
-*/
-
-
-// This is needed for the various HAVE_* macros and PROJECT_* macros.
-#include "config.h"
-
-// These are needed to define prototypes and structures for the
-// functions defined below
-#include "int64.h"
-#include "atacmds.h"
-#include "scsicmds.h"
-#include "utility.h"
-
-// This is to include whatever structures and prototypes you define in
-// os_generic.h
-#include "os_generic.h"
-
-// Needed by '-V' option (CVS versioning) of smartd/smartctl.  You
-// should have one *_H_CVSID macro appearing below for each file
-// appearing with #include "*.h" above.  Please list these (below) in
-// alphabetic/dictionary order.
-const char *os_XXXX_c_cvsid="$Id: os_generic.c,v 1.21 2006/04/12 14:54:28 ballen4705 Exp $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_GENERIC_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
-
-
-// This is here to prevent compiler warnings for unused arguments of
-// functions.
-#define ARGUSED(x) ((void)(x))
-
-// Please eliminate the following block: both the #include and
-// the 'unsupported()' function.  They are only here to warn
-// unsuspecting users that their Operating System is not supported! If
-// you wish, you can use a similar warning mechanism for any of the
-// functions in this file that you can not (or choose not to)
-// implement.
-
-
-#ifdef HAVE_UNAME
-#include <sys/utsname.h>
-#endif
-
-static void unsupported(){
-  static int warninggiven;
-
-  if (!warninggiven) {
-    char *osname;
-    extern unsigned char debugmode;
-    unsigned char savedebugmode=debugmode;
-    
-#ifdef HAVE_UNAME
-    struct utsname ostype;
-    uname(&ostype);
-    osname=ostype.sysname;
-#else
-    osname="host's";
-#endif
-
-    debugmode=1;
-    pout("\n"
-         "############################################################################\n"
-         "WARNING: smartmontools has not been ported to the %s Operating System.\n"
-         "Please see the files os_generic.c and os_generic.h for porting instructions.\n"
-         "############################################################################\n\n",
-         osname);
-    debugmode=savedebugmode;
-    warninggiven=1;
-  }
-  
-  return;
-}
-// End of the 'unsupported()' block that you should eliminate.
-
-
-// print examples for smartctl.  You should modify this function so
-// that the device paths are sensible for your OS, and to eliminate
-// unsupported commands (eg, 3ware controllers).
-void print_smartctl_examples(){
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
-         "  smartctl -a /dev/hda                       (Prints all SMART information)\n\n"
-         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
-         "                                              (Enables SMART on first disk)\n\n"
-         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n\n"
-         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-         "  smartctl -a --device=3ware,2 /dev/sda\n"
-         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
-         );
-#else
-  printf(
-         "  smartctl -a /dev/hda                       (Prints all SMART information)\n"
-         "  smartctl -s on -o on -S on /dev/hda         (Enables SMART on first disk)\n"
-         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n"
-         "  smartctl -A -l selftest -q errorsonly /dev/hda\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-         "  smartctl -a -d 3ware,2 /dev/sda\n"
-         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
-         );
-#endif
-  return;
-}
-
-// tries to guess device type given the name (a path).  See utility.h
-// for return values.
-int guess_device_type (const char* dev_name) {
-  ARGUSED(dev_name);
-  unsupported();
-  return CONTROLLER_UNKNOWN;
-}
-
-// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
-// smartd.  Returns number N of devices, or -1 if out of
-// memory. Allocates N+1 arrays: one of N pointers (devlist); the
-// other N arrays each contain null-terminated character strings.  In
-// the case N==0, no arrays are allocated because the array of 0
-// pointers has zero length, equivalent to calling malloc(0).
-int make_device_names (char*** devlist, const char* name) {
-  ARGUSED(devlist);
-  ARGUSED(name);
-  unsupported();
-  return 0;
-}
-
-// Like open().  Return non-negative integer handle, only used by the
-// functions below.  type=="ATA" or "SCSI".  If you need to store
-// extra information about your devices, create a private internal
-// array within this file (see os_freebsd.c for an example).  If you
-// can not open the device (permission denied, does not exist, etc)
-// set errno as open() does and return <0.
-int deviceopen(const char *pathname, char *type){
-  ARGUSED(pathname);
-  ARGUSED(type);
-  unsupported();
-  return -1;
-}
-
-// Like close().  Acts only on integer handles returned by
-// deviceopen() above.
-int deviceclose(int fd){
-  ARGUSED(fd);
-  unsupported();
-  return 0;
-}
-
-// Interface to ATA devices.  See os_linux.c for the cannonical example.
-// DETAILED DESCRIPTION OF ARGUMENTS
-//   device: is the integer handle provided by deviceopen()
-//   command: defines the different operations, see atacmds.h
-//   select: additional input data IF NEEDED (which log, which type of
-//           self-test).
-//   data:   location to write output data, IF NEEDED (1 or 512 bytes).
-//   Note: not all commands use all arguments.
-// RETURN VALUES (for all commands BUT command==STATUS_CHECK)
-//  -1 if the command failed
-//   0 if the command succeeded,
-// RETURN VALUES if command==STATUS_CHECK
-//  -1 if the command failed OR the disk SMART status can't be determined
-//   0 if the command succeeded and disk SMART status is "OK"
-//   1 if the command succeeded and disk SMART status is "FAILING"
-int ata_command_interface(int fd, smart_command_set command, int select, char *data){
-  ARGUSED(fd);
-  ARGUSED(command);
-  ARGUSED(select);
-  ARGUSED(data);
-  unsupported();
-  return -1;
-}
-
-int marvell_command_interface(int fd, smart_command_set command, int select, char *data){
-  ARGUSED(fd);
-  ARGUSED(command);
-  ARGUSED(select);
-  ARGUSED(data);
-  unsupported();
-  return -1;
-}
-
-// Interface to ATA devices behind 3ware escalade/apache RAID
-// controller cards.  Same description as ata_command_interface()
-// above except that 0 <= disknum <= 15 specifies the ATA disk
-// attached to the controller, and controller_type specifies the
-// precise type of 3ware controller.  See os_linux.c
-int escalade_command_interface(int fd, int disknum, int controller_type, smart_command_set command, int select, char *data){
-  ARGUSED(fd);
-  ARGUSED(disknum);
-  ARGUSED(controller_type);
-  ARGUSED(command);
-  ARGUSED(select);
-  ARGUSED(data);
-
-  unsupported();
-  return -1;
-}
-
-#include <errno.h>
-// Interface to SCSI devices.  See os_linux.c
-int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) {
-  ARGUSED(fd);
-  ARGUSED(iop);
-  ARGUSED(report);
-  unsupported();
-  return -ENOSYS;
-}
diff --git a/os_generic.cpp b/os_generic.cpp
new file mode 100644 (file)
index 0000000..2b9e33e
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * os_generic.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) YEAR YOUR_NAME <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+/*
+    NOTE: The code in this file is only called when smartmontools has
+    been compiled on an unrecognized/unsupported platform.  This file
+    can then serve as a "template" to make os_myOS.cpp if you wish to
+    build support for that platform.
+
+
+ PORTING NOTES AND COMMENTS
+ --------------------------
+
+ To port smartmontools to the OS of your choice, please:
+
+ [0] Contact smartmontools-support@lists.sourceforge.net to check
+     that it's not already been done.
+
+ [1] Make copies of os_generic.[hc] called os_myOS.[hc].
+
+ [2] Modify configure.in so that case "${host}" includes myOS.
+
+ [3] Verify that ./autogen.sh && ./configure && make compiles the
+     code.  If not, fix any compilation problems.  If your OS lacks
+     some function that is used elsewhere in the code, then add a
+     AC_CHECK_FUNCS([missingfunction]) line to configure.in, and
+     surround uses of the function with:
+     #ifdef HAVE_MISSINGFUNCTION
+     ... 
+     #endif
+     where the macro HAVE_MISSINGFUNCTION is (or is not) defined in
+     config.h.
+
+ [4] Provide the functions defined in this file by fleshing out the
+     skeletons below.  You can entirely eliminate the function
+     'unsupported()'.
+
+ [5] Contact smartmontools-support@lists.sourceforge.net to see
+     about checking your code into the smartmontools CVS archive.
+*/
+
+/*
+ Developer's note: for testing this file, use an unsupported system,
+ for example: ./configure --build=rs6000-ibm-aix && make
+*/
+
+
+// This is needed for the various HAVE_* macros and PROJECT_* macros.
+#include "config.h"
+
+// These are needed to define prototypes and structures for the
+// functions defined below
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+// This is to include whatever structures and prototypes you define in
+// os_generic.h
+#include "os_generic.h"
+
+// Needed by '-V' option (CVS versioning) of smartd/smartctl.  You
+// should have one *_H_CVSID macro appearing below for each file
+// appearing with #include "*.h" above.  Please list these (below) in
+// alphabetic/dictionary order.
+const char *os_XXXX_c_cvsid="$Id: os_generic.cpp,v 1.24 2006/09/20 16:17:31 shattered Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_GENERIC_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+
+// This is here to prevent compiler warnings for unused arguments of
+// functions.
+#define ARGUSED(x) ((void)(x))
+
+// Please eliminate the following block: both the #include and
+// the 'unsupported()' function.  They are only here to warn
+// unsuspecting users that their Operating System is not supported! If
+// you wish, you can use a similar warning mechanism for any of the
+// functions in this file that you can not (or choose not to)
+// implement.
+
+
+#ifdef HAVE_UNAME
+#include <sys/utsname.h>
+#endif
+
+static void unsupported(){
+  static int warninggiven;
+
+  if (!warninggiven) {
+    char *osname;
+    extern unsigned char debugmode;
+    unsigned char savedebugmode=debugmode;
+    
+#ifdef HAVE_UNAME
+    struct utsname ostype;
+    uname(&ostype);
+    osname=ostype.sysname;
+#else
+    osname="host's";
+#endif
+
+    debugmode=1;
+    pout("\n"
+         "############################################################################\n"
+         "WARNING: smartmontools has not been ported to the %s Operating System.\n"
+         "Please see the files os_generic.cpp and os_generic.h for porting instructions.\n"
+         "############################################################################\n\n",
+         osname);
+    debugmode=savedebugmode;
+    warninggiven=1;
+  }
+  
+  return;
+}
+// End of the 'unsupported()' block that you should eliminate.
+
+
+// print examples for smartctl.  You should modify this function so
+// that the device paths are sensible for your OS, and to eliminate
+// unsupported commands (eg, 3ware controllers).
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+         "  smartctl -a /dev/hda                       (Prints all SMART information)\n\n"
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl -a --device=3ware,2 /dev/sda\n"
+         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
+         );
+#else
+  printf(
+         "  smartctl -a /dev/hda                       (Prints all SMART information)\n"
+         "  smartctl -s on -o on -S on /dev/hda         (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl -a -d 3ware,2 /dev/sda\n"
+         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
+         );
+#endif
+  return;
+}
+
+// tries to guess device type given the name (a path).  See utility.h
+// for return values.
+int guess_device_type (const char* dev_name) {
+  ARGUSED(dev_name);
+  unsupported();
+  return CONTROLLER_UNKNOWN;
+}
+
+// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
+// smartd.  Returns number N of devices, or -1 if out of
+// memory. Allocates N+1 arrays: one of N pointers (devlist); the
+// other N arrays each contain null-terminated character strings.  In
+// the case N==0, no arrays are allocated because the array of 0
+// pointers has zero length, equivalent to calling malloc(0).
+int make_device_names (char*** devlist, const char* name) {
+  ARGUSED(devlist);
+  ARGUSED(name);
+  unsupported();
+  return 0;
+}
+
+// Like open().  Return non-negative integer handle, only used by the
+// functions below.  type=="ATA" or "SCSI".  If you need to store
+// extra information about your devices, create a private internal
+// array within this file (see os_freebsd.cpp for an example).  If you
+// can not open the device (permission denied, does not exist, etc)
+// set errno as open() does and return <0.
+int deviceopen(const char *pathname, char *type){
+  ARGUSED(pathname);
+  ARGUSED(type);
+  unsupported();
+  return -1;
+}
+
+// Like close().  Acts only on integer handles returned by
+// deviceopen() above.
+int deviceclose(int fd){
+  ARGUSED(fd);
+  unsupported();
+  return 0;
+}
+
+// Interface to ATA devices.  See os_linux.cpp for the cannonical example.
+// DETAILED DESCRIPTION OF ARGUMENTS
+//   device: is the integer handle provided by deviceopen()
+//   command: defines the different operations, see atacmds.h
+//   select: additional input data IF NEEDED (which log, which type of
+//           self-test).
+//   data:   location to write output data, IF NEEDED (1 or 512 bytes).
+//   Note: not all commands use all arguments.
+// RETURN VALUES (for all commands BUT command==STATUS_CHECK)
+//  -1 if the command failed
+//   0 if the command succeeded,
+// RETURN VALUES if command==STATUS_CHECK
+//  -1 if the command failed OR the disk SMART status can't be determined
+//   0 if the command succeeded and disk SMART status is "OK"
+//   1 if the command succeeded and disk SMART status is "FAILING"
+int ata_command_interface(int fd, smart_command_set command, int select, char *data){
+  ARGUSED(fd);
+  ARGUSED(command);
+  ARGUSED(select);
+  ARGUSED(data);
+  unsupported();
+  return -1;
+}
+
+int marvell_command_interface(int fd, smart_command_set command, int select, char *data){
+  ARGUSED(fd);
+  ARGUSED(command);
+  ARGUSED(select);
+  ARGUSED(data);
+  unsupported();
+  return -1;
+}
+
+
+int highpoint_command_interface(int fd, smart_command_set command, int select, char *data)
+{
+  ARGUSED(fd);
+  ARGUSED(command);
+  ARGUSED(select);
+  ARGUSED(data);
+  unsupported();
+  return -1;
+}
+
+// Interface to ATA devices behind 3ware escalade/apache RAID
+// controller cards.  Same description as ata_command_interface()
+// above except that 0 <= disknum <= 15 specifies the ATA disk
+// attached to the controller, and controller_type specifies the
+// precise type of 3ware controller.  See os_linux.c
+int escalade_command_interface(int fd, int disknum, int controller_type, smart_command_set command, int select, char *data){
+  ARGUSED(fd);
+  ARGUSED(disknum);
+  ARGUSED(controller_type);
+  ARGUSED(command);
+  ARGUSED(select);
+  ARGUSED(data);
+
+  unsupported();
+  return -1;
+}
+
+#include <errno.h>
+// Interface to SCSI devices.  See os_linux.c
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) {
+  ARGUSED(fd);
+  ARGUSED(iop);
+  ARGUSED(report);
+  unsupported();
+  return -ENOSYS;
+}
diff --git a/os_linux.c b/os_linux.c
deleted file mode 100644 (file)
index 26be586..0000000
+++ /dev/null
@@ -1,1527 +0,0 @@
-/* 
- *  os_linux.c
- * 
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2003-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2003-6 Doug Gilbert <dougg@torque.net>
- *
- *  Parts of this file are derived from code that was
- *
- *  Written By: Adam Radford <linux@3ware.com>
- *  Modifications By: Joel Jacobson <linux@3ware.com>
- *                   Arnaldo Carvalho de Melo <acme@conectiva.com.br>
- *                    Brad Strand <linux@3ware.com>
- *
- *  Copyright (C) 1999-2003 3ware Inc.
- *
- *  Kernel compatablity By:     Andre Hedrick <andre@suse.com>
- *  Non-Copyright (C) 2000      Andre Hedrick <andre@suse.com>
- *
- * Other ars of this file are derived from code that was
- * 
- * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
- * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- * 
- */
-
-// This file contains the linux-specific IOCTL parts of
-// smartmontools. It includes one interface routine for ATA devices,
-// one for SCSI devices, and one for ATA devices behind escalade
-// controllers.
-
-#include <errno.h>
-#include <fcntl.h>
-#include <glob.h>
-#include <scsi/scsi_ioctl.h>
-#include <scsi/sg.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#ifndef makedev // old versions of types.h do not include sysmacros.h
-#include <sys/sysmacros.h>
-#endif
-
-#include "config.h"
-#include "int64.h"
-#include "atacmds.h"
-#include "os_linux.h"
-#include "scsicmds.h"
-#include "utility.h"
-
-#ifndef ENOTSUP
-#define ENOTSUP ENOSYS
-#endif
-typedef unsigned long long u8;
-
-#define ARGUSED(x) ((void)(x))
-
-static const char *filenameandversion="$Id: os_linux.c,v 1.82 2006/04/12 16:28:56 ballen4705 Exp $";
-
-const char *os_XXXX_c_cvsid="$Id: os_linux.c,v 1.82 2006/04/12 16:28:56 ballen4705 Exp $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_LINUX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
-
-// to hold onto exit code for atexit routine
-extern int exitstatus;
-
-// global variable holding byte count of allocated memory
-extern long long bytes;
-
-
-
-/* This function will setup and fix device nodes for a 3ware controller. */
-#define MAJOR_STRING_LENGTH 3
-#define DEVICE_STRING_LENGTH 32
-#define NODE_STRING_LENGTH 16
-int setup_3ware_nodes(char *nodename, char *driver_name) {
-  int              tw_major      = 0;
-  int              index         = 0;
-  char             majorstring[MAJOR_STRING_LENGTH+1];
-  char             device_name[DEVICE_STRING_LENGTH+1];
-  char             nodestring[NODE_STRING_LENGTH];
-  struct stat      stat_buf;
-  FILE             *file;
-  
-  /* First try to open up /proc/devices */
-  if (!(file = fopen("/proc/devices", "r"))) {
-    pout("Error opening /proc/devices to check/create 3ware device nodes\n");
-    syserror("fopen");
-    return 0;  // don't fail here: user might not have /proc !
-  }
-  
-  /* Attempt to get device major number */
-  while (EOF != fscanf(file, "%3s %32s", majorstring, device_name)) {
-    majorstring[MAJOR_STRING_LENGTH]='\0';
-    device_name[DEVICE_STRING_LENGTH]='\0';
-    if (!strncmp(device_name, nodename, DEVICE_STRING_LENGTH)) {
-      tw_major = atoi(majorstring);
-      break;
-    }
-  }
-  fclose(file);
-  
-  /* See if we found a major device number */
-  if (!tw_major) {
-    pout("No major number for /dev/%s listed in /proc/devices. Is the %s driver loaded?\n", nodename, driver_name);
-    return 2;
-  }
-  
-  /* Now check if nodes are correct */
-  for (index=0; index<16; index++) {
-    sprintf(nodestring, "/dev/%s%d", nodename, index);
-         
-    /* Try to stat the node */
-    if ((stat(nodestring, &stat_buf))) {
-      /* Create a new node if it doesn't exist */
-      if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) {
-       pout("problem creating 3ware device nodes %s", nodestring);
-       syserror("mknod");
-       return 3;
-      }
-    }
-    
-    /* See if nodes major and minor numbers are correct */
-    if ((tw_major != (int)(major(stat_buf.st_rdev))) ||
-       (index    != (int)(minor(stat_buf.st_rdev))) ||
-       (!S_ISCHR(stat_buf.st_mode))) {
-      
-      /* Delete the old node */
-      if (unlink(nodestring)) {
-       pout("problem unlinking stale 3ware device node %s", nodestring);
-       syserror("unlink");
-       return 4;
-      }
-      
-      /* Make a new node */
-      if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) {
-       pout("problem creating 3ware device nodes %s", nodestring);
-       syserror("mknod");
-       return 5;
-      }
-    }
-  }
-  return 0;
-}
-
-// equivalent to open(path, flags)
-int deviceopen(const char *pathname, char *type){
-  if (!strcmp(type,"SCSI")) {
-    int fd = open(pathname, O_RDWR | O_NONBLOCK);
-    if (fd < 0 && errno == EROFS)
-      fd = open(pathname, O_RDONLY | O_NONBLOCK);
-    return fd;
-  }
-  else if (!strcmp(type,"ATA")) 
-    return open(pathname, O_RDONLY | O_NONBLOCK);
-  else if (!strcmp(type,"ATA_3WARE_9000")) {
-    // the device nodes for this controller are dynamically assigned,
-    // so we need to check that they exist with the correct major
-    // numbers and if not, create them
-    if (setup_3ware_nodes("twa", "3w-9xxx")) {
-      if (!errno)
-       errno=ENXIO;
-      return -1;
-    }
-    return open(pathname, O_RDONLY | O_NONBLOCK);
-  }
-  else if (!strcmp(type,"ATA_3WARE_678K")) {
-    // the device nodes for this controller are dynamically assigned,
-    // so we need to check that they exist with the correct major
-    // numbers and if not, create them
-    if (setup_3ware_nodes("twe", "3w-xxxx")) {
-      if (!errno)
-       errno=ENXIO;
-      return -1;
-    }
-    return open(pathname, O_RDONLY | O_NONBLOCK);
-  }
-  else
-    return -1;
-}
-
-// equivalent to close(file descriptor)
-int deviceclose(int fd){
-  return close(fd);
-}
-
-// print examples for smartctl
-void print_smartctl_examples(){
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
-         "  smartctl --all /dev/hda                    (Prints all SMART information)\n\n"
-         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
-         "                                              (Enables SMART on first disk)\n\n"
-         "  smartctl --test=long /dev/hda          (Executes extended disk self-test)\n\n"
-         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-         "  smartctl --all --device=3ware,2 /dev/sda\n"
-         "  smartctl --all --device=3ware,2 /dev/twe0\n"
-         "  smartctl --all --device=3ware,2 /dev/twa0\n"
-         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
-         );
-#else
-  printf(
-         "  smartctl -a /dev/hda                       (Prints all SMART information)\n"
-         "  smartctl -s on -o on -S on /dev/hda         (Enables SMART on first disk)\n"
-         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n"
-         "  smartctl -A -l selftest -q errorsonly /dev/hda\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-         "  smartctl -a -d 3ware,2 /dev/sda\n"
-         "  smartctl -a -d 3ware,2 /dev/twa0\n"
-         "  smartctl -a -d 3ware,2 /dev/twe0\n"
-         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
-         );
-#endif
-  return;
-}
-
-
-// we are going to take advantage of the fact that Linux's devfs will only
-// have device entries for devices that exist.  So if we get the equivalent of
-// ls /dev/hd[a-t], we have all the ATA devices on the system
-//
-// If any errors occur, leave errno set as it was returned by the
-// system call, and return <0.
-int get_dev_names(char*** names, const char* pattern, const char* name, int max) {
-  int n = 0, retglob, i, lim;
-  char** mp;
-  glob_t globbuf;
-  
-  memset(&globbuf, 0, sizeof(globbuf));
-
-  // in case of non-clean exit
-  *names=NULL;
-  
-  // Use glob to look for any directory entries matching the pattern
-  if ((retglob=glob(pattern, GLOB_ERR, NULL, &globbuf))) {
-    
-    //  glob failed: free memory and return
-    globfree(&globbuf);
-    
-    if (retglob==GLOB_NOMATCH){
-      pout("glob(3) found no matches for pattern %s\n", pattern);
-      return 0;
-    }
-    
-    if (retglob==GLOB_NOSPACE)
-      pout("glob(3) ran out of memory matching pattern %s\n", pattern);
-#ifdef GLOB_ABORTED // missing in old versions of glob.h
-    else if (retglob==GLOB_ABORTED)
-      pout("glob(3) aborted matching pattern %s\n", pattern);
-#endif
-    else
-      pout("Unexplained error in glob(3) of pattern %s\n", pattern);
-    
-    return -1;
-  }
-
-  // did we find too many paths?
-  lim = ((int)globbuf.gl_pathc < max) ? (int)globbuf.gl_pathc : max;
-  if (lim < (int)globbuf.gl_pathc)
-    pout("glob(3) found %d > MAX=%d devices matching pattern %s: ignoring %d paths\n", 
-         (int)globbuf.gl_pathc, max, pattern, (int)(globbuf.gl_pathc-max));
-  
-  // allocate space for up to lim number of ATA devices
-  if (!(mp =  (char **)calloc(lim, sizeof(char*)))){
-    pout("Out of memory constructing scan device list\n");
-    return -1;
-  }
-  
-  // now step through the list returned by glob.  If not a link, copy
-  // to list.  If it is a link, evaluate it and see if the path ends
-  // in "disc".
-  for (i=0; i<lim; i++){
-    int retlink;
-    
-    // prepare a buffer for storing the link
-    char linkbuf[1024];
-    
-    // see if path is a link
-    retlink=readlink(globbuf.gl_pathv[i], linkbuf, 1023);
-    
-    // if not a link (or a strange link), keep it
-    if (retlink<=0 || retlink>1023)
-      mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion);
-    else {
-      // or if it's a link that points to a disc, follow it
-      char *p;
-      linkbuf[retlink]='\0';
-      if ((p=strrchr(linkbuf,'/')) && !strcmp(p+1, "disc"))
-        // This is the branch of the code that gets followed if we are
-        // using devfs WITH traditional compatibility links. In this
-        // case, we add the traditional device name to the list that
-        // is returned.
-        mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion);
-      else {
-        // This is the branch of the code that gets followed if we are
-        // using devfs WITHOUT traditional compatibility links.  In
-        // this case, we check that the link to the directory is of
-        // the correct type, and then append "disc" to it.
-        char tmpname[1024]={0};
-        char *type=strcmp(name,"ATA")?"scsi":"ide";
-        if (strstr(linkbuf, type)){
-          snprintf(tmpname, 1024, "%s/disc", globbuf.gl_pathv[i]);
-          mp[n++] = CustomStrDup(tmpname, 1, __LINE__, filenameandversion);
-        }
-      }
-    }
-  }
-  
-  // free memory, track memory usage
-  globfree(&globbuf);
-  mp = realloc(mp,n*(sizeof(char*)));
-  bytes += n*(sizeof(char*));
-  
-  // and set up return values
-  *names=mp;
-  return n;
-}
-
-// makes a list of device names to scan, for either ATA or SCSI
-// devices.  Return -1 if no memory remaining, else the number of
-// devices on the list, which can be >=0.
-int make_device_names (char*** devlist, const char* name) {
-  int retval, maxdev;
-  
-#if 0
-  // for testing case where no device names are found
-  return 0;
-#endif
-  
-  if (!strcmp(name,"SCSI"))
-    retval=get_dev_names(devlist,"/dev/sd[a-z]", name, maxdev=26);
-  else if (!strcmp(name,"ATA"))
-    retval=get_dev_names(devlist,"/dev/hd[a-t]", name, maxdev=20);
-  else
-    // don't recognize disk type!
-    return 0;
-
-  // if we found traditional links, we are done
-  if (retval>0)
-    return retval;
-  
-  // else look for devfs entries without traditional links
-  return get_dev_names(devlist,"/dev/discs/disc*", name, maxdev);
-}
-
-
-// PURPOSE
-//   This is an interface routine meant to isolate the OS dependent
-//   parts of the code, and to provide a debugging interface.  Each
-//   different port and OS needs to provide it's own interface.  This
-//   is the linux one.
-// DETAILED DESCRIPTION OF ARGUMENTS
-//   device: is the file descriptor provided by open()
-//   command: defines the different operations.
-//   select: additional input data if needed (which log, which type of
-//           self-test).
-//   data:   location to write output data, if needed (512 bytes).
-//   Note: not all commands use all arguments.
-// RETURN VALUES
-//  -1 if the command failed
-//   0 if the command succeeded,
-//   STATUS_CHECK routine: 
-//  -1 if the command failed
-//   0 if the command succeeded and disk SMART status is "OK"
-//   1 if the command succeeded and disk SMART status is "FAILING"
-
-
-// huge value of buffer size needed because HDIO_DRIVE_CMD assumes
-// that buff[3] is the data size.  Since the ATA_SMART_AUTOSAVE and
-// ATA_SMART_AUTO_OFFLINE use values of 0xf1 and 0xf8 we need the space.
-// Otherwise a 4+512 byte buffer would be enough.
-#define STRANGE_BUFFER_LENGTH (4+512*0xf8)
-
-int ata_command_interface(int device, smart_command_set command, int select, char *data){
-  unsigned char buff[STRANGE_BUFFER_LENGTH];
-  // positive: bytes to write to caller.  negative: bytes to READ from
-  // caller. zero: non-data command
-  int copydata=0;
-
-  const int HDIO_DRIVE_CMD_OFFSET = 4;
-
-  // See struct hd_drive_cmd_hdr in hdreg.h.  Before calling ioctl()
-  // buff[0]: ATA COMMAND CODE REGISTER
-  // buff[1]: ATA SECTOR NUMBER REGISTER == LBA LOW REGISTER
-  // buff[2]: ATA FEATURES REGISTER
-  // buff[3]: ATA SECTOR COUNT REGISTER
-
-  // Note that on return:
-  // buff[2] contains the ATA SECTOR COUNT REGISTER
-  
-  // clear out buff.  Large enough for HDIO_DRIVE_CMD (4+512 bytes)
-  memset(buff, 0, STRANGE_BUFFER_LENGTH);
-
-  buff[0]=ATA_SMART_CMD;
-  switch (command){
-  case CHECK_POWER_MODE:
-    buff[0]=ATA_CHECK_POWER_MODE;
-    copydata=1;
-    break;
-  case READ_VALUES:
-    buff[2]=ATA_SMART_READ_VALUES;
-    buff[3]=1;
-    copydata=512;
-    break;
-  case READ_THRESHOLDS:
-    buff[2]=ATA_SMART_READ_THRESHOLDS;
-    buff[1]=buff[3]=1;
-    copydata=512;
-    break;
-  case READ_LOG:
-    buff[2]=ATA_SMART_READ_LOG_SECTOR;
-    buff[1]=select;
-    buff[3]=1;
-    copydata=512;
-    break;
-  case WRITE_LOG:
-    break;
-  case IDENTIFY:
-    buff[0]=ATA_IDENTIFY_DEVICE;
-    buff[3]=1;
-    copydata=512;
-    break;
-  case PIDENTIFY:
-    buff[0]=ATA_IDENTIFY_PACKET_DEVICE;
-    buff[3]=1;
-    copydata=512;
-    break;
-  case ENABLE:
-    buff[2]=ATA_SMART_ENABLE;
-    buff[1]=1;
-    break;
-  case DISABLE:
-    buff[2]=ATA_SMART_DISABLE;
-    buff[1]=1;
-    break;
-  case STATUS:
-    // this command only says if SMART is working.  It could be
-    // replaced with STATUS_CHECK below.
-    buff[2]=ATA_SMART_STATUS;
-    break;
-  case AUTO_OFFLINE:
-    buff[2]=ATA_SMART_AUTO_OFFLINE;
-    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
-    break;
-  case AUTOSAVE:
-    buff[2]=ATA_SMART_AUTOSAVE;
-    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
-    break;
-  case IMMEDIATE_OFFLINE:
-    buff[2]=ATA_SMART_IMMEDIATE_OFFLINE;
-    buff[1]=select;
-    break;
-  case STATUS_CHECK:
-    // This command uses HDIO_DRIVE_TASK and has different syntax than
-    // the other commands.
-    buff[1]=ATA_SMART_STATUS;
-    break;
-  default:
-    pout("Unrecognized command %d in linux_ata_command_interface()\n"
-         "Please contact " PACKAGE_BUGREPORT "\n", command);
-    errno=ENOSYS;
-    return -1;
-  }
-  
-  // This command uses the HDIO_DRIVE_TASKFILE ioctl(). This is the
-  // only ioctl() that can be used to WRITE data to the disk.
-  if (command==WRITE_LOG) {    
-    unsigned char task[sizeof(ide_task_request_t)+512];
-    ide_task_request_t *reqtask=(ide_task_request_t *) task;
-    task_struct_t      *taskfile=(task_struct_t *) reqtask->io_ports;
-    int retval;
-
-    memset(task,      0, sizeof(task));
-    
-    taskfile->data           = 0;
-    taskfile->feature        = ATA_SMART_WRITE_LOG_SECTOR;
-    taskfile->sector_count   = 1;
-    taskfile->sector_number  = select;
-    taskfile->low_cylinder   = 0x4f;
-    taskfile->high_cylinder  = 0xc2;
-    taskfile->device_head    = 0;
-    taskfile->command        = ATA_SMART_CMD;
-    
-    reqtask->data_phase      = TASKFILE_OUT;
-    reqtask->req_cmd         = IDE_DRIVE_TASK_OUT;
-    reqtask->out_size        = 512;
-    reqtask->in_size         = 0;
-    
-    // copy user data into the task request structure
-    memcpy(task+sizeof(ide_task_request_t), data, 512);
-      
-    if ((retval=ioctl(device, HDIO_DRIVE_TASKFILE, task))) {
-      if (retval==-EINVAL)
-       pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n");
-      return -1;
-    }
-    return 0;
-  }
-    
-  // There are two different types of ioctls().  The HDIO_DRIVE_TASK
-  // one is this:
-  if (command==STATUS_CHECK){
-    int retval;
-
-    // NOT DOCUMENTED in /usr/src/linux/include/linux/hdreg.h. You
-    // have to read the IDE driver source code.  Sigh.
-    // buff[0]: ATA COMMAND CODE REGISTER
-    // buff[1]: ATA FEATURES REGISTER
-    // buff[2]: ATA SECTOR_COUNT
-    // buff[3]: ATA SECTOR NUMBER
-    // buff[4]: ATA CYL LO REGISTER
-    // buff[5]: ATA CYL HI REGISTER
-    // buff[6]: ATA DEVICE HEAD
-
-    unsigned const char normal_lo=0x4f, normal_hi=0xc2;
-    unsigned const char failed_lo=0xf4, failed_hi=0x2c;
-    buff[4]=normal_lo;
-    buff[5]=normal_hi;
-    
-    if ((retval=ioctl(device, HDIO_DRIVE_TASK, buff))) {
-      if (retval==-EINVAL) {
-       pout("Error SMART Status command via HDIO_DRIVE_TASK failed");
-       pout("Rebuild older linux 2.2 kernels with HDIO_DRIVE_TASK support added\n");
-      }
-      else
-       syserror("Error SMART Status command failed");
-      return -1;
-    }
-    
-    // Cyl low and Cyl high unchanged means "Good SMART status"
-    if (buff[4]==normal_lo && buff[5]==normal_hi)
-      return 0;
-    
-    // These values mean "Bad SMART status"
-    if (buff[4]==failed_lo && buff[5]==failed_hi)
-      return 1;
-    
-    // We haven't gotten output that makes sense; print out some debugging info
-    syserror("Error SMART Status command failed");
-    pout("Please get assistance from " PACKAGE_HOMEPAGE "\n");
-    pout("Register values returned from SMART Status command are:\n");
-    pout("CMD=0x%02x\n",(int)buff[0]);
-    pout("FR =0x%02x\n",(int)buff[1]);
-    pout("NS =0x%02x\n",(int)buff[2]);
-    pout("SC =0x%02x\n",(int)buff[3]);
-    pout("CL =0x%02x\n",(int)buff[4]);
-    pout("CH =0x%02x\n",(int)buff[5]);
-    pout("SEL=0x%02x\n",(int)buff[6]);
-    return -1;   
-  }
-  
-#if 1
-  // Note to people doing ports to other OSes -- don't worry about
-  // this block -- you can safely ignore it.  I have put it here
-  // because under linux when you do IDENTIFY DEVICE to a packet
-  // device, it generates an ugly kernel syslog error message.  This
-  // is harmless but frightens users.  So this block detects packet
-  // devices and make IDENTIFY DEVICE fail "nicely" without a syslog
-  // error message.
-  //
-  // If you read only the ATA specs, it appears as if a packet device
-  // *might* respond to the IDENTIFY DEVICE command.  This is
-  // misleading - it's because around the time that SFF-8020 was
-  // incorporated into the ATA-3/4 standard, the ATA authors were
-  // sloppy. See SFF-8020 and you will see that ATAPI devices have
-  // *always* had IDENTIFY PACKET DEVICE as a mandatory part of their
-  // command set, and return 'Command Aborted' to IDENTIFY DEVICE.
-  if (command==IDENTIFY || command==PIDENTIFY){
-    unsigned short deviceid[256];
-    // check the device identity, as seen when the system was booted
-    // or the device was FIRST registered.  This will not be current
-    // if the user has subsequently changed some of the parameters. If
-    // device is a packet device, swap the command interpretations.
-    if (!ioctl(device, HDIO_GET_IDENTITY, deviceid) && (deviceid[0] & 0x8000))
-      buff[0]=(command==IDENTIFY)?ATA_IDENTIFY_PACKET_DEVICE:ATA_IDENTIFY_DEVICE;
-  }
-#endif
-  
-  // We are now doing the HDIO_DRIVE_CMD type ioctl.
-  if ((ioctl(device, HDIO_DRIVE_CMD, buff)))
-    return -1;
-
-  // CHECK POWER MODE command returns information in the Sector Count
-  // register (buff[3]).  Copy to return data buffer.
-  if (command==CHECK_POWER_MODE)
-    buff[HDIO_DRIVE_CMD_OFFSET]=buff[2];
-
-  // if the command returns data then copy it back
-  if (copydata)
-    memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata);
-  
-  return 0; 
-}
-
-// >>>>>> Start of general SCSI specific linux code
-
-/* Linux specific code.
- * Historically smartmontools (and smartsuite before it) used the
- * SCSI_IOCTL_SEND_COMMAND ioctl which is available to all linux device
- * nodes that use the SCSI subsystem. A better interface has been available
- * via the SCSI generic (sg) driver but this involves the extra step of
- * mapping disk devices (e.g. /dev/sda) to the corresponding sg device
- * (e.g. /dev/sg2). In the linux kernel 2.6 series most of the facilities of
- * the sg driver have become available via the SG_IO ioctl which is available
- * on all SCSI devices (on SCSI tape devices from lk 2.6.6).
- * So the strategy below is to find out if the SG_IO ioctl is available and
- * if so use it; failing that use the older SCSI_IOCTL_SEND_COMMAND ioctl.
- * Should work in 2.0, 2.2, 2.4 and 2.6 series linux kernels. */
-
-#define MAX_DXFER_LEN 1024      /* can be increased if necessary */
-#define SEND_IOCTL_RESP_SENSE_LEN 16    /* ioctl limitation */
-#define SG_IO_RESP_SENSE_LEN 64 /* large enough see buffer */
-#define LSCSI_DRIVER_MASK  0xf /* mask out "suggestions" */
-#define LSCSI_DRIVER_SENSE  0x8 /* alternate CHECK CONDITION indication */
-#define LSCSI_DRIVER_TIMEOUT  0x6
-#define LSCSI_DID_TIME_OUT  0x3
-#define LSCSI_DID_BUS_BUSY  0x2
-#define LSCSI_DID_NO_CONNECT  0x1
-
-#ifndef SCSI_IOCTL_SEND_COMMAND
-#define SCSI_IOCTL_SEND_COMMAND 1
-#endif
-
-#define SG_IO_PRESENT_UNKNOWN 0
-#define SG_IO_PRESENT_YES 1
-#define SG_IO_PRESENT_NO 2
-
-static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
-                        int unknown);
-static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);
-
-static int sg_io_state = SG_IO_PRESENT_UNKNOWN;
-
-/* Preferred implementation for issuing SCSI commands in linux. This
- * function uses the SG_IO ioctl. Return 0 if command issued successfully
- * (various status values should still be checked). If the SCSI command
- * cannot be issued then a negative errno value is returned. */
-static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
-                        int unknown)
-{
-#ifndef SG_IO
-    ARGUSED(dev_fd); ARGUSED(iop); ARGUSED(report);
-    return -ENOTTY;
-#else
-    struct sg_io_hdr io_hdr;
-
-    if (report > 0) {
-        int k, j;
-        const unsigned char * ucp = iop->cmnd;
-        const char * np;
-        char buff[256];
-        const int sz = (int)sizeof(buff);
-
-        np = scsi_get_opcode_name(ucp[0]);
-        j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
-        for (k = 0; k < (int)iop->cmnd_len; ++k)
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
-        if ((report > 1) && 
-            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
-            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
-                          "data, len=%d%s:\n", (int)iop->dxfer_len,
-                          (trunc ? " [only first 256 bytes shown]" : ""));
-            dStrHex((const char *)iop->dxferp, 
-                   (trunc ? 256 : iop->dxfer_len) , 1);
-        }
-        else
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
-        pout(buff);
-    }
-    memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
-    io_hdr.interface_id = 'S';
-    io_hdr.cmd_len = iop->cmnd_len;
-    io_hdr.mx_sb_len = iop->max_sense_len;
-    io_hdr.dxfer_len = iop->dxfer_len;
-    io_hdr.dxferp = iop->dxferp;
-    io_hdr.cmdp = iop->cmnd;
-    io_hdr.sbp = iop->sensep;
-    /* sg_io_hdr interface timeout has millisecond units. Timeout of 0
-       defaults to 60 seconds. */
-    io_hdr.timeout = ((0 == iop->timeout) ? 60 : iop->timeout) * 1000;
-    switch (iop->dxfer_dir) {
-        case DXFER_NONE:
-            io_hdr.dxfer_direction = SG_DXFER_NONE;
-            break;
-        case DXFER_FROM_DEVICE:
-            io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
-            break;
-        case DXFER_TO_DEVICE:
-            io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
-            break;
-        default:
-            pout("do_scsi_cmnd_io: bad dxfer_dir\n");
-            return -EINVAL;
-    }
-    iop->resp_sense_len = 0;
-    iop->scsi_status = 0;
-    iop->resid = 0;
-    if (ioctl(dev_fd, SG_IO, &io_hdr) < 0) {
-        if (report && (! unknown))
-            pout("  SG_IO ioctl failed, errno=%d [%s]\n", errno,
-                strerror(errno));
-        return -errno;
-    }
-    if (report > 0) {
-        pout("  scsi_status=0x%x, host_status=0x%x, driver_status=0x%x\n"
-            "  info=0x%x  duration=%d milliseconds\n", io_hdr.status, 
-            io_hdr.host_status, io_hdr.driver_status, io_hdr.info,
-            io_hdr.duration);
-        if (report > 1) {
-            if (DXFER_FROM_DEVICE == iop->dxfer_dir) {
-                int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-
-                pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
-                     (trunc ? " [only first 256 bytes shown]" : ""));
-                dStrHex((const char*)iop->dxferp, 
-                       (trunc ? 256 : iop->dxfer_len) , 1);
-            }
-        }
-    }
-    iop->resid = io_hdr.resid;
-    iop->scsi_status = io_hdr.status;
-
-    if (io_hdr.info | SG_INFO_CHECK) { /* error or warning */
-       int masked_driver_status = (LSCSI_DRIVER_MASK & io_hdr.driver_status);
-
-       if (0 != io_hdr.host_status) {
-            if ((LSCSI_DID_NO_CONNECT == io_hdr.host_status) ||
-                (LSCSI_DID_BUS_BUSY == io_hdr.host_status) ||
-                (LSCSI_DID_TIME_OUT == io_hdr.host_status))
-                return -ETIMEDOUT;
-           else
-               return -EIO;    /* catch all */
-        }
-        if (0 != masked_driver_status) {
-            if (LSCSI_DRIVER_TIMEOUT == masked_driver_status)
-                return -ETIMEDOUT;
-           else if (LSCSI_DRIVER_SENSE != masked_driver_status)
-               return -EIO;
-       }
-        if (LSCSI_DRIVER_SENSE == masked_driver_status)
-            iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
-        iop->resp_sense_len = io_hdr.sb_len_wr;
-        if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && 
-            iop->sensep && (iop->resp_sense_len > 0)) {
-            if (report > 1) {
-                pout("  >>> Sense buffer, len=%d:\n",
-                    (int)iop->resp_sense_len);
-                dStrHex((const char *)iop->sensep, iop->resp_sense_len , 1);
-            }
-        }
-        if (report) {
-            if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) {
-                pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
-                    iop->scsi_status, iop->sensep[2] & 0xf, iop->sensep[12],
-                    iop->sensep[13]);
-            }
-            else
-                pout("  status=0x%x\n", iop->scsi_status);
-        }
-    }
-    return 0;
-#endif
-}
-
-struct linux_ioctl_send_command
-{
-    int inbufsize;
-    int outbufsize;
-    UINT8 buff[MAX_DXFER_LEN + 16];
-};
-
-/* The Linux SCSI_IOCTL_SEND_COMMAND ioctl is primitive and it doesn't 
- * support: CDB length (guesses it from opcode), resid and timeout.
- * Patches in Linux 2.4.21 and 2.5.70 to extend SEND DIAGNOSTIC timeout
- * to 2 hours in order to allow long foreground extended self tests. */
-static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
-{
-    struct linux_ioctl_send_command wrk;
-    int status, buff_offset;
-    size_t len;
-
-    memcpy(wrk.buff, iop->cmnd, iop->cmnd_len);
-    buff_offset = iop->cmnd_len;
-    if (report > 0) {
-        int k, j;
-        const unsigned char * ucp = iop->cmnd;
-        const char * np;
-        char buff[256];
-        const int sz = (int)sizeof(buff);
-
-        np = scsi_get_opcode_name(ucp[0]);
-        j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
-        for (k = 0; k < (int)iop->cmnd_len; ++k)
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
-        if ((report > 1) && 
-            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
-            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
-                          "data, len=%d%s:\n", (int)iop->dxfer_len,
-                          (trunc ? " [only first 256 bytes shown]" : ""));
-            dStrHex((const char *)iop->dxferp, 
-                   (trunc ? 256 : iop->dxfer_len) , 1);
-        }
-        else
-            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
-        pout(buff);
-    }
-    switch (iop->dxfer_dir) {
-        case DXFER_NONE:
-            wrk.inbufsize = 0;
-            wrk.outbufsize = 0;
-            break;
-        case DXFER_FROM_DEVICE:
-            wrk.inbufsize = 0;
-            if (iop->dxfer_len > MAX_DXFER_LEN)
-                return -EINVAL;
-            wrk.outbufsize = iop->dxfer_len;
-            break;
-        case DXFER_TO_DEVICE:
-            if (iop->dxfer_len > MAX_DXFER_LEN)
-                return -EINVAL;
-            memcpy(wrk.buff + buff_offset, iop->dxferp, iop->dxfer_len);
-            wrk.inbufsize = iop->dxfer_len;
-            wrk.outbufsize = 0;
-            break;
-        default:
-            pout("do_scsi_cmnd_io: bad dxfer_dir\n");
-            return -EINVAL;
-    }
-    iop->resp_sense_len = 0;
-    iop->scsi_status = 0;
-    iop->resid = 0;
-    status = ioctl(dev_fd, SCSI_IOCTL_SEND_COMMAND, &wrk);
-    if (-1 == status) {
-        if (report)
-            pout("  SCSI_IOCTL_SEND_COMMAND ioctl failed, errno=%d [%s]\n",
-                 errno, strerror(errno));
-        return -errno;
-    }
-    if (0 == status) {
-        if (report > 0)
-            pout("  status=0\n");
-        if (DXFER_FROM_DEVICE == iop->dxfer_dir) {
-            memcpy(iop->dxferp, wrk.buff, iop->dxfer_len);
-            if (report > 1) {
-                int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-
-                pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
-                     (trunc ? " [only first 256 bytes shown]" : ""));
-                dStrHex((const char*)iop->dxferp, 
-                       (trunc ? 256 : iop->dxfer_len) , 1);
-            }
-        }
-        return 0;
-    }
-    iop->scsi_status = status & 0x7e; /* bits 0 and 7 used to be for vendors */
-    if (LSCSI_DRIVER_SENSE == ((status >> 24) & 0xf))
-        iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
-    len = (SEND_IOCTL_RESP_SENSE_LEN < iop->max_sense_len) ?
-                SEND_IOCTL_RESP_SENSE_LEN : iop->max_sense_len;
-    if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && 
-        iop->sensep && (len > 0)) {
-        memcpy(iop->sensep, wrk.buff, len);
-        iop->resp_sense_len = len;
-        if (report > 1) {
-            pout("  >>> Sense buffer, len=%d:\n", (int)len);
-            dStrHex((const char *)wrk.buff, len , 1);
-        }
-    }
-    if (report) {
-        if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) {
-            pout("  status=%x: sense_key=%x asc=%x ascq=%x\n", status & 0xff,
-                 wrk.buff[2] & 0xf, wrk.buff[12], wrk.buff[13]);
-        }
-        else
-            pout("  status=0x%x\n", status);
-    }
-    if (iop->scsi_status > 0)
-        return 0;
-    else {
-        if (report > 0)
-            pout("  ioctl status=0x%x but scsi status=0, fail with EIO\n", 
-                 status);
-        return -EIO;      /* give up, assume no device there */
-    }
-}
-
-/* SCSI command transmission interface function, linux version.
- * Returns 0 if SCSI command successfully launched and response
- * received. Even when 0 is returned the caller should check 
- * scsi_cmnd_io::scsi_status for SCSI defined errors and warnings
- * (e.g. CHECK CONDITION). If the SCSI command could not be issued
- * (e.g. device not present or timeout) or some other problem
- * (e.g. timeout) then returns a negative errno value */
-int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
-{
-    int res;
-
-    /* implementation relies on static sg_io_state variable. If not
-     * previously set tries the SG_IO ioctl. If that succeeds assume
-     * that SG_IO ioctl functional. If it fails with an errno value
-     * other than ENODEV (no device) or permission then assume 
-     * SCSI_IOCTL_SEND_COMMAND is the only option. */
-    switch (sg_io_state) {
-    case SG_IO_PRESENT_UNKNOWN:
-        /* ignore report argument */
-       if (0 == (res = sg_io_cmnd_io(dev_fd, iop, report, 1))) {
-           sg_io_state = SG_IO_PRESENT_YES;
-           return 0;
-       } else if ((-ENODEV == res) || (-EACCES == res) || (-EPERM == res))
-           return res;         /* wait until we see a device */
-        sg_io_state = SG_IO_PRESENT_NO;
-       /* drop through by design */
-    case SG_IO_PRESENT_NO:
-       return sisc_cmnd_io(dev_fd, iop, report);
-    case SG_IO_PRESENT_YES:
-       return sg_io_cmnd_io(dev_fd, iop, report, 0);
-    default:
-       pout(">>>> do_scsi_cmnd_io: bad sg_io_state=%d\n", sg_io_state); 
-       sg_io_state = SG_IO_PRESENT_UNKNOWN;
-        return -EIO;   /* report error and reset state */
-    }
-}
-
-// >>>>>> End of general SCSI specific linux code
-
-
-// prototype
-void printwarning(smart_command_set command);
-
-// PURPOSE
-//   This is an interface routine meant to isolate the OS dependent
-//   parts of the code, and to provide a debugging interface.  Each
-//   different port and OS needs to provide it's own interface.  This
-//   is the linux interface to the 3ware 3w-xxxx driver.  It allows ATA
-//   commands to be passed through the SCSI driver.
-// DETAILED DESCRIPTION OF ARGUMENTS
-//   fd: is the file descriptor provided by open()
-//   disknum is the disk number (0 to 15) in the RAID array
-//   escalade_type indicates the type of controller type, and if scsi or char interface is used
-//   command: defines the different operations.
-//   select: additional input data if needed (which log, which type of
-//           self-test).
-//   data:   location to write output data, if needed (512 bytes).
-//   Note: not all commands use all arguments.
-// RETURN VALUES
-//  -1 if the command failed
-//   0 if the command succeeded,
-//   STATUS_CHECK routine: 
-//  -1 if the command failed
-//   0 if the command succeeded and disk SMART status is "OK"
-//   1 if the command succeeded and disk SMART status is "FAILING"
-
-
-/* 512 is the max payload size: increase if needed */
-#define BUFFER_LEN_678K      ( sizeof(TW_Ioctl)                  ) // 1044 unpacked, 1041 packed
-#define BUFFER_LEN_678K_CHAR ( sizeof(TW_New_Ioctl)+512-1        ) // 1539 unpacked, 1536 packed
-#define BUFFER_LEN_9000      ( sizeof(TW_Ioctl_Buf_Apache)+512-1 ) // 2051 unpacked, 2048 packed
-#define TW_IOCTL_BUFFER_SIZE ( MAX(MAX(BUFFER_LEN_678K, BUFFER_LEN_9000), BUFFER_LEN_678K_CHAR) )
-
-int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){
-
-  // return value and buffer for ioctl()
-  int  ioctlreturn, readdata=0;
-
-  // Used by both the SCSI and char interfaces
-  TW_Passthru *passthru=NULL;
-  char ioctl_buffer[TW_IOCTL_BUFFER_SIZE];
-
-  // only used for SCSI device interface
-  TW_Ioctl   *tw_ioctl=NULL;
-  TW_Output *tw_output=NULL;
-
-  // only used for 6000/7000/8000 char device interface
-  TW_New_Ioctl *tw_ioctl_char=NULL;
-
-  // only used for 9000 character device interface
-  TW_Ioctl_Buf_Apache *tw_ioctl_apache=NULL;
-  
-  memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE);
-
-  if (escalade_type==CONTROLLER_3WARE_9000_CHAR) {
-    tw_ioctl_apache                               = (TW_Ioctl_Buf_Apache *)ioctl_buffer;
-    tw_ioctl_apache->driver_command.control_code  = TW_IOCTL_FIRMWARE_PASS_THROUGH;
-    tw_ioctl_apache->driver_command.buffer_length = 512; /* payload size */
-    passthru                                      = (TW_Passthru *)&(tw_ioctl_apache->firmware_command.command.oldcommand);
-  }
-  else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) {
-    tw_ioctl_char                                 = (TW_New_Ioctl *)ioctl_buffer;
-    tw_ioctl_char->data_buffer_length             = 512;
-    passthru                                      = (TW_Passthru *)&(tw_ioctl_char->firmware_command);
-  }
-  else if (escalade_type==CONTROLLER_3WARE_678K) {
-    tw_ioctl                                      = (TW_Ioctl *)ioctl_buffer;
-    tw_ioctl->cdb[0]                              = TW_IOCTL;
-    tw_ioctl->opcode                              = TW_ATA_PASSTHRU;
-    tw_ioctl->input_length                        = 512; // correct even for non-data commands
-    tw_ioctl->output_length                       = 512; // correct even for non-data commands
-    tw_output                                     = (TW_Output *)tw_ioctl;
-    passthru                                      = (TW_Passthru *)&(tw_ioctl->input_data);
-  }
-  else {
-    pout("Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n"
-         "Please contact " PACKAGE_BUGREPORT "\n", escalade_type, disknum);
-    errno=ENOSYS;
-    return -1;
-  }
-
-  // Same for (almost) all commands - but some reset below
-  passthru->byte0.opcode  = TW_OP_ATA_PASSTHRU;
-  passthru->request_id    = 0xFF;
-  passthru->byte3.aport   = disknum;
-  passthru->byte3.host_id = 0;
-  passthru->status        = 0;           
-  passthru->flags         = 0x1;
-  passthru->drive_head    = 0x0;
-  passthru->sector_num    = 0;
-
-  // All SMART commands use this CL/CH signature.  These are magic
-  // values from the ATA specifications.
-  passthru->cylinder_lo   = 0x4F;
-  passthru->cylinder_hi   = 0xC2;
-  
-  // SMART ATA COMMAND REGISTER value
-  passthru->command       = ATA_SMART_CMD;
-  
-  // Is this a command that reads or returns 512 bytes?
-  // passthru->param values are:
-  // 0x0 - non data command without TFR write check,
-  // 0x8 - non data command with TFR write check,
-  // 0xD - data command that returns data to host from device
-  // 0xF - data command that writes data from host to device
-  // passthru->size values are 0x5 for non-data and 0x07 for data
-  if (command == READ_VALUES     ||
-      command == READ_THRESHOLDS ||
-      command == READ_LOG        ||
-      command == IDENTIFY        ||
-      command == WRITE_LOG ) {
-    readdata=1;
-    passthru->byte0.sgloff = 0x5;
-    passthru->size         = 0x7;
-    passthru->param        = 0xD;
-    passthru->sector_count = 0x1;
-    // For 64-bit to work correctly, up the size of the command packet
-    // in dwords by 1 to account for the 64-bit single sgl 'address'
-    // field. Note that this doesn't agree with the typedefs but it's
-    // right (agree with kernel driver behavior/typedefs).
-    if (escalade_type==CONTROLLER_3WARE_9000_CHAR && sizeof(long)==8)
-      passthru->size++;
-  }
-  else {
-    // Non data command -- but doesn't use large sector 
-    // count register values.  
-    passthru->byte0.sgloff = 0x0;
-    passthru->size         = 0x5;
-    passthru->param        = 0x8;
-    passthru->sector_count = 0x0;
-  }
-  
-  // Now set ATA registers depending upon command
-  switch (command){
-  case CHECK_POWER_MODE:
-    passthru->command     = ATA_CHECK_POWER_MODE;
-    passthru->features    = 0;
-    passthru->cylinder_lo = 0;
-    passthru->cylinder_hi = 0;
-    break;
-  case READ_VALUES:
-    passthru->features = ATA_SMART_READ_VALUES;
-    break;
-  case READ_THRESHOLDS:
-    passthru->features = ATA_SMART_READ_THRESHOLDS;
-    break;
-  case READ_LOG:
-    passthru->features = ATA_SMART_READ_LOG_SECTOR;
-    // log number to return
-    passthru->sector_num  = select;
-    break;
-  case WRITE_LOG:
-    if (escalade_type == CONTROLLER_3WARE_9000_CHAR)
-      memcpy((unsigned char *)tw_ioctl_apache->data_buffer, data, 512);
-    else if (escalade_type == CONTROLLER_3WARE_678K_CHAR)
-      memcpy((unsigned char *)tw_ioctl_char->data_buffer,   data, 512);
-    else {
-      // COMMAND NOT SUPPORTED VIA SCSI IOCTL INTERFACE
-      // memcpy(tw_output->output_data, data, 512);
-      printwarning(command);
-      errno=ENOTSUP;
-      return -1;
-    }
-    readdata=0;
-    passthru->features     = ATA_SMART_WRITE_LOG_SECTOR;
-    passthru->sector_count = 1;
-    passthru->sector_num   = select;
-    passthru->param        = 0xF;  // PIO data write
-    break;
-  case IDENTIFY:
-    // ATA IDENTIFY DEVICE
-    passthru->command     = ATA_IDENTIFY_DEVICE;
-    passthru->features    = 0;
-    passthru->cylinder_lo = 0;
-    passthru->cylinder_hi = 0;
-    break;
-  case PIDENTIFY:
-    // 3WARE controller can NOT have packet device internally
-    pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", disknum);
-    pout("Note: /dev/sdX many need to be replaced with /dev/tweN or /dev/twaN\n");
-    errno=ENODEV;
-    return -1;
-  case ENABLE:
-    passthru->features = ATA_SMART_ENABLE;
-    break;
-  case DISABLE:
-    passthru->features = ATA_SMART_DISABLE;
-    break;
-  case AUTO_OFFLINE:
-    passthru->features     = ATA_SMART_AUTO_OFFLINE;
-    // Enable or disable?
-    passthru->sector_count = select;
-    break;
-  case AUTOSAVE:
-    passthru->features     = ATA_SMART_AUTOSAVE;
-    // Enable or disable?
-    passthru->sector_count = select;
-    break;
-  case IMMEDIATE_OFFLINE:
-    passthru->features    = ATA_SMART_IMMEDIATE_OFFLINE;
-    // What test type to run?
-    passthru->sector_num  = select;
-    break;
-  case STATUS_CHECK:
-    passthru->features = ATA_SMART_STATUS;
-    break;
-  case STATUS:
-    // This is JUST to see if SMART is enabled, by giving SMART status
-    // command. But it doesn't say if status was good, or failing.
-    // See below for the difference.
-    passthru->features = ATA_SMART_STATUS;
-    break;
-  default:
-    pout("Unrecognized command %d in linux_3ware_command_interface(disk %d)\n"
-         "Please contact " PACKAGE_BUGREPORT "\n", command, disknum);
-    errno=ENOSYS;
-    return -1;
-  }
-
-  // Now send the command down through an ioctl()
-  if (escalade_type==CONTROLLER_3WARE_9000_CHAR)
-    ioctlreturn=ioctl(fd, TW_IOCTL_FIRMWARE_PASS_THROUGH, tw_ioctl_apache);
-  else if (escalade_type==CONTROLLER_3WARE_678K_CHAR)
-    ioctlreturn=ioctl(fd, TW_CMD_PACKET_WITH_DATA, tw_ioctl_char);
-  else
-    ioctlreturn=ioctl(fd, SCSI_IOCTL_SEND_COMMAND, tw_ioctl);
-  
-  // Deal with the different error cases
-  if (ioctlreturn) {
-    if (CONTROLLER_3WARE_678K==escalade_type && ((command==AUTO_OFFLINE || command==AUTOSAVE) && select)){
-      // error here is probably a kernel driver whose version is too old
-      printwarning(command);
-      errno=ENOTSUP;
-    }
-    if (!errno)
-      errno=EIO;
-    return -1;
-  }
-  
-  // The passthru structure is valid after return from an ioctl if:
-  // - we are using the character interface OR 
-  // - we are using the SCSI interface and this is a NON-READ-DATA command
-  // For SCSI interface, note that we set passthru to a different
-  // value after ioctl().
-  if (CONTROLLER_3WARE_678K==escalade_type) {
-    if (readdata)
-      passthru=NULL;
-    else
-      passthru=(TW_Passthru *)&(tw_output->output_data);
-  }
-
-  // See if the ATA command failed.  Now that we have returned from
-  // the ioctl() call, if passthru is valid, then:
-  // - passthru->status contains the 3ware controller STATUS
-  // - passthru->command contains the ATA STATUS register
-  // - passthru->features contains the ATA ERROR register
-  //
-  // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS
-  // If bit 0 (error bit) is set, then ATA ERROR register is valid.
-  // While we *might* decode the ATA ERROR register, at the moment it
-  // doesn't make much sense: we don't care in detail why the error
-  // happened.
-  
-  if (passthru && (passthru->status || (passthru->command & 0x21))) {
-    errno=EIO;
-    return -1;
-  }
-  
-  // If this is a read data command, copy data to output buffer
-  if (readdata) {
-    if (escalade_type==CONTROLLER_3WARE_9000_CHAR)
-      memcpy(data, (unsigned char *)tw_ioctl_apache->data_buffer, 512);
-    else if (escalade_type==CONTROLLER_3WARE_678K_CHAR)
-      memcpy(data, (unsigned char *)tw_ioctl_char->data_buffer, 512);
-    else
-      memcpy(data, tw_output->output_data, 512);
-  }
-
-  // For STATUS_CHECK, we need to check register values
-  if (command==STATUS_CHECK) {
-    
-    // To find out if the SMART RETURN STATUS is good or failing, we
-    // need to examine the values of the Cylinder Low and Cylinder
-    // High Registers.
-    
-    unsigned short cyl_lo=passthru->cylinder_lo;
-    unsigned short cyl_hi=passthru->cylinder_hi;
-    
-    // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good.
-    if (cyl_lo==0x4F && cyl_hi==0xC2)
-      return 0;
-    
-    // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL
-    if (cyl_lo==0xF4 && cyl_hi==0x2C)
-      return 1;
-    
-    // Any other values mean that something has gone wrong with the command
-    if (CONTROLLER_3WARE_678K==escalade_type) {
-      printwarning(command);
-      errno=ENOSYS;
-      return 0;
-    }
-    else {
-      errno=EIO;
-      return -1;
-    }
-  }
-  
-  // copy sector count register (one byte!) to return data
-  if (command==CHECK_POWER_MODE)
-    *data=*(char *)&(passthru->sector_count);
-  
-  // look for nonexistent devices/ports
-  if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) {
-    errno=ENODEV;
-    return -1;
-  }
-  
-  return 0;
-}
-
-
-
-int marvell_command_interface(int device, 
-                              smart_command_set command, 
-                              int select, 
-                              char *data) {  
-  typedef struct {  
-    int  inlen;
-    int  outlen;
-    char cmd[540];
-  } mvsata_scsi_cmd;
-  
-  int copydata = 0;
-  mvsata_scsi_cmd  smart_command;
-  unsigned char *buff = (unsigned char *)&smart_command.cmd[6];
-  // See struct hd_drive_cmd_hdr in hdreg.h
-  // buff[0]: ATA COMMAND CODE REGISTER
-  // buff[1]: ATA SECTOR NUMBER REGISTER
-  // buff[2]: ATA FEATURES REGISTER
-  // buff[3]: ATA SECTOR COUNT REGISTER
-  
-  // clear out buff.  Large enough for HDIO_DRIVE_CMD (4+512 bytes)
-  memset(&smart_command, 0, sizeof(smart_command));
-  smart_command.inlen = 540;
-  smart_command.outlen = 540;
-  smart_command.cmd[0] = 0xC;  //Vendor-specific code
-  smart_command.cmd[4] = 6;     //command length
-  
-  buff[0] = ATA_SMART_CMD;
-  switch (command){
-  case CHECK_POWER_MODE:
-    buff[0]=ATA_CHECK_POWER_MODE;
-    break;
-  case READ_VALUES:
-    buff[2]=ATA_SMART_READ_VALUES;
-    copydata=buff[3]=1;
-    break;
-  case READ_THRESHOLDS:
-    buff[2]=ATA_SMART_READ_THRESHOLDS;
-    copydata=buff[1]=buff[3]=1;
-    break;
-  case READ_LOG:
-    buff[2]=ATA_SMART_READ_LOG_SECTOR;
-    buff[1]=select;
-    copydata=buff[3]=1;
-    break;
-  case IDENTIFY:
-    buff[0]=ATA_IDENTIFY_DEVICE;
-    copydata=buff[3]=1;
-    break;
-  case PIDENTIFY:
-    buff[0]=ATA_IDENTIFY_PACKET_DEVICE;
-    copydata=buff[3]=1;
-    break;
-  case ENABLE:
-    buff[2]=ATA_SMART_ENABLE;
-    buff[1]=1;
-    break;
-  case DISABLE:
-    buff[2]=ATA_SMART_DISABLE;
-    buff[1]=1;
-    break;
-  case STATUS:
-  case STATUS_CHECK:
-    // this command only says if SMART is working.  It could be
-    // replaced with STATUS_CHECK below.
-    buff[2] = ATA_SMART_STATUS;
-    break;
-  case AUTO_OFFLINE:
-    buff[2]=ATA_SMART_AUTO_OFFLINE;
-    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
-    break;
-  case AUTOSAVE:
-    buff[2]=ATA_SMART_AUTOSAVE;
-    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
-    break;
-  case IMMEDIATE_OFFLINE:
-    buff[2]=ATA_SMART_IMMEDIATE_OFFLINE;
-    buff[1]=select;
-    break;
-  default:
-    pout("Unrecognized command %d in mvsata_os_specific_handler()\n", command);
-    exit(1);
-    break;
-  }  
-  // There are two different types of ioctls().  The HDIO_DRIVE_TASK
-  // one is this:
-  // We are now doing the HDIO_DRIVE_CMD type ioctl.
-  if (ioctl(device, SCSI_IOCTL_SEND_COMMAND, (void *)&smart_command))
-      return -1;
-
-  if (command==CHECK_POWER_MODE) {
-    // LEON -- CHECK THIS PLEASE.  THIS SHOULD BE THE SECTOR COUNT
-    // REGISTER, AND IT MIGHT BE buff[2] NOT buff[3].  Bruce
-    data[0]=buff[3];
-    return 0;
-  }
-
-  // Always succeed on a SMART status, as a disk that failed returned  
-  // buff[4]=0xF4, buff[5]=0x2C, i.e. "Bad SMART status" (see below).
-  if (command == STATUS)
-    return 0;
-  //Data returned is starting from 0 offset  
-  if (command == STATUS_CHECK)
-  {
-    // Cyl low and Cyl high unchanged means "Good SMART status"
-    if (buff[4] == 0x4F && buff[5] == 0xC2)
-      return 0;    
-    // These values mean "Bad SMART status"
-    if (buff[4] == 0xF4 && buff[5] == 0x2C)
-      return 1;    
-    // We haven't gotten output that makes sense; print out some debugging info
-    syserror("Error SMART Status command failed");
-    pout("Please get assistance from %s\n",PACKAGE_BUGREPORT);
-    pout("Register values returned from SMART Status command are:\n");
-    pout("CMD =0x%02x\n",(int)buff[0]);
-    pout("FR =0x%02x\n",(int)buff[1]);
-    pout("NS =0x%02x\n",(int)buff[2]);
-    pout("SC =0x%02x\n",(int)buff[3]);
-    pout("CL =0x%02x\n",(int)buff[4]);
-    pout("CH =0x%02x\n",(int)buff[5]);
-    pout("SEL=0x%02x\n",(int)buff[6]);
-    return -1;   
-  }  
-
-  if (copydata)
-    memcpy(data, buff, 512);
-  return 0; 
-}
-
-
-// Utility function for printing warnings
-void printwarning(smart_command_set command){
-  static int printed[4]={0,0,0,0};
-  const char* message=
-    "can not be passed through the 3ware 3w-xxxx driver.  This can be fixed by\n"
-    "applying a simple 3w-xxxx driver patch that can be found here:\n"
-    PACKAGE_HOMEPAGE "\n"
-    "Alternatively, upgrade your 3w-xxxx driver to version 1.02.00.037 or greater.\n\n";
-
-  if (command==AUTO_OFFLINE && !printed[0]) {
-    printed[0]=1;
-    pout("The SMART AUTO-OFFLINE ENABLE command (smartmontools -o on option/Directive)\n%s", message);
-  } 
-  else if (command==AUTOSAVE && !printed[1]) {
-    printed[1]=1;
-    pout("The SMART AUTOSAVE ENABLE command (smartmontools -S on option/Directive)\n%s", message);
-  }
-  else if (command==STATUS_CHECK && !printed[2]) {
-    printed[2]=1;
-    pout("The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n%s", message);
-  }
-  else if (command==WRITE_LOG && !printed[3])  {
-    printed[3]=1;
-    pout("The SMART WRITE LOG command (smartmontools -t selective) only supported via char /dev/tw[ae] interface\n");
-  }
-  
-  return;
-}
-
-// Guess device type (ata or scsi) based on device name (Linux
-// specific) SCSI device name in linux can be sd, sr, scd, st, nst,
-// osst, nosst and sg.
-static const char * lin_dev_prefix = "/dev/";
-static const char * lin_dev_ata_disk_plus = "h";
-static const char * lin_dev_ata_devfs_disk_plus = "ide/";
-static const char * lin_dev_scsi_devfs_disk_plus = "scsi/";
-static const char * lin_dev_scsi_disk_plus = "s";
-static const char * lin_dev_scsi_tape1 = "ns";
-static const char * lin_dev_scsi_tape2 = "os";
-static const char * lin_dev_scsi_tape3 = "nos";
-static const char * lin_dev_3ware_9000_char = "twa";
-static const char * lin_dev_3ware_678k_char = "twe";
-
-int guess_device_type(const char * dev_name) {
-  int len;
-  int dev_prefix_len = strlen(lin_dev_prefix);
-  
-  // if dev_name null, or string length zero
-  if (!dev_name || !(len = strlen(dev_name)))
-    return CONTROLLER_UNKNOWN;
-  
-  // Remove the leading /dev/... if it's there
-  if (!strncmp(lin_dev_prefix, dev_name, dev_prefix_len)) {
-    if (len <= dev_prefix_len)
-      // if nothing else in the string, unrecognized
-      return CONTROLLER_UNKNOWN;
-    // else advance pointer to following characters
-    dev_name += dev_prefix_len;
-  }
-  
-  // form /dev/h* or h*
-  if (!strncmp(lin_dev_ata_disk_plus, dev_name,
-               strlen(lin_dev_ata_disk_plus)))
-    return CONTROLLER_ATA;
-  
-  // form /dev/ide/* or ide/*
-  if (!strncmp(lin_dev_ata_devfs_disk_plus, dev_name,
-               strlen(lin_dev_ata_devfs_disk_plus)))
-    return CONTROLLER_ATA;
-
-  // form /dev/s* or s*
-  if (!strncmp(lin_dev_scsi_disk_plus, dev_name,
-               strlen(lin_dev_scsi_disk_plus)))
-    return CONTROLLER_SCSI;
-
-  // form /dev/scsi/* or scsi/*
-  if (!strncmp(lin_dev_scsi_devfs_disk_plus, dev_name,
-               strlen(lin_dev_scsi_devfs_disk_plus)))
-    return CONTROLLER_SCSI;
-  
-  // form /dev/ns* or ns*
-  if (!strncmp(lin_dev_scsi_tape1, dev_name,
-               strlen(lin_dev_scsi_tape1)))
-    return CONTROLLER_SCSI;
-  
-  // form /dev/os* or os*
-  if (!strncmp(lin_dev_scsi_tape2, dev_name,
-               strlen(lin_dev_scsi_tape2)))
-    return CONTROLLER_SCSI;
-  
-  // form /dev/nos* or nos*
-  if (!strncmp(lin_dev_scsi_tape3, dev_name,
-               strlen(lin_dev_scsi_tape3)))
-    return CONTROLLER_SCSI;
-
-  // form /dev/twa*
-  if (!strncmp(lin_dev_3ware_9000_char, dev_name,
-               strlen(lin_dev_3ware_9000_char)))
-    return CONTROLLER_3WARE_9000_CHAR;
-
-  // form /dev/twe*
-  if (!strncmp(lin_dev_3ware_678k_char, dev_name,
-               strlen(lin_dev_3ware_678k_char)))
-    return CONTROLLER_3WARE_678K_CHAR;
-
-  // we failed to recognize any of the forms
-  return CONTROLLER_UNKNOWN;
-}
-
-
-#if 0
-
-[ed@firestorm ed]$ ls -l  /dev/discs
-total 0
-lr-xr-xr-x    1 root     root           30 Dec 31  1969 disc0 -> ../ide/host2/bus0/target0/lun0/
-lr-xr-xr-x    1 root     root           30 Dec 31  1969 disc1 -> ../ide/host2/bus1/target0/lun0/
-[ed@firestorm ed]$ ls -l  dev/ide/host*/bus*/target*/lun*/disc
-ls: dev/ide/host*/bus*/target*/lun*/disc: No such file or directory
-[ed@firestorm ed]$ ls -l  /dev/ide/host*/bus*/target*/lun*/disc
-brw-------    1 root     root      33,   0 Dec 31  1969 /dev/ide/host2/bus0/target0/lun0/disc
-brw-------    1 root     root      34,   0 Dec 31  1969 /dev/ide/host2/bus1/target0/lun0/disc
-[ed@firestorm ed]$ ls -l  /dev/ide/c*b*t*u*
-ls: /dev/ide/c*b*t*u*: No such file or directory
-[ed@firestorm ed]$ 
-Script done on Fri Nov  7 13:46:28 2003
-
-#endif
diff --git a/os_linux.cpp b/os_linux.cpp
new file mode 100644 (file)
index 0000000..3deda1f
--- /dev/null
@@ -0,0 +1,1744 @@
+/* 
+ *  os_linux.c
+ * 
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-6 Doug Gilbert <dougg@torque.net>
+ *
+ *  Parts of this file are derived from code that was
+ *
+ *  Written By: Adam Radford <linux@3ware.com>
+ *  Modifications By: Joel Jacobson <linux@3ware.com>
+ *                   Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *                    Brad Strand <linux@3ware.com>
+ *
+ *  Copyright (C) 1999-2003 3ware Inc.
+ *
+ *  Kernel compatablity By:     Andre Hedrick <andre@suse.com>
+ *  Non-Copyright (C) 2000      Andre Hedrick <andre@suse.com>
+ *
+ * Other ars of this file are derived from code that was
+ * 
+ * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
+ * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ * 
+ */
+
+// This file contains the linux-specific IOCTL parts of
+// smartmontools. It includes one interface routine for ATA devices,
+// one for SCSI devices, and one for ATA devices behind escalade
+// controllers.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/sg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#ifndef makedev // old versions of types.h do not include sysmacros.h
+#include <sys/sysmacros.h>
+#endif
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "extern.h"
+extern smartmonctrl * con;
+#include "os_linux.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+#ifndef ENOTSUP
+#define ENOTSUP ENOSYS
+#endif
+typedef unsigned long long u8;
+
+#define ARGUSED(x) ((void)(x))
+
+static const char *filenameandversion="$Id: os_linux.cpp,v 1.86 2006/09/12 01:16:54 sxzzsf Exp $";
+
+const char *os_XXXX_c_cvsid="$Id: os_linux.cpp,v 1.86 2006/09/12 01:16:54 sxzzsf Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_LINUX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// global variable holding byte count of allocated memory
+extern long long bytes;
+
+
+
+/* This function will setup and fix device nodes for a 3ware controller. */
+#define MAJOR_STRING_LENGTH 3
+#define DEVICE_STRING_LENGTH 32
+#define NODE_STRING_LENGTH 16
+int setup_3ware_nodes(char *nodename, char *driver_name) {
+  int              tw_major      = 0;
+  int              index         = 0;
+  char             majorstring[MAJOR_STRING_LENGTH+1];
+  char             device_name[DEVICE_STRING_LENGTH+1];
+  char             nodestring[NODE_STRING_LENGTH];
+  struct stat      stat_buf;
+  FILE             *file;
+  
+  /* First try to open up /proc/devices */
+  if (!(file = fopen("/proc/devices", "r"))) {
+    pout("Error opening /proc/devices to check/create 3ware device nodes\n");
+    syserror("fopen");
+    return 0;  // don't fail here: user might not have /proc !
+  }
+  
+  /* Attempt to get device major number */
+  while (EOF != fscanf(file, "%3s %32s", majorstring, device_name)) {
+    majorstring[MAJOR_STRING_LENGTH]='\0';
+    device_name[DEVICE_STRING_LENGTH]='\0';
+    if (!strncmp(device_name, nodename, DEVICE_STRING_LENGTH)) {
+      tw_major = atoi(majorstring);
+      break;
+    }
+  }
+  fclose(file);
+  
+  /* See if we found a major device number */
+  if (!tw_major) {
+    pout("No major number for /dev/%s listed in /proc/devices. Is the %s driver loaded?\n", nodename, driver_name);
+    return 2;
+  }
+  
+  /* Now check if nodes are correct */
+  for (index=0; index<16; index++) {
+    sprintf(nodestring, "/dev/%s%d", nodename, index);
+          
+    /* Try to stat the node */
+    if ((stat(nodestring, &stat_buf))) {
+      /* Create a new node if it doesn't exist */
+      if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) {
+        pout("problem creating 3ware device nodes %s", nodestring);
+        syserror("mknod");
+        return 3;
+      }
+    }
+    
+    /* See if nodes major and minor numbers are correct */
+    if ((tw_major != (int)(major(stat_buf.st_rdev))) ||
+        (index    != (int)(minor(stat_buf.st_rdev))) ||
+        (!S_ISCHR(stat_buf.st_mode))) {
+      
+      /* Delete the old node */
+      if (unlink(nodestring)) {
+        pout("problem unlinking stale 3ware device node %s", nodestring);
+        syserror("unlink");
+        return 4;
+      }
+      
+      /* Make a new node */
+      if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) {
+        pout("problem creating 3ware device nodes %s", nodestring);
+        syserror("mknod");
+        return 5;
+      }
+    }
+  }
+  return 0;
+}
+
+// equivalent to open(path, flags)
+int deviceopen(const char *pathname, char *type){
+  if (!strcmp(type,"SCSI")) {
+    int fd = open(pathname, O_RDWR | O_NONBLOCK);
+    if (fd < 0 && errno == EROFS)
+      fd = open(pathname, O_RDONLY | O_NONBLOCK);
+    return fd;
+  }
+  else if (!strcmp(type,"ATA")) 
+    return open(pathname, O_RDONLY | O_NONBLOCK);
+  else if (!strcmp(type,"ATA_3WARE_9000")) {
+    // the device nodes for this controller are dynamically assigned,
+    // so we need to check that they exist with the correct major
+    // numbers and if not, create them
+    if (setup_3ware_nodes("twa", "3w-9xxx")) {
+      if (!errno)
+        errno=ENXIO;
+      return -1;
+    }
+    return open(pathname, O_RDONLY | O_NONBLOCK);
+  }
+  else if (!strcmp(type,"ATA_3WARE_678K")) {
+    // the device nodes for this controller are dynamically assigned,
+    // so we need to check that they exist with the correct major
+    // numbers and if not, create them
+    if (setup_3ware_nodes("twe", "3w-xxxx")) {
+      if (!errno)
+        errno=ENXIO;
+      return -1;
+    }
+    return open(pathname, O_RDONLY | O_NONBLOCK);
+  }
+  else
+    return -1;
+}
+
+// equivalent to close(file descriptor)
+int deviceclose(int fd){
+  return close(fd);
+}
+
+// print examples for smartctl
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+         "  smartctl --all /dev/hda                    (Prints all SMART information)\n\n"
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl --test=long /dev/hda          (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl --all --device=3ware,2 /dev/sda\n"
+         "  smartctl --all --device=3ware,2 /dev/twe0\n"
+         "  smartctl --all --device=3ware,2 /dev/twa0\n"
+         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
+         "  smartctl --all --device=hpt,1/1/3 /dev/sda\n"
+         "          (Prints all SMART info for the SATA disk attached to the 3rd PMPort\n"
+         "           of the 1st channel on the 1st HighPoint RAID controller)\n"
+         );
+#else
+  printf(
+         "  smartctl -a /dev/hda                       (Prints all SMART information)\n"
+         "  smartctl -s on -o on -S on /dev/hda         (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl -a -d 3ware,2 /dev/sda\n"
+         "  smartctl -a -d 3ware,2 /dev/twa0\n"
+         "  smartctl -a -d 3ware,2 /dev/twe0\n"
+         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
+         "  smartctl -a -d hpt,1/1/3 /dev/sda\n"
+         "          (Prints all SMART info for the SATA disk attached to the 3rd PMPort\n"
+         "           of the 1st channel on the 1st HighPoint RAID controller)\n"
+         );
+#endif
+  return;
+}
+
+
+// we are going to take advantage of the fact that Linux's devfs will only
+// have device entries for devices that exist.  So if we get the equivalent of
+// ls /dev/hd[a-t], we have all the ATA devices on the system
+//
+// If any errors occur, leave errno set as it was returned by the
+// system call, and return <0.
+int get_dev_names(char*** names, const char* pattern, const char* name, int max) {
+  int n = 0, retglob, i, lim;
+  char** mp;
+  glob_t globbuf;
+  
+  memset(&globbuf, 0, sizeof(globbuf));
+
+  // in case of non-clean exit
+  *names=NULL;
+  
+  // Use glob to look for any directory entries matching the pattern
+  if ((retglob=glob(pattern, GLOB_ERR, NULL, &globbuf))) {
+    
+    //  glob failed: free memory and return
+    globfree(&globbuf);
+    
+    if (retglob==GLOB_NOMATCH){
+      pout("glob(3) found no matches for pattern %s\n", pattern);
+      return 0;
+    }
+    
+    if (retglob==GLOB_NOSPACE)
+      pout("glob(3) ran out of memory matching pattern %s\n", pattern);
+#ifdef GLOB_ABORTED // missing in old versions of glob.h
+    else if (retglob==GLOB_ABORTED)
+      pout("glob(3) aborted matching pattern %s\n", pattern);
+#endif
+    else
+      pout("Unexplained error in glob(3) of pattern %s\n", pattern);
+    
+    return -1;
+  }
+
+  // did we find too many paths?
+  lim = ((int)globbuf.gl_pathc < max) ? (int)globbuf.gl_pathc : max;
+  if (lim < (int)globbuf.gl_pathc)
+    pout("glob(3) found %d > MAX=%d devices matching pattern %s: ignoring %d paths\n", 
+         (int)globbuf.gl_pathc, max, pattern, (int)(globbuf.gl_pathc-max));
+  
+  // allocate space for up to lim number of ATA devices
+  if (!(mp =  (char **)calloc(lim, sizeof(char*)))){
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+  
+  // now step through the list returned by glob.  If not a link, copy
+  // to list.  If it is a link, evaluate it and see if the path ends
+  // in "disc".
+  for (i=0; i<lim; i++){
+    int retlink;
+    
+    // prepare a buffer for storing the link
+    char linkbuf[1024];
+    
+    // see if path is a link
+    retlink=readlink(globbuf.gl_pathv[i], linkbuf, 1023);
+    
+    // if not a link (or a strange link), keep it
+    if (retlink<=0 || retlink>1023)
+      mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion);
+    else {
+      // or if it's a link that points to a disc, follow it
+      char *p;
+      linkbuf[retlink]='\0';
+      if ((p=strrchr(linkbuf,'/')) && !strcmp(p+1, "disc"))
+        // This is the branch of the code that gets followed if we are
+        // using devfs WITH traditional compatibility links. In this
+        // case, we add the traditional device name to the list that
+        // is returned.
+        mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion);
+      else {
+        // This is the branch of the code that gets followed if we are
+        // using devfs WITHOUT traditional compatibility links.  In
+        // this case, we check that the link to the directory is of
+        // the correct type, and then append "disc" to it.
+        char tmpname[1024]={0};
+        const char * type = (strcmp(name,"ATA") ? "scsi" : "ide");
+        if (strstr(linkbuf, type)){
+          snprintf(tmpname, 1024, "%s/disc", globbuf.gl_pathv[i]);
+          mp[n++] = CustomStrDup(tmpname, 1, __LINE__, filenameandversion);
+        }
+      }
+    }
+  }
+  
+  // free memory, track memory usage
+  globfree(&globbuf);
+  mp = static_cast<char **>(realloc(mp,n*(sizeof(char*))));
+  bytes += n*(sizeof(char*));
+  
+  // and set up return values
+  *names=mp;
+  return n;
+}
+
+// makes a list of device names to scan, for either ATA or SCSI
+// devices.  Return -1 if no memory remaining, else the number of
+// devices on the list, which can be >=0.
+int make_device_names (char*** devlist, const char* name) {
+  int retval, maxdev;
+  
+#if 0
+  // for testing case where no device names are found
+  return 0;
+#endif
+  
+  if (!strcmp(name,"SCSI"))
+    retval=get_dev_names(devlist,"/dev/sd[a-z]", name, maxdev=26);
+  else if (!strcmp(name,"ATA"))
+    retval=get_dev_names(devlist,"/dev/hd[a-t]", name, maxdev=20);
+  else
+    // don't recognize disk type!
+    return 0;
+
+  // if we found traditional links, we are done
+  if (retval>0)
+    return retval;
+  
+  // else look for devfs entries without traditional links
+  return get_dev_names(devlist,"/dev/discs/disc*", name, maxdev);
+}
+
+
+// PURPOSE
+//   This is an interface routine meant to isolate the OS dependent
+//   parts of the code, and to provide a debugging interface.  Each
+//   different port and OS needs to provide it's own interface.  This
+//   is the linux one.
+// DETAILED DESCRIPTION OF ARGUMENTS
+//   device: is the file descriptor provided by open()
+//   command: defines the different operations.
+//   select: additional input data if needed (which log, which type of
+//           self-test).
+//   data:   location to write output data, if needed (512 bytes).
+//   Note: not all commands use all arguments.
+// RETURN VALUES
+//  -1 if the command failed
+//   0 if the command succeeded,
+//   STATUS_CHECK routine: 
+//  -1 if the command failed
+//   0 if the command succeeded and disk SMART status is "OK"
+//   1 if the command succeeded and disk SMART status is "FAILING"
+
+
+// huge value of buffer size needed because HDIO_DRIVE_CMD assumes
+// that buff[3] is the data size.  Since the ATA_SMART_AUTOSAVE and
+// ATA_SMART_AUTO_OFFLINE use values of 0xf1 and 0xf8 we need the space.
+// Otherwise a 4+512 byte buffer would be enough.
+#define STRANGE_BUFFER_LENGTH (4+512*0xf8)
+
+int ata_command_interface(int device, smart_command_set command, int select, char *data){
+  unsigned char buff[STRANGE_BUFFER_LENGTH];
+  // positive: bytes to write to caller.  negative: bytes to READ from
+  // caller. zero: non-data command
+  int copydata=0;
+
+  const int HDIO_DRIVE_CMD_OFFSET = 4;
+
+  // See struct hd_drive_cmd_hdr in hdreg.h.  Before calling ioctl()
+  // buff[0]: ATA COMMAND CODE REGISTER
+  // buff[1]: ATA SECTOR NUMBER REGISTER == LBA LOW REGISTER
+  // buff[2]: ATA FEATURES REGISTER
+  // buff[3]: ATA SECTOR COUNT REGISTER
+
+  // Note that on return:
+  // buff[2] contains the ATA SECTOR COUNT REGISTER
+  
+  // clear out buff.  Large enough for HDIO_DRIVE_CMD (4+512 bytes)
+  memset(buff, 0, STRANGE_BUFFER_LENGTH);
+
+  buff[0]=ATA_SMART_CMD;
+  switch (command){
+  case CHECK_POWER_MODE:
+    buff[0]=ATA_CHECK_POWER_MODE;
+    copydata=1;
+    break;
+  case READ_VALUES:
+    buff[2]=ATA_SMART_READ_VALUES;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case READ_THRESHOLDS:
+    buff[2]=ATA_SMART_READ_THRESHOLDS;
+    buff[1]=buff[3]=1;
+    copydata=512;
+    break;
+  case READ_LOG:
+    buff[2]=ATA_SMART_READ_LOG_SECTOR;
+    buff[1]=select;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case WRITE_LOG:
+    break;
+  case IDENTIFY:
+    buff[0]=ATA_IDENTIFY_DEVICE;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case PIDENTIFY:
+    buff[0]=ATA_IDENTIFY_PACKET_DEVICE;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case ENABLE:
+    buff[2]=ATA_SMART_ENABLE;
+    buff[1]=1;
+    break;
+  case DISABLE:
+    buff[2]=ATA_SMART_DISABLE;
+    buff[1]=1;
+    break;
+  case STATUS:
+    // this command only says if SMART is working.  It could be
+    // replaced with STATUS_CHECK below.
+    buff[2]=ATA_SMART_STATUS;
+    break;
+  case AUTO_OFFLINE:
+    buff[2]=ATA_SMART_AUTO_OFFLINE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case AUTOSAVE:
+    buff[2]=ATA_SMART_AUTOSAVE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case IMMEDIATE_OFFLINE:
+    buff[2]=ATA_SMART_IMMEDIATE_OFFLINE;
+    buff[1]=select;
+    break;
+  case STATUS_CHECK:
+    // This command uses HDIO_DRIVE_TASK and has different syntax than
+    // the other commands.
+    buff[1]=ATA_SMART_STATUS;
+    break;
+  default:
+    pout("Unrecognized command %d in linux_ata_command_interface()\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", command);
+    errno=ENOSYS;
+    return -1;
+  }
+  
+  // This command uses the HDIO_DRIVE_TASKFILE ioctl(). This is the
+  // only ioctl() that can be used to WRITE data to the disk.
+  if (command==WRITE_LOG) {    
+    unsigned char task[sizeof(ide_task_request_t)+512];
+    ide_task_request_t *reqtask=(ide_task_request_t *) task;
+    task_struct_t      *taskfile=(task_struct_t *) reqtask->io_ports;
+    int retval;
+
+    memset(task,      0, sizeof(task));
+    
+    taskfile->data           = 0;
+    taskfile->feature        = ATA_SMART_WRITE_LOG_SECTOR;
+    taskfile->sector_count   = 1;
+    taskfile->sector_number  = select;
+    taskfile->low_cylinder   = 0x4f;
+    taskfile->high_cylinder  = 0xc2;
+    taskfile->device_head    = 0;
+    taskfile->command        = ATA_SMART_CMD;
+    
+    reqtask->data_phase      = TASKFILE_OUT;
+    reqtask->req_cmd         = IDE_DRIVE_TASK_OUT;
+    reqtask->out_size        = 512;
+    reqtask->in_size         = 0;
+    
+    // copy user data into the task request structure
+    memcpy(task+sizeof(ide_task_request_t), data, 512);
+      
+    if ((retval=ioctl(device, HDIO_DRIVE_TASKFILE, task))) {
+      if (retval==-EINVAL)
+        pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n");
+      return -1;
+    }
+    return 0;
+  }
+    
+  // There are two different types of ioctls().  The HDIO_DRIVE_TASK
+  // one is this:
+  if (command==STATUS_CHECK){
+    int retval;
+
+    // NOT DOCUMENTED in /usr/src/linux/include/linux/hdreg.h. You
+    // have to read the IDE driver source code.  Sigh.
+    // buff[0]: ATA COMMAND CODE REGISTER
+    // buff[1]: ATA FEATURES REGISTER
+    // buff[2]: ATA SECTOR_COUNT
+    // buff[3]: ATA SECTOR NUMBER
+    // buff[4]: ATA CYL LO REGISTER
+    // buff[5]: ATA CYL HI REGISTER
+    // buff[6]: ATA DEVICE HEAD
+
+    unsigned const char normal_lo=0x4f, normal_hi=0xc2;
+    unsigned const char failed_lo=0xf4, failed_hi=0x2c;
+    buff[4]=normal_lo;
+    buff[5]=normal_hi;
+    
+    if ((retval=ioctl(device, HDIO_DRIVE_TASK, buff))) {
+      if (retval==-EINVAL) {
+        pout("Error SMART Status command via HDIO_DRIVE_TASK failed");
+        pout("Rebuild older linux 2.2 kernels with HDIO_DRIVE_TASK support added\n");
+      }
+      else
+        syserror("Error SMART Status command failed");
+      return -1;
+    }
+    
+    // Cyl low and Cyl high unchanged means "Good SMART status"
+    if (buff[4]==normal_lo && buff[5]==normal_hi)
+      return 0;
+    
+    // These values mean "Bad SMART status"
+    if (buff[4]==failed_lo && buff[5]==failed_hi)
+      return 1;
+    
+    // We haven't gotten output that makes sense; print out some debugging info
+    syserror("Error SMART Status command failed");
+    pout("Please get assistance from " PACKAGE_HOMEPAGE "\n");
+    pout("Register values returned from SMART Status command are:\n");
+    pout("CMD=0x%02x\n",(int)buff[0]);
+    pout("FR =0x%02x\n",(int)buff[1]);
+    pout("NS =0x%02x\n",(int)buff[2]);
+    pout("SC =0x%02x\n",(int)buff[3]);
+    pout("CL =0x%02x\n",(int)buff[4]);
+    pout("CH =0x%02x\n",(int)buff[5]);
+    pout("SEL=0x%02x\n",(int)buff[6]);
+    return -1;   
+  }
+  
+#if 1
+  // Note to people doing ports to other OSes -- don't worry about
+  // this block -- you can safely ignore it.  I have put it here
+  // because under linux when you do IDENTIFY DEVICE to a packet
+  // device, it generates an ugly kernel syslog error message.  This
+  // is harmless but frightens users.  So this block detects packet
+  // devices and make IDENTIFY DEVICE fail "nicely" without a syslog
+  // error message.
+  //
+  // If you read only the ATA specs, it appears as if a packet device
+  // *might* respond to the IDENTIFY DEVICE command.  This is
+  // misleading - it's because around the time that SFF-8020 was
+  // incorporated into the ATA-3/4 standard, the ATA authors were
+  // sloppy. See SFF-8020 and you will see that ATAPI devices have
+  // *always* had IDENTIFY PACKET DEVICE as a mandatory part of their
+  // command set, and return 'Command Aborted' to IDENTIFY DEVICE.
+  if (command==IDENTIFY || command==PIDENTIFY){
+    unsigned short deviceid[256];
+    // check the device identity, as seen when the system was booted
+    // or the device was FIRST registered.  This will not be current
+    // if the user has subsequently changed some of the parameters. If
+    // device is a packet device, swap the command interpretations.
+    if (!ioctl(device, HDIO_GET_IDENTITY, deviceid) && (deviceid[0] & 0x8000))
+      buff[0]=(command==IDENTIFY)?ATA_IDENTIFY_PACKET_DEVICE:ATA_IDENTIFY_DEVICE;
+  }
+#endif
+  
+  // We are now doing the HDIO_DRIVE_CMD type ioctl.
+  if ((ioctl(device, HDIO_DRIVE_CMD, buff)))
+    return -1;
+
+  // CHECK POWER MODE command returns information in the Sector Count
+  // register (buff[3]).  Copy to return data buffer.
+  if (command==CHECK_POWER_MODE)
+    buff[HDIO_DRIVE_CMD_OFFSET]=buff[2];
+
+  // if the command returns data then copy it back
+  if (copydata)
+    memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata);
+  
+  return 0; 
+}
+
+// >>>>>> Start of general SCSI specific linux code
+
+/* Linux specific code.
+ * Historically smartmontools (and smartsuite before it) used the
+ * SCSI_IOCTL_SEND_COMMAND ioctl which is available to all linux device
+ * nodes that use the SCSI subsystem. A better interface has been available
+ * via the SCSI generic (sg) driver but this involves the extra step of
+ * mapping disk devices (e.g. /dev/sda) to the corresponding sg device
+ * (e.g. /dev/sg2). In the linux kernel 2.6 series most of the facilities of
+ * the sg driver have become available via the SG_IO ioctl which is available
+ * on all SCSI devices (on SCSI tape devices from lk 2.6.6).
+ * So the strategy below is to find out if the SG_IO ioctl is available and
+ * if so use it; failing that use the older SCSI_IOCTL_SEND_COMMAND ioctl.
+ * Should work in 2.0, 2.2, 2.4 and 2.6 series linux kernels. */
+
+#define MAX_DXFER_LEN 1024      /* can be increased if necessary */
+#define SEND_IOCTL_RESP_SENSE_LEN 16    /* ioctl limitation */
+#define SG_IO_RESP_SENSE_LEN 64 /* large enough see buffer */
+#define LSCSI_DRIVER_MASK  0xf /* mask out "suggestions" */
+#define LSCSI_DRIVER_SENSE  0x8 /* alternate CHECK CONDITION indication */
+#define LSCSI_DRIVER_TIMEOUT  0x6
+#define LSCSI_DID_TIME_OUT  0x3
+#define LSCSI_DID_BUS_BUSY  0x2
+#define LSCSI_DID_NO_CONNECT  0x1
+
+#ifndef SCSI_IOCTL_SEND_COMMAND
+#define SCSI_IOCTL_SEND_COMMAND 1
+#endif
+
+#define SG_IO_PRESENT_UNKNOWN 0
+#define SG_IO_PRESENT_YES 1
+#define SG_IO_PRESENT_NO 2
+
+static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
+                         int unknown);
+static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);
+
+static int sg_io_state = SG_IO_PRESENT_UNKNOWN;
+
+/* Preferred implementation for issuing SCSI commands in linux. This
+ * function uses the SG_IO ioctl. Return 0 if command issued successfully
+ * (various status values should still be checked). If the SCSI command
+ * cannot be issued then a negative errno value is returned. */
+static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
+                         int unknown)
+{
+#ifndef SG_IO
+    ARGUSED(dev_fd); ARGUSED(iop); ARGUSED(report);
+    return -ENOTTY;
+#else
+    struct sg_io_hdr io_hdr;
+
+    if (report > 0) {
+        int k, j;
+        const unsigned char * ucp = iop->cmnd;
+        const char * np;
+        char buff[256];
+        const int sz = (int)sizeof(buff);
+
+        np = scsi_get_opcode_name(ucp[0]);
+        j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+        for (k = 0; k < (int)iop->cmnd_len; ++k)
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+        if ((report > 1) && 
+            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+                          "data, len=%d%s:\n", (int)iop->dxfer_len,
+                          (trunc ? " [only first 256 bytes shown]" : ""));
+            dStrHex((const char *)iop->dxferp, 
+                    (trunc ? 256 : iop->dxfer_len) , 1);
+        }
+        else
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+        pout(buff);
+    }
+    memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = iop->cmnd_len;
+    io_hdr.mx_sb_len = iop->max_sense_len;
+    io_hdr.dxfer_len = iop->dxfer_len;
+    io_hdr.dxferp = iop->dxferp;
+    io_hdr.cmdp = iop->cmnd;
+    io_hdr.sbp = iop->sensep;
+    /* sg_io_hdr interface timeout has millisecond units. Timeout of 0
+       defaults to 60 seconds. */
+    io_hdr.timeout = ((0 == iop->timeout) ? 60 : iop->timeout) * 1000;
+    switch (iop->dxfer_dir) {
+        case DXFER_NONE:
+            io_hdr.dxfer_direction = SG_DXFER_NONE;
+            break;
+        case DXFER_FROM_DEVICE:
+            io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+            break;
+        case DXFER_TO_DEVICE:
+            io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
+            break;
+        default:
+            pout("do_scsi_cmnd_io: bad dxfer_dir\n");
+            return -EINVAL;
+    }
+    iop->resp_sense_len = 0;
+    iop->scsi_status = 0;
+    iop->resid = 0;
+    if (ioctl(dev_fd, SG_IO, &io_hdr) < 0) {
+        if (report && (! unknown))
+            pout("  SG_IO ioctl failed, errno=%d [%s]\n", errno,
+                 strerror(errno));
+        return -errno;
+    }
+    if (report > 0) {
+        pout("  scsi_status=0x%x, host_status=0x%x, driver_status=0x%x\n"
+             "  info=0x%x  duration=%d milliseconds\n", io_hdr.status, 
+             io_hdr.host_status, io_hdr.driver_status, io_hdr.info,
+             io_hdr.duration);
+        if (report > 1) {
+            if (DXFER_FROM_DEVICE == iop->dxfer_dir) {
+                int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+                pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+                     (trunc ? " [only first 256 bytes shown]" : ""));
+                dStrHex((const char*)iop->dxferp, 
+                        (trunc ? 256 : iop->dxfer_len) , 1);
+            }
+        }
+    }
+    iop->resid = io_hdr.resid;
+    iop->scsi_status = io_hdr.status;
+
+    if (io_hdr.info | SG_INFO_CHECK) { /* error or warning */
+        int masked_driver_status = (LSCSI_DRIVER_MASK & io_hdr.driver_status);
+
+        if (0 != io_hdr.host_status) {
+            if ((LSCSI_DID_NO_CONNECT == io_hdr.host_status) ||
+                (LSCSI_DID_BUS_BUSY == io_hdr.host_status) ||
+                (LSCSI_DID_TIME_OUT == io_hdr.host_status))
+                return -ETIMEDOUT;
+            else
+                return -EIO;    /* catch all */
+        }
+        if (0 != masked_driver_status) {
+            if (LSCSI_DRIVER_TIMEOUT == masked_driver_status)
+                return -ETIMEDOUT;
+            else if (LSCSI_DRIVER_SENSE != masked_driver_status)
+                return -EIO;
+        }
+        if (LSCSI_DRIVER_SENSE == masked_driver_status)
+            iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
+        iop->resp_sense_len = io_hdr.sb_len_wr;
+        if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && 
+            iop->sensep && (iop->resp_sense_len > 0)) {
+            if (report > 1) {
+                pout("  >>> Sense buffer, len=%d:\n",
+                     (int)iop->resp_sense_len);
+                dStrHex((const char *)iop->sensep, iop->resp_sense_len , 1);
+            }
+        }
+        if (report) {
+            if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) {
+                if ((iop->sensep[0] & 0x7f) > 0x71)
+                    pout("  status=%x: [desc] sense_key=%x asc=%x ascq=%x\n",
+                         iop->scsi_status, iop->sensep[1] & 0xf,
+                         iop->sensep[2], iop->sensep[3]);
+                else
+                    pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
+                         iop->scsi_status, iop->sensep[2] & 0xf,
+                         iop->sensep[12], iop->sensep[13]);
+            }
+            else
+                pout("  status=0x%x\n", iop->scsi_status);
+        }
+    }
+    return 0;
+#endif
+}
+
+struct linux_ioctl_send_command
+{
+    int inbufsize;
+    int outbufsize;
+    UINT8 buff[MAX_DXFER_LEN + 16];
+};
+
+/* The Linux SCSI_IOCTL_SEND_COMMAND ioctl is primitive and it doesn't 
+ * support: CDB length (guesses it from opcode), resid and timeout.
+ * Patches in Linux 2.4.21 and 2.5.70 to extend SEND DIAGNOSTIC timeout
+ * to 2 hours in order to allow long foreground extended self tests. */
+static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
+{
+    struct linux_ioctl_send_command wrk;
+    int status, buff_offset;
+    size_t len;
+
+    memcpy(wrk.buff, iop->cmnd, iop->cmnd_len);
+    buff_offset = iop->cmnd_len;
+    if (report > 0) {
+        int k, j;
+        const unsigned char * ucp = iop->cmnd;
+        const char * np;
+        char buff[256];
+        const int sz = (int)sizeof(buff);
+
+        np = scsi_get_opcode_name(ucp[0]);
+        j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+        for (k = 0; k < (int)iop->cmnd_len; ++k)
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+        if ((report > 1) && 
+            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+                          "data, len=%d%s:\n", (int)iop->dxfer_len,
+                          (trunc ? " [only first 256 bytes shown]" : ""));
+            dStrHex((const char *)iop->dxferp, 
+                    (trunc ? 256 : iop->dxfer_len) , 1);
+        }
+        else
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+        pout(buff);
+    }
+    switch (iop->dxfer_dir) {
+        case DXFER_NONE:
+            wrk.inbufsize = 0;
+            wrk.outbufsize = 0;
+            break;
+        case DXFER_FROM_DEVICE:
+            wrk.inbufsize = 0;
+            if (iop->dxfer_len > MAX_DXFER_LEN)
+                return -EINVAL;
+            wrk.outbufsize = iop->dxfer_len;
+            break;
+        case DXFER_TO_DEVICE:
+            if (iop->dxfer_len > MAX_DXFER_LEN)
+                return -EINVAL;
+            memcpy(wrk.buff + buff_offset, iop->dxferp, iop->dxfer_len);
+            wrk.inbufsize = iop->dxfer_len;
+            wrk.outbufsize = 0;
+            break;
+        default:
+            pout("do_scsi_cmnd_io: bad dxfer_dir\n");
+            return -EINVAL;
+    }
+    iop->resp_sense_len = 0;
+    iop->scsi_status = 0;
+    iop->resid = 0;
+    status = ioctl(dev_fd, SCSI_IOCTL_SEND_COMMAND, &wrk);
+    if (-1 == status) {
+        if (report)
+            pout("  SCSI_IOCTL_SEND_COMMAND ioctl failed, errno=%d [%s]\n",
+                 errno, strerror(errno));
+        return -errno;
+    }
+    if (0 == status) {
+        if (report > 0)
+            pout("  status=0\n");
+        if (DXFER_FROM_DEVICE == iop->dxfer_dir) {
+            memcpy(iop->dxferp, wrk.buff, iop->dxfer_len);
+            if (report > 1) {
+                int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+                pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+                     (trunc ? " [only first 256 bytes shown]" : ""));
+                dStrHex((const char*)iop->dxferp, 
+                        (trunc ? 256 : iop->dxfer_len) , 1);
+            }
+        }
+        return 0;
+    }
+    iop->scsi_status = status & 0x7e; /* bits 0 and 7 used to be for vendors */
+    if (LSCSI_DRIVER_SENSE == ((status >> 24) & 0xf))
+        iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
+    len = (SEND_IOCTL_RESP_SENSE_LEN < iop->max_sense_len) ?
+                SEND_IOCTL_RESP_SENSE_LEN : iop->max_sense_len;
+    if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && 
+        iop->sensep && (len > 0)) {
+        memcpy(iop->sensep, wrk.buff, len);
+        iop->resp_sense_len = len;
+        if (report > 1) {
+            pout("  >>> Sense buffer, len=%d:\n", (int)len);
+            dStrHex((const char *)wrk.buff, len , 1);
+        }
+    }
+    if (report) {
+        if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) {
+            pout("  status=%x: sense_key=%x asc=%x ascq=%x\n", status & 0xff,
+                 wrk.buff[2] & 0xf, wrk.buff[12], wrk.buff[13]);
+        }
+        else
+            pout("  status=0x%x\n", status);
+    }
+    if (iop->scsi_status > 0)
+        return 0;
+    else {
+        if (report > 0)
+            pout("  ioctl status=0x%x but scsi status=0, fail with EIO\n", 
+                 status);
+        return -EIO;      /* give up, assume no device there */
+    }
+}
+
+/* SCSI command transmission interface function, linux version.
+ * Returns 0 if SCSI command successfully launched and response
+ * received. Even when 0 is returned the caller should check 
+ * scsi_cmnd_io::scsi_status for SCSI defined errors and warnings
+ * (e.g. CHECK CONDITION). If the SCSI command could not be issued
+ * (e.g. device not present or timeout) or some other problem
+ * (e.g. timeout) then returns a negative errno value */
+int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
+{
+    int res;
+
+    /* implementation relies on static sg_io_state variable. If not
+     * previously set tries the SG_IO ioctl. If that succeeds assume
+     * that SG_IO ioctl functional. If it fails with an errno value
+     * other than ENODEV (no device) or permission then assume 
+     * SCSI_IOCTL_SEND_COMMAND is the only option. */
+    switch (sg_io_state) {
+    case SG_IO_PRESENT_UNKNOWN:
+        /* ignore report argument */
+        if (0 == (res = sg_io_cmnd_io(dev_fd, iop, report, 1))) {
+            sg_io_state = SG_IO_PRESENT_YES;
+            return 0;
+        } else if ((-ENODEV == res) || (-EACCES == res) || (-EPERM == res))
+            return res;         /* wait until we see a device */
+        sg_io_state = SG_IO_PRESENT_NO;
+        /* drop through by design */
+    case SG_IO_PRESENT_NO:
+        return sisc_cmnd_io(dev_fd, iop, report);
+    case SG_IO_PRESENT_YES:
+        return sg_io_cmnd_io(dev_fd, iop, report, 0);
+    default:
+        pout(">>>> do_scsi_cmnd_io: bad sg_io_state=%d\n", sg_io_state); 
+        sg_io_state = SG_IO_PRESENT_UNKNOWN;
+        return -EIO;    /* report error and reset state */
+    }
+}
+
+// >>>>>> End of general SCSI specific linux code
+
+
+// prototype
+void printwarning(smart_command_set command);
+
+// PURPOSE
+//   This is an interface routine meant to isolate the OS dependent
+//   parts of the code, and to provide a debugging interface.  Each
+//   different port and OS needs to provide it's own interface.  This
+//   is the linux interface to the 3ware 3w-xxxx driver.  It allows ATA
+//   commands to be passed through the SCSI driver.
+// DETAILED DESCRIPTION OF ARGUMENTS
+//   fd: is the file descriptor provided by open()
+//   disknum is the disk number (0 to 15) in the RAID array
+//   escalade_type indicates the type of controller type, and if scsi or char interface is used
+//   command: defines the different operations.
+//   select: additional input data if needed (which log, which type of
+//           self-test).
+//   data:   location to write output data, if needed (512 bytes).
+//   Note: not all commands use all arguments.
+// RETURN VALUES
+//  -1 if the command failed
+//   0 if the command succeeded,
+//   STATUS_CHECK routine: 
+//  -1 if the command failed
+//   0 if the command succeeded and disk SMART status is "OK"
+//   1 if the command succeeded and disk SMART status is "FAILING"
+
+
+/* 512 is the max payload size: increase if needed */
+#define BUFFER_LEN_678K      ( sizeof(TW_Ioctl)                  ) // 1044 unpacked, 1041 packed
+#define BUFFER_LEN_678K_CHAR ( sizeof(TW_New_Ioctl)+512-1        ) // 1539 unpacked, 1536 packed
+#define BUFFER_LEN_9000      ( sizeof(TW_Ioctl_Buf_Apache)+512-1 ) // 2051 unpacked, 2048 packed
+#define TW_IOCTL_BUFFER_SIZE ( MAX(MAX(BUFFER_LEN_678K, BUFFER_LEN_9000), BUFFER_LEN_678K_CHAR) )
+
+int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){
+
+  // return value and buffer for ioctl()
+  int  ioctlreturn, readdata=0;
+
+  // Used by both the SCSI and char interfaces
+  TW_Passthru *passthru=NULL;
+  char ioctl_buffer[TW_IOCTL_BUFFER_SIZE];
+
+  // only used for SCSI device interface
+  TW_Ioctl   *tw_ioctl=NULL;
+  TW_Output *tw_output=NULL;
+
+  // only used for 6000/7000/8000 char device interface
+  TW_New_Ioctl *tw_ioctl_char=NULL;
+
+  // only used for 9000 character device interface
+  TW_Ioctl_Buf_Apache *tw_ioctl_apache=NULL;
+  
+  memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE);
+
+  if (escalade_type==CONTROLLER_3WARE_9000_CHAR) {
+    tw_ioctl_apache                               = (TW_Ioctl_Buf_Apache *)ioctl_buffer;
+    tw_ioctl_apache->driver_command.control_code  = TW_IOCTL_FIRMWARE_PASS_THROUGH;
+    tw_ioctl_apache->driver_command.buffer_length = 512; /* payload size */
+    passthru                                      = (TW_Passthru *)&(tw_ioctl_apache->firmware_command.command.oldcommand);
+  }
+  else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) {
+    tw_ioctl_char                                 = (TW_New_Ioctl *)ioctl_buffer;
+    tw_ioctl_char->data_buffer_length             = 512;
+    passthru                                      = (TW_Passthru *)&(tw_ioctl_char->firmware_command);
+  }
+  else if (escalade_type==CONTROLLER_3WARE_678K) {
+    tw_ioctl                                      = (TW_Ioctl *)ioctl_buffer;
+    tw_ioctl->cdb[0]                              = TW_IOCTL;
+    tw_ioctl->opcode                              = TW_ATA_PASSTHRU;
+    tw_ioctl->input_length                        = 512; // correct even for non-data commands
+    tw_ioctl->output_length                       = 512; // correct even for non-data commands
+    tw_output                                     = (TW_Output *)tw_ioctl;
+    passthru                                      = (TW_Passthru *)&(tw_ioctl->input_data);
+  }
+  else {
+    pout("Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", escalade_type, disknum);
+    errno=ENOSYS;
+    return -1;
+  }
+
+  // Same for (almost) all commands - but some reset below
+  passthru->byte0.opcode  = TW_OP_ATA_PASSTHRU;
+  passthru->request_id    = 0xFF;
+  passthru->byte3.aport   = disknum;
+  passthru->byte3.host_id = 0;
+  passthru->status        = 0;           
+  passthru->flags         = 0x1;
+  passthru->drive_head    = 0x0;
+  passthru->sector_num    = 0;
+
+  // All SMART commands use this CL/CH signature.  These are magic
+  // values from the ATA specifications.
+  passthru->cylinder_lo   = 0x4F;
+  passthru->cylinder_hi   = 0xC2;
+  
+  // SMART ATA COMMAND REGISTER value
+  passthru->command       = ATA_SMART_CMD;
+  
+  // Is this a command that reads or returns 512 bytes?
+  // passthru->param values are:
+  // 0x0 - non data command without TFR write check,
+  // 0x8 - non data command with TFR write check,
+  // 0xD - data command that returns data to host from device
+  // 0xF - data command that writes data from host to device
+  // passthru->size values are 0x5 for non-data and 0x07 for data
+  if (command == READ_VALUES     ||
+      command == READ_THRESHOLDS ||
+      command == READ_LOG        ||
+      command == IDENTIFY        ||
+      command == WRITE_LOG ) {
+    readdata=1;
+    passthru->byte0.sgloff = 0x5;
+    passthru->size         = 0x7;
+    passthru->param        = 0xD;
+    passthru->sector_count = 0x1;
+    // For 64-bit to work correctly, up the size of the command packet
+    // in dwords by 1 to account for the 64-bit single sgl 'address'
+    // field. Note that this doesn't agree with the typedefs but it's
+    // right (agree with kernel driver behavior/typedefs).
+    if (escalade_type==CONTROLLER_3WARE_9000_CHAR && sizeof(long)==8)
+      passthru->size++;
+  }
+  else {
+    // Non data command -- but doesn't use large sector 
+    // count register values.  
+    passthru->byte0.sgloff = 0x0;
+    passthru->size         = 0x5;
+    passthru->param        = 0x8;
+    passthru->sector_count = 0x0;
+  }
+  
+  // Now set ATA registers depending upon command
+  switch (command){
+  case CHECK_POWER_MODE:
+    passthru->command     = ATA_CHECK_POWER_MODE;
+    passthru->features    = 0;
+    passthru->cylinder_lo = 0;
+    passthru->cylinder_hi = 0;
+    break;
+  case READ_VALUES:
+    passthru->features = ATA_SMART_READ_VALUES;
+    break;
+  case READ_THRESHOLDS:
+    passthru->features = ATA_SMART_READ_THRESHOLDS;
+    break;
+  case READ_LOG:
+    passthru->features = ATA_SMART_READ_LOG_SECTOR;
+    // log number to return
+    passthru->sector_num  = select;
+    break;
+  case WRITE_LOG:
+    if (escalade_type == CONTROLLER_3WARE_9000_CHAR)
+      memcpy((unsigned char *)tw_ioctl_apache->data_buffer, data, 512);
+    else if (escalade_type == CONTROLLER_3WARE_678K_CHAR)
+      memcpy((unsigned char *)tw_ioctl_char->data_buffer,   data, 512);
+    else {
+      // COMMAND NOT SUPPORTED VIA SCSI IOCTL INTERFACE
+      // memcpy(tw_output->output_data, data, 512);
+      printwarning(command);
+      errno=ENOTSUP;
+      return -1;
+    }
+    readdata=0;
+    passthru->features     = ATA_SMART_WRITE_LOG_SECTOR;
+    passthru->sector_count = 1;
+    passthru->sector_num   = select;
+    passthru->param        = 0xF;  // PIO data write
+    break;
+  case IDENTIFY:
+    // ATA IDENTIFY DEVICE
+    passthru->command     = ATA_IDENTIFY_DEVICE;
+    passthru->features    = 0;
+    passthru->cylinder_lo = 0;
+    passthru->cylinder_hi = 0;
+    break;
+  case PIDENTIFY:
+    // 3WARE controller can NOT have packet device internally
+    pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", disknum);
+    pout("Note: /dev/sdX many need to be replaced with /dev/tweN or /dev/twaN\n");
+    errno=ENODEV;
+    return -1;
+  case ENABLE:
+    passthru->features = ATA_SMART_ENABLE;
+    break;
+  case DISABLE:
+    passthru->features = ATA_SMART_DISABLE;
+    break;
+  case AUTO_OFFLINE:
+    passthru->features     = ATA_SMART_AUTO_OFFLINE;
+    // Enable or disable?
+    passthru->sector_count = select;
+    break;
+  case AUTOSAVE:
+    passthru->features     = ATA_SMART_AUTOSAVE;
+    // Enable or disable?
+    passthru->sector_count = select;
+    break;
+  case IMMEDIATE_OFFLINE:
+    passthru->features    = ATA_SMART_IMMEDIATE_OFFLINE;
+    // What test type to run?
+    passthru->sector_num  = select;
+    break;
+  case STATUS_CHECK:
+    passthru->features = ATA_SMART_STATUS;
+    break;
+  case STATUS:
+    // This is JUST to see if SMART is enabled, by giving SMART status
+    // command. But it doesn't say if status was good, or failing.
+    // See below for the difference.
+    passthru->features = ATA_SMART_STATUS;
+    break;
+  default:
+    pout("Unrecognized command %d in linux_3ware_command_interface(disk %d)\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", command, disknum);
+    errno=ENOSYS;
+    return -1;
+  }
+
+  // Now send the command down through an ioctl()
+  if (escalade_type==CONTROLLER_3WARE_9000_CHAR)
+    ioctlreturn=ioctl(fd, TW_IOCTL_FIRMWARE_PASS_THROUGH, tw_ioctl_apache);
+  else if (escalade_type==CONTROLLER_3WARE_678K_CHAR)
+    ioctlreturn=ioctl(fd, TW_CMD_PACKET_WITH_DATA, tw_ioctl_char);
+  else
+    ioctlreturn=ioctl(fd, SCSI_IOCTL_SEND_COMMAND, tw_ioctl);
+  
+  // Deal with the different error cases
+  if (ioctlreturn) {
+    if (CONTROLLER_3WARE_678K==escalade_type && ((command==AUTO_OFFLINE || command==AUTOSAVE) && select)){
+      // error here is probably a kernel driver whose version is too old
+      printwarning(command);
+      errno=ENOTSUP;
+    }
+    if (!errno)
+      errno=EIO;
+    return -1;
+  }
+  
+  // The passthru structure is valid after return from an ioctl if:
+  // - we are using the character interface OR 
+  // - we are using the SCSI interface and this is a NON-READ-DATA command
+  // For SCSI interface, note that we set passthru to a different
+  // value after ioctl().
+  if (CONTROLLER_3WARE_678K==escalade_type) {
+    if (readdata)
+      passthru=NULL;
+    else
+      passthru=(TW_Passthru *)&(tw_output->output_data);
+  }
+
+  // See if the ATA command failed.  Now that we have returned from
+  // the ioctl() call, if passthru is valid, then:
+  // - passthru->status contains the 3ware controller STATUS
+  // - passthru->command contains the ATA STATUS register
+  // - passthru->features contains the ATA ERROR register
+  //
+  // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS
+  // If bit 0 (error bit) is set, then ATA ERROR register is valid.
+  // While we *might* decode the ATA ERROR register, at the moment it
+  // doesn't make much sense: we don't care in detail why the error
+  // happened.
+  
+  if (passthru && (passthru->status || (passthru->command & 0x21))) {
+    errno=EIO;
+    return -1;
+  }
+  
+  // If this is a read data command, copy data to output buffer
+  if (readdata) {
+    if (escalade_type==CONTROLLER_3WARE_9000_CHAR)
+      memcpy(data, (unsigned char *)tw_ioctl_apache->data_buffer, 512);
+    else if (escalade_type==CONTROLLER_3WARE_678K_CHAR)
+      memcpy(data, (unsigned char *)tw_ioctl_char->data_buffer, 512);
+    else
+      memcpy(data, tw_output->output_data, 512);
+  }
+
+  // For STATUS_CHECK, we need to check register values
+  if (command==STATUS_CHECK) {
+    
+    // To find out if the SMART RETURN STATUS is good or failing, we
+    // need to examine the values of the Cylinder Low and Cylinder
+    // High Registers.
+    
+    unsigned short cyl_lo=passthru->cylinder_lo;
+    unsigned short cyl_hi=passthru->cylinder_hi;
+    
+    // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good.
+    if (cyl_lo==0x4F && cyl_hi==0xC2)
+      return 0;
+    
+    // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL
+    if (cyl_lo==0xF4 && cyl_hi==0x2C)
+      return 1;
+    
+    // Any other values mean that something has gone wrong with the command
+    if (CONTROLLER_3WARE_678K==escalade_type) {
+      printwarning(command);
+      errno=ENOSYS;
+      return 0;
+    }
+    else {
+      errno=EIO;
+      return -1;
+    }
+  }
+  
+  // copy sector count register (one byte!) to return data
+  if (command==CHECK_POWER_MODE)
+    *data=*(char *)&(passthru->sector_count);
+  
+  // look for nonexistent devices/ports
+  if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) {
+    errno=ENODEV;
+    return -1;
+  }
+  
+  return 0;
+}
+
+
+
+int marvell_command_interface(int device, 
+                              smart_command_set command, 
+                              int select, 
+                              char *data) {  
+  typedef struct {  
+    int  inlen;
+    int  outlen;
+    char cmd[540];
+  } mvsata_scsi_cmd;
+  
+  int copydata = 0;
+  mvsata_scsi_cmd  smart_command;
+  unsigned char *buff = (unsigned char *)&smart_command.cmd[6];
+  // See struct hd_drive_cmd_hdr in hdreg.h
+  // buff[0]: ATA COMMAND CODE REGISTER
+  // buff[1]: ATA SECTOR NUMBER REGISTER
+  // buff[2]: ATA FEATURES REGISTER
+  // buff[3]: ATA SECTOR COUNT REGISTER
+  
+  // clear out buff.  Large enough for HDIO_DRIVE_CMD (4+512 bytes)
+  memset(&smart_command, 0, sizeof(smart_command));
+  smart_command.inlen = 540;
+  smart_command.outlen = 540;
+  smart_command.cmd[0] = 0xC;  //Vendor-specific code
+  smart_command.cmd[4] = 6;     //command length
+  
+  buff[0] = ATA_SMART_CMD;
+  switch (command){
+  case CHECK_POWER_MODE:
+    buff[0]=ATA_CHECK_POWER_MODE;
+    break;
+  case READ_VALUES:
+    buff[2]=ATA_SMART_READ_VALUES;
+    copydata=buff[3]=1;
+    break;
+  case READ_THRESHOLDS:
+    buff[2]=ATA_SMART_READ_THRESHOLDS;
+    copydata=buff[1]=buff[3]=1;
+    break;
+  case READ_LOG:
+    buff[2]=ATA_SMART_READ_LOG_SECTOR;
+    buff[1]=select;
+    copydata=buff[3]=1;
+    break;
+  case IDENTIFY:
+    buff[0]=ATA_IDENTIFY_DEVICE;
+    copydata=buff[3]=1;
+    break;
+  case PIDENTIFY:
+    buff[0]=ATA_IDENTIFY_PACKET_DEVICE;
+    copydata=buff[3]=1;
+    break;
+  case ENABLE:
+    buff[2]=ATA_SMART_ENABLE;
+    buff[1]=1;
+    break;
+  case DISABLE:
+    buff[2]=ATA_SMART_DISABLE;
+    buff[1]=1;
+    break;
+  case STATUS:
+  case STATUS_CHECK:
+    // this command only says if SMART is working.  It could be
+    // replaced with STATUS_CHECK below.
+    buff[2] = ATA_SMART_STATUS;
+    break;
+  case AUTO_OFFLINE:
+    buff[2]=ATA_SMART_AUTO_OFFLINE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case AUTOSAVE:
+    buff[2]=ATA_SMART_AUTOSAVE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case IMMEDIATE_OFFLINE:
+    buff[2]=ATA_SMART_IMMEDIATE_OFFLINE;
+    buff[1]=select;
+    break;
+  default:
+    pout("Unrecognized command %d in mvsata_os_specific_handler()\n", command);
+    exit(1);
+    break;
+  }  
+  // There are two different types of ioctls().  The HDIO_DRIVE_TASK
+  // one is this:
+  // We are now doing the HDIO_DRIVE_CMD type ioctl.
+  if (ioctl(device, SCSI_IOCTL_SEND_COMMAND, (void *)&smart_command))
+      return -1;
+
+  if (command==CHECK_POWER_MODE) {
+    // LEON -- CHECK THIS PLEASE.  THIS SHOULD BE THE SECTOR COUNT
+    // REGISTER, AND IT MIGHT BE buff[2] NOT buff[3].  Bruce
+    data[0]=buff[3];
+    return 0;
+  }
+
+  // Always succeed on a SMART status, as a disk that failed returned  
+  // buff[4]=0xF4, buff[5]=0x2C, i.e. "Bad SMART status" (see below).
+  if (command == STATUS)
+    return 0;
+  //Data returned is starting from 0 offset  
+  if (command == STATUS_CHECK)
+  {
+    // Cyl low and Cyl high unchanged means "Good SMART status"
+    if (buff[4] == 0x4F && buff[5] == 0xC2)
+      return 0;    
+    // These values mean "Bad SMART status"
+    if (buff[4] == 0xF4 && buff[5] == 0x2C)
+      return 1;    
+    // We haven't gotten output that makes sense; print out some debugging info
+    syserror("Error SMART Status command failed");
+    pout("Please get assistance from %s\n",PACKAGE_BUGREPORT);
+    pout("Register values returned from SMART Status command are:\n");
+    pout("CMD =0x%02x\n",(int)buff[0]);
+    pout("FR =0x%02x\n",(int)buff[1]);
+    pout("NS =0x%02x\n",(int)buff[2]);
+    pout("SC =0x%02x\n",(int)buff[3]);
+    pout("CL =0x%02x\n",(int)buff[4]);
+    pout("CH =0x%02x\n",(int)buff[5]);
+    pout("SEL=0x%02x\n",(int)buff[6]);
+    return -1;   
+  }  
+
+  if (copydata)
+    memcpy(data, buff, 512);
+  return 0; 
+}
+
+// this implementation is derived from ata_command_interface with a header
+// packing for highpoint linux driver ioctl interface
+// 
+// ioctl(fd,HPTIO_CTL,buff)
+//          ^^^^^^^^^
+//
+// structure of hpt_buff
+// +----+----+----+----+--------------------.....---------------------+
+// | 1  | 2  | 3  | 4  | 5                                            |
+// +----+----+----+----+--------------------.....---------------------+
+// 
+// 1: The target controller                     [ int    ( 4 Bytes ) ]
+// 2: The channel of the target controllee      [ int    ( 4 Bytes ) ]
+// 3: HDIO_ ioctl call                          [ int    ( 4 Bytes ) ]
+//    available from ${LINUX_KERNEL_SOURCE}/Documentation/ioctl/hdio
+// 4: the pmport that disk attached,            [ int    ( 4 Bytes ) ]
+//    if no pmport device, set to 1 or leave blank
+// 5: data                                      [ void * ( var leangth ) ]
+// 
+int highpoint_command_interface(int device, smart_command_set command,
+                                int select, char *data)
+{
+  unsigned char hpt_buff[4*sizeof(int) + STRANGE_BUFFER_LENGTH];
+  unsigned int *hpt = (unsigned int *)hpt_buff;
+  unsigned char *buff = &hpt_buff[4*sizeof(int)];
+  int copydata = 0;
+  const int HDIO_DRIVE_CMD_OFFSET = 4;
+
+  memset(hpt_buff, 0, 4*sizeof(int) + STRANGE_BUFFER_LENGTH);
+  hpt[0] = con->hpt_data[0]; // controller id
+  hpt[1] = con->hpt_data[1]; // channel number
+  hpt[3] = con->hpt_data[2]; // pmport number
+
+  buff[0]=ATA_SMART_CMD;
+  switch (command){
+  case CHECK_POWER_MODE:
+    buff[0]=ATA_CHECK_POWER_MODE;
+    copydata=1;
+    break;
+  case READ_VALUES:
+    buff[2]=ATA_SMART_READ_VALUES;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case READ_THRESHOLDS:
+    buff[2]=ATA_SMART_READ_THRESHOLDS;
+    buff[1]=buff[3]=1;
+    copydata=512;
+    break;
+  case READ_LOG:
+    buff[2]=ATA_SMART_READ_LOG_SECTOR;
+    buff[1]=select;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case WRITE_LOG:
+    break;
+  case IDENTIFY:
+    buff[0]=ATA_IDENTIFY_DEVICE;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case PIDENTIFY:
+    buff[0]=ATA_IDENTIFY_PACKET_DEVICE;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case ENABLE:
+    buff[2]=ATA_SMART_ENABLE;
+    buff[1]=1;
+    break;
+  case DISABLE:
+    buff[2]=ATA_SMART_DISABLE;
+    buff[1]=1;
+    break;
+  case STATUS:
+    buff[2]=ATA_SMART_STATUS;
+    break;
+  case AUTO_OFFLINE:
+    buff[2]=ATA_SMART_AUTO_OFFLINE;
+    buff[3]=select;
+    break;
+  case AUTOSAVE:
+    buff[2]=ATA_SMART_AUTOSAVE;
+    buff[3]=select;
+    break;
+  case IMMEDIATE_OFFLINE:
+    buff[2]=ATA_SMART_IMMEDIATE_OFFLINE;
+    buff[1]=select;
+    break;
+  case STATUS_CHECK:
+    buff[1]=ATA_SMART_STATUS;
+    break;
+  default:
+    pout("Unrecognized command %d in linux_highpoint_command_interface()\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", command);
+    errno=ENOSYS;
+    return -1;
+  }
+
+  if (command==WRITE_LOG) {
+    unsigned char task[4*sizeof(int)+sizeof(ide_task_request_t)+512];
+    unsigned int *hpt = (unsigned int *)task;
+    ide_task_request_t *reqtask = (ide_task_request_t *)(&task[4*sizeof(int)]);
+    task_struct_t *taskfile = (task_struct_t *)reqtask->io_ports;
+    int retval;
+
+    memset(task, 0, sizeof(task));
+
+    hpt[0] = con->hpt_data[0]; // controller id
+    hpt[1] = con->hpt_data[1]; // channel number
+    hpt[3] = con->hpt_data[2]; // pmport number
+    hpt[2] = HDIO_DRIVE_TASKFILE; // real hd ioctl
+
+    taskfile->data           = 0;
+    taskfile->feature        = ATA_SMART_WRITE_LOG_SECTOR;
+    taskfile->sector_count   = 1;
+    taskfile->sector_number  = select;
+    taskfile->low_cylinder   = 0x4f;
+    taskfile->high_cylinder  = 0xc2;
+    taskfile->device_head    = 0;
+    taskfile->command        = ATA_SMART_CMD;
+    
+    reqtask->data_phase      = TASKFILE_OUT;
+    reqtask->req_cmd         = IDE_DRIVE_TASK_OUT;
+    reqtask->out_size        = 512;
+    reqtask->in_size         = 0;
+    
+    memcpy(task+sizeof(ide_task_request_t)+4*sizeof(int), data, 512);
+
+    if ((retval=ioctl(device, HPTIO_CTL, task))) {
+      if (retval==-EINVAL)
+        pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n");
+      return -1;
+    }
+    return 0;
+  }
+    
+  if (command==STATUS_CHECK){
+    int retval;
+    unsigned const char normal_lo=0x4f, normal_hi=0xc2;
+    unsigned const char failed_lo=0xf4, failed_hi=0x2c;
+    buff[4]=normal_lo;
+    buff[5]=normal_hi;
+
+    hpt[2] = HDIO_DRIVE_TASK;
+
+    if ((retval=ioctl(device, HPTIO_CTL, hpt_buff))) {
+      if (retval==-EINVAL) {
+        pout("Error SMART Status command via HDIO_DRIVE_TASK failed");
+        pout("Rebuild older linux 2.2 kernels with HDIO_DRIVE_TASK support added\n");
+      }
+      else
+        syserror("Error SMART Status command failed");
+      return -1;
+    }
+    
+    if (buff[4]==normal_lo && buff[5]==normal_hi)
+      return 0;
+    
+    if (buff[4]==failed_lo && buff[5]==failed_hi)
+      return 1;
+    
+    syserror("Error SMART Status command failed");
+    pout("Please get assistance from " PACKAGE_HOMEPAGE "\n");
+    pout("Register values returned from SMART Status command are:\n");
+    pout("CMD=0x%02x\n",(int)buff[0]);
+    pout("FR =0x%02x\n",(int)buff[1]);
+    pout("NS =0x%02x\n",(int)buff[2]);
+    pout("SC =0x%02x\n",(int)buff[3]);
+    pout("CL =0x%02x\n",(int)buff[4]);
+    pout("CH =0x%02x\n",(int)buff[5]);
+    pout("SEL=0x%02x\n",(int)buff[6]);
+    return -1;   
+  }
+  
+#if 1
+  if (command==IDENTIFY || command==PIDENTIFY) {
+    unsigned char deviceid[4*sizeof(int)+512*sizeof(char)];
+    unsigned int *hpt = (unsigned int *)deviceid;
+
+    hpt[0] = con->hpt_data[0]; // controller id
+    hpt[1] = con->hpt_data[1]; // channel number
+    hpt[3] = con->hpt_data[2]; // pmport number
+
+    hpt[2] = HDIO_GET_IDENTITY;
+    if (!ioctl(device, HPTIO_CTL, deviceid) && (deviceid[4*sizeof(int)] & 0x8000))
+      buff[0]=(command==IDENTIFY)?ATA_IDENTIFY_PACKET_DEVICE:ATA_IDENTIFY_DEVICE;
+  }
+#endif
+  
+  hpt[2] = HDIO_DRIVE_CMD;
+  if ((ioctl(device, HPTIO_CTL, hpt_buff)))
+    return -1;
+
+  if (command==CHECK_POWER_MODE)
+    buff[HDIO_DRIVE_CMD_OFFSET]=buff[2];
+
+  if (copydata)
+    memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata);
+  
+  return 0; 
+}
+
+
+// Utility function for printing warnings
+void printwarning(smart_command_set command){
+  static int printed[4]={0,0,0,0};
+  const char* message=
+    "can not be passed through the 3ware 3w-xxxx driver.  This can be fixed by\n"
+    "applying a simple 3w-xxxx driver patch that can be found here:\n"
+    PACKAGE_HOMEPAGE "\n"
+    "Alternatively, upgrade your 3w-xxxx driver to version 1.02.00.037 or greater.\n\n";
+
+  if (command==AUTO_OFFLINE && !printed[0]) {
+    printed[0]=1;
+    pout("The SMART AUTO-OFFLINE ENABLE command (smartmontools -o on option/Directive)\n%s", message);
+  } 
+  else if (command==AUTOSAVE && !printed[1]) {
+    printed[1]=1;
+    pout("The SMART AUTOSAVE ENABLE command (smartmontools -S on option/Directive)\n%s", message);
+  }
+  else if (command==STATUS_CHECK && !printed[2]) {
+    printed[2]=1;
+    pout("The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n%s", message);
+  }
+  else if (command==WRITE_LOG && !printed[3])  {
+    printed[3]=1;
+    pout("The SMART WRITE LOG command (smartmontools -t selective) only supported via char /dev/tw[ae] interface\n");
+  }
+  
+  return;
+}
+
+// Guess device type (ata or scsi) based on device name (Linux
+// specific) SCSI device name in linux can be sd, sr, scd, st, nst,
+// osst, nosst and sg.
+static const char * lin_dev_prefix = "/dev/";
+static const char * lin_dev_ata_disk_plus = "h";
+static const char * lin_dev_ata_devfs_disk_plus = "ide/";
+static const char * lin_dev_scsi_devfs_disk_plus = "scsi/";
+static const char * lin_dev_scsi_disk_plus = "s";
+static const char * lin_dev_scsi_tape1 = "ns";
+static const char * lin_dev_scsi_tape2 = "os";
+static const char * lin_dev_scsi_tape3 = "nos";
+static const char * lin_dev_3ware_9000_char = "twa";
+static const char * lin_dev_3ware_678k_char = "twe";
+
+int guess_device_type(const char * dev_name) {
+  int len;
+  int dev_prefix_len = strlen(lin_dev_prefix);
+  
+  // if dev_name null, or string length zero
+  if (!dev_name || !(len = strlen(dev_name)))
+    return CONTROLLER_UNKNOWN;
+  
+  // Remove the leading /dev/... if it's there
+  if (!strncmp(lin_dev_prefix, dev_name, dev_prefix_len)) {
+    if (len <= dev_prefix_len)
+      // if nothing else in the string, unrecognized
+      return CONTROLLER_UNKNOWN;
+    // else advance pointer to following characters
+    dev_name += dev_prefix_len;
+  }
+  
+  // form /dev/h* or h*
+  if (!strncmp(lin_dev_ata_disk_plus, dev_name,
+               strlen(lin_dev_ata_disk_plus)))
+    return CONTROLLER_ATA;
+  
+  // form /dev/ide/* or ide/*
+  if (!strncmp(lin_dev_ata_devfs_disk_plus, dev_name,
+               strlen(lin_dev_ata_devfs_disk_plus)))
+    return CONTROLLER_ATA;
+
+  // form /dev/s* or s*
+  if (!strncmp(lin_dev_scsi_disk_plus, dev_name,
+               strlen(lin_dev_scsi_disk_plus)))
+    return CONTROLLER_SCSI;
+
+  // form /dev/scsi/* or scsi/*
+  if (!strncmp(lin_dev_scsi_devfs_disk_plus, dev_name,
+               strlen(lin_dev_scsi_devfs_disk_plus)))
+    return CONTROLLER_SCSI;
+  
+  // form /dev/ns* or ns*
+  if (!strncmp(lin_dev_scsi_tape1, dev_name,
+               strlen(lin_dev_scsi_tape1)))
+    return CONTROLLER_SCSI;
+  
+  // form /dev/os* or os*
+  if (!strncmp(lin_dev_scsi_tape2, dev_name,
+               strlen(lin_dev_scsi_tape2)))
+    return CONTROLLER_SCSI;
+  
+  // form /dev/nos* or nos*
+  if (!strncmp(lin_dev_scsi_tape3, dev_name,
+               strlen(lin_dev_scsi_tape3)))
+    return CONTROLLER_SCSI;
+
+  // form /dev/twa*
+  if (!strncmp(lin_dev_3ware_9000_char, dev_name,
+               strlen(lin_dev_3ware_9000_char)))
+    return CONTROLLER_3WARE_9000_CHAR;
+
+  // form /dev/twe*
+  if (!strncmp(lin_dev_3ware_678k_char, dev_name,
+               strlen(lin_dev_3ware_678k_char)))
+    return CONTROLLER_3WARE_678K_CHAR;
+
+  // we failed to recognize any of the forms
+  return CONTROLLER_UNKNOWN;
+}
+
+
+#if 0
+
+[ed@firestorm ed]$ ls -l  /dev/discs
+total 0
+lr-xr-xr-x    1 root     root           30 Dec 31  1969 disc0 -> ../ide/host2/bus0/target0/lun0/
+lr-xr-xr-x    1 root     root           30 Dec 31  1969 disc1 -> ../ide/host2/bus1/target0/lun0/
+[ed@firestorm ed]$ ls -l  dev/ide/host*/bus*/target*/lun*/disc
+ls: dev/ide/host*/bus*/target*/lun*/disc: No such file or directory
+[ed@firestorm ed]$ ls -l  /dev/ide/host*/bus*/target*/lun*/disc
+brw-------    1 root     root      33,   0 Dec 31  1969 /dev/ide/host2/bus0/target0/lun0/disc
+brw-------    1 root     root      34,   0 Dec 31  1969 /dev/ide/host2/bus1/target0/lun0/disc
+[ed@firestorm ed]$ ls -l  /dev/ide/c*b*t*u*
+ls: /dev/ide/c*b*t*u*: No such file or directory
+[ed@firestorm ed]$ 
+Script done on Fri Nov  7 13:46:28 2003
+
+#endif
index 71a41022df4e32538349f1fb22037bbfaca62cf7..7ef5cb16cb936b890b19eae1d9fc6ae576310584 100644 (file)
@@ -9,7 +9,7 @@
  *
  *  Written By: Adam Radford <linux@3ware.com>
  *  Modifications By: Joel Jacobson <linux@3ware.com>
- *                   Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *                    Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  *                    Brad Strand <linux@3ware.com>
  *
  *  Copyright (C) 1999-2003 3ware Inc.
@@ -38,7 +38,7 @@
 #ifndef OS_LINUX_H_
 #define OS_LINUX_H_
 
-#define OS_LINUX_H_CVSID "$Id: os_linux.h,v 1.24 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define OS_LINUX_H_CVSID "$Id: os_linux.h,v 1.25 2006/08/25 06:06:25 sxzzsf Exp $\n"
 
 /* 
    The following definitions/macros/prototypes are used for three
@@ -387,4 +387,6 @@ typedef struct ide_task_request_s {
 #define HDIO_DRIVE_TASKFILE       0x031d
 #define HDIO_GET_IDENTITY         0x030d
 
+#define HPTIO_CTL                       0x03ff // ioctl interface for HighPoint raid device
+
 #endif /* OS_LINUX_H_ */
diff --git a/os_netbsd.c b/os_netbsd.c
deleted file mode 100644 (file)
index 4f6cee0..0000000
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * os_netbsd.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2003-6 Sergey Svishchev <smartmontools-support@lists.sourceforge.net>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include "config.h"
-#include "int64.h"
-#include "atacmds.h"
-#include "scsicmds.h"
-#include "utility.h"
-#include "os_netbsd.h"
-#include <unistd.h>
-
-const char *os_XXXX_c_cvsid = "$Id: os_netbsd.c,v 1.15 2006/04/12 14:54:28 ballen4705 Exp $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_NETBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
-
-/* global variable holding byte count of allocated memory */
-extern long long bytes;
-
-enum warnings {
-  BAD_SMART, NO_3WARE, MAX_MSG
-};
-
-/* Utility function for printing warnings */
-void
-printwarning(int msgNo, const char *extra)
-{
-  static int printed[] = {0, 0};
-  static const char *message[] = {
-    "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
-    PACKAGE_STRING " does not currentlly support twe(4) devices (3ware Escalade)\n",
-  };
-
-  if (msgNo >= 0 && msgNo <= MAX_MSG) {
-    if (!printed[msgNo]) {
-      printed[msgNo] = 1;
-      pout("%s", message[msgNo]);
-      if (extra)
-       pout("%s", extra);
-    }
-  }
-  return;
-}
-
-static const char *net_dev_prefix = "/dev/";
-static const char *net_dev_ata_disk = "wd";
-static const char *net_dev_scsi_disk = "sd";
-static const char *net_dev_scsi_tape = "enrst";
-
-/* Guess device type(ata or scsi) based on device name */
-int
-guess_device_type(const char *dev_name)
-{
-  int len;
-  int dev_prefix_len = strlen(net_dev_prefix);
-
-  if (!dev_name || !(len = strlen(dev_name)))
-    return CONTROLLER_UNKNOWN;
-
-  if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) {
-    if (len <= dev_prefix_len)
-      return CONTROLLER_UNKNOWN;
-    else
-      dev_name += dev_prefix_len;
-  }
-  if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk)))
-    return CONTROLLER_ATA;
-
-  if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk)))
-    return CONTROLLER_SCSI;
-
-  if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape)))
-    return CONTROLLER_SCSI;
-
-  return CONTROLLER_UNKNOWN;
-}
-
-int
-get_dev_names(char ***names, const char *prefix)
-{
-  char *disknames, *p, **mp;
-  int n = 0;
-  int sysctl_mib[2];
-  size_t sysctl_len;
-
-  *names = NULL;
-
-  sysctl_mib[0] = CTL_HW;
-  sysctl_mib[1] = HW_DISKNAMES;
-  if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
-    pout("Failed to get value of sysctl `hw.disknames'\n");
-    return -1;
-  }
-  if (!(disknames = malloc(sysctl_len))) {
-    pout("Out of memory constructing scan device list\n");
-    return -1;
-  }
-  if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
-    pout("Failed to get value of sysctl `hw.disknames'\n");
-    return -1;
-  }
-  if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
-    pout("Out of memory constructing scan device list\n");
-    return -1;
-  }
-  for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) {
-    if (strncmp(p, prefix, strlen(prefix))) {
-      continue;
-    }
-    mp[n] = malloc(strlen(net_dev_prefix) + strlen(p) + 2);
-    if (!mp[n]) {
-      pout("Out of memory constructing scan device list\n");
-      return -1;
-    }
-    sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
-    bytes += strlen(mp[n]) + 1;
-    n++;
-  }
-
-  mp = realloc(mp, n * (sizeof(char *)));
-  bytes += (n) * (sizeof(char *));
-  *names = mp;
-  return n;
-}
-
-int
-make_device_names(char ***devlist, const char *name)
-{
-  if (!strcmp(name, "SCSI"))
-    return get_dev_names(devlist, net_dev_scsi_disk);
-  else if (!strcmp(name, "ATA"))
-    return get_dev_names(devlist, net_dev_ata_disk);
-  else
-    return 0;
-}
-
-int
-deviceopen(const char *pathname, char *type)
-{
-  if (!strcmp(type, "SCSI")) {
-    int fd = open(pathname, O_RDWR | O_NONBLOCK);
-    if (fd < 0 && errno == EROFS)
-      fd = open(pathname, O_RDONLY | O_NONBLOCK);
-    return fd;
-  } else if (!strcmp(type, "ATA"))
-    return open(pathname, O_RDWR | O_NONBLOCK);
-  else
-    return -1;
-}
-
-int
-deviceclose(int fd)
-{
-  return close(fd);
-}
-
-int
-marvell_command_interface(int fd, smart_command_set command, int select, char *data)
-{ return -1; }
-
-int
-ata_command_interface(int fd, smart_command_set command, int select, char *data)
-{
-  struct atareq req;
-  unsigned char inbuf[DEV_BSIZE];
-  int retval, copydata = 0;
-
-  memset(&req, 0, sizeof(req));
-  memset(&inbuf, 0, sizeof(inbuf));
-
-  switch (command) {
-  case READ_VALUES:
-    req.flags = ATACMD_READ;
-    req.features = WDSM_RD_DATA;
-    req.command = WDCC_SMART;
-    req.databuf = inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = WDSMART_CYL;
-    req.timeout = 1000;
-    copydata = 1;
-    break;
-  case READ_THRESHOLDS:
-    req.flags = ATACMD_READ;
-    req.features = WDSM_RD_THRESHOLDS;
-    req.command = WDCC_SMART;
-    req.databuf = inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = WDSMART_CYL;
-    req.timeout = 1000;
-    copydata = 1;
-    break;
-  case READ_LOG:
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_READ_LOG_SECTOR;  /* XXX missing from wdcreg.h */
-    req.command = WDCC_SMART;
-    req.databuf = inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = WDSMART_CYL;
-    req.sec_num = select;
-    req.sec_count = 1;
-    req.timeout = 1000;
-    copydata = 1;
-    break;
-  case WRITE_LOG:
-    memcpy(inbuf, data, 512);
-    req.flags = ATACMD_WRITE;
-    req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */
-    req.command = WDCC_SMART;
-    req.databuf = inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = WDSMART_CYL;
-    req.sec_num = select;
-    req.sec_count = 1;
-    req.timeout = 1000;
-    break;
-  case IDENTIFY:
-    req.flags = ATACMD_READ;
-    req.command = WDCC_IDENTIFY;
-    req.databuf = (caddr_t) inbuf;
-    req.datalen = sizeof(inbuf);
-    req.timeout = 1000;
-    copydata = 1;
-    break;
-  case PIDENTIFY:
-    req.flags = ATACMD_READ;
-    req.command = ATAPI_IDENTIFY_DEVICE;
-    req.databuf = (caddr_t) inbuf;
-    req.datalen = sizeof(inbuf);
-    req.timeout = 1000;
-    copydata = 1;
-    break;
-  case ENABLE:
-    req.flags = ATACMD_READ;
-    req.features = WDSM_ENABLE_OPS;
-    req.command = WDCC_SMART;
-    req.cylinder = WDSMART_CYL;
-    req.timeout = 1000;
-    break;
-  case DISABLE:
-    req.flags = ATACMD_READ;
-    req.features = WDSM_DISABLE_OPS;
-    req.command = WDCC_SMART;
-    req.cylinder = WDSMART_CYL;
-    req.timeout = 1000;
-    break;
-  case AUTO_OFFLINE:
-    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_AUTO_OFFLINE;     /* XXX missing from wdcreg.h */
-    req.command = WDCC_SMART;
-    req.databuf = inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = WDSMART_CYL;
-    req.sec_num = select;
-    req.sec_count = 1;
-    req.timeout = 1000;
-    break;
-  case AUTOSAVE:
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */
-    req.command = WDCC_SMART;
-    req.cylinder = WDSMART_CYL;
-    req.sec_count = 0xf1;
-    /* to enable autosave */
-    req.timeout = 1000;
-    break;
-  case IMMEDIATE_OFFLINE:
-    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_IMMEDIATE_OFFLINE;        /* XXX missing from wdcreg.h */
-    req.command = WDCC_SMART;
-    req.databuf = inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = WDSMART_CYL;
-    req.sec_num = select;
-    req.sec_count = 1;
-    req.timeout = 1000;
-    break;
-  case STATUS_CHECK:
-    /* same command, no HDIO in NetBSD */
-  case STATUS:
-    req.flags = ATACMD_READ;
-    req.features = WDSM_STATUS;
-    req.command = WDCC_SMART;
-    req.cylinder = WDSMART_CYL;
-    req.timeout = 1000;
-    break;
-  case CHECK_POWER_MODE:
-    req.flags = ATACMD_READREG;
-    req.command = WDCC_CHECK_PWR;
-    req.timeout = 1000;
-    break;
-  default:
-    pout("Unrecognized command %d in ata_command_interface()\n", command);
-    errno = ENOSYS;
-    return -1;
-  }
-
-  if (command == STATUS_CHECK) {
-    char buf[512];
-
-    unsigned const short normal = WDSMART_CYL, failed = 0x2cf4;
-
-    if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
-      perror("Failed command");
-      return -1;
-    }
-    /* Cyl low and Cyl high unchanged means "Good SMART status" */
-    if (req.cylinder == normal)
-      return 0;
-
-    /* These values mean "Bad SMART status" */
-    if (req.cylinder == failed)
-      return 1;
-
-    /* We haven't gotten output that makes sense; 
-     * print out some debugging info */
-    snprintf(buf, sizeof(buf),
-      "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
-      (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num,
-      (int) (le16toh(req.cylinder) & 0xff), (int) ((le16toh(req.cylinder) >> 8) & 0xff),
-      (int) req.error);
-    printwarning(BAD_SMART, buf);
-    return 0;
-  }
-  if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
-    perror("Failed command");
-    return -1;
-  }
-  if (command == CHECK_POWER_MODE)
-    data[0] = req.sec_count;
-
-  if (copydata)
-    memcpy(data, inbuf, 512);
-
-  return 0;
-}
-
-int
-escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data)
-{
-  printwarning(NO_3WARE, NULL);
-  return -1;
-}
-
-int
-do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
-{
-  struct scsireq sc;
-
-  if (report > 0) {
-    size_t k;
-
-    const unsigned char *ucp = iop->cmnd;
-    const char *np;
-
-    np = scsi_get_opcode_name(ucp[0]);
-    pout(" [%s: ", np ? np : "<unknown opcode>");
-    for (k = 0; k < iop->cmnd_len; ++k)
-      pout("%02x ", ucp[k]);
-    if ((report > 1) &&
-      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
-      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-
-      pout("]\n  Outgoing data, len=%d%s:\n", (int) iop->dxfer_len,
-       (trunc ? " [only first 256 bytes shown]" : ""));
-      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
-    } else
-      pout("]");
-  }
-  memset(&sc, 0, sizeof(sc));
-  memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
-  sc.cmdlen = iop->cmnd_len;
-  sc.databuf = iop->dxferp;
-  sc.datalen = iop->dxfer_len;
-  sc.senselen = iop->max_sense_len;
-  sc.timeout = iop->timeout == 0 ? 60000 : (1000 * iop->timeout);
-  sc.flags =
-    (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ :       /* XXX */
-    (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
-
-  if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
-    warn("error sending SCSI ccb");
-    return -1;
-  }
-  iop->resid = sc.datalen - sc.datalen_used;
-  iop->scsi_status = sc.status;
-  if (iop->sensep) {
-    memcpy(iop->sensep, sc.sense, sc.senselen_used);
-    iop->resp_sense_len = sc.senselen_used;
-  }
-  if (report > 0) {
-    int trunc;
-
-    pout("  status=0\n");
-    trunc = (iop->dxfer_len > 256) ? 1 : 0;
-
-    pout("  Incoming data, len=%d%s:\n", (int) iop->dxfer_len,
-      (trunc ? " [only first 256 bytes shown]" : ""));
-    dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
-  }
-  return 0;
-}
-
-/* print examples for smartctl */
-void 
-print_smartctl_examples()
-{
-  char p;
-
-  p = 'a' + getrawpartition();
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
-    "  smartctl -a /dev/wd0%c                      (Prints all SMART information)\n\n"
-    "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
-    "                                              (Enables SMART on first disk)\n\n"
-    "  smartctl -t long /dev/wd0%c             (Executes extended disk self-test)\n\n"
-    "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
-    "                                      (Prints Self-Test & Attribute errors)\n",
-    p, p, p, p
-    );
-#else
-  printf(
-    "  smartctl -a /dev/wd0%c                     (Prints all SMART information)\n"
-    "  smartctl -s on -o on -S on /dev/wd0%c        (Enables SMART on first disk)\n"
-    "  smartctl -t long /dev/wd0%c            (Executes extended disk self-test)\n"
-    "  smartctl -A -l selftest -q errorsonly /dev/wd0%c"
-    "                                      (Prints Self-Test & Attribute errors)\n",
-    p, p, p, p
-    );
-#endif
-  return;
-}
diff --git a/os_netbsd.cpp b/os_netbsd.cpp
new file mode 100644 (file)
index 0000000..8e179b3
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * os_netbsd.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Sergey Svishchev <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+#include "os_netbsd.h"
+#include <unistd.h>
+
+const char *os_XXXX_c_cvsid = "$Id: os_netbsd.cpp,v 1.19 2006/09/20 16:17:31 shattered Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_NETBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+/* global variable holding byte count of allocated memory */
+extern long long bytes;
+
+enum warnings {
+  BAD_SMART, NO_3WARE, MAX_MSG
+};
+
+/* Utility function for printing warnings */
+void
+printwarning(int msgNo, const char *extra)
+{
+  static int printed[] = {0, 0};
+  static const char *message[] = {
+    "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
+    PACKAGE_STRING " does not currentlly support twe(4) devices (3ware Escalade)\n",
+  };
+
+  if (msgNo >= 0 && msgNo <= MAX_MSG) {
+    if (!printed[msgNo]) {
+      printed[msgNo] = 1;
+      pout("%s", message[msgNo]);
+      if (extra)
+       pout("%s", extra);
+    }
+  }
+  return;
+}
+
+static const char *net_dev_prefix = "/dev/";
+static const char *net_dev_ata_disk = "wd";
+static const char *net_dev_scsi_disk = "sd";
+static const char *net_dev_scsi_tape = "enrst";
+
+/* Guess device type(ata or scsi) based on device name */
+int
+guess_device_type(const char *dev_name)
+{
+  int len;
+  int dev_prefix_len = strlen(net_dev_prefix);
+
+  if (!dev_name || !(len = strlen(dev_name)))
+    return CONTROLLER_UNKNOWN;
+
+  if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) {
+    if (len <= dev_prefix_len)
+      return CONTROLLER_UNKNOWN;
+    else
+      dev_name += dev_prefix_len;
+  }
+  if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk)))
+    return CONTROLLER_ATA;
+
+  if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk)))
+    return CONTROLLER_SCSI;
+
+  if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape)))
+    return CONTROLLER_SCSI;
+
+  return CONTROLLER_UNKNOWN;
+}
+
+int
+get_dev_names(char ***names, const char *prefix)
+{
+  char *disknames, *p, **mp;
+  int n = 0;
+  int sysctl_mib[2];
+  size_t sysctl_len;
+
+  *names = NULL;
+
+  sysctl_mib[0] = CTL_HW;
+  sysctl_mib[1] = HW_DISKNAMES;
+  if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
+    pout("Failed to get value of sysctl `hw.disknames'\n");
+    return -1;
+  }
+  if (!(disknames = (char *)malloc(sysctl_len))) {
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+  if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
+    pout("Failed to get value of sysctl `hw.disknames'\n");
+    return -1;
+  }
+  if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+  for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) {
+    if (strncmp(p, prefix, strlen(prefix))) {
+      continue;
+    }
+    mp[n] = (char *)malloc(strlen(net_dev_prefix) + strlen(p) + 2);
+    if (!mp[n]) {
+      pout("Out of memory constructing scan device list\n");
+      return -1;
+    }
+    sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
+    bytes += strlen(mp[n]) + 1;
+    n++;
+  }
+
+  mp = (char **)realloc(mp, n * (sizeof(char *)));
+  bytes += (n) * (sizeof(char *));
+  *names = mp;
+  return n;
+}
+
+int
+make_device_names(char ***devlist, const char *name)
+{
+  if (!strcmp(name, "SCSI"))
+    return get_dev_names(devlist, net_dev_scsi_disk);
+  else if (!strcmp(name, "ATA"))
+    return get_dev_names(devlist, net_dev_ata_disk);
+  else
+    return 0;
+}
+
+int
+deviceopen(const char *pathname, char *type)
+{
+  if (!strcmp(type, "SCSI")) {
+    int fd = open(pathname, O_RDWR | O_NONBLOCK);
+    if (fd < 0 && errno == EROFS)
+      fd = open(pathname, O_RDONLY | O_NONBLOCK);
+    return fd;
+  } else if (!strcmp(type, "ATA"))
+    return open(pathname, O_RDWR | O_NONBLOCK);
+  else
+    return -1;
+}
+
+int
+deviceclose(int fd)
+{
+  return close(fd);
+}
+
+int
+marvell_command_interface(int fd, smart_command_set command, int select, char *data)
+{ return -1; }
+
+int highpoint_command_interface(int fd, smart_command_set command, int select, char *data)
+{
+  return -1;
+}
+
+int
+ata_command_interface(int fd, smart_command_set command, int select, char *data)
+{
+  struct atareq req;
+  unsigned char inbuf[DEV_BSIZE];
+  int retval, copydata = 0;
+
+  memset(&req, 0, sizeof(req));
+  memset(&inbuf, 0, sizeof(inbuf));
+
+  switch (command) {
+  case READ_VALUES:
+    req.flags = ATACMD_READ;
+    req.features = WDSM_RD_DATA;
+    req.command = WDCC_SMART;
+    req.databuf = (char *)inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case READ_THRESHOLDS:
+    req.flags = ATACMD_READ;
+    req.features = WDSM_RD_THRESHOLDS;
+    req.command = WDCC_SMART;
+    req.databuf = (char *)inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case READ_LOG:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_READ_LOG_SECTOR;  /* XXX missing from wdcreg.h */
+    req.command = WDCC_SMART;
+    req.databuf = (char *)inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case WRITE_LOG:
+    memcpy(inbuf, data, 512);
+    req.flags = ATACMD_WRITE;
+    req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */
+    req.command = WDCC_SMART;
+    req.databuf = (char *)inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case IDENTIFY:
+    req.flags = ATACMD_READ;
+    req.command = WDCC_IDENTIFY;
+    req.databuf = (char *)inbuf;
+    req.datalen = sizeof(inbuf);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case PIDENTIFY:
+    req.flags = ATACMD_READ;
+    req.command = ATAPI_IDENTIFY_DEVICE;
+    req.databuf = (char *)inbuf;
+    req.datalen = sizeof(inbuf);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case ENABLE:
+    req.flags = ATACMD_READ;
+    req.features = WDSM_ENABLE_OPS;
+    req.command = WDCC_SMART;
+    req.cylinder = WDSMART_CYL;
+    req.timeout = 1000;
+    break;
+  case DISABLE:
+    req.flags = ATACMD_READ;
+    req.features = WDSM_DISABLE_OPS;
+    req.command = WDCC_SMART;
+    req.cylinder = WDSMART_CYL;
+    req.timeout = 1000;
+    break;
+  case AUTO_OFFLINE:
+    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_AUTO_OFFLINE;     /* XXX missing from wdcreg.h */
+    req.command = WDCC_SMART;
+    req.databuf = (char *)inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case AUTOSAVE:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */
+    req.command = WDCC_SMART;
+    req.cylinder = WDSMART_CYL;
+    req.sec_count = 0xf1;
+    /* to enable autosave */
+    req.timeout = 1000;
+    break;
+  case IMMEDIATE_OFFLINE:
+    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_IMMEDIATE_OFFLINE;        /* XXX missing from wdcreg.h */
+    req.command = WDCC_SMART;
+    req.databuf = (char *)inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case STATUS_CHECK:
+    /* same command, no HDIO in NetBSD */
+  case STATUS:
+    req.flags = ATACMD_READ;
+    req.features = WDSM_STATUS;
+    req.command = WDCC_SMART;
+    req.cylinder = WDSMART_CYL;
+    req.timeout = 1000;
+    break;
+  case CHECK_POWER_MODE:
+    req.flags = ATACMD_READREG;
+    req.command = WDCC_CHECK_PWR;
+    req.timeout = 1000;
+    break;
+  default:
+    pout("Unrecognized command %d in ata_command_interface()\n", command);
+    errno = ENOSYS;
+    return -1;
+  }
+
+  if (command == STATUS_CHECK) {
+    char buf[512];
+
+    unsigned const short normal = WDSMART_CYL, failed = 0x2cf4;
+
+    if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
+      perror("Failed command");
+      return -1;
+    }
+    /* Cyl low and Cyl high unchanged means "Good SMART status" */
+    if (req.cylinder == normal)
+      return 0;
+
+    /* These values mean "Bad SMART status" */
+    if (req.cylinder == failed)
+      return 1;
+
+    /* We haven't gotten output that makes sense; 
+     * print out some debugging info */
+    snprintf(buf, sizeof(buf),
+      "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
+      (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num,
+      (int) (le16toh(req.cylinder) & 0xff), (int) ((le16toh(req.cylinder) >> 8) & 0xff),
+      (int) req.error);
+    printwarning(BAD_SMART, buf);
+    return 0;
+  }
+  if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
+    perror("Failed command");
+    return -1;
+  }
+  if (command == CHECK_POWER_MODE)
+    data[0] = req.sec_count;
+
+  if (copydata)
+    memcpy(data, inbuf, 512);
+
+  return 0;
+}
+
+int
+escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data)
+{
+  printwarning(NO_3WARE, NULL);
+  return -1;
+}
+
+int
+do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
+{
+  struct scsireq sc;
+
+  if (report > 0) {
+    size_t k;
+
+    const unsigned char *ucp = iop->cmnd;
+    const char *np;
+
+    np = scsi_get_opcode_name(ucp[0]);
+    pout(" [%s: ", np ? np : "<unknown opcode>");
+    for (k = 0; k < iop->cmnd_len; ++k)
+      pout("%02x ", ucp[k]);
+    if ((report > 1) &&
+      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+      pout("]\n  Outgoing data, len=%d%s:\n", (int) iop->dxfer_len,
+       (trunc ? " [only first 256 bytes shown]" : ""));
+      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
+    } else
+      pout("]");
+  }
+  memset(&sc, 0, sizeof(sc));
+  memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
+  sc.cmdlen = iop->cmnd_len;
+  sc.databuf = (char *)iop->dxferp;
+  sc.datalen = iop->dxfer_len;
+  sc.senselen = iop->max_sense_len;
+  sc.timeout = iop->timeout == 0 ? 60000 : (1000 * iop->timeout);
+  sc.flags =
+    (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ :
+    (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
+
+  if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
+    warn("error sending SCSI ccb");
+    return -1;
+  }
+  iop->resid = sc.datalen - sc.datalen_used;
+  iop->scsi_status = sc.status;
+  if (iop->sensep) {
+    memcpy(iop->sensep, sc.sense, sc.senselen_used);
+    iop->resp_sense_len = sc.senselen_used;
+  }
+  if (report > 0) {
+    int trunc;
+
+    pout("  status=0\n");
+    trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+    pout("  Incoming data, len=%d%s:\n", (int) iop->dxfer_len,
+      (trunc ? " [only first 256 bytes shown]" : ""));
+    dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
+  }
+  return 0;
+}
+
+/* print examples for smartctl */
+void 
+print_smartctl_examples()
+{
+  char p;
+
+  p = 'a' + getrawpartition();
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+    "  smartctl -a /dev/wd0%c                      (Prints all SMART information)\n\n"
+    "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
+    "                                              (Enables SMART on first disk)\n\n"
+    "  smartctl -t long /dev/wd0%c             (Executes extended disk self-test)\n\n"
+    "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
+    "                                      (Prints Self-Test & Attribute errors)\n",
+    p, p, p, p
+    );
+#else
+  printf(
+    "  smartctl -a /dev/wd0%c                     (Prints all SMART information)\n"
+    "  smartctl -s on -o on -S on /dev/wd0%c        (Enables SMART on first disk)\n"
+    "  smartctl -t long /dev/wd0%c            (Executes extended disk self-test)\n"
+    "  smartctl -A -l selftest -q errorsonly /dev/wd0%c"
+    "                                      (Prints Self-Test & Attribute errors)\n",
+    p, p, p, p
+    );
+#endif
+  return;
+}
diff --git a/os_openbsd.c b/os_openbsd.c
deleted file mode 100644 (file)
index 33a7039..0000000
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- * os_openbsd.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2004-6 David Snyder <smartmontools-support@lists.sourceforge.net>
- *
- * Derived from os_netbsd.c by Sergey Svishchev <smartmontools-support@lists.sourceforge.net>, Copyright (C) 2003-6 
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include "config.h"
-#include "int64.h"
-#include "atacmds.h"
-#include "scsicmds.h"
-#include "utility.h"
-#include "os_openbsd.h"
-
-const char *os_XXXX_c_cvsid = "$Id: os_openbsd.c,v 1.10 2006/04/12 14:54:28 ballen4705 Exp $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_OPENBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
-
-/* global variable holding byte count of allocated memory */
-extern long long bytes;
-
-enum warnings {
-  BAD_SMART, NO_3WARE, MAX_MSG
-};
-
-/* Utility function for printing warnings */
-void
-printwarning(int msgNo, const char *extra)
-{
-  static int printed[] = {0, 0};
-  static const char *message[] = {
-    "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
-    PACKAGE_STRING " does not currentlly support twe(4) devices (3ware Escalade)\n",
-  };
-
-  if (msgNo >= 0 && msgNo <= MAX_MSG) {
-    if (!printed[msgNo]) {
-      printed[msgNo] = 1;
-      pout("%s", message[msgNo]);
-      if (extra)
-       pout("%s", extra);
-    }
-  }
-  return;
-}
-
-static const char *net_dev_prefix = "/dev/";
-static const char *net_dev_ata_disk = "wd";
-static const char *net_dev_scsi_disk = "sd";
-static const char *net_dev_scsi_tape = "st";
-
-/* Guess device type(ata or scsi) based on device name */
-int
-guess_device_type(const char *dev_name)
-{
-  int len;
-  int dev_prefix_len = strlen(net_dev_prefix);
-
-  if (!dev_name || !(len = strlen(dev_name)))
-    return CONTROLLER_UNKNOWN;
-
-  if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) {
-    if (len <= dev_prefix_len)
-      return CONTROLLER_UNKNOWN;
-    else
-      dev_name += dev_prefix_len;
-  }
-  if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk)))
-    return CONTROLLER_ATA;
-
-  if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk)))
-    return CONTROLLER_SCSI;
-
-  if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape)))
-    return CONTROLLER_SCSI;
-
-  return CONTROLLER_UNKNOWN;
-}
-
-int
-get_dev_names(char ***names, const char *prefix)
-{
-  char *disknames, *p, **mp;
-  int n = 0;
-  int sysctl_mib[2];
-  size_t sysctl_len;
-
-  *names = NULL;
-
-  sysctl_mib[0] = CTL_HW;
-  sysctl_mib[1] = HW_DISKNAMES;
-  if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
-    pout("Failed to get value of sysctl `hw.disknames'\n");
-    return -1;
-  }
-  if (!(disknames = malloc(sysctl_len))) {
-    pout("Out of memory constructing scan device list\n");
-    return -1;
-  }
-  if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
-    pout("Failed to get value of sysctl `hw.disknames'\n");
-    return -1;
-  }
-  if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
-    pout("Out of memory constructing scan device list\n");
-    return -1;
-  }
-  for (p = strtok(disknames, ","); p; p = strtok(NULL, ",")) {
-    if (strncmp(p, prefix, strlen(prefix))) {
-      continue;
-    }
-    mp[n] = malloc(strlen(net_dev_prefix) + strlen(p) + 2);
-    if (!mp[n]) {
-      pout("Out of memory constructing scan device list\n");
-      return -1;
-    }
-    sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
-    bytes += strlen(mp[n]) + 1;
-    n++;
-  }
-
-  mp = realloc(mp, n * (sizeof(char *)));
-  bytes += (n) * (sizeof(char *));
-  *names = mp;
-  return n;
-}
-
-int
-make_device_names(char ***devlist, const char *name)
-{
-  if (!strcmp(name, "SCSI"))
-    return get_dev_names(devlist, net_dev_scsi_disk);
-  else if (!strcmp(name, "ATA"))
-    return get_dev_names(devlist, net_dev_ata_disk);
-  else
-    return 0;
-}
-
-int
-deviceopen(const char *pathname, char *type)
-{
-  if (!strcmp(type, "SCSI")) {
-    int fd = open(pathname, O_RDWR | O_NONBLOCK);
-    if (fd < 0 && errno == EROFS)
-      fd = open(pathname, O_RDONLY | O_NONBLOCK);
-    return fd;
-  } else if (!strcmp(type, "ATA"))
-    return open(pathname, O_RDWR | O_NONBLOCK);
-  else
-    return -1;
-}
-
-int
-deviceclose(int fd)
-{
-  return close(fd);
-}
-
-int
-marvell_command_interface(int fd, smart_command_set command, int select, char *data)
-{ return -1; }
-
-int
-ata_command_interface(int fd, smart_command_set command, int select, char *data)
-{
-  struct atareq req;
-  unsigned char inbuf[DEV_BSIZE];
-  int retval, copydata = 0;
-
-  memset(&req, 0, sizeof(req));
-  memset(&inbuf, 0, sizeof(inbuf));
-
-  switch (command) {
-  case READ_VALUES:
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_READ_VALUES;
-    req.command = ATAPI_SMART;
-    req.databuf = (caddr_t) inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = htole16(WDSMART_CYL);
-    req.timeout = 1000;
-    copydata = 1;
-    break;
-  case READ_THRESHOLDS:
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_READ_THRESHOLDS;
-    req.command = ATAPI_SMART;
-    req.databuf = (caddr_t) inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = htole16(WDSMART_CYL);
-    req.timeout = 1000;
-    copydata = 1;
-    break;
-  case READ_LOG:
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_READ_LOG_SECTOR;  /* XXX missing from wdcreg.h */
-    req.command = ATAPI_SMART;
-    req.databuf = (caddr_t) inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = htole16(WDSMART_CYL);
-    req.sec_num = select;
-    req.sec_count = 1;
-    req.timeout = 1000;
-    copydata = 1;
-    break;
-  case WRITE_LOG:
-    memcpy(inbuf, data, 512);
-    req.flags = ATACMD_WRITE;
-    req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */
-    req.command = ATAPI_SMART;
-    req.databuf = (caddr_t) inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = htole16(WDSMART_CYL);
-    req.sec_num = select;
-    req.sec_count = 1;
-    req.timeout = 1000;
-    break;
-  case IDENTIFY:
-    req.flags = ATACMD_READ;
-    req.command = WDCC_IDENTIFY;
-    req.databuf = (caddr_t) inbuf;
-    req.datalen = sizeof(inbuf);
-    req.timeout = 1000;
-    copydata = 1;
-    break;
-  case PIDENTIFY:
-    req.flags = ATACMD_READ;
-    req.command = ATAPI_IDENTIFY_DEVICE;
-    req.databuf = (caddr_t) inbuf;
-    req.datalen = sizeof(inbuf);
-    req.timeout = 1000;
-    copydata = 1;
-    break;
-  case ENABLE:
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_ENABLE;
-    req.command = ATAPI_SMART;
-    req.cylinder = htole16(WDSMART_CYL);
-    req.timeout = 1000;
-    break;
-  case DISABLE:
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_DISABLE;
-    req.command = ATAPI_SMART;
-    req.cylinder = htole16(WDSMART_CYL);
-    req.timeout = 1000;
-    break;
-  case AUTO_OFFLINE:
-    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_AUTO_OFFLINE;     /* XXX missing from wdcreg.h */
-    req.command = ATAPI_SMART;
-    req.databuf = (caddr_t) inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = htole16(WDSMART_CYL);
-    req.sec_num = select;
-    req.sec_count = 1;
-    req.timeout = 1000;
-    break;
-  case AUTOSAVE:
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */
-    req.command = ATAPI_SMART;
-    req.cylinder = htole16(WDSMART_CYL);
-    req.sec_count = 0xf1;
-    /* to enable autosave */
-    req.timeout = 1000;
-    break;
-  case IMMEDIATE_OFFLINE:
-    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_IMMEDIATE_OFFLINE;        /* XXX missing from wdcreg.h */
-    req.command = ATAPI_SMART;
-    req.databuf = (caddr_t) inbuf;
-    req.datalen = sizeof(inbuf);
-    req.cylinder = htole16(WDSMART_CYL);
-    req.sec_num = select;
-    req.sec_count = 1;
-    req.timeout = 1000;
-    break;
-  case STATUS_CHECK:
-    /* same command, no HDIO in NetBSD */
-  case STATUS:
-    req.flags = ATACMD_READ;
-    req.features = ATA_SMART_STATUS;
-    req.command = ATAPI_SMART;
-    req.cylinder = htole16(WDSMART_CYL);
-    req.timeout = 1000;
-    break;
-  case CHECK_POWER_MODE:
-    req.flags = ATACMD_READREG;
-    req.command = WDCC_CHECK_PWR;
-    req.timeout = 1000;
-    break;
-  default:
-    pout("Unrecognized command %d in ata_command_interface()\n", command);
-    errno = ENOSYS;
-    return -1;
-  }
-
-  if (command == STATUS_CHECK) {
-    char buf[512];
-
-    unsigned const short normal = WDSMART_CYL, failed = 0x2cf4;
-
-    if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
-      perror("Failed command");
-      return -1;
-    }
-    /* Cyl low and Cyl high unchanged means "Good SMART status" */
-    if (letoh16(req.cylinder) == normal)
-      return 0;
-
-    /* These values mean "Bad SMART status" */
-    if (letoh16(req.cylinder) == failed)
-      return 1;
-
-    /* We haven't gotten output that makes sense; 
-     * print out some debugging info */
-    snprintf(buf, sizeof(buf),
-      "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
-      (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num,
-      (int) (letoh16(req.cylinder) & 0xff), (int) ((letoh16(req.cylinder) >> 8) & 0xff),
-      (int) req.error);
-    printwarning(BAD_SMART, buf);
-    return 0;
-  }
-  if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
-    perror("Failed command");
-    return -1;
-  }
-  if (command == CHECK_POWER_MODE)
-    data[0] = req.sec_count;
-
-  if (copydata)
-    memcpy(data, inbuf, 512);
-
-  return 0;
-}
-
-int
-escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data)
-{
-  printwarning(NO_3WARE, NULL);
-  return -1;
-}
-
-int
-do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
-{
-  struct scsireq sc;
-
-  if (report > 0) {
-    size_t k;
-
-    const unsigned char *ucp = iop->cmnd;
-    const char *np;
-
-    np = scsi_get_opcode_name(ucp[0]);
-    pout(" [%s: ", np ? np : "<unknown opcode>");
-    for (k = 0; k < iop->cmnd_len; ++k)
-      pout("%02x ", ucp[k]);
-    if ((report > 1) &&
-      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
-      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-
-      pout("]\n  Outgoing data, len=%d%s:\n", (int) iop->dxfer_len,
-       (trunc ? " [only first 256 bytes shown]" : ""));
-      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
-    } else
-      pout("]");
-  }
-  memset(&sc, 0, sizeof(sc));
-  memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
-  sc.cmdlen = iop->cmnd_len;
-  sc.databuf = iop->dxferp;
-  sc.datalen = iop->dxfer_len;
-  sc.senselen = iop->max_sense_len;
-  sc.timeout = iop->timeout == 0 ? 60000 : iop->timeout;       /* XXX */
-  sc.flags =
-    (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ :       /* XXX */
-    (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
-
-  if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
-    warn("error sending SCSI ccb");
-    return -1;
-  }
-  iop->resid = sc.datalen - sc.datalen_used;
-  iop->scsi_status = sc.status;
-  if (iop->sensep) {
-    memcpy(iop->sensep, sc.sense, sc.senselen_used);
-    iop->resp_sense_len = sc.senselen_used;
-  }
-  if (report > 0) {
-    int trunc;
-
-    pout("  status=0\n");
-    trunc = (iop->dxfer_len > 256) ? 1 : 0;
-
-    pout("  Incoming data, len=%d%s:\n", (int) iop->dxfer_len,
-      (trunc ? " [only first 256 bytes shown]" : ""));
-    dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
-  }
-  return 0;
-}
-
-/* print examples for smartctl */
-void 
-print_smartctl_examples()
-{
-  char p;
-
-  p = 'a' + getrawpartition();
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
-    "  smartctl -a /dev/wd0%c                      (Prints all SMART information)\n\n"
-    "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
-    "                                              (Enables SMART on first disk)\n\n"
-    "  smartctl -t long /dev/wd0%c             (Executes extended disk self-test)\n\n"
-    "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
-    "                                      (Prints Self-Test & Attribute errors)\n",
-    p, p, p, p
-    );
-#else
-  printf(
-    "  smartctl -a /dev/wd0%c                     (Prints all SMART information)\n"
-    "  smartctl -s on -o on -S on /dev/wd0%c        (Enables SMART on first disk)\n"
-    "  smartctl -t long /dev/wd0%c            (Executes extended disk self-test)\n"
-    "  smartctl -A -l selftest -q errorsonly /dev/wd0%c"
-    "                                      (Prints Self-Test & Attribute errors)\n",
-    p, p, p, p
-    );
-#endif
-  return;
-}
diff --git a/os_openbsd.cpp b/os_openbsd.cpp
new file mode 100644 (file)
index 0000000..c74b9aa
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+ * os_openbsd.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 David Snyder <smartmontools-support@lists.sourceforge.net>
+ *
+ * Derived from os_netbsd.cpp by Sergey Svishchev <smartmontools-support@lists.sourceforge.net>, Copyright (C) 2003-6 
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+#include "os_openbsd.h"
+
+const char *os_XXXX_c_cvsid = "$Id: os_openbsd.cpp,v 1.13 2006/09/20 16:17:31 shattered Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_OPENBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+/* global variable holding byte count of allocated memory */
+extern long long bytes;
+
+enum warnings {
+  BAD_SMART, NO_3WARE, MAX_MSG
+};
+
+/* Utility function for printing warnings */
+void
+printwarning(int msgNo, const char *extra)
+{
+  static int printed[] = {0, 0};
+  static const char *message[] = {
+    "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
+    PACKAGE_STRING " does not currentlly support twe(4) devices (3ware Escalade)\n",
+  };
+
+  if (msgNo >= 0 && msgNo <= MAX_MSG) {
+    if (!printed[msgNo]) {
+      printed[msgNo] = 1;
+      pout("%s", message[msgNo]);
+      if (extra)
+       pout("%s", extra);
+    }
+  }
+  return;
+}
+
+static const char *net_dev_prefix = "/dev/";
+static const char *net_dev_ata_disk = "wd";
+static const char *net_dev_scsi_disk = "sd";
+static const char *net_dev_scsi_tape = "st";
+
+/* Guess device type(ata or scsi) based on device name */
+int
+guess_device_type(const char *dev_name)
+{
+  int len;
+  int dev_prefix_len = strlen(net_dev_prefix);
+
+  if (!dev_name || !(len = strlen(dev_name)))
+    return CONTROLLER_UNKNOWN;
+
+  if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) {
+    if (len <= dev_prefix_len)
+      return CONTROLLER_UNKNOWN;
+    else
+      dev_name += dev_prefix_len;
+  }
+  if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk)))
+    return CONTROLLER_ATA;
+
+  if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk)))
+    return CONTROLLER_SCSI;
+
+  if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape)))
+    return CONTROLLER_SCSI;
+
+  return CONTROLLER_UNKNOWN;
+}
+
+int
+get_dev_names(char ***names, const char *prefix)
+{
+  char *disknames, *p, **mp;
+  int n = 0;
+  int sysctl_mib[2];
+  size_t sysctl_len;
+
+  *names = NULL;
+
+  sysctl_mib[0] = CTL_HW;
+  sysctl_mib[1] = HW_DISKNAMES;
+  if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
+    pout("Failed to get value of sysctl `hw.disknames'\n");
+    return -1;
+  }
+  if (!(disknames = (char*)malloc(sysctl_len))) {
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+  if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
+    pout("Failed to get value of sysctl `hw.disknames'\n");
+    return -1;
+  }
+  if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+  for (p = strtok(disknames, ","); p; p = strtok(NULL, ",")) {
+    if (strncmp(p, prefix, strlen(prefix))) {
+      continue;
+    }
+    mp[n] = (char*)malloc(strlen(net_dev_prefix) + strlen(p) + 2);
+    if (!mp[n]) {
+      pout("Out of memory constructing scan device list\n");
+      return -1;
+    }
+    sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
+    bytes += strlen(mp[n]) + 1;
+    n++;
+  }
+
+  mp = (char**)realloc(mp, n * (sizeof(char *)));
+  bytes += (n) * (sizeof(char *));
+  *names = mp;
+  return n;
+}
+
+int
+make_device_names(char ***devlist, const char *name)
+{
+  if (!strcmp(name, "SCSI"))
+    return get_dev_names(devlist, net_dev_scsi_disk);
+  else if (!strcmp(name, "ATA"))
+    return get_dev_names(devlist, net_dev_ata_disk);
+  else
+    return 0;
+}
+
+int
+deviceopen(const char *pathname, char *type)
+{
+  if (!strcmp(type, "SCSI")) {
+    int fd = open(pathname, O_RDWR | O_NONBLOCK);
+    if (fd < 0 && errno == EROFS)
+      fd = open(pathname, O_RDONLY | O_NONBLOCK);
+    return fd;
+  } else if (!strcmp(type, "ATA"))
+    return open(pathname, O_RDWR | O_NONBLOCK);
+  else
+    return -1;
+}
+
+int
+deviceclose(int fd)
+{
+  return close(fd);
+}
+
+int
+marvell_command_interface(int fd, smart_command_set command, int select, char *data)
+{ return -1; }
+
+int highpoint_command_interface(int fd, smart_command_set command, int select, char *data)
+{
+  return -1;
+}
+
+int
+ata_command_interface(int fd, smart_command_set command, int select, char *data)
+{
+  struct atareq req;
+  unsigned char inbuf[DEV_BSIZE];
+  int retval, copydata = 0;
+
+  memset(&req, 0, sizeof(req));
+  memset(&inbuf, 0, sizeof(inbuf));
+
+  switch (command) {
+  case READ_VALUES:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_READ_VALUES;
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case READ_THRESHOLDS:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_READ_THRESHOLDS;
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case READ_LOG:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_READ_LOG_SECTOR;  /* XXX missing from wdcreg.h */
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case WRITE_LOG:
+    memcpy(inbuf, data, 512);
+    req.flags = ATACMD_WRITE;
+    req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case IDENTIFY:
+    req.flags = ATACMD_READ;
+    req.command = WDCC_IDENTIFY;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case PIDENTIFY:
+    req.flags = ATACMD_READ;
+    req.command = ATAPI_IDENTIFY_DEVICE;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case ENABLE:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_ENABLE;
+    req.command = ATAPI_SMART;
+    req.cylinder = htole16(WDSMART_CYL);
+    req.timeout = 1000;
+    break;
+  case DISABLE:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_DISABLE;
+    req.command = ATAPI_SMART;
+    req.cylinder = htole16(WDSMART_CYL);
+    req.timeout = 1000;
+    break;
+  case AUTO_OFFLINE:
+    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_AUTO_OFFLINE;     /* XXX missing from wdcreg.h */
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case AUTOSAVE:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */
+    req.command = ATAPI_SMART;
+    req.cylinder = htole16(WDSMART_CYL);
+    req.sec_count = 0xf1;
+    /* to enable autosave */
+    req.timeout = 1000;
+    break;
+  case IMMEDIATE_OFFLINE:
+    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_IMMEDIATE_OFFLINE;        /* XXX missing from wdcreg.h */
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case STATUS_CHECK:
+    /* same command, no HDIO in NetBSD */
+  case STATUS:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_STATUS;
+    req.command = ATAPI_SMART;
+    req.cylinder = htole16(WDSMART_CYL);
+    req.timeout = 1000;
+    break;
+  case CHECK_POWER_MODE:
+    req.flags = ATACMD_READREG;
+    req.command = WDCC_CHECK_PWR;
+    req.timeout = 1000;
+    break;
+  default:
+    pout("Unrecognized command %d in ata_command_interface()\n", command);
+    errno = ENOSYS;
+    return -1;
+  }
+
+  if (command == STATUS_CHECK) {
+    char buf[512];
+
+    unsigned const short normal = WDSMART_CYL, failed = 0x2cf4;
+
+    if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
+      perror("Failed command");
+      return -1;
+    }
+    /* Cyl low and Cyl high unchanged means "Good SMART status" */
+    if (letoh16(req.cylinder) == normal)
+      return 0;
+
+    /* These values mean "Bad SMART status" */
+    if (letoh16(req.cylinder) == failed)
+      return 1;
+
+    /* We haven't gotten output that makes sense; 
+     * print out some debugging info */
+    snprintf(buf, sizeof(buf),
+      "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
+      (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num,
+      (int) (letoh16(req.cylinder) & 0xff), (int) ((letoh16(req.cylinder) >> 8) & 0xff),
+      (int) req.error);
+    printwarning(BAD_SMART, buf);
+    return 0;
+  }
+  if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
+    perror("Failed command");
+    return -1;
+  }
+  if (command == CHECK_POWER_MODE)
+    data[0] = req.sec_count;
+
+  if (copydata)
+    memcpy(data, inbuf, 512);
+
+  return 0;
+}
+
+int
+escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data)
+{
+  printwarning(NO_3WARE, NULL);
+  return -1;
+}
+
+int
+do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
+{
+  struct scsireq sc;
+
+  if (report > 0) {
+    size_t k;
+
+    const unsigned char *ucp = iop->cmnd;
+    const char *np;
+
+    np = scsi_get_opcode_name(ucp[0]);
+    pout(" [%s: ", np ? np : "<unknown opcode>");
+    for (k = 0; k < iop->cmnd_len; ++k)
+      pout("%02x ", ucp[k]);
+    if ((report > 1) &&
+      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+      pout("]\n  Outgoing data, len=%d%s:\n", (int) iop->dxfer_len,
+       (trunc ? " [only first 256 bytes shown]" : ""));
+      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
+    } else
+      pout("]");
+  }
+  memset(&sc, 0, sizeof(sc));
+  memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
+  sc.cmdlen = iop->cmnd_len;
+  sc.databuf = (char*)iop->dxferp;
+  sc.datalen = iop->dxfer_len;
+  sc.senselen = iop->max_sense_len;
+  sc.timeout = iop->timeout == 0 ? 60000 : iop->timeout;       /* XXX */
+  sc.flags =
+    (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ :       /* XXX */
+    (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
+
+  if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
+    warn("error sending SCSI ccb");
+    return -1;
+  }
+  iop->resid = sc.datalen - sc.datalen_used;
+  iop->scsi_status = sc.status;
+  if (iop->sensep) {
+    memcpy(iop->sensep, sc.sense, sc.senselen_used);
+    iop->resp_sense_len = sc.senselen_used;
+  }
+  if (report > 0) {
+    int trunc;
+
+    pout("  status=0\n");
+    trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+    pout("  Incoming data, len=%d%s:\n", (int) iop->dxfer_len,
+      (trunc ? " [only first 256 bytes shown]" : ""));
+    dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
+  }
+  return 0;
+}
+
+/* print examples for smartctl */
+void 
+print_smartctl_examples()
+{
+  char p;
+
+  p = 'a' + getrawpartition();
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+    "  smartctl -a /dev/wd0%c                      (Prints all SMART information)\n\n"
+    "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
+    "                                              (Enables SMART on first disk)\n\n"
+    "  smartctl -t long /dev/wd0%c             (Executes extended disk self-test)\n\n"
+    "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
+    "                                      (Prints Self-Test & Attribute errors)\n",
+    p, p, p, p
+    );
+#else
+  printf(
+    "  smartctl -a /dev/wd0%c                     (Prints all SMART information)\n"
+    "  smartctl -s on -o on -S on /dev/wd0%c        (Enables SMART on first disk)\n"
+    "  smartctl -t long /dev/wd0%c            (Executes extended disk self-test)\n"
+    "  smartctl -A -l selftest -q errorsonly /dev/wd0%c"
+    "                                      (Prints Self-Test & Attribute errors)\n",
+    p, p, p, p
+    );
+#endif
+  return;
+}
index 356343a9fa166778abb9c6ab719951e919c2441a..eb7f63943eac4dfb1c540b5a629718a3b0085bb0 100644 (file)
@@ -26,7 +26,7 @@
 #ifndef OS_OPENBSD_H_
 #define OS_OPENBSD_H_
 
-#define OS_OPENBSD_H_CVSID "$Id: os_openbsd.h,v 1.4 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define OS_OPENBSD_H_CVSID "$Id: os_openbsd.h,v 1.5 2006/09/19 07:22:09 snyderx Exp $\n"
 
 /* from NetBSD: atareg.h,v 1.17, by Manuel Bouyer */
 /* Actually fits _perfectly_ into OBSDs wdcreg.h, but... */
@@ -51,5 +51,6 @@
 #include <err.h>
 #include <fcntl.h>
 #include <util.h>
+#include <unistd.h>
 
 #endif /* OS_OPENBSD_H_ */
diff --git a/os_os2.cpp b/os_os2.cpp
new file mode 100644 (file)
index 0000000..797de35
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+ * os_os2.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Yuri Dario <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ *
+ * Thanks to Daniela Engert for providing sample code for SMART ioctl access.
+ *
+ */
+
+// These are needed to define prototypes for the functions defined below
+#include <errno.h>
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+// This is to include whatever prototypes you define in os_generic.h
+#include "os_os2.h"
+
+// Needed by '-V' option (CVS versioning) of smartd/smartctl
+const char *os_XXXX_c_cvsid="$Id: os_os2.cpp,v 1.7 2006/09/20 16:17:31 shattered Exp $" \
+ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+// global handle to device driver
+static HFILE hDevice;
+
+// Please eliminate the following block: both the two #includes and
+// the 'unsupported()' function.  They are only here to warn
+// unsuspecting users that their Operating System is not supported! If
+// you wish, you can use a similar warning mechanism for any of the
+// functions in this file that you can not (or choose not to)
+// implement.
+
+#include "config.h"
+
+typedef struct _IDEREGS {
+       UCHAR  bFeaturesReg;
+       UCHAR  bSectorCountReg;
+       UCHAR  bSectorNumberReg;
+       UCHAR  bCylLowReg;
+       UCHAR  bCylHighReg;
+       UCHAR  bDriveHeadReg;
+       UCHAR  bCommandReg;
+       UCHAR  bReserved;
+} IDEREGS, *PIDEREGS, *LPIDEREGS;
+
+static void unsupported(int which){
+  static int warninggiven[4];
+
+  if (which<0 || which>3)
+    return;
+
+  if (!warninggiven[which]) {
+    char msg;
+    debugmode=1;
+    warninggiven[which]=1;
+
+    switch (which) {
+    case 0:
+      msg="generate a list of devices";
+      break;
+    case 1:
+      msg="interface to Marvell-based SATA controllers";
+      break;
+    case 2:
+      msg="interface to 3ware-based RAID controllers";
+      break;
+    case 3:
+      msg="interface to SCSI devices";
+      break;
+    }
+    pout("Under OS/2, smartmontools can not %s\n");
+  }
+  return;
+}
+
+// print examples for smartctl.  You should modify this function so
+// that the device paths are sensible for your OS, and to eliminate
+// unsupported commands (eg, 3ware controllers).
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+         "  smartctl -a /dev/hda                       (Prints all SMART information)\n\n"
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl -a --device=3ware,2 /dev/sda\n"
+         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
+         );
+#else
+  printf(
+         "  smartctl -a /dev/hda                       (Prints all SMART information)\n"
+         "  smartctl -s on -o on -S on /dev/hda         (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl -a -d 3ware,2 /dev/sda\n"
+         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
+         );
+#endif
+  return;
+}
+
+static const char * skipdev(const char * s)
+{
+       return (!strncmp(s, "/dev/", 5) ? s + 5 : s);
+}
+
+// tries to guess device type given the name (a path).  See utility.h
+// for return values.
+int guess_device_type (const char* dev_name) {
+
+   //printf( "dev_name %s\n", dev_name);
+   dev_name = skipdev(dev_name);
+       if (!strncmp(dev_name, "hd", 2))
+               return CONTROLLER_ATA;
+       if (!strncmp(dev_name, "scsi", 4))
+               return CONTROLLER_SCSI;
+  return CONTROLLER_UNKNOWN;
+}
+
+// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
+// smartd.  Returns number N of devices, or -1 if out of
+// memory. Allocates N+1 arrays: one of N pointers (devlist); the
+// other N arrays each contain null-terminated character strings.  In
+// the case N==0, no arrays are allocated because the array of 0
+// pointers has zero length, equivalent to calling malloc(0).
+int make_device_names (char*** devlist, const char* name) {
+  unsupported(0);
+  return 0;
+}
+
+// Like open().  Return non-negative integer handle, only used by the
+// functions below.  type=="ATA" or "SCSI".  If you need to store
+// extra information about your devices, create a private internal
+// array within this file (see os_freebsd.cpp for an example).  If you
+// can not open the device (permission denied, does not exist, etc)
+// set errno as open() does and return <0.
+int deviceopen(const char *pathname, char *type){
+
+  int fd;
+  APIRET rc;
+  ULONG ActionTaken;
+
+  //printf( "deviceopen pathname %s\n", pathname);
+  rc = DosOpen ("\\DEV\\IBMS506$", &hDevice, &ActionTaken, 0,  FILE_SYSTEM,
+              OPEN_ACTION_OPEN_IF_EXISTS, OPEN_SHARE_DENYNONE |
+              OPEN_FLAGS_NOINHERIT | OPEN_ACCESS_READONLY, NULL);
+  if (rc) {
+    char errmsg[256];
+    snprintf(errmsg,256,"Smartctl open driver IBMS506$ failed (%d)", rc);
+    errmsg[255]='\0';
+    syserror(errmsg);
+    return -1;
+  }
+
+  pathname = skipdev(pathname);
+  fd = tolower(pathname[2]) - 'a';
+
+  return fd;
+}
+
+// Like close().  Acts only on integer handles returned by
+// deviceopen() above.
+int deviceclose(int fd){
+
+  DosClose( hDevice);
+  hDevice = NULL;
+
+  return 0;
+}
+
+static void print_ide_regs(const IDEREGS * r, int out)
+{
+       pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, NS=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
+       (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
+       r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
+}
+
+//
+// OS/2 direct ioctl interface to IBMS506$
+//
+int dani_ioctl( int device, int request, void* arg)
+{
+   unsigned char* buff = (unsigned char*) arg;
+   APIRET rc;
+   DSKSP_CommandParameters Parms;
+   ULONG PLen = 1;
+   ULONG DLen = 512; //sizeof (*buf);
+   UCHAR temp;
+   ULONG value = 0;
+   IDEREGS  regs;
+
+   //printf( "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]);
+
+   Parms.byPhysicalUnit = device;
+   switch( buff[0]) {
+   case WIN_IDENTIFY:
+      rc = DosDevIOCtl (hDevice, DSKSP_CAT_GENERIC, DSKSP_GET_INQUIRY_DATA,
+                    (PVOID)&Parms, PLen, &PLen, (PVOID)arg+4, DLen, &DLen);
+      if (rc != 0)
+      {
+          printf ("DANIS506 ATA GET HD Failed (%d,0x%x)\n", rc, rc);
+          return -1;
+      }
+      break;
+   case WIN_SMART:
+      switch( buff[2]) {
+      case SMART_STATUS:
+         DLen = sizeof(value);
+         // OS/2 already checks CL/CH in IBM1S506 code!! see s506rte.c (ddk)
+         // value: -1=not supported, 0=ok, 1=failing
+         rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GETSTATUS,
+                    (PVOID)&Parms, PLen, &PLen, (PVOID)&value, DLen, &DLen);
+         if (rc)
+         {
+             printf ("DANIS506 ATA GET SMART_STATUS failed (%d,0x%x)\n", rc, rc);
+             return -1;
+         }
+         buff[4] = (unsigned char)value;
+         break;
+      case SMART_READ_VALUES:
+         rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_ATTRIBUTES,
+                    (PVOID)&Parms, PLen, &PLen, (PVOID)arg+4, DLen, &DLen);
+         if (rc)
+         {
+             printf ("DANIS506 ATA GET DSKSP_SMART_GET_ATTRIBUTES failed (%d,0x%x)\n", rc, rc);
+             return -1;
+         }
+         break;
+      case SMART_READ_THRESHOLDS:
+         rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_GET_THRESHOLDS,
+                    (PVOID)&Parms, PLen, &PLen, (PVOID)arg+4, DLen, &DLen);
+         if (rc)
+         {
+             printf ("DANIS506 ATA GET DSKSP_SMART_GET_THRESHOLDS failed (%d,0x%x)\n", rc, rc);
+             return -1;
+         }
+         break;
+      case SMART_READ_LOG_SECTOR:
+         buff[4] = buff[1]; // copy select field
+         rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_READ_LOG,
+                    (PVOID)&Parms, PLen, &PLen, (PVOID)arg+4, DLen, &DLen);
+         if (rc)
+         {
+             printf ("DANIS506 ATA GET DSKSP_SMART_READ_LOG failed (%d,0x%x)\n", rc, rc);
+             return -1;
+         }
+         break;
+      case SMART_ENABLE:
+         buff[0] = 1; // enable
+         DLen = 1;
+         rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF,
+                           (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
+         if (rc) {
+             printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%d,0x%x)\n", rc, rc);
+             return -1;
+         }
+         break;
+      case SMART_DISABLE:
+         buff[0] = 0; // disable
+         DLen = 1;
+         rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_ONOFF,
+                           (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
+         if (rc) {
+             printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%d,0x%x)\n", rc, rc);
+             return -1;
+         }
+         break;
+#if 0
+      case SMART_AUTO_OFFLINE:
+         buff[0] = buff[3];   // select field
+         DLen = 1;
+         rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_AUTO_OFFLINE,
+                           (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
+         if (rc) {
+             printf ("DANIS506 ATA GET DSKSP_SMART_ONOFF failed (%d,0x%x)\n", rc, rc);
+             return -1;
+         }
+         break;
+#endif
+      case SMART_AUTOSAVE:
+         buff[0] = buff[3];   // select field
+         DLen = 1;
+         rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_AUTOSAVE_ONOFF,
+                           (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
+         if (rc) {
+             printf ("DANIS506 ATA DSKSP_SMART_AUTOSAVE_ONOFF failed (%d,0x%x)\n", rc, rc);
+             return -1;
+         }
+         break;
+      case SMART_IMMEDIATE_OFFLINE:
+         buff[0] = buff[1];   // select field
+         DLen = 1;
+         rc = DosDevIOCtl (hDevice, DSKSP_CAT_SMART, DSKSP_SMART_EOLI,
+                           (PVOID)&Parms, PLen, &PLen, (PVOID)buff, DLen, &DLen);
+         if (rc) {
+             printf ("DANIS506 ATA GET DSKSP_SMART_EXEC_OFFLINE failed (%d,0x%x)\n", rc, rc);
+             return -1;
+         }
+         break;
+
+      default:
+         fprintf( stderr, "device %d, request 0x%x, arg[0] 0x%x, arg[2] 0x%x\n", device, request, buff[0], buff[2]);
+         fprintf( stderr, "unknown ioctl\n");
+         return -1;
+         break;
+      }
+      break;
+   //case WIN_PIDENTIFY:
+   //   break;
+   default:
+      fprintf( stderr, "unknown ioctl\n");
+      return -1;
+      break;
+   }
+
+   // ok
+   return 0;
+}
+
+// Interface to ATA devices.  See os_linux.cpp for the cannonical example.
+// DETAILED DESCRIPTION OF ARGUMENTS
+//   device: is the integer handle provided by deviceopen()
+//   command: defines the different operations, see atacmds.h
+//   select: additional input data IF NEEDED (which log, which type of
+//           self-test).
+//   data:   location to write output data, IF NEEDED (1 or 512 bytes).
+//   Note: not all commands use all arguments.
+// RETURN VALUES (for all commands BUT command==STATUS_CHECK)
+//  -1 if the command failed
+//   0 if the command succeeded,
+// RETURN VALUES if command==STATUS_CHECK
+//  -1 if the command failed OR the disk SMART status can't be determined
+//   0 if the command succeeded and disk SMART status is "OK"
+//   1 if the command succeeded and disk SMART status is "FAILING"
+
+// huge value of buffer size needed because HDIO_DRIVE_CMD assumes
+// that buff[3] is the data size.  Since the ATA_SMART_AUTOSAVE and
+// ATA_SMART_AUTO_OFFLINE use values of 0xf1 and 0xf8 we need the space.
+// Otherwise a 4+512 byte buffer would be enough.
+#define STRANGE_BUFFER_LENGTH (4+512*0xf8)
+
+int ata_command_interface(int device, smart_command_set command, int select, char *data){
+  unsigned char buff[STRANGE_BUFFER_LENGTH];
+  // positive: bytes to write to caller.  negative: bytes to READ from
+  // caller. zero: non-data command
+  int copydata=0;
+
+  const int HDIO_DRIVE_CMD_OFFSET = 4;
+
+  // See struct hd_drive_cmd_hdr in hdreg.h.  Before calling ioctl()
+  // buff[0]: ATA COMMAND CODE REGISTER
+  // buff[1]: ATA SECTOR NUMBER REGISTER == LBA LOW REGISTER
+  // buff[2]: ATA FEATURES REGISTER
+  // buff[3]: ATA SECTOR COUNT REGISTER
+
+  // Note that on return:
+  // buff[2] contains the ATA SECTOR COUNT REGISTER
+
+  // clear out buff.  Large enough for HDIO_DRIVE_CMD (4+512 bytes)
+  memset(buff, 0, STRANGE_BUFFER_LENGTH);
+
+  //printf( "command, select %d,%d\n", command, select);
+  buff[0]=ATA_SMART_CMD;
+  switch (command){
+  case CHECK_POWER_MODE:
+    buff[0]=ATA_CHECK_POWER_MODE;
+    copydata=1;
+    break;
+  case READ_VALUES:
+    buff[2]=ATA_SMART_READ_VALUES;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case READ_THRESHOLDS:
+    buff[2]=ATA_SMART_READ_THRESHOLDS;
+    buff[1]=buff[3]=1;
+    copydata=512;
+    break;
+  case READ_LOG:
+    buff[2]=ATA_SMART_READ_LOG_SECTOR;
+    buff[1]=select;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case WRITE_LOG:
+    break;
+  case IDENTIFY:
+    buff[0]=ATA_IDENTIFY_DEVICE;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case PIDENTIFY:
+    buff[0]=ATA_IDENTIFY_PACKET_DEVICE;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case ENABLE:
+    buff[2]=ATA_SMART_ENABLE;
+    buff[1]=1;
+    break;
+  case DISABLE:
+    buff[2]=ATA_SMART_DISABLE;
+    buff[1]=1;
+    break;
+  case STATUS:
+  case STATUS_CHECK:
+    // this command only says if SMART is working.  It could be
+    // replaced with STATUS_CHECK below.
+    buff[2]=ATA_SMART_STATUS;
+    buff[4]=0;
+    break;
+  case AUTO_OFFLINE:
+    buff[2]=ATA_SMART_AUTO_OFFLINE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case AUTOSAVE:
+    buff[2]=ATA_SMART_AUTOSAVE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case IMMEDIATE_OFFLINE:
+    buff[2]=ATA_SMART_IMMEDIATE_OFFLINE;
+    buff[1]=select;
+    break;
+  //case STATUS_CHECK:
+  //  // This command uses HDIO_DRIVE_TASK and has different syntax than
+  //  // the other commands.
+  //  buff[1]=ATA_SMART_STATUS;
+  //  break;
+  default:
+    pout("Unrecognized command %d in linux_ata_command_interface()\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", command);
+    errno=ENOSYS;
+    return -1;
+  }
+
+#if 0
+  // This command uses the HDIO_DRIVE_TASKFILE ioctl(). This is the
+  // only ioctl() that can be used to WRITE data to the disk.
+  if (command==WRITE_LOG) {
+    unsigned char task[sizeof(ide_task_request_t)+512];
+    ide_task_request_t *reqtask=(ide_task_request_t *) task;
+    task_struct_t      *taskfile=(task_struct_t *) reqtask->io_ports;
+    int retval;
+
+    memset(task,      0, sizeof(task));
+
+    taskfile->data           = 0;
+    taskfile->feature        = ATA_SMART_WRITE_LOG_SECTOR;
+    taskfile->sector_count   = 1;
+    taskfile->sector_number  = select;
+    taskfile->low_cylinder   = 0x4f;
+    taskfile->high_cylinder  = 0xc2;
+    taskfile->device_head    = 0;
+    taskfile->command        = ATA_SMART_CMD;
+
+    reqtask->data_phase      = TASKFILE_OUT;
+    reqtask->req_cmd         = IDE_DRIVE_TASK_OUT;
+    reqtask->out_size        = 512;
+    reqtask->in_size         = 0;
+
+    // copy user data into the task request structure
+    memcpy(task+sizeof(ide_task_request_t), data, 512);
+
+    if ((retval=dani_ioctl(device, HDIO_DRIVE_TASKFILE, task))) {
+      if (retval==-EINVAL)
+       pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n");
+      return -1;
+    }
+    return 0;
+  }
+#endif // 0
+
+  // We are now doing the HDIO_DRIVE_CMD type ioctl.
+  if ((dani_ioctl(device, HDIO_DRIVE_CMD, buff)))
+    return -1;
+
+  // There are two different types of ioctls().  The HDIO_DRIVE_TASK
+  // one is this:
+  if (command==STATUS_CHECK){
+    int retval;
+
+    // Cyl low and Cyl high unchanged means "Good SMART status"
+    if (buff[4]==0)
+      return 0;
+
+    // These values mean "Bad SMART status"
+    if (buff[4]==1)
+      return 1;
+
+    // We haven't gotten output that makes sense; print out some debugging info
+    syserror("Error SMART Status command failed");
+    pout("Please get assistance from " PACKAGE_HOMEPAGE "\n");
+    return -1;
+  }
+
+  // CHECK POWER MODE command returns information in the Sector Count
+  // register (buff[3]).  Copy to return data buffer.
+  if (command==CHECK_POWER_MODE)
+    buff[HDIO_DRIVE_CMD_OFFSET]=buff[2];
+
+  // if the command returns data then copy it back
+  if (copydata)
+    memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata);
+
+  return 0;
+}
+
+int marvell_command_interface(int fd, smart_command_set command, int select, char *data){
+  unsupported(1);
+  return -1;
+}
+
+int highpoint_command_interface(int fd, smart_command_set command, int select, char *data)
+{
+  unsupported(1);
+  return -1;
+}
+
+// Interface to ATA devices behind 3ware escalade RAID controller
+// cards.  Same description as ata_command_interface() above except
+// that 0 <= disknum <= 15 specifies the ATA disk attached to the
+// controller.
+int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){
+  unsupported(2);
+  return -1;
+}
+
+// Interface to SCSI devices.  See os_linux.c
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) {
+  unsupported(3);
+  return -ENOSYS;
+}
diff --git a/os_os2.h b/os_os2.h
new file mode 100644 (file)
index 0000000..4a9c130
--- /dev/null
+++ b/os_os2.h
@@ -0,0 +1,70 @@
+/*
+ * os_os2.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Yuri Dario <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef OS_OS2_H_
+#define OS_OS2_H_
+
+#define OS_XXXX_H_CVSID "$Id: os_os2.h,v 1.4 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+// Additional material should start here.  Note: to keep the '-V' CVS
+// reporting option working as intended, you should only #include
+// system include files <something.h>.  Local #include files
+// <"something.h"> should be #included in os_generic.c
+
+#define INCL_DOS
+#include <os2.h>
+
+#include "os_os2\hdreg.h"
+#include "os_linux.h"
+
+#pragma pack(1)
+
+#define DSKSP_CAT_SMART             0x80  /* SMART IOCTL category */
+#define DSKSP_SMART_ONOFF           0x20  /* turn SMART on or off */
+#define DSKSP_SMART_AUTOSAVE_ONOFF  0x21  /* turn SMART autosave on or off */
+#define DSKSP_SMART_SAVE            0x22  /* force save of SMART data */
+#define DSKSP_SMART_GETSTATUS       0x23  /* get SMART status (pass/fail) */
+#define DSKSP_SMART_GET_ATTRIBUTES  0x24  /* get SMART attributes table */
+#define DSKSP_SMART_GET_THRESHOLDS  0x25  /* get SMART thresholds table */
+#define DSKSP_SMART_READ_LOG        0x26  
+#define DSKSP_SMART_WRITE_LOG       0x27  
+#define DSKSP_SMART_READ_LOG_EXT    0x28  
+#define DSKSP_SMART_WRITE_LOG_EXT   0x29  
+#define DSKSP_SMART_EOLI            0x30  /* EXECUTE OFF-LINE IMMEDIATE */
+
+#define SMART_CMD_ON      1   /* on  value for related SMART functions */
+#define SMART_CMD_OFF     0   /* off value for related SMART functions */
+
+#define DSKSP_CAT_GENERIC           0x90  /* generic IOCTL category */
+#define DSKSP_GET_INQUIRY_DATA      0x42  /* get ATA/ATAPI inquiry data */
+
+typedef struct _DSKSP_CommandParameters {
+  BYTE byPhysicalUnit;            /* physical unit number 0-n */
+                                  /* 0 = 1st disk, 1 = 2nd disk, ...*/
+                                  /* 0x80 = Pri/Mas, 0x81=Pri/Sla, 0x82=Sec/Mas,*/
+} DSKSP_CommandParameters, *PDSKSP_CommandParameters;
+
+struct SMART_ParamExt {
+  UCHAR      byPhysicalUnit;  // 0=Pri/Mas, 1=Pri/Sla, 2=Sec/Mas, etc.
+  ULONG      LogAddress;      // valid values 0-255. See ATA/ATPI standard
+                              // for details
+  ULONG      SectorCount;     // valid values 0-255  See ATA/ATPI standard
+                              // for details
+  ULONG      reserved;        // reserved. must be set to 0
+};
+
+#endif /* OS_GENERIC_H_ */
diff --git a/os_os2/configure.os2 b/os_os2/configure.os2
new file mode 100644 (file)
index 0000000..121b22c
--- /dev/null
@@ -0,0 +1,9 @@
+#! /bin/sh
+CFLAGS="-s -Zomf -O3 -march=pentium -mcpu=pentium3" \
+CXXFLAGS="-s -Zomf -O3 -march=pentium -mcpu=pentium3" \
+LDFLAGS="-s -Zmap -Zhigh-mem -Zomf -Zexe -Zstack 0x100" \
+LIBS=" -lsyslog -lsocket" \
+LN_CP_F="cp.exe" \
+RANLIB="echo" \
+AR="emxomfar" \
+./configure --prefix=/usr/local/smartmontools
diff --git a/os_os2/hdreg.h b/os_os2/hdreg.h
new file mode 100644 (file)
index 0000000..65c4ed1
--- /dev/null
@@ -0,0 +1,333 @@
+#ifndef _LINUX_HDREG_H
+#define _LINUX_HDREG_H
+
+/*
+ * This file contains some defines for the AT-hd-controller.
+ * Various sources.  
+ */
+
+#define HD_IRQ 14              /* the standard disk interrupt */
+
+/* ide.c has its own port definitions in "ide.h" */
+
+/* Hd controller regs. Ref: IBM AT Bios-listing */
+#define HD_DATA                0x1f0   /* _CTL when writing */
+#define HD_ERROR       0x1f1   /* see err-bits */
+#define HD_NSECTOR     0x1f2   /* nr of sectors to read/write */
+#define HD_SECTOR      0x1f3   /* starting sector */
+#define HD_LCYL                0x1f4   /* starting cylinder */
+#define HD_HCYL                0x1f5   /* high byte of starting cyl */
+#define HD_CURRENT     0x1f6   /* 101dhhhh , d=drive, hhhh=head */
+#define HD_STATUS      0x1f7   /* see status-bits */
+#define HD_FEATURE HD_ERROR    /* same io address, read=error, write=feature */
+#define HD_PRECOMP HD_FEATURE  /* obsolete use of this port - predates IDE */
+#define HD_COMMAND HD_STATUS   /* same io address, read=status, write=cmd */
+
+#define HD_CMD         0x3f6   /* used for resets */
+#define HD_ALTSTATUS   0x3f6   /* same as HD_STATUS but doesn't clear irq */
+
+/* remainder is shared between hd.c, ide.c, ide-cd.c, and the hdparm utility */
+
+/* Bits of HD_STATUS */
+#define ERR_STAT       0x01
+#define INDEX_STAT     0x02
+#define ECC_STAT       0x04    /* Corrected error */
+#define DRQ_STAT       0x08
+#define SEEK_STAT      0x10
+#define WRERR_STAT     0x20
+#define READY_STAT     0x40
+#define BUSY_STAT      0x80
+
+/* Values for HD_COMMAND */
+#define WIN_RESTORE            0x10
+#define WIN_READ               0x20
+#define WIN_WRITE              0x30
+#define WIN_WRITE_VERIFY       0x3C
+#define WIN_VERIFY             0x40
+#define WIN_FORMAT             0x50
+#define WIN_INIT               0x60
+#define WIN_SEEK               0x70
+#define WIN_DIAGNOSE           0x90
+#define WIN_SPECIFY            0x91    /* set drive geometry translation */
+#define WIN_IDLEIMMEDIATE      0xE1    /* force drive to become "ready" */
+#define WIN_SETIDLE1           0xE3
+#define WIN_SETIDLE2           0x97
+
+#define WIN_STANDBYNOW1                0xE0
+#define WIN_STANDBYNOW2                0x94
+#define WIN_SLEEPNOW1          0xE6
+#define WIN_SLEEPNOW2          0x99
+#define WIN_CHECKPOWERMODE1    0xE5
+#define WIN_CHECKPOWERMODE2    0x98
+
+#define WIN_DOORLOCK           0xDE    /* lock door on removable drives */
+#define WIN_DOORUNLOCK         0xDF    /* unlock door on removable drives */
+
+#define WIN_MULTREAD           0xC4    /* read sectors using multiple mode */
+#define WIN_MULTWRITE          0xC5    /* write sectors using multiple mode */
+#define WIN_SETMULT            0xC6    /* enable/disable multiple mode */
+#define WIN_IDENTIFY           0xEC    /* ask drive to identify itself */
+#define WIN_IDENTIFY_DMA       0xEE    /* same as WIN_IDENTIFY, but DMA */
+#define WIN_SETFEATURES                0xEF    /* set special drive features */
+#define WIN_READDMA            0xC8    /* read sectors using DMA transfers */
+#define WIN_WRITEDMA           0xCA    /* write sectors using DMA transfers */
+
+#define WIN_QUEUED_SERVICE     0xA2    /* */
+#define WIN_READDMA_QUEUED     0xC7    /* read sectors using Queued DMA transfers */
+#define WIN_WRITEDMA_QUEUED    0xCC    /* write sectors using Queued DMA transfers */
+
+#define WIN_READ_BUFFER                0xE4    /* force read only 1 sector */
+#define WIN_WRITE_BUFFER       0xE8    /* force write only 1 sector */
+
+#define WIN_SMART              0xB0    /* self-monitoring and reporting */
+
+/* Additional drive command codes used by ATAPI devices. */
+#define WIN_PIDENTIFY          0xA1    /* identify ATAPI device        */
+#define WIN_SRST               0x08    /* ATAPI soft reset command */
+#define WIN_PACKETCMD          0xA0    /* Send a packet command. */
+
+#define DISABLE_SEAGATE                0xFB
+#define EXABYTE_ENABLE_NEST    0xF0
+
+/* WIN_SMART sub-commands */
+
+#define SMART_READ_VALUES      0xd0
+#define SMART_READ_THRESHOLDS  0xd1
+#define SMART_AUTOSAVE         0xd2
+#define SMART_SAVE             0xd3
+#define SMART_IMMEDIATE_OFFLINE        0xd4
+#define SMART_READ_LOG_SECTOR  0xd5
+#define SMART_WRITE_LOG_SECTOR 0xd6
+#define SMART_ENABLE           0xd8
+#define SMART_DISABLE          0xd9
+#define SMART_STATUS           0xda
+#define SMART_AUTO_OFFLINE     0xdb
+
+/* WIN_SETFEATURES sub-commands */
+
+#define SETFEATURES_EN_WCACHE  0x02    /* Enable write cache */
+#define SETFEATURES_XFER       0x03    /* Set transfer mode */
+#      define XFER_UDMA_7      0x47    /* 0100|0111 */
+#      define XFER_UDMA_6      0x46    /* 0100|0110 */
+#      define XFER_UDMA_5      0x45    /* 0100|0101 */
+#      define XFER_UDMA_4      0x44    /* 0100|0100 */
+#      define XFER_UDMA_3      0x43    /* 0100|0011 */
+#      define XFER_UDMA_2      0x42    /* 0100|0010 */
+#      define XFER_UDMA_1      0x41    /* 0100|0001 */
+#      define XFER_UDMA_0      0x40    /* 0100|0000 */
+#      define XFER_MW_DMA_2    0x22    /* 0010|0010 */
+#      define XFER_MW_DMA_1    0x21    /* 0010|0001 */
+#      define XFER_MW_DMA_0    0x20    /* 0010|0000 */
+#      define XFER_SW_DMA_2    0x12    /* 0001|0010 */
+#      define XFER_SW_DMA_1    0x11    /* 0001|0001 */
+#      define XFER_SW_DMA_0    0x10    /* 0001|0000 */
+#      define XFER_PIO_4       0x0C    /* 0000|1100 */
+#      define XFER_PIO_3       0x0B    /* 0000|1011 */
+#      define XFER_PIO_2       0x0A    /* 0000|1010 */
+#      define XFER_PIO_1       0x09    /* 0000|1001 */
+#      define XFER_PIO_0       0x08    /* 0000|1000 */
+#      define XFER_PIO_SLOW    0x00    /* 0000|0000 */
+#define SETFEATURES_DIS_DEFECT 0x04    /* Disable Defect Management */
+#define SETFEATURES_EN_APM     0x05    /* Enable advanced power management */
+#define SETFEATURES_DIS_MSN    0x31    /* Disable Media Status Notification */
+#define SETFEATURES_DIS_RLA    0x55    /* Disable read look-ahead feature */
+#define SETFEATURES_EN_RI      0x5D    /* Enable release interrupt */
+#define SETFEATURES_EN_SI      0x5E    /* Enable SERVICE interrupt */
+#define SETFEATURES_DIS_RPOD   0x66    /* Disable reverting to power on defaults */
+#define SETFEATURES_DIS_WCACHE 0x82    /* Disable write cache */
+#define SETFEATURES_EN_DEFECT  0x84    /* Enable Defect Management */
+#define SETFEATURES_DIS_APM    0x85    /* Disable advanced power management */
+#define SETFEATURES_EN_MSN     0x95    /* Enable Media Status Notification */
+#define SETFEATURES_EN_RLA     0xAA    /* Enable read look-ahead feature */
+#define SETFEATURES_PREFETCH   0xAB    /* Sets drive prefetch value */
+#define SETFEATURES_EN_RPOD    0xCC    /* Enable reverting to power on defaults */
+#define SETFEATURES_DIS_RI     0xDD    /* Disable release interrupt */
+#define SETFEATURES_DIS_SI     0xDE    /* Disable SERVICE interrupt */
+
+/* WIN_SECURITY sub-commands */
+
+#define SECURITY_SET_PASSWORD          0xBA    /* 0xF1 */
+#define SECURITY_UNLOCK                        0xBB    /* 0xF2 */
+#define SECURITY_ERASE_PREPARE         0xBC    /* 0xF3 */
+#define SECURITY_ERASE_UNIT            0xBD    /* 0xF4 */
+#define SECURITY_FREEZE_LOCK           0xBE    /* 0xF5 */
+#define SECURITY_DISABLE_PASSWORD      0xBF    /* 0xF6 */
+
+/* Bits for HD_ERROR */
+#define MARK_ERR       0x01    /* Bad address mark */
+#define TRK0_ERR       0x02    /* couldn't find track 0 */
+#define ABRT_ERR       0x04    /* Command aborted */
+#define MCR_ERR                0x08    /* media change request */
+#define ID_ERR         0x10    /* ID field not found */
+#define ECC_ERR                0x40    /* Uncorrectable ECC error */
+#define        BBD_ERR         0x80    /* pre-EIDE meaning:  block marked bad */
+#define        ICRC_ERR        0x80    /* new meaning:  CRC error during transfer */
+
+struct hd_geometry {
+      unsigned char heads;
+      unsigned char sectors;
+      unsigned short cylinders;
+      unsigned long start;
+};
+
+/* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x030n/0x031n */
+#define HDIO_GETGEO            0x0301  /* get device geometry */
+#define HDIO_GET_UNMASKINTR    0x0302  /* get current unmask setting */
+#define HDIO_GET_MULTCOUNT     0x0304  /* get current IDE blockmode setting */
+#define HDIO_OBSOLETE_IDENTITY 0x0307  /* OBSOLETE, DO NOT USE: returns 142 bytes */
+#define HDIO_GET_KEEPSETTINGS  0x0308  /* get keep-settings-on-reset flag */
+#define HDIO_GET_32BIT         0x0309  /* get current io_32bit setting */
+#define HDIO_GET_NOWERR                0x030a  /* get ignore-write-error flag */
+#define HDIO_GET_DMA           0x030b  /* get use-dma flag */
+#define HDIO_GET_NICE          0x030c  /* get nice flags */
+#define HDIO_GET_IDENTITY      0x030d  /* get IDE identification info */
+
+#define HDIO_DRIVE_RESET       0x031c  /* execute a device reset */
+#define HDIO_TRISTATE_HWIF     0x031d  /* execute a channel tristate */
+#ifndef __EMX__
+#define HDIO_DRIVE_TASK                0x031e  /* execute task and special drive command */
+#endif
+#define HDIO_DRIVE_CMD         0x031f  /* execute a special drive command */
+
+#define HDIO_DRIVE_CMD_AEB     HDIO_DRIVE_TASK
+
+/* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */
+#define HDIO_SET_MULTCOUNT     0x0321  /* change IDE blockmode */
+#define HDIO_SET_UNMASKINTR    0x0322  /* permit other irqs during I/O */
+#define HDIO_SET_KEEPSETTINGS  0x0323  /* keep ioctl settings on reset */
+#define HDIO_SET_32BIT         0x0324  /* change io_32bit flags */
+#define HDIO_SET_NOWERR                0x0325  /* change ignore-write-error flag */
+#define HDIO_SET_DMA           0x0326  /* change use-dma flag */
+#define HDIO_SET_PIO_MODE      0x0327  /* reconfig interface to new speed */
+#define HDIO_SCAN_HWIF         0x0328  /* register and (re)scan interface */
+#define HDIO_SET_NICE          0x0329  /* set nice flags */
+#define HDIO_UNREGISTER_HWIF   0x032a  /* unregister interface */
+
+/* BIG GEOMETRY */
+struct hd_big_geometry {
+       unsigned char heads;
+       unsigned char sectors;
+       unsigned int cylinders;
+       unsigned long start;
+};
+
+/* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x033n/0x033n */
+#define HDIO_GETGEO_BIG                0x0330  /* */
+#define HDIO_GETGEO_BIG_RAW    0x0331  /* */
+
+#define __NEW_HD_DRIVE_ID
+/* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */
+struct hd_driveid {
+       unsigned short  config;         /* lots of obsolete bit flags */
+       unsigned short  cyls;           /* "physical" cyls */
+       unsigned short  reserved2;      /* reserved (word 2) */
+       unsigned short  heads;          /* "physical" heads */
+       unsigned short  track_bytes;    /* unformatted bytes per track */
+       unsigned short  sector_bytes;   /* unformatted bytes per sector */
+       unsigned short  sectors;        /* "physical" sectors per track */
+       unsigned short  vendor0;        /* vendor unique */
+       unsigned short  vendor1;        /* vendor unique */
+       unsigned short  vendor2;        /* vendor unique */
+       unsigned char   serial_no[20];  /* 0 = not_specified */
+       unsigned short  buf_type;
+       unsigned short  buf_size;       /* 512 byte increments; 0 = not_specified */
+       unsigned short  ecc_bytes;      /* for r/w long cmds; 0 = not_specified */
+       unsigned char   fw_rev[8];      /* 0 = not_specified */
+       unsigned char   model[40];      /* 0 = not_specified */
+       unsigned char   max_multsect;   /* 0=not_implemented */
+       unsigned char   vendor3;        /* vendor unique */
+       unsigned short  dword_io;       /* 0=not_implemented; 1=implemented */
+       unsigned char   vendor4;        /* vendor unique */
+       unsigned char   capability;     /* bits 0:DMA 1:LBA 2:IORDYsw 3:IORDYsup*/
+       unsigned short  reserved50;     /* reserved (word 50) */
+       unsigned char   vendor5;        /* vendor unique */
+       unsigned char   tPIO;           /* 0=slow, 1=medium, 2=fast */
+       unsigned char   vendor6;        /* vendor unique */
+       unsigned char   tDMA;           /* 0=slow, 1=medium, 2=fast */
+       unsigned short  field_valid;    /* bits 0:cur_ok 1:eide_ok */
+       unsigned short  cur_cyls;       /* logical cylinders */
+       unsigned short  cur_heads;      /* logical heads */
+       unsigned short  cur_sectors;    /* logical sectors per track */
+       unsigned short  cur_capacity0;  /* logical total sectors on drive */
+       unsigned short  cur_capacity1;  /*  (2 words, misaligned int)     */
+       unsigned char   multsect;       /* current multiple sector count */
+       unsigned char   multsect_valid; /* when (bit0==1) multsect is ok */
+       unsigned int    lba_capacity;   /* total number of sectors */
+       unsigned short  dma_1word;      /* single-word dma info */
+       unsigned short  dma_mword;      /* multiple-word dma info */
+       unsigned short  eide_pio_modes; /* bits 0:mode3 1:mode4 */
+       unsigned short  eide_dma_min;   /* min mword dma cycle time (ns) */
+       unsigned short  eide_dma_time;  /* recommended mword dma cycle time (ns) */
+       unsigned short  eide_pio;       /* min cycle time (ns), no IORDY  */
+       unsigned short  eide_pio_iordy; /* min cycle time (ns), with IORDY */
+       unsigned short  words69_70[2];  /* reserved words 69-70 */
+       /* HDIO_GET_IDENTITY currently returns only words 0 through 70 */
+       unsigned short  words71_74[4];  /* reserved words 71-74 */
+       unsigned short  queue_depth;    /*  */
+       unsigned short  words76_79[4];  /* reserved words 76-79 */
+       unsigned short  major_rev_num;  /*  */
+       unsigned short  minor_rev_num;  /*  */
+       unsigned short  command_set_1;  /* bits 0:Smart 1:Security 2:Removable 3:PM */
+       unsigned short  command_set_2;  /* bits 14:Smart Enabled 13:0 zero */
+       unsigned short  cfsse;          /* command set-feature supported extensions */
+       unsigned short  cfs_enable_1;   /* command set-feature enabled */
+       unsigned short  cfs_enable_2;   /* command set-feature enabled */
+       unsigned short  csf_default;    /* command set-feature default */
+       unsigned short  dma_ultra;      /*  */
+       unsigned short  word89;         /* reserved (word 89) */
+       unsigned short  word90;         /* reserved (word 90) */
+       unsigned short  CurAPMvalues;   /* current APM values */
+       unsigned short  word92;         /* reserved (word 92) */
+       unsigned short  hw_config;      /* hardware config */
+       unsigned short  words94_125[32];/* reserved words 94-125 */
+       unsigned short  last_lun;       /* reserved (word 126) */
+       unsigned short  word127;        /* reserved (word 127) */
+       unsigned short  dlf;            /* device lock function
+                                        * 15:9 reserved
+                                        * 8    security level 1:max 0:high
+                                        * 7:6  reserved
+                                        * 5    enhanced erase
+                                        * 4    expire
+                                        * 3    frozen
+                                        * 2    locked
+                                        * 1    en/disabled
+                                        * 0    capability
+                                        */
+       unsigned short  csfo;           /* current set features options
+                                        * 15:4 reserved
+                                        * 3    auto reassign
+                                        * 2    reverting
+                                        * 1    read-look-ahead
+                                        * 0    write cache
+                                        */
+       unsigned short  words130_155[26];/* reserved vendor words 130-155 */
+       unsigned short  word156;
+       unsigned short  words157_159[3];/* reserved vendor words 157-159 */
+       unsigned short  words160_255[95];/* reserved words 160-255 */
+};
+
+/*
+ * IDE "nice" flags. These are used on a per drive basis to determine
+ * when to be nice and give more bandwidth to the other devices which
+ * share the same IDE bus.
+ */
+#define IDE_NICE_DSC_OVERLAP   (0)     /* per the DSC overlap protocol */
+#define IDE_NICE_ATAPI_OVERLAP (1)     /* not supported yet */
+#define IDE_NICE_0             (2)     /* when sure that it won't affect us */
+#define IDE_NICE_1             (3)     /* when probably won't affect us much */
+#define IDE_NICE_2             (4)     /* when we know it's on our expense */
+
+#ifdef __KERNEL__
+/*
+ * These routines are used for kernel command line parameters from main.c:
+ */
+#include <linux/config.h>
+
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+int ide_register(int io_port, int ctl_port, int irq);
+void ide_unregister(unsigned int);
+#endif /* CONFIG_BLK_DEV_IDE || CONFIG_BLK_DEV_IDE_MODULE */
+
+#endif  /* __KERNEL__ */
+
+#endif /* _LINUX_HDREG_H */
diff --git a/os_solaris.c b/os_solaris.c
deleted file mode 100644 (file)
index d68197c..0000000
+++ /dev/null
@@ -1,434 +0,0 @@
-/*
- * os_solaris.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2003-6 SAWADA Keiji <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2003-6 Casper Dik <smartmontools-support@lists.sourceforge.net>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-#include <dirent.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/param.h>
-
-// These are needed to define prototypes for the functions defined below
-#include "config.h"
-#include "int64.h"
-#include "atacmds.h"
-#include "scsicmds.h"
-#include "utility.h"
-
-// This is to include whatever prototypes you define in os_solaris.h
-#include "os_solaris.h"
-
-#define ARGUSED(x) ((void)(x))
-
-extern long long bytes;
-
-static const char *filenameandversion="$Id: os_solaris.c,v 1.26 2006/04/12 14:54:28 ballen4705 Exp $";
-
-const char *os_XXXX_c_cvsid="$Id: os_solaris.c,v 1.26 2006/04/12 14:54:28 ballen4705 Exp $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_SOLARIS_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
-
-// The printwarning() function warns about unimplemented functions
-int printedout[2];
-char *unimplemented[2]={
-  "ATA command routine ata_command_interface()",
-  "3ware Escalade Controller command routine escalade_command_interface()",
-};
-
-int printwarning(int which){
-  if (!unimplemented[which])
-    return 0;
-
-  if (printedout[which])
-    return 1;
-  
-  printedout[which]=1;
-  
-  pout("\n"
-       "#######################################################################\n"
-       "%s NOT IMPLEMENTED under Solaris.\n"
-       "Please contact " PACKAGE_BUGREPORT " if\n"
-       "you want to help in porting smartmontools to Solaris.\n"
-       "#######################################################################\n"
-       "\n",
-       unimplemented[which]);
-
-  return 1;
-}
-
-// print examples for smartctl
-void print_smartctl_examples(){
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
-         "  smartctl -a /dev/rdsk/c0t0d0s0             (Prints all SMART information)\n\n"
-         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/rdsk/c0t0d0s0\n"
-         "                                              (Enables SMART on first disk)\n\n"
-         "  smartctl -t long /dev/rdsk/c0t0d0s0 (Executes extended disk self-test)\n\n"
-         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/rdsk/c0t0d0s0\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-         );
-#else
-  printf(
-         "  smartctl -a /dev/rdsk/c0t0d0s0               (Prints all SMART information)\n"
-         "  smartctl -s on -o on -S on /dev/rdsk/c0t0d0s0 (Enables SMART on first disk)\n"
-         "  smartctl -t long /dev/rdsk/c0t0d0s0      (Executes extended disk self-test)\n"
-         "  smartctl -A -l selftest -q errorsonly /dev/rdsk/c0t0d0s0\n"
-         "                                        (Prints Self-Test & Attribute errors)\n"
-         );
-#endif
-  return;
-}
-
-static const char *uscsidrvrs[] = {
-        "sd",
-        "ssd",
-        "st"
-};
-
-static const char *atadrvrs[] = {
-        "cmdk",
-        "dad",
-};
-
-static int
-isdevtype(const char *dev_name, const char *table[], int tsize)
-{
-  char devpath[MAXPATHLEN];
-  int i;
-  char *basename;
-
-  if (realpath(dev_name, devpath) == NULL)
-    return 0;
-  if ((basename = strrchr(devpath, '/')) == NULL)
-    return 0;
-
-  basename++;
-
-  for (i = 0; i < tsize; i++) {
-    int l = strlen(table[i]);
-    if (strncmp(basename, table[i], l) == 0 && basename[l] == '@')
-      return 1;
-  }
-  return 0;
-}
-
-static int
-isscsidev(const char *path)
-{
-  return isdevtype(path, uscsidrvrs, sizeof (uscsidrvrs) / sizeof (char *));
-}
-
-static int
-isatadev(const char *path)
-{
-  return isdevtype(path, atadrvrs, sizeof (atadrvrs) / sizeof (char *));
-}
-
-// tries to guess device type given the name (a path)
-int guess_device_type (const char* dev_name) {
-  if (isscsidev(dev_name))
-    return CONTROLLER_SCSI;
-  else if (isatadev(dev_name))
-    return CONTROLLER_ATA;
-  else
-    return CONTROLLER_UNKNOWN;
-}
-
-struct pathlist {
-        char **names;
-        int  nnames;
-        int  maxnames;
-};
-
-static int
-addpath(const char *path, struct pathlist *res)
-{
-        if (++res->nnames > res->maxnames) {
-                res->maxnames += 16;
-                res->names = realloc(res->names, res->maxnames * sizeof (char *));
-                if (res->names == NULL)
-                        return -1;
-                bytes += 16*sizeof(char *);
-        }
-        if (!(res->names[res->nnames-1] = CustomStrDup((char *)path, 1, __LINE__, filenameandversion)))
-                return -1;
-        return 0;
-}
-
-static int 
-grokdir(const char *dir, struct pathlist *res, int testfun(const char *))
-{
-        char pathbuf[MAXPATHLEN];
-        size_t len;
-        DIR *dp;
-        struct dirent *de;
-        int isdisk = strstr(dir, "dsk") != NULL;
-        char *p;
-
-        len = snprintf(pathbuf, sizeof (pathbuf), "%s/", dir);
-        if (len >= sizeof (pathbuf))
-                return -1;
-
-        dp = opendir(dir);
-        if (dp == NULL)
-                return 0;
-
-        while ((de = readdir(dp)) != NULL) {
-                if (de->d_name[0] == '.')
-                        continue;
-
-                if (strlen(de->d_name) + len >= sizeof (pathbuf))
-                        continue;
-
-                if (isdisk) {
-                        /* Disk represented by slice 0 */
-                        p = strstr(de->d_name, "s0");
-                        /* String doesn't end in "s0\0" */
-                        if (p == NULL || p[2] != '\0')
-                                continue;
-                } else {
-                        /* Tape drive represented by the all-digit device */
-                        for (p = de->d_name; *p; p++)
-                                if (!isdigit((int)(*p)))
-                                        break;
-                        if (*p != '\0')
-                                continue;
-                }
-                strcpy(&pathbuf[len], de->d_name);
-                if (testfun(pathbuf)) {
-                        if (addpath(pathbuf, res) == -1) {
-                                closedir(dp);
-                                return -1;
-                        }
-                }
-        }
-        closedir(dp);
-
-        return 0;
-}
-
-// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
-// smartd.  Returns number of devices, or -1 if out of memory.
-int make_device_names (char*** devlist, const char* name) {
-        struct pathlist res;
-
-        res.nnames = res.maxnames = 0;
-        res.names = NULL;
-        if (strcmp(name, "SCSI") == 0) {
-                if (grokdir("/dev/rdsk", &res, isscsidev) == -1)
-                        return -1;
-                if (grokdir("/dev/rmt", &res, isscsidev) == -1)
-                        return -1;
-       } else if (strcmp(name, "ATA") == 0) {
-                if (grokdir("/dev/rdsk", &res, isatadev) == -1)
-                        return -1;
-       } else {
-                // non-SCSI and non-ATA case not implemented
-                *devlist=NULL;
-                return 0;
-       }
-
-       // shrink array to min possible size
-       res.names = realloc(res.names, res.nnames * sizeof (char *));
-       bytes -= sizeof(char *)*(res.maxnames-res.nnames);
-
-       // pass list back
-       *devlist = res.names;
-       return res.nnames;
-}
-
-// Like open().  Return integer handle, used by functions below only.
-// type="ATA" or "SCSI".
-int deviceopen(const char *pathname, char *type){
-  if (!strcmp(type,"SCSI")) 
-    return open(pathname, O_RDWR | O_NONBLOCK);
-  else if (!strcmp(type,"ATA")) 
-    return open(pathname, O_RDONLY | O_NONBLOCK);
-  else
-    return -1;
-}
-
-// Like close().  Acts on handles returned by above function.
-int deviceclose(int fd){
-    return close(fd);
-}
-
-static void swap_sector(void *p)
-{
-    int i;
-    unsigned char t, *cp = p;
-    for(i = 0; i < 256; i++) {
-        t = cp[0]; cp[0] = cp[1]; cp[1] = t;
-        cp += 2;
-    }
-}
-
-// Interface to ATA devices.  See os_linux.c
-int marvell_command_interface(int fd, smart_command_set command, int select, char *data){
-    ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
-    return -1;
-}
-
-int ata_command_interface(int fd, smart_command_set command, int select, char *data){
-#if defined(__sparc)
-    int err;
-    switch (command){
-    case CHECK_POWER_MODE:
-       /* currently not recognized */
-       return -1;
-    case READ_VALUES:
-       return smart_read_data(fd, data);
-    case READ_THRESHOLDS:
-       return smart_read_thresholds(fd, data);
-    case READ_LOG:
-       return smart_read_log(fd, select, 1, data);
-    case IDENTIFY:
-       err = ata_identify(fd, data);
-       if(err) return err;
-       swap_sector(data);
-       return 0;
-    case PIDENTIFY:
-       err = ata_pidentify(fd, data);
-       if(err) return err;
-       swap_sector(data);
-       return 0;
-    case ENABLE:
-       return smart_enable(fd);
-    case DISABLE:
-       return smart_disable(fd);
-    case STATUS:
-       return smart_status(fd);
-    case AUTO_OFFLINE:
-       return smart_auto_offline(fd, select);
-    case AUTOSAVE:
-       return smart_auto_save(fd, select);
-    case IMMEDIATE_OFFLINE:
-       return smart_immediate_offline(fd, select);
-    case STATUS_CHECK:
-       return smart_status_check(fd);
-    default:
-       pout("Unrecognized command %d in ata_command_interface() of os_solaris.c\n", command);
-       exit(1);
-       break;
-    }
-#else /* __sparc */
-    // avoid gcc warnings//
-    fd=command=select=0;
-    data=NULL;
-
-    /* Above smart_* routines uses undocumented ioctls of "dada"
-     * driver, which is specific to SPARC Solaris.  See
-     * os_solaris_ata.s for further details. x86 Solaris seems not to
-     * provide similar or alternative interface... */
-    if (printwarning(0))
-       return -1;
-#endif
-    return -1;
-}
-
-// Interface to ATA devices behind 3ware escalade RAID controller cards.  See os_linux.c
-int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){
-  // avoid gcc warnings//
-  fd=disknum=escalade_type=command=select=0;
-  data=NULL;
-
-  if (printwarning(1))
-    return -1;
-  return -1;
-}
-
-#include <errno.h>
-#include <sys/scsi/generic/commands.h>
-#include <sys/scsi/generic/status.h>
-#include <sys/scsi/impl/types.h>
-#include <sys/scsi/impl/uscsi.h>
-
-// Interface to SCSI devices.  See os_linux.c
-int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) {
-  struct uscsi_cmd uscsi;
-
-    if (report > 0) {
-        int k;
-        const unsigned char * ucp = iop->cmnd;
-        const char * np;
-
-        np = scsi_get_opcode_name(ucp[0]);
-        pout(" [%s: ", np ? np : "<unknown opcode>");
-        for (k = 0; k < (int)iop->cmnd_len; ++k)
-            pout("%02x ", ucp[k]);
-        if ((report > 1) && 
-            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
-            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-
-            pout("]\n  Outgoing data, len=%d%s:\n", (int)iop->dxfer_len,
-                 (trunc ? " [only first 256 bytes shown]" : ""));
-            dStrHex((char *)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
-        }
-        else
-            pout("]");
-    }
-
-
-  memset(&uscsi, 0, sizeof (uscsi));
-
-  uscsi.uscsi_cdb = (void *)iop->cmnd;
-  uscsi.uscsi_cdblen = iop->cmnd_len;
-  if (iop->timeout == 0)
-    uscsi.uscsi_timeout = 60; /* XXX */
-  else
-    uscsi.uscsi_timeout = iop->timeout;
-  uscsi.uscsi_bufaddr = (void *)iop->dxferp;
-  uscsi.uscsi_buflen = iop->dxfer_len;
-  uscsi.uscsi_rqbuf = (void *)iop->sensep;
-  uscsi.uscsi_rqlen = iop->max_sense_len;
-
-  switch (iop->dxfer_dir) {
-  case DXFER_NONE:
-  case DXFER_FROM_DEVICE:
-    uscsi.uscsi_flags = USCSI_READ;
-    break;
-  case DXFER_TO_DEVICE:
-    uscsi.uscsi_flags = USCSI_WRITE;
-    break;
-  default:
-    return -EINVAL;
-  }
-  uscsi.uscsi_flags |= USCSI_ISOLATE;
-
-  if (ioctl(fd, USCSICMD, &uscsi))
-    return -errno;
-
-  iop->scsi_status = uscsi.uscsi_status;
-  iop->resid = uscsi.uscsi_resid;
-  iop->resp_sense_len = iop->max_sense_len - uscsi.uscsi_rqresid;
-
-  if (report > 0) {
-    int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-    pout("  status=0\n");
-    
-    pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
-         (trunc ? " [only first 256 bytes shown]" : ""));
-    dStrHex((char *)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
-  }
-
-  return (0);
-}
diff --git a/os_solaris.cpp b/os_solaris.cpp
new file mode 100644 (file)
index 0000000..980ed16
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * os_solaris.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 SAWADA Keiji <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-6 Casper Dik <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+// These are needed to define prototypes for the functions defined below
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+// This is to include whatever prototypes you define in os_solaris.h
+#include "os_solaris.h"
+
+#define ARGUSED(x) ((void)(x))
+
+extern long long bytes;
+
+static const char *filenameandversion="$Id: os_solaris.cpp,v 1.28 2006/08/25 06:06:25 sxzzsf Exp $";
+
+const char *os_XXXX_c_cvsid="$Id: os_solaris.cpp,v 1.28 2006/08/25 06:06:25 sxzzsf Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_SOLARIS_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+// The printwarning() function warns about unimplemented functions
+int printedout[2];
+char *unimplemented[2]={
+  "ATA command routine ata_command_interface()",
+  "3ware Escalade Controller command routine escalade_command_interface()",
+};
+
+int printwarning(int which){
+  if (!unimplemented[which])
+    return 0;
+
+  if (printedout[which])
+    return 1;
+  
+  printedout[which]=1;
+  
+  pout("\n"
+       "#######################################################################\n"
+       "%s NOT IMPLEMENTED under Solaris.\n"
+       "Please contact " PACKAGE_BUGREPORT " if\n"
+       "you want to help in porting smartmontools to Solaris.\n"
+       "#######################################################################\n"
+       "\n",
+       unimplemented[which]);
+
+  return 1;
+}
+
+// print examples for smartctl
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+         "  smartctl -a /dev/rdsk/c0t0d0s0             (Prints all SMART information)\n\n"
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/rdsk/c0t0d0s0\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl -t long /dev/rdsk/c0t0d0s0 (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/rdsk/c0t0d0s0\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         );
+#else
+  printf(
+         "  smartctl -a /dev/rdsk/c0t0d0s0               (Prints all SMART information)\n"
+         "  smartctl -s on -o on -S on /dev/rdsk/c0t0d0s0 (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/rdsk/c0t0d0s0      (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/rdsk/c0t0d0s0\n"
+         "                                        (Prints Self-Test & Attribute errors)\n"
+         );
+#endif
+  return;
+}
+
+static const char *uscsidrvrs[] = {
+        "sd",
+        "ssd",
+        "st"
+};
+
+static const char *atadrvrs[] = {
+        "cmdk",
+        "dad",
+};
+
+static int
+isdevtype(const char *dev_name, const char *table[], int tsize)
+{
+  char devpath[MAXPATHLEN];
+  int i;
+  char *basename;
+
+  if (realpath(dev_name, devpath) == NULL)
+    return 0;
+  if ((basename = strrchr(devpath, '/')) == NULL)
+    return 0;
+
+  basename++;
+
+  for (i = 0; i < tsize; i++) {
+    int l = strlen(table[i]);
+    if (strncmp(basename, table[i], l) == 0 && basename[l] == '@')
+      return 1;
+  }
+  return 0;
+}
+
+static int
+isscsidev(const char *path)
+{
+  return isdevtype(path, uscsidrvrs, sizeof (uscsidrvrs) / sizeof (char *));
+}
+
+static int
+isatadev(const char *path)
+{
+  return isdevtype(path, atadrvrs, sizeof (atadrvrs) / sizeof (char *));
+}
+
+// tries to guess device type given the name (a path)
+int guess_device_type (const char* dev_name) {
+  if (isscsidev(dev_name))
+    return CONTROLLER_SCSI;
+  else if (isatadev(dev_name))
+    return CONTROLLER_ATA;
+  else
+    return CONTROLLER_UNKNOWN;
+}
+
+struct pathlist {
+        char **names;
+        int  nnames;
+        int  maxnames;
+};
+
+static int
+addpath(const char *path, struct pathlist *res)
+{
+        if (++res->nnames > res->maxnames) {
+                res->maxnames += 16;
+                res->names = static_cast<char**>(realloc(res->names, res->maxnames * sizeof (char *)));
+                if (res->names == NULL)
+                        return -1;
+                bytes += 16*sizeof(char *);
+        }
+        if (!(res->names[res->nnames-1] = CustomStrDup((char *)path, 1, __LINE__, filenameandversion)))
+                return -1;
+        return 0;
+}
+
+static int 
+grokdir(const char *dir, struct pathlist *res, int testfun(const char *))
+{
+        char pathbuf[MAXPATHLEN];
+        size_t len;
+        DIR *dp;
+        struct dirent *de;
+        int isdisk = strstr(dir, "dsk") != NULL;
+        char *p;
+
+        len = snprintf(pathbuf, sizeof (pathbuf), "%s/", dir);
+        if (len >= sizeof (pathbuf))
+                return -1;
+
+        dp = opendir(dir);
+        if (dp == NULL)
+                return 0;
+
+        while ((de = readdir(dp)) != NULL) {
+                if (de->d_name[0] == '.')
+                        continue;
+
+                if (strlen(de->d_name) + len >= sizeof (pathbuf))
+                        continue;
+
+                if (isdisk) {
+                        /* Disk represented by slice 0 */
+                        p = strstr(de->d_name, "s0");
+                        /* String doesn't end in "s0\0" */
+                        if (p == NULL || p[2] != '\0')
+                                continue;
+                } else {
+                        /* Tape drive represented by the all-digit device */
+                        for (p = de->d_name; *p; p++)
+                                if (!isdigit((int)(*p)))
+                                        break;
+                        if (*p != '\0')
+                                continue;
+                }
+                strcpy(&pathbuf[len], de->d_name);
+                if (testfun(pathbuf)) {
+                        if (addpath(pathbuf, res) == -1) {
+                                closedir(dp);
+                                return -1;
+                        }
+                }
+        }
+        closedir(dp);
+
+        return 0;
+}
+
+// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
+// smartd.  Returns number of devices, or -1 if out of memory.
+int make_device_names (char*** devlist, const char* name) {
+        struct pathlist res;
+
+        res.nnames = res.maxnames = 0;
+        res.names = NULL;
+        if (strcmp(name, "SCSI") == 0) {
+                if (grokdir("/dev/rdsk", &res, isscsidev) == -1)
+                        return -1;
+                if (grokdir("/dev/rmt", &res, isscsidev) == -1)
+                        return -1;
+       } else if (strcmp(name, "ATA") == 0) {
+                if (grokdir("/dev/rdsk", &res, isatadev) == -1)
+                        return -1;
+       } else {
+                // non-SCSI and non-ATA case not implemented
+                *devlist=NULL;
+                return 0;
+       }
+
+       // shrink array to min possible size
+       res.names = static_cast<char**>(realloc(res.names, res.nnames * sizeof (char *)));
+       bytes -= sizeof(char *)*(res.maxnames-res.nnames);
+
+       // pass list back
+       *devlist = res.names;
+       return res.nnames;
+}
+
+// Like open().  Return integer handle, used by functions below only.
+// type="ATA" or "SCSI".
+int deviceopen(const char *pathname, char *type){
+  if (!strcmp(type,"SCSI")) 
+    return open(pathname, O_RDWR | O_NONBLOCK);
+  else if (!strcmp(type,"ATA")) 
+    return open(pathname, O_RDONLY | O_NONBLOCK);
+  else
+    return -1;
+}
+
+// Like close().  Acts on handles returned by above function.
+int deviceclose(int fd){
+    return close(fd);
+}
+
+#if defined(__sparc)
+// swap each 2-byte pairs in a sector
+static void swap_sector(void *p)
+{
+    int i;
+    char t, *cp = static_cast<char*>(p);
+    for(i = 0; i < 256; i++) {
+        t = cp[0]; cp[0] = cp[1]; cp[1] = t;
+        cp += 2;
+    }
+}
+#endif
+
+// Interface to ATA devices.  See os_linux.c
+int marvell_command_interface(int fd, smart_command_set command, int select, char *data){
+    ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
+    return -1;
+}
+
+int highpoint_command_interface(int fd, smart_command_set command, int select, char *data)
+{
+    ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
+    return -1;
+}
+
+int ata_command_interface(int fd, smart_command_set command, int select, char *data){
+#if defined(__sparc)
+    int err;
+    switch (command){
+    case CHECK_POWER_MODE:
+       /* currently not recognized */
+       return -1;
+    case READ_VALUES:
+       return smart_read_data(fd, data);
+    case READ_THRESHOLDS:
+       return smart_read_thresholds(fd, data);
+    case READ_LOG:
+       return smart_read_log(fd, select, 1, data);
+    case IDENTIFY:
+       err = ata_identify(fd, data);
+       if(err) return err;
+       swap_sector(static_cast<void*>(data));
+       return 0;
+    case PIDENTIFY:
+       err = ata_pidentify(fd, data);
+       if(err) return err;
+       swap_sector(static_cast<void*>(data));
+       return 0;
+    case ENABLE:
+       return smart_enable(fd);
+    case DISABLE:
+       return smart_disable(fd);
+    case STATUS:
+       return smart_status(fd);
+    case AUTO_OFFLINE:
+       return smart_auto_offline(fd, select);
+    case AUTOSAVE:
+       return smart_auto_save(fd, select);
+    case IMMEDIATE_OFFLINE:
+       return smart_immediate_offline(fd, select);
+    case STATUS_CHECK:
+       return smart_status_check(fd);
+    default:
+       pout("Unrecognized command %d in ata_command_interface() of os_solaris.c\n", command);
+       exit(1);
+       break;
+    }
+#else /* __sparc */
+    ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
+
+    /* Above smart_* routines uses undocumented ioctls of "dada"
+     * driver, which is specific to SPARC Solaris.  See
+     * os_solaris_ata.s for further details. x86 Solaris seems not to
+     * provide similar or alternative interface... */
+    if (printwarning(0))
+       return -1;
+#endif
+    return -1;
+}
+
+// Interface to ATA devices behind 3ware escalade RAID controller cards.  See os_linux.c
+int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){
+  ARGUSED(fd);  ARGUSED(disknum);  ARGUSED(escalade_type);
+  ARGUSED(command);  ARGUSED(select);  ARGUSED(data); 
+
+  if (printwarning(1))
+    return -1;
+  return -1;
+}
+
+#include <errno.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/generic/status.h>
+#include <sys/scsi/impl/types.h>
+#include <sys/scsi/impl/uscsi.h>
+
+// Interface to SCSI devices.  See os_linux.c
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) {
+  struct uscsi_cmd uscsi;
+
+    if (report > 0) {
+        int k;
+        const unsigned char * ucp = iop->cmnd;
+        const char * np;
+
+        np = scsi_get_opcode_name(ucp[0]);
+        pout(" [%s: ", np ? np : "<unknown opcode>");
+        for (k = 0; k < (int)iop->cmnd_len; ++k)
+            pout("%02x ", ucp[k]);
+        if ((report > 1) && 
+            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+            pout("]\n  Outgoing data, len=%d%s:\n", (int)iop->dxfer_len,
+                 (trunc ? " [only first 256 bytes shown]" : ""));
+            dStrHex((char *)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+        }
+        else
+            pout("]");
+    }
+
+
+  memset(&uscsi, 0, sizeof (uscsi));
+
+  uscsi.uscsi_cdb = reinterpret_cast<char*>(iop->cmnd);
+  uscsi.uscsi_cdblen = iop->cmnd_len;
+  if (iop->timeout == 0)
+    uscsi.uscsi_timeout = 60; /* XXX */
+  else
+    uscsi.uscsi_timeout = iop->timeout;
+  uscsi.uscsi_bufaddr = reinterpret_cast<char*>(iop->dxferp);
+  uscsi.uscsi_buflen = iop->dxfer_len;
+  uscsi.uscsi_rqbuf = reinterpret_cast<char*>(iop->sensep);
+  uscsi.uscsi_rqlen = iop->max_sense_len;
+
+  switch (iop->dxfer_dir) {
+  case DXFER_NONE:
+  case DXFER_FROM_DEVICE:
+    uscsi.uscsi_flags = USCSI_READ;
+    break;
+  case DXFER_TO_DEVICE:
+    uscsi.uscsi_flags = USCSI_WRITE;
+    break;
+  default:
+    return -EINVAL;
+  }
+  uscsi.uscsi_flags |= USCSI_ISOLATE;
+
+  if (ioctl(fd, USCSICMD, &uscsi))
+    return -errno;
+
+  iop->scsi_status = uscsi.uscsi_status;
+  iop->resid = uscsi.uscsi_resid;
+  iop->resp_sense_len = iop->max_sense_len - uscsi.uscsi_rqresid;
+
+  if (report > 0) {
+    int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+    pout("  status=0\n");
+    
+    pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+         (trunc ? " [only first 256 bytes shown]" : ""));
+    dStrHex((char *)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+  }
+
+  return (0);
+}
index 202dcac7af78f122efbaa6f206abda9f0fcf6c46..4d3d7945495eb5316935746d4fd6a1429d4b4b25 100644 (file)
@@ -25,7 +25,7 @@
 #ifndef OS_SOLARIS_H_
 #define OS_SOLARIS_H_
 
-#define OS_SOLARIS_H_CVSID "$Id: os_solaris.h,v 1.12 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define OS_SOLARIS_H_CVSID "$Id: os_solaris.h,v 1.13 2006/08/12 05:41:13 card_captor Exp $\n"
 
 // Additional material should start here.  Note: to keep the '-V' CVS
 // reporting option working as intended, you should only #include
 #include <fcntl.h>
 
 // function prototypes for functions defined in os_solaris_ata.s
-int smart_read_data(int fd, void *data);
-int smart_read_thresholds(int fd, void *data);
-int smart_read_log(int fd, int s, int count, void *data);
-int ata_identify(int fd, void *data);
-int ata_pidentify(int fd, void *data);
-int smart_enable(int fd);
-int smart_disable(int fd);
-int smart_status(int fd);
-int smart_auto_offline(int fd, int s);
-int smart_auto_save(int fd, int s);
-int smart_immediate_offline(int fd, int s);
-int smart_status_check(int fd);
+extern "C" {
+  int smart_read_data(int fd, void *data);
+  int smart_read_thresholds(int fd, void *data);
+  int smart_read_log(int fd, int s, int count, void *data);
+  int ata_identify(int fd, void *data);
+  int ata_pidentify(int fd, void *data);
+  int smart_enable(int fd);
+  int smart_disable(int fd);
+  int smart_status(int fd);
+  int smart_auto_offline(int fd, int s);
+  int smart_auto_save(int fd, int s);
+  int smart_immediate_offline(int fd, int s);
+  int smart_status_check(int fd);
+}
 
 // wrapper macros
 #define smart_enable_auto_save(fd)     smart_auto_save(fd, 0xf1)
diff --git a/os_win32.c b/os_win32.c
deleted file mode 100644 (file)
index a210e39..0000000
+++ /dev/null
@@ -1,1619 +0,0 @@
-/*
- * os_win32.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include "config.h"
-#include "int64.h"
-#include "atacmds.h"
-#include "extern.h"
-extern smartmonctrl * con; // con->permissive,reportataioctl
-#include "scsicmds.h"
-#include "utility.h"
-extern int64_t bytes; // malloc() byte count
-
-#include <errno.h>
-#ifdef _DEBUG
-#include <assert.h>
-#else
-#define assert(x) /**/
-#endif
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <stddef.h> // offsetof()
-#include <io.h> // access()
-
-#define ARGUSED(x) ((void)(x))
-
-// Needed by '-V' option (CVS versioning) of smartd/smartctl
-const char *os_XXXX_c_cvsid="$Id: os_win32.c,v 1.33 2006/04/07 15:02:19 chrfranke Exp $"
-ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
-
-
-#ifndef HAVE_GET_OS_VERSION_STR
-#error define of HAVE_GET_OS_VERSION_STR missing in config.h
-#endif
-
-// Return build host and OS version as static string
-const char * get_os_version_str()
-{
-       static char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1+sizeof("-2003r2-sp2.1")+13];
-       char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1;
-       const int vlen = sizeof(vstr)-(sizeof(SMARTMONTOOLS_BUILD_HOST)-3);
-
-       OSVERSIONINFOEXA vi;
-       const char * w;
-
-       // remove "-pc" to avoid long lines
-       assert(!strncmp(SMARTMONTOOLS_BUILD_HOST+5, "pc-", 3));
-       strcpy(vstr, "i686-"); strcpy(vstr+5, SMARTMONTOOLS_BUILD_HOST+5+3);
-       assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
-
-       memset(&vi, 0, sizeof(vi));
-       vi.dwOSVersionInfoSize = sizeof(vi);
-       if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
-               memset(&vi, 0, sizeof(vi));
-               vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
-               if (!GetVersionExA((OSVERSIONINFOA *)&vi))
-                       return vstr;
-       }
-
-       if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff)
-               return vstr;
-
-       switch (vi.dwPlatformId << 16 | vi.dwMajorVersion << 8 | vi.dwMinorVersion) {
-         case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400| 0:
-               w = (vi.szCSDVersion[1] == 'B' ||
-                    vi.szCSDVersion[1] == 'C'     ? "95-osr2" : "95");    break;
-         case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|10:
-               w = (vi.szCSDVersion[1] == 'A'     ? "98se"    : "98");    break;
-         case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|90: w = "me";     break;
-       //case VER_PLATFORM_WIN32_NT     <<16|0x0300|51: w = "nt3.51"; break;
-         case VER_PLATFORM_WIN32_NT     <<16|0x0400| 0: w = "nt4";    break;
-         case VER_PLATFORM_WIN32_NT     <<16|0x0500| 0: w = "2000";   break;
-         case VER_PLATFORM_WIN32_NT     <<16|0x0500| 1:
-               w = (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ?   "xp"
-                                                            :   "xp-mc"); break;
-         case VER_PLATFORM_WIN32_NT     <<16|0x0500| 2:
-               w = (!GetSystemMetrics(89/*SM_SERVERR2*/) ?      "2003"
-                                                            :   "2003r2"); break;
-         case VER_PLATFORM_WIN32_NT     <<16|0x0600| 0: w = "vista";  break;
-         default: w = 0; break;
-       }
-
-       if (!w)
-               snprintf(vptr, vlen, "-%s%lu.%lu",
-                       (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
-                       vi.dwMajorVersion, vi.dwMinorVersion);
-       else if (vi.wServicePackMinor)
-               snprintf(vptr, vlen, "-%s-sp%u.%u", w, vi.wServicePackMajor, vi.wServicePackMinor);
-       else if (vi.wServicePackMajor)
-               snprintf(vptr, vlen, "-%s-sp%u", w, vi.wServicePackMajor);
-       else
-               snprintf(vptr, vlen, "-%s", w);
-       return vstr;
-}
-
-
-static int ata_open(int drive);
-static void ata_close(int fd);
-static int ata_scan(unsigned long * drives);
-
-static int aspi_open(unsigned adapter, unsigned id);
-static void aspi_close(int fd);
-static int aspi_scan(unsigned long * drives);
-
-
-static int is_permissive()
-{
-       if (con->permissive <= 0) {
-               pout("To continue, add one or more '-T permissive' options.\n");
-               return 0;
-       }
-       con->permissive--;
-       return 1;
-}
-
-static const char * skipdev(const char * s)
-{
-       return (!strncmp(s, "/dev/", 5) ? s + 5 : s);
-}
-
-
-// tries to guess device type given the name (a path).  See utility.h
-// for return values.
-int guess_device_type (const char * dev_name)
-{
-       dev_name = skipdev(dev_name);
-       if (!strncmp(dev_name, "hd", 2))
-               return CONTROLLER_ATA;
-       if (!strncmp(dev_name, "scsi", 4))
-               return CONTROLLER_SCSI;
-       return CONTROLLER_UNKNOWN;
-}
-
-
-// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
-// smartd.  Returns number N of devices, or -1 if out of
-// memory. Allocates N+1 arrays: one of N pointers (devlist), the
-// others each contain null-terminated character strings.
-int make_device_names (char*** devlist, const char* type)
-{
-       unsigned long drives[3];
-       int i, j, n, nmax, sz;
-       const char * path;
-
-       drives[0] = drives[1] = drives[2] = 0;
-       if (!strcmp(type, "ATA")) {
-               // bit i set => drive i present
-               n = ata_scan(drives);
-               path = "/dev/hda";
-               nmax = 10;
-       }
-       else if (!strcmp(type, "SCSI")) {
-               // bit i set => drive with ID (i & 0x7) on adapter (i >> 3) present
-               n = aspi_scan(drives);
-               path = "/dev/scsi00";
-               nmax = 10*8;
-       }
-       else
-               return -1;
-
-       if (n <= 0)
-               return 0;
-
-       // Alloc devlist
-       sz = n * sizeof(char **);
-       *devlist = (char **)malloc(sz); bytes += sz;
-
-       // Add devices
-       for (i = j = 0; i < n; i++) {
-               char * s;
-               sz = strlen(path)+1;
-               s = (char *)malloc(sz); bytes += sz;
-               strcpy(s, path);
-               while (j < nmax && !(drives[j >> 5] & (1L << (j & 0x1f))))
-                       j++;
-               assert(j < nmax);
-               if (nmax <= 10) {
-                       assert(j <= 9);
-                       s[sz-2] += j; // /dev/hd[a-j]
-               }
-               else {
-                       assert((j >> 3) <= 9);
-                       s[sz-3] += (j >> 3);  // /dev/scsi[0-9].....
-                       s[sz-2] += (j & 0x7); //          .....[0-7]
-               }
-               (*devlist)[i] = s;
-               j++;
-       }
-       return n;
-}
-
-
-// Like open().  Return positive integer handle, only used by
-// functions below.  type="ATA" or "SCSI".  If you need to store extra
-// information about your devices, create a private internal array
-// within this file (see os_freebsd.c for an example).
-int deviceopen(const char * pathname, char *type)
-{
-       int len;
-       pathname = skipdev(pathname);
-       len = strlen(pathname);
-
-       if (!strcmp(type, "ATA")) {
-               // hd[a-z] => ATA 0-9
-               if (!(len  == 3 && pathname[0] == 'h' && pathname[1] == 'd'
-                         && 'a' <= pathname[2] && pathname[2] <= 'j')) {
-                       errno = ENOENT;
-                       return -1;
-               }
-               return ata_open(pathname[2] - 'a');
-       }
-
-       if (!strcmp(type, "SCSI")) {
-               // scsi[0-9][0-f] => SCSI Adapter 0-9, ID 0-15, LUN 0
-               unsigned adapter = ~0, id = ~0; int n = -1;
-               if (!(sscanf(pathname,"scsi%1u%1x%n", &adapter, &id, &n) == 2 && n == len)) {
-                       errno = ENOENT;
-                       return -1;
-               }
-               return aspi_open(adapter, id);
-       }
-       errno = ENOENT;
-       return -1;
-}
-
-
-// Like close().  Acts only on handles returned by above function.
-// (Never called in smartctl!)
-int deviceclose(int fd)
-{
-       if (fd < 0x100) {
-               ata_close(fd);
-       }
-       else {
-               aspi_close(fd);
-       }
-       return 0;
-}
-
-
-// print examples for smartctl
-void print_smartctl_examples(){
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
-         "  smartctl -a /dev/hda                       (Prints all SMART information)\n\n"
-#ifdef HAVE_GETOPT_LONG
-         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
-         "                                              (Enables SMART on first disk)\n\n"
-         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n\n"
-         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-#else
-         "  smartctl -s on -o on -S on /dev/hda         (Enables SMART on first disk)\n"
-         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n"
-         "  smartctl -A -l selftest -q errorsonly /dev/hda\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-#endif
-         "  smartctl -a /dev/scsi21\n"
-         "             (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
-  );
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// ATA Interface
-/////////////////////////////////////////////////////////////////////////////
-
-// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
-
-// Deklarations from:
-// http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntdddisk.h?rev=1.3
-
-#define FILE_READ_ACCESS       0x0001
-#define FILE_WRITE_ACCESS      0x0002
-#define METHOD_BUFFERED             0
-#define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
-
-#define FILE_DEVICE_DISK          7
-#define IOCTL_DISK_BASE        FILE_DEVICE_DISK
-
-#define SMART_GET_VERSION \
-  CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS)
-
-#define SMART_RCV_DRIVE_DATA \
-  CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
-
-#define SMART_SEND_DRIVE_COMMAND \
-  CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
-
-#define SMART_CYL_LOW  0x4F
-#define SMART_CYL_HI   0xC2
-
-#pragma pack(1)
-
-typedef struct _GETVERSIONOUTPARAMS {
-       UCHAR  bVersion;
-       UCHAR  bRevision;
-       UCHAR  bReserved;
-       UCHAR  bIDEDeviceMap;
-       ULONG  fCapabilities;
-       ULONG  dwReserved[4];
-} GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS;
-
-typedef struct _IDEREGS {
-       UCHAR  bFeaturesReg;
-       UCHAR  bSectorCountReg;
-       UCHAR  bSectorNumberReg;
-       UCHAR  bCylLowReg;
-       UCHAR  bCylHighReg;
-       UCHAR  bDriveHeadReg;
-       UCHAR  bCommandReg;
-       UCHAR  bReserved;
-} IDEREGS, *PIDEREGS, *LPIDEREGS;
-
-typedef struct _SENDCMDINPARAMS {
-       ULONG  cBufferSize;
-       IDEREGS  irDriveRegs;
-       UCHAR  bDriveNumber;
-       UCHAR  bReserved[3];
-       ULONG  dwReserved[4];
-       UCHAR  bBuffer[1];
-} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS;
-
-/* DRIVERSTATUS.bDriverError constants (just for info, not used)
-#define SMART_NO_ERROR                    0
-#define SMART_IDE_ERROR                   1
-#define SMART_INVALID_FLAG                2
-#define SMART_INVALID_COMMAND             3
-#define SMART_INVALID_BUFFER              4
-#define SMART_INVALID_DRIVE               5
-#define SMART_INVALID_IOCTL               6
-#define SMART_ERROR_NO_MEM                7
-#define SMART_INVALID_REGISTER            8
-#define SMART_NOT_SUPPORTED               9
-#define SMART_NO_IDE_DEVICE               10
-*/
-
-typedef struct _DRIVERSTATUS {
-       UCHAR  bDriverError;
-       UCHAR  bIDEError;
-       UCHAR  bReserved[2];
-       ULONG  dwReserved[2];
-} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS;
-
-typedef struct _SENDCMDOUTPARAMS {
-       ULONG  cBufferSize;
-       DRIVERSTATUS  DriverStatus;
-       UCHAR  bBuffer[1];
-} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS;
-
-#pragma pack()
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-static void print_ide_regs(const IDEREGS * r, int out)
-{
-       pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, NS=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
-       (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
-       r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
-}
-
-static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
-{
-       pout("    Input : "); print_ide_regs(ri, 0);
-       if (ro) {
-               pout("    Output: "); print_ide_regs(ro, 1);
-       }
-}
-
-/////////////////////////////////////////////////////////////////////////////
-
-// call SMART_* ioctl
-
-static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize)
-{
-       SENDCMDINPARAMS inpar;
-       unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
-       const SENDCMDOUTPARAMS * outpar;
-       DWORD code, num_out;
-       unsigned int size_out;
-       const char * name;
-
-       assert(SMART_SEND_DRIVE_COMMAND == 0x07c084);
-       assert(SMART_RCV_DRIVE_DATA == 0x07c088);
-       assert(sizeof(SENDCMDINPARAMS)-1 == 32);
-       assert(sizeof(SENDCMDOUTPARAMS)-1 == 16);
-
-       memset(&inpar, 0, sizeof(inpar));
-       inpar.irDriveRegs = *regs;
-       // drive is set to 0-3 on Win9x only
-       inpar.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4);
-       inpar.bDriveNumber = drive;
-
-       assert(datasize == 0 || datasize == 512);
-       if (datasize) {
-               code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
-               inpar.cBufferSize = size_out = 512;
-       }
-       else {
-               code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
-               if (regs->bFeaturesReg == ATA_SMART_STATUS)
-                       size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
-                       // Note: cBufferSize must be 0 on Win9x
-               else
-                       size_out = 0;
-       }
-
-       memset(&outbuf, 0, sizeof(outbuf));
-
-       if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
-               outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
-               // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()
-               long err = GetLastError();
-               if (con->reportataioctl && (err != ERROR_INVALID_PARAMETER || con->reportataioctl > 1)) {
-                       pout("  %s failed, Error=%ld\n", name, err);
-                       print_ide_regs_io(regs, NULL);
-               }
-               errno = (   err == ERROR_INVALID_FUNCTION /*9x*/
-                        || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/ ? ENOSYS : EIO);
-               return -1;
-       }
-       // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
-
-       outpar = (const SENDCMDOUTPARAMS *)outbuf;
-
-       if (outpar->DriverStatus.bDriverError) {
-               if (con->reportataioctl) {
-                       pout("  %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
-                               outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
-                       print_ide_regs_io(regs, NULL);
-               }
-               errno = EIO;
-               return -1;
-       }
-
-       if (con->reportataioctl > 1) {
-               pout("  %s suceeded, bytes returned: %lu (buffer %lu)\n", name,
-                       num_out, outpar->cBufferSize);
-               print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
-                       (const IDEREGS *)(outpar->bBuffer) : NULL));
-       }
-
-       if (datasize)
-               memcpy(data, outpar->bBuffer, 512);
-       else if (regs->bFeaturesReg == ATA_SMART_STATUS)
-               *regs = *(const IDEREGS *)(outpar->bBuffer);
-
-       return 0;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-// IDE PASS THROUGH for W2K/XP (does not work on W9x/NT4)
-// Only used for SMART commands not supported by SMART_* IOCTLs
-//
-// Based on WinATA.cpp, 2002 c't/Matthias Withopf
-// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
-
-#define FILE_DEVICE_CONTROLLER  4
-#define IOCTL_SCSI_BASE         FILE_DEVICE_CONTROLLER
-
-#define IOCTL_IDE_PASS_THROUGH \
-  CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
-
-#pragma pack(1)
-
-typedef struct {
-       IDEREGS IdeReg;
-       ULONG   DataBufferSize;
-       UCHAR   DataBuffer[1];
-} ATA_PASS_THROUGH;
-
-#pragma pack()
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
-{ 
-       unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
-       ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
-       DWORD num_out;
-       const unsigned char magic = 0xcf;
-       assert(sizeof(ATA_PASS_THROUGH)-1 == 12);
-       assert(IOCTL_IDE_PASS_THROUGH == 0x04d028);
-
-       if (!buf) {
-               errno = ENOMEM;
-               return -1;
-       }
-
-       buf->IdeReg = *regs;
-       buf->DataBufferSize = datasize;
-       if (datasize)
-               buf->DataBuffer[0] = magic;
-
-       if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
-               buf, size, buf, size, &num_out, NULL)) {
-               long err = GetLastError();
-               if (con->reportataioctl)
-                       pout("  IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
-               VirtualFree(buf, 0, MEM_RELEASE);
-               errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
-               return -1;
-       }
-
-       // Check ATA status
-       if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
-               if (con->reportataioctl) {
-                       pout("  IOCTL_IDE_PASS_THROUGH command failed:\n");
-                       print_ide_regs_io(regs, &buf->IdeReg);
-               }
-               VirtualFree(buf, 0, MEM_RELEASE);
-               errno = EIO;
-               return -1;
-       }
-
-       // Check and copy data
-       if (datasize) {
-               if (   num_out != size
-                   || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
-                       if (con->reportataioctl) {
-                               pout("  IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
-                                       num_out, buf->DataBufferSize);
-                               print_ide_regs_io(regs, &buf->IdeReg);
-                       }
-                       VirtualFree(buf, 0, MEM_RELEASE);
-                       errno = EIO;
-                       return -1;
-               }
-               memcpy(data, buf->DataBuffer, datasize);
-       }
-
-       if (con->reportataioctl > 1) {
-               pout("  IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
-                       num_out, buf->DataBufferSize);
-               print_ide_regs_io(regs, &buf->IdeReg);
-       }
-       *regs = buf->IdeReg;
-
-       // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
-       VirtualFree(buf, 0, MEM_RELEASE);
-       return 0;
-}
-
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-// ATA PASS THROUGH via SCSI PASS THROUGH for NT4 only
-// Only used for SMART commands not supported by SMART_* IOCTLs
-
-// Declarations from:
-// http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntddscsi.h?rev=1.2
-
-#define IOCTL_SCSI_PASS_THROUGH \
-       CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
-
-#define SCSI_IOCTL_DATA_OUT          0
-#define SCSI_IOCTL_DATA_IN           1
-#define SCSI_IOCTL_DATA_UNSPECIFIED  2
-// undocumented SCSI opcode to for ATA passthrough
-#define SCSIOP_ATA_PASSTHROUGH    0xCC
-
-typedef struct _SCSI_PASS_THROUGH {
-       USHORT  Length;
-       UCHAR  ScsiStatus;
-       UCHAR  PathId;
-       UCHAR  TargetId;
-       UCHAR  Lun;
-       UCHAR  CdbLength;
-       UCHAR  SenseInfoLength;
-       UCHAR  DataIn;
-       ULONG  DataTransferLength;
-       ULONG  TimeOutValue;
-       ULONG/*_PTR*/ DataBufferOffset;
-       ULONG  SenseInfoOffset;
-       UCHAR  Cdb[16];
-} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
-{
-       typedef struct {
-               SCSI_PASS_THROUGH spt;
-               ULONG Filler;
-               UCHAR ucSenseBuf[32];
-               UCHAR ucDataBuf[512];
-       } SCSI_PASS_THROUGH_WITH_BUFFERS;
-
-       SCSI_PASS_THROUGH_WITH_BUFFERS sb;
-       IDEREGS * cdbregs;
-       unsigned int size;
-       DWORD num_out;
-       const unsigned char magic = 0xcf;
-
-       assert(sizeof(SCSI_PASS_THROUGH) == 44);
-       assert(IOCTL_SCSI_PASS_THROUGH == 0x04d004);
-
-       memset(&sb, 0, sizeof(sb));
-       sb.spt.Length = sizeof(SCSI_PASS_THROUGH);
-       //sb.spt.PathId = 0;
-       sb.spt.TargetId = 1;
-       //sb.spt.Lun = 0;
-       sb.spt.CdbLength = 10; sb.spt.SenseInfoLength = 24;
-       sb.spt.TimeOutValue = 10;
-       sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
-       size = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
-       sb.spt.DataBufferOffset = size;
-       if (datasize) {
-               if (datasize > sizeof(sb.ucDataBuf)) {
-                       errno = EINVAL;
-                       return -1;
-               }
-               sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
-               sb.spt.DataTransferLength = datasize;
-               size += datasize;
-               sb.ucDataBuf[0] = magic;
-       }
-       else {
-               sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
-               //sb.spt.DataTransferLength = 0;
-       }
-
-       // Use pseudo SCSI command followed by registers
-       sb.spt.Cdb[0] = SCSIOP_ATA_PASSTHROUGH;
-       cdbregs = (IDEREGS *)(sb.spt.Cdb+2);
-       *cdbregs = *regs;
-
-       if (!DeviceIoControl(hdevice, IOCTL_SCSI_PASS_THROUGH,
-               &sb, size, &sb, size, &num_out, NULL)) {
-               long err = GetLastError();
-               if (con->reportataioctl)
-                       pout("  ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err);
-               errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
-               return -1;
-       }
-
-       // Cannot check ATA status, because command does not return IDEREGS
-
-       // Check and copy data
-       if (datasize) {
-               if (   num_out != size
-                   || (sb.ucDataBuf[0] == magic && !nonempty(sb.ucDataBuf+1, datasize-1))) {
-                       if (con->reportataioctl) {
-                               pout("  ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out);
-                               print_ide_regs_io(regs, NULL);
-                       }
-                       errno = EIO;
-                       return -1;
-               }
-               memcpy(data, sb.ucDataBuf, datasize);
-       }
-
-       if (con->reportataioctl > 1) {
-               pout("  ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
-               print_ide_regs_io(regs, NULL);
-       }
-       return 0;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-static HANDLE h_ata_ioctl = 0;
-
-
-// Print SMARTVSD error message, return errno
-
-static int smartvsd_error()
-{
-       char path[MAX_PATH];
-       unsigned len;
-       if (!(5 <= (len = GetSystemDirectoryA(path, MAX_PATH)) && len < MAX_PATH/2))
-               return ENOENT;
-       // SMARTVSD.VXD present?
-       strcpy(path+len, "\\IOSUBSYS\\SMARTVSD.VXD");
-       if (!access(path, 0)) {
-               // Yes, standard IDE driver used?
-               HANDLE h;
-               if (   (h = CreateFileA("\\\\.\\ESDI_506",
-                            GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
-                            NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE
-                   && GetLastError() == ERROR_FILE_NOT_FOUND                             ) {
-                       pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n");
-                       return ENOENT;
-               }
-               else {
-                       if (h != INVALID_HANDLE_VALUE) // should not happen
-                               CloseHandle(h);
-                       pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");
-                       return ENOSYS;
-               }
-       }
-       else {
-               // Some Windows versions install SMARTVSD.VXD in SYSTEM directory
-               // http://support.microsoft.com/default.aspx?scid=kb;en-us;265854
-               strcpy(path+len, "\\SMARTVSD.VXD");
-               if (!access(path, 0)) {
-                       path[len] = 0;
-                       pout("SMART driver is not properly installed,\n"
-                                " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n"
-                                " and reboot Windows.\n", path, path);
-               }
-               else {
-                       path[len] = 0;
-                       pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path);
-               }
-               return ENOSYS;
-       }
-}
-
-
-static int ata_open(int drive)
-{
-       int win9x;
-       char devpath[30];
-       GETVERSIONOUTPARAMS vers;
-       DWORD num_out;
-
-       assert(SMART_GET_VERSION == 0x074080);
-       assert(sizeof(GETVERSIONOUTPARAMS) == 24);
-
-       // TODO: This version does not allow to open more than 1 ATA devices
-       if (h_ata_ioctl) {
-               errno = ENFILE;
-               return -1;
-       }
-
-       win9x = ((GetVersion() & 0x80000000) != 0);
-
-       if (!(0 <= drive && drive <= (win9x ? 3 : 9))) {
-               errno = ENOENT;
-               return -1;
-       }
-       // path depends on Windows Version
-       if (win9x)
-               strcpy(devpath, "\\\\.\\SMARTVSD");
-       else
-               snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", drive);
-
-       // Open device
-       if ((h_ata_ioctl = CreateFileA(devpath,
-               GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
-               NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
-               long err = GetLastError();      
-               pout("Cannot open device %s, Error=%ld\n", devpath, err);
-               if (err == ERROR_FILE_NOT_FOUND)
-                       errno = (win9x ? smartvsd_error() : ENOENT);
-               else if (err == ERROR_ACCESS_DENIED) {
-                       if (!win9x)
-                               pout("Administrator rights are necessary to access physical drives.\n");
-                       errno = EACCES;
-               }
-               else
-                       errno = EIO;
-               h_ata_ioctl = 0;
-               return -1;
-       }
-
-       // Get drive map
-       memset(&vers, 0, sizeof(vers));
-       if (!DeviceIoControl(h_ata_ioctl, SMART_GET_VERSION,
-               NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
-               pout("%s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError());
-               if (!win9x)
-                       pout("If this is a SCSI disk, try \"scsi<adapter><id>\".\n");
-               if (!is_permissive()) {
-                       CloseHandle(h_ata_ioctl); h_ata_ioctl = 0;
-                       errno = ENOSYS;
-                       return -1;
-               }
-       }
-
-       if (con->reportataioctl > 1)
-               pout("%s: SMART_GET_VERSION (%ld bytes):\n"
-                    " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
-                       devpath, num_out, vers.bVersion, vers.bRevision,
-                       vers.fCapabilities, vers.bIDEDeviceMap);
-
-       // TODO: Check vers.fCapabilities here?
-
-       if (!win9x)
-               // NT4/2K/XP: Drive exists, Drive number not necessary for ioctl
-               return 0;
-
-       // Win9x/ME: Check device presence & type
-       if (((vers.bIDEDeviceMap >> drive) & 0x11) != 0x01) {
-               unsigned char atapi = (vers.bIDEDeviceMap >> drive) & 0x10;
-               pout((  atapi
-                     ? "Drive %d is an ATAPI device (IDEDeviceMap=0x%02x).\n"
-                         : "Drive %d does not exist (IDEDeviceMap=0x%02x).\n"),
-                       drive, vers.bIDEDeviceMap);
-               // Win9x Drive existence check may not work as expected
-               // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01
-               // http://support.microsoft.com/support/kb/articles/Q196/1/20.ASP
-               if (!is_permissive()) {
-                       CloseHandle(h_ata_ioctl); h_ata_ioctl = 0;
-                       errno = (atapi ? ENOSYS : ENOENT);
-                       return -1;
-               }
-       }
-       // Use drive number as fd for ioctl
-       return drive;
-}
-
-
-static void ata_close(int fd)
-{
-       ARGUSED(fd);
-       CloseHandle(h_ata_ioctl);
-       h_ata_ioctl = 0;
-}
-
-
-// Scan for ATA drives, fill bitmask of drives present, return #drives
-
-static int ata_scan(unsigned long * drives)
-{
-       int win9x = ((GetVersion() & 0x80000000) != 0);
-       int cnt = 0, i;
-
-       for (i = 0; i <= 9; i++) {
-               char devpath[30];
-               GETVERSIONOUTPARAMS vers;
-               DWORD num_out;
-               HANDLE h;
-               if (win9x)
-                       strcpy(devpath, "\\\\.\\SMARTVSD");
-               else
-                       snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", i);
-
-               // Open device
-               if ((h = CreateFileA(devpath,
-                       GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
-                       NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
-                       if (con->reportataioctl > 1)
-                               pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError());
-                       if (win9x)
-                               break; // SMARTVSD.VXD missing or no ATA devices
-                       continue; // Disk not found or access denied (break;?)
-               }
-
-               // Get drive map
-               memset(&vers, 0, sizeof(vers));
-               if (!DeviceIoControl(h, SMART_GET_VERSION,
-                       NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
-                       if (con->reportataioctl)
-                               pout(" %s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError());
-                       CloseHandle(h);
-                       if (win9x)
-                               break; // Should not happen
-                       continue; // Non ATA disk or no SMART ioctl support (possibly SCSI disk)
-               }
-               CloseHandle(h);
-
-               if (con->reportataioctl)
-                       pout(" %s: SMART_GET_VERSION (%ld bytes):\n"
-                            "  Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
-                               devpath, num_out, vers.bVersion, vers.bRevision,
-                               vers.fCapabilities, vers.bIDEDeviceMap);
-
-               if (win9x) {
-                       // Check ATA device presence, remove ATAPI devices
-                       drives[0] = (vers.bIDEDeviceMap & 0xf) & ~((vers.bIDEDeviceMap >> 4) & 0xf);
-                       cnt = (drives[0]&1) + ((drives[0]>>1)&1) + ((drives[0]>>2)&1) + ((drives[0]>>3)&1);
-                       break;
-               }
-
-               // ATA drive exists and driver supports SMART ioctl
-               drives[0] |= (1L << i);
-               cnt++;
-       }
-
-       return cnt;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-// Interface to ATA devices.  See os_linux.c
-int ata_command_interface(int fd, smart_command_set command, int select, char * data)
-{
-       IDEREGS regs;
-       int copydata, try_ioctl;
-
-       if (!(0 <= fd && fd <= 3)) {
-               errno = EBADF;
-               return -1;
-       }
-
-       // CMD,CYL default to SMART, changed by P?IDENTIFY and CHECK_POWER_MODE
-       memset(&regs, 0, sizeof(regs));
-       regs.bCommandReg = ATA_SMART_CMD;
-       regs.bCylHighReg = SMART_CYL_HI; regs.bCylLowReg = SMART_CYL_LOW;
-       copydata = 0;
-       try_ioctl = 0x01; // 0x01=SMART_*, 0x02=IDE_PASS_THROUGH [, 0x04=ATA_PASS_THROUGH]
-
-       switch (command) {
-         case WRITE_LOG:
-               // TODO. Not supported by SMART IOCTL (no data out ioctl available),
-               //  also not supported by IOCTL_IDE_PASS_THROUGH (data out not working)
-               errno = ENOSYS;
-               return -1;
-         case CHECK_POWER_MODE:
-               regs.bCommandReg = ATA_CHECK_POWER_MODE;
-               regs.bCylLowReg = regs.bCylHighReg = 0;
-               try_ioctl = 0x02; // IOCTL_IDE_PASS_THROUGH
-               // Note: returns SectorCountReg in data[0]
-               break;
-         case READ_VALUES:
-               regs.bFeaturesReg = ATA_SMART_READ_VALUES;
-               regs.bSectorNumberReg = regs.bSectorCountReg = 1;
-               copydata = 1;
-               break;
-         case READ_THRESHOLDS:
-               regs.bFeaturesReg = ATA_SMART_READ_THRESHOLDS;
-               regs.bSectorNumberReg = regs.bSectorCountReg = 1;
-               copydata = 1;
-               break;
-         case READ_LOG:
-               regs.bFeaturesReg = ATA_SMART_READ_LOG_SECTOR;
-               regs.bSectorNumberReg = select;
-               regs.bSectorCountReg = 1;
-               // Read log only supported on Win9x, retry with pass through command
-               try_ioctl = 0x03; // SMART_RCV_DRIVE_DATA, then IOCTL_IDE_PASS_THROUGH
-               copydata = 1;
-               break;
-         case IDENTIFY:
-               // Note: WinNT4/2000/XP return identify data cached during boot
-               // (true for SMART_RCV_DRIVE_DATA and IOCTL_IDE_PASS_THROUGH)
-               regs.bCommandReg = ATA_IDENTIFY_DEVICE;
-               regs.bCylLowReg = regs.bCylHighReg = 0;
-               regs.bSectorCountReg = 1;
-               copydata = 1;
-               break;
-         case PIDENTIFY:
-               regs.bCommandReg = ATA_IDENTIFY_PACKET_DEVICE;
-               regs.bCylLowReg = regs.bCylHighReg = 0;
-               regs.bSectorCountReg = 1;
-               copydata = 1;
-               break;
-         case ENABLE:
-               regs.bFeaturesReg = ATA_SMART_ENABLE;
-               regs.bSectorNumberReg = 1;
-               break;
-         case DISABLE:
-               regs.bFeaturesReg = ATA_SMART_DISABLE;
-               regs.bSectorNumberReg = 1;
-               break;
-         case STATUS:
-         case STATUS_CHECK:
-               regs.bFeaturesReg = ATA_SMART_STATUS;
-               break;
-         case AUTO_OFFLINE:
-               regs.bFeaturesReg = ATA_SMART_AUTO_OFFLINE;
-               regs.bSectorCountReg = select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
-               break;
-         case AUTOSAVE:
-               regs.bFeaturesReg = ATA_SMART_AUTOSAVE;
-               regs.bSectorCountReg = select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
-               break;
-         case IMMEDIATE_OFFLINE:
-               regs.bFeaturesReg = ATA_SMART_IMMEDIATE_OFFLINE;
-               regs.bSectorNumberReg = select;
-               if (select == ABORT_SELF_TEST) // Abort only supported on Win9x, try
-                       try_ioctl = 0x03; // SMART_SEND_DRIVE_COMMAND, then IOCTL_IDE_PASS_THROUGH
-               break;
-         default:
-               pout("Unrecognized command %d in win32_ata_command_interface()\n"
-                "Please contact " PACKAGE_BUGREPORT "\n", command);
-               errno = ENOSYS;
-               return -1;
-       }
-
-       if (try_ioctl & 0x01) {
-               if (smart_ioctl(h_ata_ioctl, fd, &regs, data, (copydata?512:0))) {
-                       if (!(try_ioctl & 0x02) || errno != ENOSYS)
-                               return -1;
-                       // CAUTION: smart_ioctl() MUST NOT change "regs" Parameter in this case
-               }
-               else
-                       try_ioctl = 0;
-       }
-
-       if (try_ioctl & 0x02) {
-               errno = 0;
-               if ((GetVersion() & 0x8000ffff) == 0x00000004) {
-                       // Special case WinNT4
-                       if (command == CHECK_POWER_MODE) { // SCSI_PASS_THROUGH does not return regs!
-                               errno = ENOSYS;
-                               return -1;
-                       }
-                       if (ata_via_scsi_pass_through_ioctl(h_ata_ioctl, &regs, data, (copydata?512:0)))
-                               return -1;
-               }
-               else {
-                       if (ide_pass_through_ioctl(h_ata_ioctl, &regs, data, (copydata?512:0)))
-                               return -1;
-               }
-               try_ioctl = 0;
-       }
-       assert(!try_ioctl);
-
-       switch (command) {
-         case CHECK_POWER_MODE:
-               // Return power mode from SectorCountReg in data[0]
-               data[0] = regs.bSectorCountReg;
-               return 0;
-
-         case STATUS_CHECK:
-               // Cyl low and Cyl high unchanged means "Good SMART status"
-               if (regs.bCylHighReg == SMART_CYL_HI && regs.bCylLowReg == SMART_CYL_LOW)
-                 return 0;
-
-               // These values mean "Bad SMART status"
-               if (regs.bCylHighReg == 0x2c && regs.bCylLowReg == 0xf4)
-                 return 1;
-
-               // We haven't gotten output that makes sense; print out some debugging info
-               syserror("Error SMART Status command failed");
-               pout("Please get assistance from %s\n", PACKAGE_HOMEPAGE);
-               print_ide_regs(&regs, 1);
-               errno = EIO;
-               return -1;
-
-         default:
-               return 0;
-       }
-       /*NOTREACHED*/
-}
-
-
-#ifndef HAVE_ATA_IDENTIFY_IS_CACHED
-#error define of HAVE_ATA_IDENTIFY_IS_CACHED missing in config.h
-#endif
-
-// Return true if OS caches the ATA identify sector
-int ata_identify_is_cached(int fd)
-{
-       ARGUSED(fd);
-       // WinNT4/2000/XP => true, Win9x/ME => false
-       return ((GetVersion() & 0x80000000) == 0);
-}
-
-
-// Print not implemeted warning once
-static void pr_not_impl(const char * what, int * warned)
-{
-       if (*warned)
-               return;
-       pout(
-               "#######################################################################\n"
-               "%s\n"
-               "NOT IMPLEMENTED under Win32.\n"
-               "Please contact " PACKAGE_BUGREPORT " if\n"
-               "you want to help in porting smartmontools to Win32.\n"
-               "#######################################################################\n"
-               "\n", what
-       );
-       *warned = 1;
-}
-
-// Interface to ATA devices behind 3ware escalade RAID controller cards.  See os_linux.c
-int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data)
-{
-       static int warned = 0;
-       ARGUSED(fd); ARGUSED(disknum); ARGUSED(escalade_type); ARGUSED(command); ARGUSED(select); ARGUSED(data);
-       pr_not_impl("3ware Escalade Controller command routine escalade_command_interface()", &warned);
-       errno = ENOSYS;
-       return -1;
-}
-
-// Interface to ATA devices behind Marvell chip-set based controllers.  See os_linux.c
-int marvell_command_interface(int fd, smart_command_set command, int select, char * data)
-{
-       static int warned = 0;
-       ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
-       pr_not_impl("Marvell chip-set command routine marvell_command_interface()", &warned);
-       errno = ENOSYS;
-       return -1;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// ASPI Interface
-/////////////////////////////////////////////////////////////////////////////
-
-#pragma pack(1)
-
-#define ASPI_SENSE_SIZE 18
-
-// ASPI SCSI Request block header
-
-typedef struct {
-       unsigned char cmd;             // 00: Command code
-       unsigned char status;          // 01: ASPI status
-       unsigned char adapter;         // 02: Host adapter number
-       unsigned char flags;           // 03: Request flags
-       unsigned char reserved[4];     // 04: 0
-} ASPI_SRB_HEAD;
-
-// SRB for host adapter inquiry
-
-typedef struct {
-       ASPI_SRB_HEAD h;               // 00: Header
-       unsigned char adapters;        // 08: Number of adapters
-       unsigned char target_id;       // 09: Target ID ?
-       char manager_id[16];           // 10: SCSI manager ID
-       char adapter_id[16];           // 26: Host adapter ID
-       unsigned char parameters[16];  // 42: Host adapter unique parmameters
-} ASPI_SRB_INQUIRY;
-
-// SRB for get device type
-
-typedef struct {
-       ASPI_SRB_HEAD h;               // 00: Header
-       unsigned char target_id;       // 08: Target ID
-       unsigned char lun;             // 09: LUN
-       unsigned char devtype;         // 10: Device type
-       unsigned char reserved;        // 11: Reserved
-} ASPI_SRB_DEVTYPE;
-
-// SRB for SCSI I/O
-
-typedef struct {
-       ASPI_SRB_HEAD h;               // 00: Header
-       unsigned char target_id;       // 08: Target ID
-       unsigned char lun;             // 09: LUN
-       unsigned char reserved[2];     // 10: Reserved
-       unsigned long data_size;       // 12: Data alloc. lenght
-       void * data_addr;              // 16: Data buffer pointer
-       unsigned char sense_size;      // 20: Sense alloc. length
-       unsigned char cdb_size;        // 21: CDB length
-       unsigned char host_status;     // 22: Host status
-       unsigned char target_status;   // 23: Target status
-       void * event_handle;           // 24: Event handle
-       unsigned char workspace[20];   // 28: ASPI workspace
-       unsigned char cdb[16+ASPI_SENSE_SIZE];
-} ASPI_SRB_IO;
-
-// Macro to retrieve start of sense information
-#define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)
-
-// SRB union
-
-typedef union {
-       ASPI_SRB_HEAD h;       // Common header
-       ASPI_SRB_INQUIRY q;    // Inquiry
-       ASPI_SRB_DEVTYPE t;    // Device type
-       ASPI_SRB_IO i;         // I/O
-} ASPI_SRB;
-
-#pragma pack()
-
-// ASPI commands
-#define ASPI_CMD_ADAPTER_INQUIRE        0x00
-#define ASPI_CMD_GET_DEVICE_TYPE        0x01
-#define ASPI_CMD_EXECUTE_IO             0x02
-#define ASPI_CMD_ABORT_IO               0x03
-
-// Request flags
-#define ASPI_REQFLAG_DIR_TO_HOST        0x08
-#define ASPI_REQFLAG_DIR_TO_TARGET      0x10
-#define ASPI_REQFLAG_DIR_NO_XFER        0x18
-#define ASPI_REQFLAG_EVENT_NOTIFY       0x40
-
-// ASPI status
-#define ASPI_STATUS_IN_PROGRESS         0x00
-#define ASPI_STATUS_NO_ERROR            0x01
-#define ASPI_STATUS_ABORTED             0x02
-#define ASPI_STATUS_ABORT_ERR           0x03
-#define ASPI_STATUS_ERROR               0x04
-#define ASPI_STATUS_INVALID_COMMAND     0x80
-#define ASPI_STATUS_INVALID_ADAPTER     0x81
-#define ASPI_STATUS_INVALID_TARGET      0x82
-#define ASPI_STATUS_NO_ADAPTERS         0xE8
-
-// Adapter (host) status
-#define ASPI_HSTATUS_NO_ERROR           0x00
-#define ASPI_HSTATUS_SELECTION_TIMEOUT  0x11
-#define ASPI_HSTATUS_DATA_OVERRUN       0x12
-#define ASPI_HSTATUS_BUS_FREE           0x13
-#define ASPI_HSTATUS_BUS_PHASE_ERROR    0x14
-#define ASPI_HSTATUS_BAD_SGLIST         0x1A
-
-// Target status
-#define ASPI_TSTATUS_NO_ERROR           0x00
-#define ASPI_TSTATUS_CHECK_CONDITION    0x02
-#define ASPI_TSTATUS_BUSY               0x08
-#define ASPI_TSTATUS_RESERV_CONFLICT    0x18
-
-
-static HINSTANCE h_aspi_dll; // DLL handle
-static UINT (* aspi_entry)(ASPI_SRB * srb); // ASPI entrypoint
-static unsigned num_aspi_adapters;
-
-#ifdef __CYGWIN__
-// h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
-static DWORD aspi_dll_pid; // PID of DLL owner to detect fork()
-#define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))
-#else
-#define aspi_entry_valid() (!!aspi_entry)
-#endif
-
-
-static int aspi_call(ASPI_SRB * srb)
-{
-       int i;
-       aspi_entry(srb);
-       i = 0;
-       while (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
-               if (++i > 100/*10sek*/) {
-                       pout("ASPI Adapter %u: Timed out\n", srb->h.adapter);
-                       aspi_entry = 0;
-                       h_aspi_dll = INVALID_HANDLE_VALUE;
-                       errno = EIO;
-                       return -1;
-               }
-               if (con->reportscsiioctl > 1)
-                       pout("ASPI Adapter %u: Waiting (%d) ...\n", srb->h.adapter, i);
-               Sleep(100);
-       }
-       return 0;
-}
-
-
-// Get ASPI entrypoint from wnaspi32.dll
-
-static FARPROC aspi_get_address(const char * name, int verbose)
-{
-       FARPROC addr;
-       assert(h_aspi_dll && h_aspi_dll != INVALID_HANDLE_VALUE);
-
-       if (!(addr = GetProcAddress(h_aspi_dll, name))) {
-               if (verbose)
-                       pout("Missing %s() in WNASPI32.DLL\n", name);
-               aspi_entry = 0;
-               FreeLibrary(h_aspi_dll);
-               h_aspi_dll = INVALID_HANDLE_VALUE;
-               errno = ENOSYS;
-               return 0;
-       }
-       return addr;
-}
-
-
-static int aspi_open_dll(int verbose)
-{
-       UINT (*aspi_info)(void);
-       UINT info, rc;
-
-       assert(!aspi_entry_valid());
-
-       // Check structure layout
-       assert(sizeof(ASPI_SRB_HEAD) == 8);
-       assert(sizeof(ASPI_SRB_INQUIRY) == 58);
-       assert(sizeof(ASPI_SRB_DEVTYPE) == 12);
-       assert(sizeof(ASPI_SRB_IO) == 64+ASPI_SENSE_SIZE);
-       assert(offsetof(ASPI_SRB,h.cmd) == 0);
-       assert(offsetof(ASPI_SRB,h.flags) == 3);
-       assert(offsetof(ASPI_SRB_IO,lun) == 9);
-       assert(offsetof(ASPI_SRB_IO,data_addr) == 16);
-       assert(offsetof(ASPI_SRB_IO,workspace) == 28);
-       assert(offsetof(ASPI_SRB_IO,cdb) == 48);
-
-       if (h_aspi_dll == INVALID_HANDLE_VALUE) {
-               // do not retry
-               errno = ENOENT;
-               return -1;
-       }
-
-       // Load ASPI DLL
-       if (!(h_aspi_dll = LoadLibraryA("WNASPI32.DLL"))) {
-               if (verbose)
-                       pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());
-               h_aspi_dll = INVALID_HANDLE_VALUE;
-               errno = ENOENT;
-               return -1;
-       }
-       if (con->reportscsiioctl > 1) {
-               // Print full path of WNASPI32.DLL
-               char path[MAX_PATH];
-               if (!GetModuleFileName(h_aspi_dll, path, sizeof(path)))
-                       strcpy(path, "*unknown*");
-               pout("Using ASPI interface \"%s\"\n", path);
-       }
-
-       // Get ASPI entrypoints
-       if (!(aspi_info = (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose)))
-               return -1;
-       if (!(aspi_entry = (UINT (*)(ASPI_SRB *))aspi_get_address("SendASPI32Command", verbose)))
-               return -1;
-
-       // Init ASPI manager and get number of adapters
-       info = (aspi_info)();
-       if (con->reportscsiioctl > 1)
-               pout("GetASPI32SupportInfo() returns 0x%04x\n", info);
-       rc = (info >> 8) & 0xff;
-       if (rc == ASPI_STATUS_NO_ADAPTERS) {
-               num_aspi_adapters = 0;
-       }
-       else if (rc == ASPI_STATUS_NO_ERROR) {
-               num_aspi_adapters = info & 0xff;
-       }
-       else {
-               if (verbose)
-                       pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info);
-               aspi_entry = 0;
-               FreeLibrary(h_aspi_dll);
-               h_aspi_dll = INVALID_HANDLE_VALUE;
-               errno = ENOENT;
-               return -1;
-       }
-
-       if (con->reportscsiioctl)
-               pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
-
-#ifdef __CYGWIN__
-       // save PID to detect fork() in aspi_entry_valid()
-       aspi_dll_pid = GetCurrentProcessId();
-#endif
-       assert(aspi_entry_valid());
-       return 0;
-}
-
-
-static int aspi_io_call(ASPI_SRB * srb, unsigned timeout)
-{
-       HANDLE event;
-       // Create event
-       if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL))) {
-               pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO;
-       }
-       srb->i.event_handle = event;
-       srb->h.flags |= ASPI_REQFLAG_EVENT_NOTIFY;
-       // Start ASPI request
-       aspi_entry(srb);
-       if (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
-               // Wait for event
-               DWORD rc = WaitForSingleObject(event, timeout*1000L);
-               if (rc != WAIT_OBJECT_0) {
-                       if (rc == WAIT_TIMEOUT) {
-                               pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n",
-                                       srb->h.adapter, srb->i.target_id, timeout);
-                       }
-                       else {
-                               pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",
-                                       (unsigned long)event, rc, rc, GetLastError());
-                       }
-                       // TODO: ASPI_ABORT_IO command
-                       aspi_entry = 0;
-                       h_aspi_dll = INVALID_HANDLE_VALUE;
-                       return -EIO;
-               }
-       }
-       CloseHandle(event);
-       return 0;
-}
-
-
-static int aspi_open(unsigned adapter, unsigned id)
-{
-       ASPI_SRB srb;
-       if (!(adapter <= 9 && id < 16)) {
-               errno = ENOENT;
-               return -1;
-       }
-
-       if (!aspi_entry_valid()) {
-               if (aspi_open_dll(1/*verbose*/))
-                       return -1;
-       }
-
-       // Adapter OK?
-       if (adapter >= num_aspi_adapters) {
-               pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n",
-                       adapter, num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
-               if (!is_permissive()) {
-                       errno = ENOENT;
-                       return -1;
-               }
-       }
-
-       // Device present ?
-       memset(&srb, 0, sizeof(srb));
-       srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
-       srb.h.adapter = adapter; srb.i.target_id = id;
-       if (aspi_call(&srb)) {
-               errno = EIO;
-               return -1;
-       }
-       if (srb.h.status != ASPI_STATUS_NO_ERROR) {
-               pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter, id, srb.h.status);
-               if (!is_permissive()) {
-                       errno = (srb.h.status == ASPI_STATUS_INVALID_TARGET ? ENOENT : EIO);
-                       return -1;
-               }
-       }
-       else if (con->reportscsiioctl)
-               pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype);
-
-       return (0x0100 | ((adapter & 0xf)<<4) | (id & 0xf));
-}
-
-
-static void aspi_close(int fd)
-{
-       // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
-       ARGUSED(fd);
-}
-
-
-// Scan for SCSI drives, fill bitmask [adapter:0-9][id:0-7] of drives present,
-// return #drives
-
-static int aspi_scan(unsigned long * drives)
-{
-       int cnt = 0;
-       unsigned ad;
-
-       if (!aspi_entry_valid()) {
-               if (aspi_open_dll(con->reportscsiioctl/*default is quiet*/))
-                       return 0;
-       }
-
-       for (ad = 0; ad < num_aspi_adapters; ad++) {
-               ASPI_SRB srb; unsigned id;
-
-               if (ad > 9) {
-                       if (con->reportscsiioctl)
-                               pout(" ASPI Adapter %u: Ignored\n", ad);
-                       continue;
-               }
-
-               // Get adapter name
-               memset(&srb, 0, sizeof(srb));
-               srb.h.cmd = ASPI_CMD_ADAPTER_INQUIRE;
-               srb.h.adapter = ad;
-               if (aspi_call(&srb))
-                       return 0;
-
-               if (srb.h.status != ASPI_STATUS_NO_ERROR) {
-                       if (con->reportscsiioctl)
-                               pout(" ASPI Adapter %u: Status=0x%02x\n", ad, srb.h.status);
-                       continue;
-               }
-
-               if (con->reportscsiioctl) {
-                       int i;
-                       for (i = 1; i < 16 && srb.q.adapter_id[i]; i++)
-                               if (!(' ' <= srb.q.adapter_id[i] && srb.q.adapter_id[i] <= '~'))
-                                       srb.q.adapter_id[i] = '?';
-                       pout(" ASPI Adapter %u (\"%.16s\"):\n", ad, srb.q.adapter_id);
-               }
-
-               for (id = 0; id <= 7; id++) {
-                       // Get device type
-                       memset(&srb, 0, sizeof(srb));
-                       srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
-                       srb.h.adapter = ad; srb.i.target_id = id;
-                       if (aspi_call(&srb))
-                               return 0;
-                       if (srb.h.status != ASPI_STATUS_NO_ERROR) {
-                               if (con->reportscsiioctl > 1)
-                                       pout("  ID %u: No such device (Status=0x%02x)\n", id, srb.h.status);
-                               continue;
-                       }
-                       if (con->reportscsiioctl)
-                               pout("  ID %u: Device Type=0x%02x\n", id, srb.t.devtype);
-                       if (srb.t.devtype == 0x00/*HDD*/) {
-                               drives[ad >> 2] |= (1L << (((ad & 0x3) << 3) + id));
-                               cnt++;
-                       }
-               }
-       }
-       return cnt;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-// Interface to SCSI devices.  See os_linux.c
-int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
-{
-       ASPI_SRB srb;
-
-       if (!aspi_entry_valid())
-               return -EBADF;
-       if (!((fd & ~0xff) == 0x100))
-               return -EBADF;
-
-       if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12)) {
-               pout("do_scsi_cmnd_io: bad CDB length\n");
-               return -EINVAL;
-       }
-
-       if (report > 0) {
-               // From os_linux.c
-               int k, j;
-               const unsigned char * ucp = iop->cmnd;
-               const char * np;
-               char buff[256];
-               const int sz = (int)sizeof(buff);
-
-               np = scsi_get_opcode_name(ucp[0]);
-               j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
-               for (k = 0; k < (int)iop->cmnd_len; ++k)
-                       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
-               if ((report > 1) && 
-                       (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
-                       int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-
-                       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
-                                                 "data, len=%d%s:\n", (int)iop->dxfer_len,
-                                                 (trunc ? " [only first 256 bytes shown]" : ""));
-                       dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
-               }
-               else
-                       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
-               pout(buff);
-       }
-
-       memset(&srb, 0, sizeof(srb));
-       srb.h.cmd = ASPI_CMD_EXECUTE_IO;
-       srb.h.adapter = ((fd >> 4) & 0xf);
-       srb.i.target_id = (fd & 0xf);
-       //srb.i.lun = 0;
-       srb.i.sense_size = ASPI_SENSE_SIZE;
-       srb.i.cdb_size = iop->cmnd_len;
-       memcpy(srb.i.cdb, iop->cmnd, iop->cmnd_len);
-
-       switch (iop->dxfer_dir) {
-               case DXFER_NONE:
-                       srb.h.flags = ASPI_REQFLAG_DIR_NO_XFER;
-                       break;
-               case DXFER_FROM_DEVICE:
-                       srb.h.flags = ASPI_REQFLAG_DIR_TO_HOST;
-                       srb.i.data_size = iop->dxfer_len;
-                       srb.i.data_addr = iop->dxferp;
-                       break;
-               case DXFER_TO_DEVICE:
-                       srb.h.flags = ASPI_REQFLAG_DIR_TO_TARGET;
-                       srb.i.data_size = iop->dxfer_len;
-                       srb.i.data_addr = iop->dxferp;
-                       break;
-               default:
-                       pout("do_scsi_cmnd_io: bad dxfer_dir\n");
-                       return -EINVAL;
-       }
-
-       iop->resp_sense_len = 0;
-       iop->scsi_status = 0;
-       iop->resid = 0;
-
-       if (aspi_io_call(&srb, (iop->timeout ? iop->timeout : 60))) {
-               // Timeout
-               return -EIO;
-       }
-
-       if (srb.h.status != ASPI_STATUS_NO_ERROR) {
-               if (   srb.h.status        == ASPI_STATUS_ERROR
-                   && srb.i.host_status   == ASPI_HSTATUS_NO_ERROR
-                   && srb.i.target_status == ASPI_TSTATUS_CHECK_CONDITION) {
-                       // Sense valid
-                       const unsigned char * sense = ASPI_SRB_SENSE(&srb.i, iop->cmnd_len);
-                       int len = (ASPI_SENSE_SIZE < iop->max_sense_len ? ASPI_SENSE_SIZE : iop->max_sense_len);
-                       iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
-                       if (len > 0 && iop->sensep) {
-                               memcpy(iop->sensep, sense, len);
-                               iop->resp_sense_len = len;
-                               if (report > 1) {
-                                       pout("  >>> Sense buffer, len=%d:\n", (int)len);
-                                       dStrHex(iop->sensep, len , 1);
-                               }
-                       }
-                       if (report) {
-                               pout("  sense_key=%x asc=%x ascq=%x\n",
-                                sense[2] & 0xf, sense[12], sense[13]);
-                       }
-                       return 0;
-               }
-               else {
-                       if (report)
-                               pout("  ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb.h.status, srb.i.host_status, srb.i.target_status);
-                       return -EIO;
-               }
-       }
-
-       if (report > 0)
-               pout("  OK\n");
-
-       if (iop->dxfer_dir == DXFER_FROM_DEVICE && report > 1) {
-                int trunc = (iop->dxfer_len > 256) ? 1 : 0;
-                pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
-                         (trunc ? " [only first 256 bytes shown]" : ""));
-                               dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
-       }
-
-       return 0;
-}
diff --git a/os_win32.cpp b/os_win32.cpp
new file mode 100644 (file)
index 0000000..565dfe6
--- /dev/null
@@ -0,0 +1,2069 @@
+/*
+ * os_win32.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "extern.h"
+extern smartmonctrl * con; // con->permissive,reportataioctl
+#include "scsicmds.h"
+#include "utility.h"
+extern int64_t bytes; // malloc() byte count
+
+#include <errno.h>
+#ifdef _DEBUG
+#include <assert.h>
+#else
+#define assert(x) /**/
+#endif
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <stddef.h> // offsetof()
+#include <io.h> // access()
+
+#define ARGUSED(x) ((void)(x))
+
+// Macro to check constants at compile time using a dummy typedef
+#define ASSERT_CONST(c, n) \
+  typedef char assert_const_##c[((c) == (n)) ? 1 : -1]
+#define ASSERT_SIZEOF(t, n) \
+  typedef char assert_sizeof_##t[(sizeof(t) == (n)) ? 1 : -1]
+
+
+// Needed by '-V' option (CVS versioning) of smartd/smartctl
+const char *os_XXXX_c_cvsid="$Id: os_win32.cpp,v 1.42 2006/09/27 21:42:03 chrfranke Exp $"
+ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+
+#ifndef HAVE_GET_OS_VERSION_STR
+#error define of HAVE_GET_OS_VERSION_STR missing in config.h
+#endif
+
+// Return build host and OS version as static string
+const char * get_os_version_str()
+{
+       static char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1+sizeof("-2003r2-sp2.1")+13];
+       char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1;
+       const int vlen = sizeof(vstr)-(sizeof(SMARTMONTOOLS_BUILD_HOST)-3);
+
+       OSVERSIONINFOEXA vi;
+       const char * w;
+
+       // remove "-pc" to avoid long lines
+       assert(!strncmp(SMARTMONTOOLS_BUILD_HOST+5, "pc-", 3));
+       strcpy(vstr, "i686-"); strcpy(vstr+5, SMARTMONTOOLS_BUILD_HOST+5+3);
+       assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
+
+       memset(&vi, 0, sizeof(vi));
+       vi.dwOSVersionInfoSize = sizeof(vi);
+       if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
+               memset(&vi, 0, sizeof(vi));
+               vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+               if (!GetVersionExA((OSVERSIONINFOA *)&vi))
+                       return vstr;
+       }
+
+       if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff)
+               return vstr;
+
+       switch (vi.dwPlatformId << 16 | vi.dwMajorVersion << 8 | vi.dwMinorVersion) {
+         case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400| 0:
+               w = (vi.szCSDVersion[1] == 'B' ||
+                    vi.szCSDVersion[1] == 'C'     ? "95-osr2" : "95");    break;
+         case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|10:
+               w = (vi.szCSDVersion[1] == 'A'     ? "98se"    : "98");    break;
+         case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|90: w = "me";     break;
+       //case VER_PLATFORM_WIN32_NT     <<16|0x0300|51: w = "nt3.51"; break;
+         case VER_PLATFORM_WIN32_NT     <<16|0x0400| 0: w = "nt4";    break;
+         case VER_PLATFORM_WIN32_NT     <<16|0x0500| 0: w = "2000";   break;
+         case VER_PLATFORM_WIN32_NT     <<16|0x0500| 1:
+               w = (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ?   "xp"
+                                                            :   "xp-mc"); break;
+         case VER_PLATFORM_WIN32_NT     <<16|0x0500| 2:
+               w = (!GetSystemMetrics(89/*SM_SERVERR2*/) ?      "2003"
+                                                            :   "2003r2"); break;
+         case VER_PLATFORM_WIN32_NT     <<16|0x0600| 0: w = "vista";  break;
+         default: w = 0; break;
+       }
+
+       if (!w)
+               snprintf(vptr, vlen, "-%s%lu.%lu",
+                       (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
+                       vi.dwMajorVersion, vi.dwMinorVersion);
+       else if (vi.wServicePackMinor)
+               snprintf(vptr, vlen, "-%s-sp%u.%u", w, vi.wServicePackMajor, vi.wServicePackMinor);
+       else if (vi.wServicePackMajor)
+               snprintf(vptr, vlen, "-%s-sp%u", w, vi.wServicePackMajor);
+       else
+               snprintf(vptr, vlen, "-%s", w);
+       return vstr;
+}
+
+
+static int ata_open(int drive, const char * options, int port);
+static void ata_close(int fd);
+static int ata_scan(unsigned long * drives, int * rdriveno, unsigned long * rdrives);
+static const char * ata_get_def_options(void);
+
+static int aspi_open(unsigned adapter, unsigned id);
+static void aspi_close(int fd);
+static int aspi_scan(unsigned long * drives);
+
+
+static int is_permissive()
+{
+       if (!con->permissive) {
+               pout("To continue, add one or more '-T permissive' options.\n");
+               return 0;
+       }
+       con->permissive--;
+       return 1;
+}
+
+static const char * skipdev(const char * s)
+{
+       return (!strncmp(s, "/dev/", 5) ? s + 5 : s);
+}
+
+
+// tries to guess device type given the name (a path).  See utility.h
+// for return values.
+int guess_device_type (const char * dev_name)
+{
+       dev_name = skipdev(dev_name);
+       if (!strncmp(dev_name, "hd", 2))
+               return CONTROLLER_ATA;
+       if (!strncmp(dev_name, "scsi", 4))
+               return CONTROLLER_SCSI;
+       return CONTROLLER_UNKNOWN;
+}
+
+
+// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
+// smartd.  Returns number N of devices, or -1 if out of
+// memory. Allocates N+1 arrays: one of N pointers (devlist), the
+// others each contain null-terminated character strings.
+int make_device_names (char*** devlist, const char* type)
+{
+       unsigned long drives[3];
+       int rdriveno[2];
+       unsigned long rdrives[2];
+       int i, j, n, nmax, sz;
+       const char * path;
+
+       drives[0] = drives[1] = drives[2] = 0;
+       rdriveno[0] = rdriveno[1] = -1;
+       rdrives[0] = rdrives[1] = 0;
+       
+       if (!strcmp(type, "ATA")) {
+               // bit i set => drive i present
+               n = ata_scan(drives, rdriveno, rdrives);
+               path = "/dev/hda";
+               nmax = 10;
+       }
+       else if (!strcmp(type, "SCSI")) {
+               // bit i set => drive with ID (i & 0x7) on adapter (i >> 3) present
+               n = aspi_scan(drives);
+               path = "/dev/scsi00";
+               nmax = 10*8;
+       }
+       else
+               return -1;
+
+       if (n <= 0)
+               return 0;
+
+       // Alloc devlist
+       sz = n * sizeof(char **);
+       *devlist = (char **)malloc(sz); bytes += sz;
+
+       // Add devices
+       for (i = j = 0; i < n; ) {
+               while (j < nmax && !(drives[j >> 5] & (1L << (j & 0x1f))))
+                       j++;
+               assert(j < nmax);
+
+               if (j == rdriveno[0] || j == rdriveno[1]) {
+                       // Add physical drives behind this logical drive
+                       int ci = (j == rdriveno[0] ? 0 : 1);
+                       for (int pi = 0; pi < 32 && i < n; pi++) {
+                               if (!(rdrives[ci] & (1L << pi)))
+                                       continue;
+                               char rpath[20];
+                               sprintf(rpath, "/dev/hd%c,%u", 'a'+j, pi);
+                               sz = strlen(rpath)+1;
+                               char * s = (char *)malloc(sz); bytes += sz;
+                               strcpy(s, rpath);
+                               (*devlist)[i++] = s;
+                       }
+               }
+               else {
+                       sz = strlen(path)+1;
+                       char * s = (char *)malloc(sz); bytes += sz;
+                       strcpy(s, path);
+
+                       if (nmax <= 10) {
+                               assert(j <= 9);
+                               s[sz-2] += j; // /dev/hd[a-j]
+                       }
+                       else {
+                               assert((j >> 3) <= 9);
+                               s[sz-3] += (j >> 3);  // /dev/scsi[0-9].....
+                               s[sz-2] += (j & 0x7); //          .....[0-7]
+                       }
+                       (*devlist)[i++] = s;
+               }
+               j++;
+       }
+
+       return n;
+}
+
+
+// Like open().  Return positive integer handle, only used by
+// functions below.  type="ATA" or "SCSI".  If you need to store extra
+// information about your devices, create a private internal array
+// within this file (see os_freebsd.cpp for an example).
+int deviceopen(const char * pathname, char *type)
+{
+       int len;
+       pathname = skipdev(pathname);
+       len = strlen(pathname);
+
+       if (!strcmp(type, "ATA")) {
+               // hd[a-j](:[saic]+)? => ATA 0-9 with options
+               char drive[1+1] = "", options[5+1] = ""; int n1 = -1, n2 = -1;
+               if (   sscanf(pathname, "hd%1[a-j]%n:%5[saicp]%n", drive, &n1, options, &n2) >= 1
+                       && ((n1 == len && !options[0]) || n2 == len)                                 ) {
+                       return ata_open(drive[0] - 'a', options, -1);
+               }
+               // hd[a-j],N => Physical drive 0-9, RAID port N
+               drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;
+               unsigned port = ~0;
+               if (   sscanf(pathname, "hd%1[a-j],%u%n:%5[saicp]%n", drive, &port, &n1, options, &n2) >= 2
+                   && port < 32 && ((n1 == len && !options[0]) || n2 == len)                              ) {
+                       return ata_open(drive[0] - 'a', options, port);
+               }
+       }
+
+       else if (!strcmp(type, "SCSI")) {
+               // scsi[0-9][0-f] => SCSI Adapter 0-9, ID 0-15, LUN 0
+               unsigned adapter = ~0, id = ~0; int n = -1;
+               if (sscanf(pathname,"scsi%1u%1x%n", &adapter, &id, &n) == 2 && n == len) {
+                       return aspi_open(adapter, id);
+               }
+       }
+
+       errno = EINVAL;
+       return -1;
+}
+
+
+// Like close().  Acts only on handles returned by above function.
+// (Never called in smartctl!)
+int deviceclose(int fd)
+{
+       if ((fd & 0xff00) != 0x0100) {
+               ata_close(fd);
+       }
+       else {
+               aspi_close(fd);
+       }
+       return 0;
+}
+
+
+// print examples for smartctl
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
+         "  smartctl -a /dev/hda                       (Prints all SMART information)\n\n"
+#ifdef HAVE_GETOPT_LONG
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+#else
+         "  smartctl -s on -o on -S on /dev/hda         (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+#endif
+         "  smartctl -a /dev/scsi21\n"
+         "             (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
+         "  smartctl -A /dev/hdb,3\n"
+         "                (Prints Attributes for physical drive 3 on 3ware 9000 RAID)\n"
+         "\n"
+         "  ATA SMART access methods and ordering may be specified by modifiers\n"
+         "  following the device name: /dev/hdX:[saic], where\n"
+         "  's': SMART_* IOCTLs,         'a': IOCTL_ATA_PASS_THROUGH,\n"
+         "  'i': IOCTL_IDE_PASS_THROUGH, 'c': ATA via IOCTL_SCSI_PASS_THROUGH.\n"
+         "  The default on this system is /dev/hdX:%s\n", ata_get_def_options()
+  );
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// ATA Interface
+/////////////////////////////////////////////////////////////////////////////
+
+// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
+
+// Deklarations from:
+// http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntdddisk.h?rev=1.3
+
+#define FILE_READ_ACCESS       0x0001
+#define FILE_WRITE_ACCESS      0x0002
+#define METHOD_BUFFERED             0
+#define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
+
+#define FILE_DEVICE_DISK          7
+#define IOCTL_DISK_BASE        FILE_DEVICE_DISK
+
+#define SMART_GET_VERSION \
+  CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define SMART_SEND_DRIVE_COMMAND \
+  CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define SMART_RCV_DRIVE_DATA \
+  CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+ASSERT_CONST(SMART_GET_VERSION       , 0x074080);
+ASSERT_CONST(SMART_SEND_DRIVE_COMMAND, 0x07c084);
+ASSERT_CONST(SMART_RCV_DRIVE_DATA    , 0x07c088);
+
+#define SMART_CYL_LOW  0x4F
+#define SMART_CYL_HI   0xC2
+
+
+#pragma pack(1)
+
+typedef struct _GETVERSIONOUTPARAMS {
+       UCHAR  bVersion;
+       UCHAR  bRevision;
+       UCHAR  bReserved;
+       UCHAR  bIDEDeviceMap;
+       ULONG  fCapabilities;
+       ULONG  dwReserved[4];
+} GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS;
+
+ASSERT_SIZEOF(GETVERSIONOUTPARAMS, 24);
+
+
+#define SMART_VENDOR_3WARE      0x13C1  // identifies 3ware specific parameters
+
+typedef struct _GETVERSIONINPARAMS_EX {
+       BYTE    bVersion;
+       BYTE    bRevision;
+       BYTE    bReserved;
+       BYTE    bIDEDeviceMap;
+       DWORD   fCapabilities;
+       DWORD   dwDeviceMapEx;  // 3ware specific: RAID drive bit map
+       WORD    wIdentifier;    // Vendor specific identifier
+       WORD    wControllerId;  // 3ware specific: Controller ID (0,1,...)
+       ULONG   dwReserved[2];
+} GETVERSIONINPARAMS_EX, *PGETVERSIONINPARAMS_EX, *LPGETVERSIONINPARAMS_EX;
+
+ASSERT_SIZEOF(GETVERSIONINPARAMS_EX, sizeof(GETVERSIONOUTPARAMS));
+
+
+typedef struct _IDEREGS {
+       UCHAR  bFeaturesReg;
+       UCHAR  bSectorCountReg;
+       UCHAR  bSectorNumberReg;
+       UCHAR  bCylLowReg;
+       UCHAR  bCylHighReg;
+       UCHAR  bDriveHeadReg;
+       UCHAR  bCommandReg;
+       UCHAR  bReserved;
+} IDEREGS, *PIDEREGS, *LPIDEREGS;
+
+typedef struct _SENDCMDINPARAMS {
+       ULONG  cBufferSize;
+       IDEREGS  irDriveRegs;
+       UCHAR  bDriveNumber;
+       UCHAR  bReserved[3];
+       ULONG  dwReserved[4];
+       UCHAR  bBuffer[1];
+} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS;
+
+ASSERT_SIZEOF(SENDCMDINPARAMS, 32+1);
+
+typedef struct _SENDCMDINPARAMS_EX {
+       DWORD   cBufferSize;
+       IDEREGS irDriveRegs;
+       BYTE    bDriveNumber;
+       BYTE    bPortNumber;   // 3ware specific: port number
+       WORD    wIdentifier;   // Vendor specific identifier
+       DWORD   dwReserved[4];
+       BYTE    bBuffer[1];
+} SENDCMDINPARAMS_EX, *PSENDCMDINPARAMS_EX, *LPSENDCMDINPARAMS_EX;
+
+ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS));
+
+
+/* DRIVERSTATUS.bDriverError constants (just for info, not used)
+#define SMART_NO_ERROR                    0
+#define SMART_IDE_ERROR                   1
+#define SMART_INVALID_FLAG                2
+#define SMART_INVALID_COMMAND             3
+#define SMART_INVALID_BUFFER              4
+#define SMART_INVALID_DRIVE               5
+#define SMART_INVALID_IOCTL               6
+#define SMART_ERROR_NO_MEM                7
+#define SMART_INVALID_REGISTER            8
+#define SMART_NOT_SUPPORTED               9
+#define SMART_NO_IDE_DEVICE               10
+*/
+
+typedef struct _DRIVERSTATUS {
+       UCHAR  bDriverError;
+       UCHAR  bIDEError;
+       UCHAR  bReserved[2];
+       ULONG  dwReserved[2];
+} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS;
+
+typedef struct _SENDCMDOUTPARAMS {
+       ULONG  cBufferSize;
+       DRIVERSTATUS  DriverStatus;
+       UCHAR  bBuffer[1];
+} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS;
+
+ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1);
+
+#pragma pack()
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+static void print_ide_regs(const IDEREGS * r, int out)
+{
+       pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, NS=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
+       (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
+       r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
+}
+
+static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
+{
+       pout("    Input : "); print_ide_regs(ri, 0);
+       if (ro) {
+               pout("    Output: "); print_ide_regs(ro, 1);
+       }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+// call SMART_GET_VERSION, return device map or -1 on error
+
+static int smart_get_version(HANDLE hdevice, unsigned long * portmap = 0)
+{
+       GETVERSIONOUTPARAMS vers;
+       const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
+       DWORD num_out;
+
+       memset(&vers, 0, sizeof(vers));
+       if (!DeviceIoControl(hdevice, SMART_GET_VERSION,
+               NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
+               if (con->reportataioctl)
+                       pout("  SMART_GET_VERSION failed, Error=%ld\n", GetLastError());
+               errno = ENOSYS;
+               return -1;
+       }
+       assert(num_out == sizeof(GETVERSIONOUTPARAMS));
+
+       if (portmap) {
+               // Return bitmask of valid RAID ports
+               if (vers_ex.wIdentifier != SMART_VENDOR_3WARE) {
+                       pout("  SMART_GET_VERSION returns unknown Identifier = %04x\n"
+                                "  This is no 3ware 9000 controller or driver has no SMART support.\n", vers_ex.wIdentifier);
+                       errno = ENOENT;
+                       return -1;
+               }
+               *portmap = vers_ex.dwDeviceMapEx;
+       }
+
+       if (con->reportataioctl > 1) {
+               pout("  SMART_GET_VERSION suceeded, bytes returned: %lu\n"
+                    "    Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
+                       num_out, vers.bVersion, vers.bRevision,
+                       vers.fCapabilities, vers.bIDEDeviceMap);
+               if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
+                       pout("    Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",
+                       vers_ex.wIdentifier, vers_ex.wControllerId, vers_ex.dwDeviceMapEx);
+       }
+
+       // TODO: Check vers.fCapabilities here?
+       return vers.bIDEDeviceMap;
+}
+
+
+// call SMART_* ioctl
+
+static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize, int port)
+{
+       SENDCMDINPARAMS inpar;
+       SENDCMDINPARAMS_EX & inpar_ex = (SENDCMDINPARAMS_EX &)inpar;
+
+       unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
+       const SENDCMDOUTPARAMS * outpar;
+       DWORD code, num_out;
+       unsigned int size_out;
+       const char * name;
+
+       memset(&inpar, 0, sizeof(inpar));
+       inpar.irDriveRegs = *regs;
+       // drive is set to 0-3 on Win9x only
+       inpar.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4);
+       inpar.bDriveNumber = drive;
+
+       if (port >= 0) {
+               // Set RAID port
+               inpar_ex.wIdentifier = SMART_VENDOR_3WARE;
+               inpar_ex.bPortNumber = port;
+       }
+
+       assert(datasize == 0 || datasize == 512);
+       if (datasize) {
+               code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
+               inpar.cBufferSize = size_out = 512;
+       }
+       else {
+               code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
+               if (regs->bFeaturesReg == ATA_SMART_STATUS) {
+                       size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
+                       // Note: cBufferSize must be 0 on Win9x
+                       inpar.cBufferSize = size_out;
+               }
+               else
+                       size_out = 0;
+       }
+
+       memset(&outbuf, 0, sizeof(outbuf));
+
+       if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
+               outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
+               // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()
+               long err = GetLastError();
+               if (con->reportataioctl && (err != ERROR_INVALID_PARAMETER || con->reportataioctl > 1)) {
+                       pout("  %s failed, Error=%ld\n", name, err);
+                       print_ide_regs_io(regs, NULL);
+               }
+               errno = (   err == ERROR_INVALID_FUNCTION /*9x*/
+                        || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/ ? ENOSYS : EIO);
+               return -1;
+       }
+       // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
+
+       outpar = (const SENDCMDOUTPARAMS *)outbuf;
+
+       if (outpar->DriverStatus.bDriverError) {
+               if (con->reportataioctl) {
+                       pout("  %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
+                               outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
+                       print_ide_regs_io(regs, NULL);
+               }
+               errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO);
+               return -1;
+       }
+
+       if (con->reportataioctl > 1) {
+               pout("  %s suceeded, bytes returned: %lu (buffer %lu)\n", name,
+                       num_out, outpar->cBufferSize);
+               print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
+                       (const IDEREGS *)(outpar->bBuffer) : NULL));
+       }
+
+       if (datasize)
+               memcpy(data, outpar->bBuffer, 512);
+       else if (regs->bFeaturesReg == ATA_SMART_STATUS) {
+               if (nonempty(const_cast<unsigned char *>(outpar->bBuffer), sizeof(IDEREGS)))
+                       *regs = *(const IDEREGS *)(outpar->bBuffer);
+               else {  // Workaround for driver not returning regs
+                       if (con->reportataioctl)
+                               pout("  WARNING: driver does not return ATA registers in output buffer!\n");
+                       *regs = inpar.irDriveRegs;
+               }
+       }
+
+       return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// IDE PASS THROUGH (2000, XP, undocumented)
+//
+// Based on WinATA.cpp, 2002 c't/Matthias Withopf
+// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
+
+#define FILE_DEVICE_CONTROLLER  4
+#define IOCTL_SCSI_BASE         FILE_DEVICE_CONTROLLER
+
+#define IOCTL_IDE_PASS_THROUGH \
+  CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028);
+
+#pragma pack(1)
+
+typedef struct {
+       IDEREGS IdeReg;
+       ULONG   DataBufferSize;
+       UCHAR   DataBuffer[1];
+} ATA_PASS_THROUGH;
+
+ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1);
+
+#pragma pack()
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
+{ 
+       unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
+       ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
+       DWORD num_out;
+       const unsigned char magic = 0xcf;
+
+       if (!buf) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       buf->IdeReg = *regs;
+       buf->DataBufferSize = datasize;
+       if (datasize)
+               buf->DataBuffer[0] = magic;
+
+       if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
+               buf, size, buf, size, &num_out, NULL)) {
+               long err = GetLastError();
+               if (con->reportataioctl) {
+                       pout("  IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
+                       print_ide_regs_io(regs, NULL);
+               }
+               VirtualFree(buf, 0, MEM_RELEASE);
+               errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+               return -1;
+       }
+
+       // Check ATA status
+       if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
+               if (con->reportataioctl) {
+                       pout("  IOCTL_IDE_PASS_THROUGH command failed:\n");
+                       print_ide_regs_io(regs, &buf->IdeReg);
+               }
+               VirtualFree(buf, 0, MEM_RELEASE);
+               errno = EIO;
+               return -1;
+       }
+
+       // Check and copy data
+       if (datasize) {
+               if (   num_out != size
+                   || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
+                       if (con->reportataioctl) {
+                               pout("  IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
+                                       num_out, buf->DataBufferSize);
+                               print_ide_regs_io(regs, &buf->IdeReg);
+                       }
+                       VirtualFree(buf, 0, MEM_RELEASE);
+                       errno = EIO;
+                       return -1;
+               }
+               memcpy(data, buf->DataBuffer, datasize);
+       }
+
+       if (con->reportataioctl > 1) {
+               pout("  IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
+                       num_out, buf->DataBufferSize);
+               print_ide_regs_io(regs, &buf->IdeReg);
+       }
+       *regs = buf->IdeReg;
+
+       // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
+       VirtualFree(buf, 0, MEM_RELEASE);
+       return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// ATA PASS THROUGH (Win2003, XP SP2)
+
+#define IOCTL_ATA_PASS_THROUGH \
+       CTL_CODE(IOCTL_SCSI_BASE, 0x040B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+ASSERT_CONST(IOCTL_ATA_PASS_THROUGH, 0x04d02c);
+
+typedef struct _ATA_PASS_THROUGH_EX {
+       USHORT  Length;
+       USHORT  AtaFlags;
+       UCHAR  PathId;
+       UCHAR  TargetId;
+       UCHAR  Lun;
+       UCHAR  ReservedAsUchar;
+       ULONG  DataTransferLength;
+       ULONG  TimeOutValue;
+       ULONG  ReservedAsUlong;
+       ULONG/*_PTR*/ DataBufferOffset;
+       UCHAR  PreviousTaskFile[8];
+       UCHAR  CurrentTaskFile[8];
+} ATA_PASS_THROUGH_EX, *PATA_PASS_THROUGH_EX;
+
+ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, 40);
+
+#define ATA_FLAGS_DRDY_REQUIRED 0x01
+#define ATA_FLAGS_DATA_IN       0x02
+#define ATA_FLAGS_DATA_OUT      0x04
+#define ATA_FLAGS_48BIT_COMMAND 0x08
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
+{ 
+       typedef struct {
+               ATA_PASS_THROUGH_EX apt;
+               ULONG Filler;
+               UCHAR ucDataBuf[512];
+       } ATA_PASS_THROUGH_EX_WITH_BUFFERS;
+
+       ATA_PASS_THROUGH_EX_WITH_BUFFERS ab;
+       IDEREGS * ctfregs;
+       unsigned int size;
+       DWORD num_out;
+       const unsigned char magic = 0xcf;
+
+       memset(&ab, 0, sizeof(ab));
+       ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX);
+       //ab.apt.PathId = 0;
+       //ab.apt.TargetId = 0;
+       //ab.apt.Lun = 0;
+       ab.apt.TimeOutValue = 10;
+       size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
+       ab.apt.DataBufferOffset = size;
+       if (datasize) {
+               if (!(0 <= datasize && datasize <= (int)sizeof(ab.ucDataBuf))) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               ab.apt.AtaFlags = ATA_FLAGS_DATA_IN;
+               ab.apt.DataTransferLength = datasize;
+               size += datasize;
+               ab.ucDataBuf[0] = magic;
+       }
+       else {
+               //ab.apt.AtaFlags = 0;
+               //ab.apt.DataTransferLength = 0;
+       }
+
+       assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
+       ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
+       *ctfregs = *regs;
+
+       if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH,
+               &ab, size, &ab, size, &num_out, NULL)) {
+               long err = GetLastError();
+               if (con->reportataioctl) {
+                       pout("  IOCTL_ATA_PASS_THROUGH_EX failed, Error=%ld\n", err);
+                       print_ide_regs_io(regs, NULL);
+               }
+               errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+               return -1;
+       }
+
+       // Check and copy data
+       if (datasize) {
+               if (   num_out != size
+                   || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
+                       if (con->reportataioctl) {
+                               pout("  IOCTL_ATA_PASS_THROUGH_EX output data missing (%lu)\n", num_out);
+                               print_ide_regs_io(regs, ctfregs);
+                       }
+                       errno = EIO;
+                       return -1;
+               }
+               memcpy(data, ab.ucDataBuf, datasize);
+       }
+
+       if (con->reportataioctl > 1) {
+               pout("  IOCTL_ATA_PASS_THROUGH_EX suceeded, bytes returned: %lu\n", num_out);
+               print_ide_regs_io(regs, ctfregs);
+       }
+       *regs = *ctfregs;
+
+       return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)
+
+// Declarations from:
+// http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntddscsi.h?rev=1.2
+
+#define IOCTL_SCSI_PASS_THROUGH \
+       CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004);
+
+#define SCSI_IOCTL_DATA_OUT          0
+#define SCSI_IOCTL_DATA_IN           1
+#define SCSI_IOCTL_DATA_UNSPECIFIED  2
+// undocumented SCSI opcode to for ATA passthrough
+#define SCSIOP_ATA_PASSTHROUGH    0xCC
+
+typedef struct _SCSI_PASS_THROUGH {
+       USHORT  Length;
+       UCHAR  ScsiStatus;
+       UCHAR  PathId;
+       UCHAR  TargetId;
+       UCHAR  Lun;
+       UCHAR  CdbLength;
+       UCHAR  SenseInfoLength;
+       UCHAR  DataIn;
+       ULONG  DataTransferLength;
+       ULONG  TimeOutValue;
+       ULONG/*_PTR*/ DataBufferOffset;
+       ULONG  SenseInfoOffset;
+       UCHAR  Cdb[16];
+} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
+
+ASSERT_SIZEOF(SCSI_PASS_THROUGH, 44);
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
+{
+       typedef struct {
+               SCSI_PASS_THROUGH spt;
+               ULONG Filler;
+               UCHAR ucSenseBuf[32];
+               UCHAR ucDataBuf[512];
+       } SCSI_PASS_THROUGH_WITH_BUFFERS;
+
+       SCSI_PASS_THROUGH_WITH_BUFFERS sb;
+       IDEREGS * cdbregs;
+       unsigned int size;
+       DWORD num_out;
+       const unsigned char magic = 0xcf;
+
+       memset(&sb, 0, sizeof(sb));
+       sb.spt.Length = sizeof(SCSI_PASS_THROUGH);
+       //sb.spt.PathId = 0;
+       sb.spt.TargetId = 1;
+       //sb.spt.Lun = 0;
+       sb.spt.CdbLength = 10; sb.spt.SenseInfoLength = 24;
+       sb.spt.TimeOutValue = 10;
+       sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
+       size = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
+       sb.spt.DataBufferOffset = size;
+       if (datasize) {
+               if (datasize > sizeof(sb.ucDataBuf)) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+               sb.spt.DataTransferLength = datasize;
+               size += datasize;
+               sb.ucDataBuf[0] = magic;
+       }
+       else {
+               sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+               //sb.spt.DataTransferLength = 0;
+       }
+
+       // Use pseudo SCSI command followed by registers
+       sb.spt.Cdb[0] = SCSIOP_ATA_PASSTHROUGH;
+       cdbregs = (IDEREGS *)(sb.spt.Cdb+2);
+       *cdbregs = *regs;
+
+       if (!DeviceIoControl(hdevice, IOCTL_SCSI_PASS_THROUGH,
+               &sb, size, &sb, size, &num_out, NULL)) {
+               long err = GetLastError();
+               if (con->reportataioctl)
+                       pout("  ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err);
+               errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+               return -1;
+       }
+
+       // Cannot check ATA status, because command does not return IDEREGS
+
+       // Check and copy data
+       if (datasize) {
+               if (   num_out != size
+                   || (sb.ucDataBuf[0] == magic && !nonempty(sb.ucDataBuf+1, datasize-1))) {
+                       if (con->reportataioctl) {
+                               pout("  ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out);
+                               print_ide_regs_io(regs, NULL);
+                       }
+                       errno = EIO;
+                       return -1;
+               }
+               memcpy(data, sb.ucDataBuf, datasize);
+       }
+
+       if (con->reportataioctl > 1) {
+               pout("  ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
+               print_ide_regs_io(regs, NULL);
+       }
+       return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Call GetDevicePowerState() if available (Win98/ME/2000/XP/2003)
+// returns: 1=active, 0=standby, -1=error
+// (This would also work for SCSI drives)
+
+static int get_device_power_state(HANDLE hdevice)
+{
+       static HINSTANCE h_kernel_dll = 0;
+#ifdef __CYGWIN__
+       static DWORD kernel_dll_pid = 0;
+#endif
+       static BOOL (WINAPI * GetDevicePowerState_p)(HANDLE, BOOL *) = 0;
+
+       BOOL state = TRUE;
+
+       if (!GetDevicePowerState_p
+#ifdef __CYGWIN__
+           || kernel_dll_pid != GetCurrentProcessId() // detect fork()
+#endif
+          ) {
+               if (h_kernel_dll == INVALID_HANDLE_VALUE) {
+                       errno = ENOSYS;
+                       return -1;
+               }
+               if (!(h_kernel_dll = LoadLibraryA("KERNEL32.DLL"))) {
+                       pout("Cannot load KERNEL32.DLL, Error=%ld\n", GetLastError());
+                       h_kernel_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
+                       errno = ENOSYS;
+                       return -1;
+               }
+               if (!(GetDevicePowerState_p = (BOOL (WINAPI *)(HANDLE, BOOL *))
+                                             GetProcAddress(h_kernel_dll, "GetDevicePowerState"))) {
+                       if (con->reportataioctl)
+                               pout("  GetDevicePowerState() not found, Error=%ld\n", GetLastError());
+                       FreeLibrary(h_kernel_dll);
+                       h_kernel_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
+                       errno = ENOSYS;
+                       return -1;
+               }
+#ifdef __CYGWIN__
+               kernel_dll_pid = GetCurrentProcessId();
+#endif
+       }
+
+       if (!GetDevicePowerState_p(hdevice, &state)) {
+               long err = GetLastError();
+               if (con->reportataioctl)
+                       pout("  GetDevicePowerState() failed, Error=%ld\n", err);
+               errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+               // TODO: This may not work as expected on transient errors,
+               // because smartd interprets -1 as SLEEP mode regardless of errno.
+               return -1;
+       }
+
+       if (con->reportataioctl > 1)
+               pout("  GetDevicePowerState() succeeded, state=%d\n", state);
+       return state;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// TODO: Put in a struct indexed by fd (or better a C++ object of course ;-)
+static HANDLE h_ata_ioctl = 0;
+static const char * ata_def_options;
+static char * ata_cur_options;
+static int ata_driveno; // Drive number
+static char ata_smartver_state[10]; // SMART_GET_VERSION: 0=unknown, 1=OK, 2=failed
+
+// Print SMARTVSD error message, return errno
+
+static int smartvsd_error()
+{
+       char path[MAX_PATH];
+       unsigned len;
+       if (!(5 <= (len = GetSystemDirectoryA(path, MAX_PATH)) && len < MAX_PATH/2))
+               return ENOENT;
+       // SMARTVSD.VXD present?
+       strcpy(path+len, "\\IOSUBSYS\\SMARTVSD.VXD");
+       if (!access(path, 0)) {
+               // Yes, standard IDE driver used?
+               HANDLE h;
+               if (   (h = CreateFileA("\\\\.\\ESDI_506",
+                            GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE
+                   && GetLastError() == ERROR_FILE_NOT_FOUND                             ) {
+                       pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n");
+                       return ENOENT;
+               }
+               else {
+                       if (h != INVALID_HANDLE_VALUE) // should not happen
+                               CloseHandle(h);
+                       pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");
+                       return ENOSYS;
+               }
+       }
+       else {
+               strcpy(path+len, "\\SMARTVSD.VXD");
+               if (!access(path, 0)) {
+                       // Some Windows versions install SMARTVSD.VXD in SYSTEM directory
+                       // (http://support.microsoft.com/kb/265854/en-us).
+                       path[len] = 0;
+                       pout("SMART driver is not properly installed,\n"
+                                " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n"
+                                " and reboot Windows.\n", path, path);
+               }
+               else {
+                       // Some Windows versions do not provide SMARTVSD.VXD
+                       // (http://support.microsoft.com/kb/199886/en-us).
+                       path[len] = 0;
+                       pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path);
+               }
+               return ENOSYS;
+       }
+}
+
+
+// Get default ATA device options
+
+static const char * ata_get_def_options()
+{
+       DWORD ver = GetVersion();
+       if ((ver & 0x80000000) || (ver & 0xff) < 4) // Win9x/ME
+               return "s"; // SMART_* only
+       else if ((ver & 0xff) == 4) // WinNT4
+               return "sc"; // SMART_*, SCSI_PASS_THROUGH
+       else // WinXP, 2003, Vista
+               return "psai"; // GetDevicePowerState(), SMART_*, ATA_, IDE_PASS_THROUGH
+}
+
+
+// Open ATA device
+
+static int ata_open(int drive, const char * options, int port)
+{
+       int win9x;
+       char devpath[30];
+       int devmap;
+
+       // TODO: This version does not allow to open more than 1 ATA devices
+       if (h_ata_ioctl) {
+               errno = ENFILE;
+               return -1;
+       }
+
+       win9x = ((GetVersion() & 0x80000000) != 0);
+
+       if (!(0 <= drive && drive <= (win9x ? 7 : 9))) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       // path depends on Windows Version
+       if (win9x)
+               // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details
+               strcpy(devpath, (drive <= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE"));
+       else
+               snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", drive);
+
+       // Open device
+       if ((h_ata_ioctl = CreateFileA(devpath,
+               GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+               NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
+               long err = GetLastError();      
+               pout("Cannot open device %s, Error=%ld\n", devpath, err);
+               if (err == ERROR_FILE_NOT_FOUND)
+                       errno = (win9x && drive <= 3 ? smartvsd_error() : ENOENT);
+               else if (err == ERROR_ACCESS_DENIED) {
+                       if (!win9x)
+                               pout("Administrator rights are necessary to access physical drives.\n");
+                       errno = EACCES;
+               }
+               else
+                       errno = EIO;
+               h_ata_ioctl = 0;
+               return -1;
+       }
+
+       if (con->reportataioctl > 1)
+               pout("%s: successfully opened\n", devpath);
+
+       // Save options
+       if (!*options) {
+               // Set default options according to Windows version
+               if (!ata_def_options)
+                       ata_def_options = ata_get_def_options();
+               options = (port < 0 ? ata_def_options : "s"); // RAID: SMART_* only
+       }
+       ata_cur_options = strdup(options);
+
+       // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
+       ata_driveno = drive;
+       if (!win9x && port < 0)
+               return 0;
+
+       // Win9X/ME: Get drive map
+       // RAID: Get port map
+       unsigned long portmap = 0;
+       devmap = smart_get_version(h_ata_ioctl, (port >= 0 ? &portmap : 0));
+       if (devmap < 0) {
+               if (!is_permissive()) {
+                       ata_close(0);
+                       errno = ENOSYS;
+                       return -1;
+               }
+               devmap = 0x0f;
+       }
+       ata_smartver_state[drive] = 1;
+
+       if (port >= 0) {
+               // RAID: Check port existence
+               if (!(portmap & (1L << port))) {
+                       pout("%s: Port %d is empty or does not exist\n", devpath, port);
+                       if (!is_permissive()) {
+                               ata_close(0);
+                               errno = ENOENT;
+                               return -1;
+                       }
+               }
+               // Encode port into pseudo fd
+               return (0x0200 | port);
+       }
+
+       // Win9x/ME: Check device presence & type
+       if (((devmap >> (drive & 0x3)) & 0x11) != 0x01) {
+               unsigned char atapi = (devmap >> (drive & 0x3)) & 0x10;
+               pout("%s: Drive %d %s (IDEDeviceMap=0x%02x).\n", devpath,
+                    drive, (atapi?"is an ATAPI device":"does not exist"), devmap);
+               // Win9x drive existence check may not work as expected
+               // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01
+               // (The related KB Article Q196120 is no longer available)
+               if (!is_permissive()) {
+                       ata_close(0);
+                       errno = (atapi ? ENOSYS : ENOENT);
+                       return -1;
+               }
+       }
+       // Use drive number as fd for ioctl
+       return (drive & 0x3);
+}
+
+
+static void ata_close(int fd)
+{
+       ARGUSED(fd);
+       CloseHandle(h_ata_ioctl);
+       h_ata_ioctl = 0;
+       if (ata_cur_options) {
+               free(ata_cur_options);
+               ata_cur_options = 0;
+       }
+}
+
+
+// Scan for ATA drives, fill bitmask of drives present, return #drives
+
+static int ata_scan(unsigned long * drives, int * rdriveno, unsigned long * rdrives)
+{
+       int win9x = ((GetVersion() & 0x80000000) != 0);
+       int cnt = 0, i;
+
+       for (i = 0; i <= 9; i++) {
+               char devpath[30];
+               GETVERSIONOUTPARAMS vers;
+               const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
+               DWORD num_out;
+               HANDLE h;
+               if (win9x)
+                       strcpy(devpath, "\\\\.\\SMARTVSD");
+               else
+                       snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", i);
+
+               // Open device
+               if ((h = CreateFileA(devpath,
+                       GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                       NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
+                       if (con->reportataioctl > 1)
+                               pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError());
+                       if (win9x)
+                               break; // SMARTVSD.VXD missing or no ATA devices
+                       continue; // Disk not found or access denied (break;?)
+               }
+
+               // Get drive map
+               memset(&vers, 0, sizeof(vers));
+               if (!DeviceIoControl(h, SMART_GET_VERSION,
+                       NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
+                       if (con->reportataioctl)
+                               pout(" %s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError());
+                       CloseHandle(h);
+                       if (win9x)
+                               break; // Should not happen
+                       continue; // Non ATA disk or no SMART ioctl support (possibly SCSI disk)
+               }
+               CloseHandle(h);
+
+               if (con->reportataioctl) {
+                       pout(" %s: SMART_GET_VERSION (%ld bytes):\n"
+                            "  Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
+                               devpath, num_out, vers.bVersion, vers.bRevision,
+                               vers.fCapabilities, vers.bIDEDeviceMap);
+                       if (vers_ex.wIdentifier == SMART_VENDOR_3WARE)
+                               pout("  Identifier = %04x(3WARE), ControllerId=%u, DeviceMapEx = 0x%08lx\n",
+                                       vers_ex.wIdentifier, vers_ex.wControllerId, vers_ex.dwDeviceMapEx);
+               }
+
+               if (win9x) {
+                       // Check ATA device presence, remove ATAPI devices
+                       drives[0] = (vers.bIDEDeviceMap & 0xf) & ~((vers.bIDEDeviceMap >> 4) & 0xf);
+                       cnt = (drives[0]&1) + ((drives[0]>>1)&1) + ((drives[0]>>2)&1) + ((drives[0]>>3)&1);
+                       break;
+               }
+
+               if (vers_ex.wIdentifier == SMART_VENDOR_3WARE) {
+                       // Skip if more than 2 controllers or logical drive from this controller already seen
+                       if (vers_ex.wControllerId >= 2 || rdriveno[vers_ex.wControllerId] >= 0)
+                               continue;
+                       assert(rdrives[vers_ex.wControllerId] == 0);
+                       // Count physical drives
+                       int pcnt = 0;
+                       for (int pi = 0; pi < 32; pi++) {
+                               if (vers_ex.dwDeviceMapEx & (1L << pi))
+                                       pcnt++;
+                       }
+                       if (!pcnt)
+                               continue; // Should not happen
+                       rdrives[vers_ex.wControllerId] = vers_ex.dwDeviceMapEx;
+                       rdriveno[vers_ex.wControllerId] = i;
+                       cnt += pcnt-1;
+               }
+
+               // ATA drive exists and driver supports SMART ioctl
+               drives[0] |= (1L << i);
+               cnt++;
+       }
+
+       return cnt;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Interface to ATA devices.  See os_linux.c
+int ata_command_interface(int fd, smart_command_set command, int select, char * data)
+{
+       IDEREGS regs;
+       int datasize;
+       const char * valid_options;
+       int i;
+
+       int port = -1;
+       if ((fd & ~0x1f) == 0x0200) {
+               // RAID Port encoded into pseudo fd
+               port = fd & 0x1f;
+               fd = 0;
+       }
+
+       if (!(0 <= fd && fd <= 3)) {
+               errno = EBADF;
+               return -1;
+       }
+
+       // CMD,CYL default to SMART, changed by P?IDENTIFY and CHECK_POWER_MODE
+       memset(&regs, 0, sizeof(regs));
+       regs.bCommandReg = ATA_SMART_CMD;
+       regs.bCylHighReg = SMART_CYL_HI; regs.bCylLowReg = SMART_CYL_LOW;
+       datasize = 0;
+
+       // Try all IOCTLS by default: SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH
+       valid_options = "saic";
+
+       switch (command) {
+         case WRITE_LOG:
+               // TODO. Requires DATA OUT support
+               errno = ENOSYS;
+               return -1;
+         case CHECK_POWER_MODE:
+               // Not a SMART command, needs IDE register return
+               regs.bCommandReg = ATA_CHECK_POWER_MODE;
+               regs.bCylLowReg = regs.bCylHighReg = 0;
+               valid_options = "pai"; // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
+               // Note: returns SectorCountReg in data[0]
+               break;
+         case READ_VALUES:
+               regs.bFeaturesReg = ATA_SMART_READ_VALUES;
+               regs.bSectorNumberReg = regs.bSectorCountReg = 1;
+               datasize = 512;
+               break;
+         case READ_THRESHOLDS:
+               regs.bFeaturesReg = ATA_SMART_READ_THRESHOLDS;
+               regs.bSectorNumberReg = regs.bSectorCountReg = 1;
+               datasize = 512;
+               break;
+         case READ_LOG:
+               regs.bFeaturesReg = ATA_SMART_READ_LOG_SECTOR;
+               regs.bSectorNumberReg = select;
+               regs.bSectorCountReg = 1;
+               // Note: SMART_RCV_DRIVE_DATA supports this only on Win9x/ME
+               datasize = 512;
+               break;
+         case IDENTIFY:
+               // Note: WinNT4/2000/XP return identify data cached during boot
+               // (true for SMART_RCV_DRIVE_DATA and IOCTL_IDE_PASS_THROUGH)
+               regs.bCommandReg = ATA_IDENTIFY_DEVICE;
+               regs.bCylLowReg = regs.bCylHighReg = 0;
+               regs.bSectorCountReg = 1;
+               datasize = 512;
+               break;
+         case PIDENTIFY:
+               regs.bCommandReg = ATA_IDENTIFY_PACKET_DEVICE;
+               regs.bCylLowReg = regs.bCylHighReg = 0;
+               regs.bSectorCountReg = 1;
+               datasize = 512;
+               break;
+         case ENABLE:
+               regs.bFeaturesReg = ATA_SMART_ENABLE;
+               regs.bSectorNumberReg = 1;
+               break;
+         case DISABLE:
+               regs.bFeaturesReg = ATA_SMART_DISABLE;
+               regs.bSectorNumberReg = 1;
+               break;
+         case STATUS_CHECK:
+               valid_options = "sai"; // Needs IDE register return
+         case STATUS:
+               regs.bFeaturesReg = ATA_SMART_STATUS;
+               break;
+         case AUTO_OFFLINE:
+               regs.bFeaturesReg = ATA_SMART_AUTO_OFFLINE;
+               regs.bSectorCountReg = select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+               break;
+         case AUTOSAVE:
+               regs.bFeaturesReg = ATA_SMART_AUTOSAVE;
+               regs.bSectorCountReg = select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+               break;
+         case IMMEDIATE_OFFLINE:
+               regs.bFeaturesReg = ATA_SMART_IMMEDIATE_OFFLINE;
+               regs.bSectorNumberReg = select;
+               // Note: SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME
+               break;
+         default:
+               pout("Unrecognized command %d in win32_ata_command_interface()\n"
+                "Please contact " PACKAGE_BUGREPORT "\n", command);
+               errno = ENOSYS;
+               return -1;
+       }
+
+       // Try all valid ioctls in the order specified in dev_ioctls;
+       for (i = 0; ; i++) {
+               char opt = ata_cur_options[i];
+               int rc;
+
+               if (!opt) {
+                       // No IOCTL found
+                       errno = ENOSYS;
+                       return -1;
+               }
+               if (!strchr(valid_options, opt))
+                       // Invalid for this command
+                       continue;
+
+               errno = 0;
+               assert(datasize == 0 || datasize == 512);
+               switch (opt) {
+                 default: assert(0);
+                 case 's':
+                       // call SMART_GET_VERSION once for each drive
+                       assert(0 <= ata_driveno && ata_driveno < sizeof(ata_smartver_state));
+                       if (ata_smartver_state[ata_driveno] > 1) {
+                               rc = -1; errno = ENOSYS;
+                               break;
+                       }
+                       if (!ata_smartver_state[ata_driveno]) {
+                               assert(port == -1);
+                               if (smart_get_version(h_ata_ioctl) < 0) {
+                                       if (!con->permissive) {
+                                               pout("ATA/SATA driver is possibly a SCSI class driver not supporting SMART.\n");
+                                               pout("If this is a SCSI disk, try \"scsi<adapter><id>\".\n");
+                                               ata_smartver_state[ata_driveno] = 2;
+                                               rc = -1; errno = ENOSYS;
+                                               break;
+                                       }
+                                       con->permissive--;
+                               }
+                               ata_smartver_state[ata_driveno] = 1;
+                       }
+                       rc = smart_ioctl(h_ata_ioctl, fd, &regs, data, datasize, port);
+                       break;
+                 case 'a':
+                       rc = ata_pass_through_ioctl(h_ata_ioctl, &regs, data, datasize);
+                       break;
+                 case 'i':
+                       rc = ide_pass_through_ioctl(h_ata_ioctl, &regs, data, datasize);
+                       break;
+                 case 'c':
+                       rc = ata_via_scsi_pass_through_ioctl(h_ata_ioctl, &regs, data, datasize);
+                       break;
+                 case 'p':
+                       assert(command == CHECK_POWER_MODE && datasize == 0);
+                       rc = get_device_power_state(h_ata_ioctl);
+                       if (rc >= 0) { // Simulate ATA command result
+                               regs.bSectorCountReg = (rc ? 0xff/*ACTIVE/IDLE*/ : 0x00/*STANDBY*/);
+                               rc = 0;
+                       }
+                       break;
+               }
+
+               if (!rc)
+                       // Working ioctl found
+                       break;
+
+               if (errno != ENOSYS)
+                       // Abort on I/O error
+                       return -1;
+
+               // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
+       }
+
+       switch (command) {
+         case CHECK_POWER_MODE:
+               // Return power mode from SectorCountReg in data[0]
+               data[0] = regs.bSectorCountReg;
+               return 0;
+
+         case STATUS_CHECK:
+               // Cyl low and Cyl high unchanged means "Good SMART status"
+               if (regs.bCylHighReg == SMART_CYL_HI && regs.bCylLowReg == SMART_CYL_LOW)
+                 return 0;
+
+               // These values mean "Bad SMART status"
+               if (regs.bCylHighReg == 0x2c && regs.bCylLowReg == 0xf4)
+                 return 1;
+
+               // We haven't gotten output that makes sense; print out some debugging info
+               syserror("Error SMART Status command failed");
+               pout("Please get assistance from %s\n", PACKAGE_HOMEPAGE);
+               print_ide_regs(&regs, 1);
+               errno = EIO;
+               return -1;
+
+         default:
+               return 0;
+       }
+       /*NOTREACHED*/
+}
+
+
+#ifndef HAVE_ATA_IDENTIFY_IS_CACHED
+#error define of HAVE_ATA_IDENTIFY_IS_CACHED missing in config.h
+#endif
+
+// Return true if OS caches the ATA identify sector
+int ata_identify_is_cached(int fd)
+{
+       // Not RAID and WinNT4/2000/XP => true, RAID or Win9x/ME => false
+       return (!(fd & 0xff00) && (GetVersion() & 0x80000000) == 0);
+}
+
+
+// Print not implemeted warning once
+static void pr_not_impl(const char * what, int * warned)
+{
+       if (*warned)
+               return;
+       pout(
+               "#######################################################################\n"
+               "%s\n"
+               "NOT IMPLEMENTED under Win32.\n"
+               "Please contact " PACKAGE_BUGREPORT " if\n"
+               "you want to help in porting smartmontools to Win32.\n"
+               "#######################################################################\n"
+               "\n", what
+       );
+       *warned = 1;
+}
+
+// Interface to ATA devices behind 3ware escalade RAID controller cards.  See os_linux.c
+int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data)
+{
+       static int warned = 0;
+       ARGUSED(fd); ARGUSED(escalade_type); ARGUSED(command); ARGUSED(select); ARGUSED(data);
+       if (!warned) {
+               pout("Option '-d 3ware,%d' does not work on Windows.\n"
+                    "Controller port can be specified in the device name: '/dev/hd%c,%d'.\n\n",
+                       disknum, 'a'+ata_driveno, disknum);
+               warned = 1;
+       }
+       errno = ENOSYS;
+       return -1;
+}
+
+// Interface to ATA devices behind Marvell chip-set based controllers.  See os_linux.c
+int marvell_command_interface(int fd, smart_command_set command, int select, char * data)
+{
+       static int warned = 0;
+       ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
+       pr_not_impl("Marvell chip-set command routine marvell_command_interface()", &warned);
+       errno = ENOSYS;
+       return -1;
+}
+
+// Interface to ATA devices behind HighPoint Raid controllers.  See os_linux.c
+int highpoint_command_interface(int fd, smart_command_set command, int select, char * data)
+{
+       static int warned = 0;
+       ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
+       pr_not_impl("HighPoint raid controller command routine highpoint_command_interface()", &warned);
+       errno = ENOSYS;
+       return -1;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// ASPI Interface
+/////////////////////////////////////////////////////////////////////////////
+
+#pragma pack(1)
+
+#define ASPI_SENSE_SIZE 18
+
+// ASPI SCSI Request block header
+
+typedef struct {
+       unsigned char cmd;             // 00: Command code
+       unsigned char status;          // 01: ASPI status
+       unsigned char adapter;         // 02: Host adapter number
+       unsigned char flags;           // 03: Request flags
+       unsigned char reserved[4];     // 04: 0
+} ASPI_SRB_HEAD;
+
+// SRB for host adapter inquiry
+
+typedef struct {
+       ASPI_SRB_HEAD h;               // 00: Header
+       unsigned char adapters;        // 08: Number of adapters
+       unsigned char target_id;       // 09: Target ID ?
+       char manager_id[16];           // 10: SCSI manager ID
+       char adapter_id[16];           // 26: Host adapter ID
+       unsigned char parameters[16];  // 42: Host adapter unique parmameters
+} ASPI_SRB_INQUIRY;
+
+// SRB for get device type
+
+typedef struct {
+       ASPI_SRB_HEAD h;               // 00: Header
+       unsigned char target_id;       // 08: Target ID
+       unsigned char lun;             // 09: LUN
+       unsigned char devtype;         // 10: Device type
+       unsigned char reserved;        // 11: Reserved
+} ASPI_SRB_DEVTYPE;
+
+// SRB for SCSI I/O
+
+typedef struct {
+       ASPI_SRB_HEAD h;               // 00: Header
+       unsigned char target_id;       // 08: Target ID
+       unsigned char lun;             // 09: LUN
+       unsigned char reserved[2];     // 10: Reserved
+       unsigned long data_size;       // 12: Data alloc. lenght
+       void * data_addr;              // 16: Data buffer pointer
+       unsigned char sense_size;      // 20: Sense alloc. length
+       unsigned char cdb_size;        // 21: CDB length
+       unsigned char host_status;     // 22: Host status
+       unsigned char target_status;   // 23: Target status
+       void * event_handle;           // 24: Event handle
+       unsigned char workspace[20];   // 28: ASPI workspace
+       unsigned char cdb[16+ASPI_SENSE_SIZE];
+} ASPI_SRB_IO;
+
+// Macro to retrieve start of sense information
+#define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)
+
+// SRB union
+
+typedef union {
+       ASPI_SRB_HEAD h;       // Common header
+       ASPI_SRB_INQUIRY q;    // Inquiry
+       ASPI_SRB_DEVTYPE t;    // Device type
+       ASPI_SRB_IO i;         // I/O
+} ASPI_SRB;
+
+#pragma pack()
+
+// ASPI commands
+#define ASPI_CMD_ADAPTER_INQUIRE        0x00
+#define ASPI_CMD_GET_DEVICE_TYPE        0x01
+#define ASPI_CMD_EXECUTE_IO             0x02
+#define ASPI_CMD_ABORT_IO               0x03
+
+// Request flags
+#define ASPI_REQFLAG_DIR_TO_HOST        0x08
+#define ASPI_REQFLAG_DIR_TO_TARGET      0x10
+#define ASPI_REQFLAG_DIR_NO_XFER        0x18
+#define ASPI_REQFLAG_EVENT_NOTIFY       0x40
+
+// ASPI status
+#define ASPI_STATUS_IN_PROGRESS         0x00
+#define ASPI_STATUS_NO_ERROR            0x01
+#define ASPI_STATUS_ABORTED             0x02
+#define ASPI_STATUS_ABORT_ERR           0x03
+#define ASPI_STATUS_ERROR               0x04
+#define ASPI_STATUS_INVALID_COMMAND     0x80
+#define ASPI_STATUS_INVALID_ADAPTER     0x81
+#define ASPI_STATUS_INVALID_TARGET      0x82
+#define ASPI_STATUS_NO_ADAPTERS         0xE8
+
+// Adapter (host) status
+#define ASPI_HSTATUS_NO_ERROR           0x00
+#define ASPI_HSTATUS_SELECTION_TIMEOUT  0x11
+#define ASPI_HSTATUS_DATA_OVERRUN       0x12
+#define ASPI_HSTATUS_BUS_FREE           0x13
+#define ASPI_HSTATUS_BUS_PHASE_ERROR    0x14
+#define ASPI_HSTATUS_BAD_SGLIST         0x1A
+
+// Target status
+#define ASPI_TSTATUS_NO_ERROR           0x00
+#define ASPI_TSTATUS_CHECK_CONDITION    0x02
+#define ASPI_TSTATUS_BUSY               0x08
+#define ASPI_TSTATUS_RESERV_CONFLICT    0x18
+
+
+static HINSTANCE h_aspi_dll; // DLL handle
+static UINT (* aspi_entry)(ASPI_SRB * srb); // ASPI entrypoint
+static unsigned num_aspi_adapters;
+
+#ifdef __CYGWIN__
+// h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
+static DWORD aspi_dll_pid; // PID of DLL owner to detect fork()
+#define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))
+#else
+#define aspi_entry_valid() (!!aspi_entry)
+#endif
+
+
+static int aspi_call(ASPI_SRB * srb)
+{
+       int i;
+       aspi_entry(srb);
+       i = 0;
+       while (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
+               if (++i > 100/*10sek*/) {
+                       pout("ASPI Adapter %u: Timed out\n", srb->h.adapter);
+                       aspi_entry = 0;
+                       h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
+                       errno = EIO;
+                       return -1;
+               }
+               if (con->reportscsiioctl > 1)
+                       pout("ASPI Adapter %u: Waiting (%d) ...\n", srb->h.adapter, i);
+               Sleep(100);
+       }
+       return 0;
+}
+
+
+// Get ASPI entrypoint from wnaspi32.dll
+
+static FARPROC aspi_get_address(const char * name, int verbose)
+{
+       FARPROC addr;
+       assert(h_aspi_dll && h_aspi_dll != INVALID_HANDLE_VALUE);
+
+       if (!(addr = GetProcAddress(h_aspi_dll, name))) {
+               if (verbose)
+                       pout("Missing %s() in WNASPI32.DLL\n", name);
+               aspi_entry = 0;
+               FreeLibrary(h_aspi_dll);
+               h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
+               errno = ENOSYS;
+               return 0;
+       }
+       return addr;
+}
+
+
+static int aspi_open_dll(int verbose)
+{
+       UINT (*aspi_info)(void);
+       UINT info, rc;
+
+       assert(!aspi_entry_valid());
+
+       // Check structure layout
+       assert(sizeof(ASPI_SRB_HEAD) == 8);
+       assert(sizeof(ASPI_SRB_INQUIRY) == 58);
+       assert(sizeof(ASPI_SRB_DEVTYPE) == 12);
+       assert(sizeof(ASPI_SRB_IO) == 64+ASPI_SENSE_SIZE);
+       assert(offsetof(ASPI_SRB,h.cmd) == 0);
+       assert(offsetof(ASPI_SRB,h.flags) == 3);
+       assert(offsetof(ASPI_SRB_IO,lun) == 9);
+       assert(offsetof(ASPI_SRB_IO,data_addr) == 16);
+       assert(offsetof(ASPI_SRB_IO,workspace) == 28);
+       assert(offsetof(ASPI_SRB_IO,cdb) == 48);
+
+       if (h_aspi_dll == INVALID_HANDLE_VALUE) {
+               // do not retry
+               errno = ENOENT;
+               return -1;
+       }
+
+       // Load ASPI DLL
+       if (!(h_aspi_dll = LoadLibraryA("WNASPI32.DLL"))) {
+               if (verbose)
+                       pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());
+               h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
+               errno = ENOENT;
+               return -1;
+       }
+       if (con->reportscsiioctl > 1) {
+               // Print full path of WNASPI32.DLL
+               char path[MAX_PATH];
+               if (!GetModuleFileName(h_aspi_dll, path, sizeof(path)))
+                       strcpy(path, "*unknown*");
+               pout("Using ASPI interface \"%s\"\n", path);
+       }
+
+       // Get ASPI entrypoints
+       if (!(aspi_info = (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose)))
+               return -1;
+       if (!(aspi_entry = (UINT (*)(ASPI_SRB *))aspi_get_address("SendASPI32Command", verbose)))
+               return -1;
+
+       // Init ASPI manager and get number of adapters
+       info = (aspi_info)();
+       if (con->reportscsiioctl > 1)
+               pout("GetASPI32SupportInfo() returns 0x%04x\n", info);
+       rc = (info >> 8) & 0xff;
+       if (rc == ASPI_STATUS_NO_ADAPTERS) {
+               num_aspi_adapters = 0;
+       }
+       else if (rc == ASPI_STATUS_NO_ERROR) {
+               num_aspi_adapters = info & 0xff;
+       }
+       else {
+               if (verbose)
+                       pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info);
+               aspi_entry = 0;
+               FreeLibrary(h_aspi_dll);
+               h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
+               errno = ENOENT;
+               return -1;
+       }
+
+       if (con->reportscsiioctl)
+               pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
+
+#ifdef __CYGWIN__
+       // save PID to detect fork() in aspi_entry_valid()
+       aspi_dll_pid = GetCurrentProcessId();
+#endif
+       assert(aspi_entry_valid());
+       return 0;
+}
+
+
+static int aspi_io_call(ASPI_SRB * srb, unsigned timeout)
+{
+       HANDLE event;
+       // Create event
+       if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL))) {
+               pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO;
+       }
+       srb->i.event_handle = event;
+       srb->h.flags |= ASPI_REQFLAG_EVENT_NOTIFY;
+       // Start ASPI request
+       aspi_entry(srb);
+       if (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
+               // Wait for event
+               DWORD rc = WaitForSingleObject(event, timeout*1000L);
+               if (rc != WAIT_OBJECT_0) {
+                       if (rc == WAIT_TIMEOUT) {
+                               pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n",
+                                       srb->h.adapter, srb->i.target_id, timeout);
+                       }
+                       else {
+                               pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",
+                                       (unsigned long)event, rc, rc, GetLastError());
+                       }
+                       // TODO: ASPI_ABORT_IO command
+                       aspi_entry = 0;
+                       h_aspi_dll = (HINSTANCE)INVALID_HANDLE_VALUE;
+                       return -EIO;
+               }
+       }
+       CloseHandle(event);
+       return 0;
+}
+
+
+static int aspi_open(unsigned adapter, unsigned id)
+{
+       ASPI_SRB srb;
+       if (!(adapter <= 9 && id < 16)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       if (!aspi_entry_valid()) {
+               if (aspi_open_dll(1/*verbose*/))
+                       return -1;
+       }
+
+       // Adapter OK?
+       if (adapter >= num_aspi_adapters) {
+               pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n",
+                       adapter, num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
+               if (!is_permissive()) {
+                       errno = ENOENT;
+                       return -1;
+               }
+       }
+
+       // Device present ?
+       memset(&srb, 0, sizeof(srb));
+       srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
+       srb.h.adapter = adapter; srb.i.target_id = id;
+       if (aspi_call(&srb)) {
+               errno = EIO;
+               return -1;
+       }
+       if (srb.h.status != ASPI_STATUS_NO_ERROR) {
+               pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter, id, srb.h.status);
+               if (!is_permissive()) {
+                       errno = (srb.h.status == ASPI_STATUS_INVALID_TARGET ? ENOENT : EIO);
+                       return -1;
+               }
+       }
+       else if (con->reportscsiioctl)
+               pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype);
+
+       return (0x0100 | ((adapter & 0xf)<<4) | (id & 0xf));
+}
+
+
+static void aspi_close(int fd)
+{
+       // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
+       ARGUSED(fd);
+}
+
+
+// Scan for SCSI drives, fill bitmask [adapter:0-9][id:0-7] of drives present,
+// return #drives
+
+static int aspi_scan(unsigned long * drives)
+{
+       int cnt = 0;
+       unsigned ad;
+
+       if (!aspi_entry_valid()) {
+               if (aspi_open_dll(con->reportscsiioctl/*default is quiet*/))
+                       return 0;
+       }
+
+       for (ad = 0; ad < num_aspi_adapters; ad++) {
+               ASPI_SRB srb; unsigned id;
+
+               if (ad > 9) {
+                       if (con->reportscsiioctl)
+                               pout(" ASPI Adapter %u: Ignored\n", ad);
+                       continue;
+               }
+
+               // Get adapter name
+               memset(&srb, 0, sizeof(srb));
+               srb.h.cmd = ASPI_CMD_ADAPTER_INQUIRE;
+               srb.h.adapter = ad;
+               if (aspi_call(&srb))
+                       return 0;
+
+               if (srb.h.status != ASPI_STATUS_NO_ERROR) {
+                       if (con->reportscsiioctl)
+                               pout(" ASPI Adapter %u: Status=0x%02x\n", ad, srb.h.status);
+                       continue;
+               }
+
+               if (con->reportscsiioctl) {
+                       int i;
+                       for (i = 1; i < 16 && srb.q.adapter_id[i]; i++)
+                               if (!(' ' <= srb.q.adapter_id[i] && srb.q.adapter_id[i] <= '~'))
+                                       srb.q.adapter_id[i] = '?';
+                       pout(" ASPI Adapter %u (\"%.16s\"):\n", ad, srb.q.adapter_id);
+               }
+
+               bool ignore = !strnicmp(srb.q.adapter_id, "3ware", 5);
+
+               for (id = 0; id <= 7; id++) {
+                       // Get device type
+                       memset(&srb, 0, sizeof(srb));
+                       srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
+                       srb.h.adapter = ad; srb.i.target_id = id;
+                       if (aspi_call(&srb))
+                               return 0;
+                       if (srb.h.status != ASPI_STATUS_NO_ERROR) {
+                               if (con->reportscsiioctl > 1)
+                                       pout("  ID %u: No such device (Status=0x%02x)\n", id, srb.h.status);
+                               continue;
+                       }
+
+                       if (!ignore && srb.t.devtype == 0x00/*HDD*/) {
+                               if (con->reportscsiioctl)
+                                       pout("  ID %u: Device Type=0x%02x\n", id, srb.t.devtype);
+                               drives[ad >> 2] |= (1L << (((ad & 0x3) << 3) + id));
+                               cnt++;
+                       }
+                       else if (con->reportscsiioctl)
+                               pout("  ID %u: Device Type=0x%02x (ignored)\n", id, srb.t.devtype);
+               }
+       }
+       return cnt;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Interface to SCSI devices.  See os_linux.c
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
+{
+       ASPI_SRB srb;
+
+       if (!aspi_entry_valid())
+               return -EBADF;
+       if (!((fd & ~0xff) == 0x100))
+               return -EBADF;
+
+       if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12 || iop->cmnd_len == 16)) {
+               pout("do_scsi_cmnd_io: bad CDB length\n");
+               return -EINVAL;
+       }
+
+       if (report > 0) {
+               // From os_linux.c
+               int k, j;
+               const unsigned char * ucp = iop->cmnd;
+               const char * np;
+               char buff[256];
+               const int sz = (int)sizeof(buff);
+
+               np = scsi_get_opcode_name(ucp[0]);
+               j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+               for (k = 0; k < (int)iop->cmnd_len; ++k)
+                       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+               if ((report > 1) && 
+                       (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+                       int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+                       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+                                                 "data, len=%d%s:\n", (int)iop->dxfer_len,
+                                                 (trunc ? " [only first 256 bytes shown]" : ""));
+                       dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+               }
+               else
+                       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+               pout(buff);
+       }
+
+       memset(&srb, 0, sizeof(srb));
+       srb.h.cmd = ASPI_CMD_EXECUTE_IO;
+       srb.h.adapter = ((fd >> 4) & 0xf);
+       srb.i.target_id = (fd & 0xf);
+       //srb.i.lun = 0;
+       srb.i.sense_size = ASPI_SENSE_SIZE;
+       srb.i.cdb_size = iop->cmnd_len;
+       memcpy(srb.i.cdb, iop->cmnd, iop->cmnd_len);
+
+       switch (iop->dxfer_dir) {
+               case DXFER_NONE:
+                       srb.h.flags = ASPI_REQFLAG_DIR_NO_XFER;
+                       break;
+               case DXFER_FROM_DEVICE:
+                       srb.h.flags = ASPI_REQFLAG_DIR_TO_HOST;
+                       srb.i.data_size = iop->dxfer_len;
+                       srb.i.data_addr = iop->dxferp;
+                       break;
+               case DXFER_TO_DEVICE:
+                       srb.h.flags = ASPI_REQFLAG_DIR_TO_TARGET;
+                       srb.i.data_size = iop->dxfer_len;
+                       srb.i.data_addr = iop->dxferp;
+                       break;
+               default:
+                       pout("do_scsi_cmnd_io: bad dxfer_dir\n");
+                       return -EINVAL;
+       }
+
+       iop->resp_sense_len = 0;
+       iop->scsi_status = 0;
+       iop->resid = 0;
+
+       if (aspi_io_call(&srb, (iop->timeout ? iop->timeout : 60))) {
+               // Timeout
+               return -EIO;
+       }
+
+       if (srb.h.status != ASPI_STATUS_NO_ERROR) {
+               if (   srb.h.status        == ASPI_STATUS_ERROR
+                   && srb.i.host_status   == ASPI_HSTATUS_NO_ERROR
+                   && srb.i.target_status == ASPI_TSTATUS_CHECK_CONDITION) {
+                       // Sense valid
+                       const unsigned char * sense = ASPI_SRB_SENSE(&srb.i, iop->cmnd_len);
+                       int len = (ASPI_SENSE_SIZE < iop->max_sense_len ? ASPI_SENSE_SIZE : iop->max_sense_len);
+                       iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
+                       if (len > 0 && iop->sensep) {
+                               memcpy(iop->sensep, sense, len);
+                               iop->resp_sense_len = len;
+                               if (report > 1) {
+                                       pout("  >>> Sense buffer, len=%d:\n", (int)len);
+                                       dStrHex(iop->sensep, len , 1);
+                               }
+                       }
+                       if (report) {
+                               pout("  sense_key=%x asc=%x ascq=%x\n",
+                                sense[2] & 0xf, sense[12], sense[13]);
+                       }
+                       return 0;
+               }
+               else {
+                       if (report)
+                               pout("  ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb.h.status, srb.i.host_status, srb.i.target_status);
+                       return -EIO;
+               }
+       }
+
+       if (report > 0)
+               pout("  OK\n");
+
+       if (iop->dxfer_dir == DXFER_FROM_DEVICE && report > 1) {
+                int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+                pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+                         (trunc ? " [only first 256 bytes shown]" : ""));
+                               dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+       }
+
+       return 0;
+}
diff --git a/os_win32/daemon_win32.c b/os_win32/daemon_win32.c
deleted file mode 100644 (file)
index 2cac47f..0000000
+++ /dev/null
@@ -1,1266 +0,0 @@
-/*
- * os_win32/daemon_win32.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <io.h>
-
-#define WIN32_LEAN_AND_MEAN
-// Need MB_SERVICE_NOTIFICATION (NT4/2000/XP), IsDebuggerPresent() (Win98/ME/NT4/2000/XP)
-#define _WIN32_WINNT 0x0400 
-#include <windows.h>
-#ifdef _DEBUG
-#include <crtdbg.h>
-#endif
-
-#include "daemon_win32.h"
-
-const char *daemon_win32_c_cvsid = "$Id: daemon_win32.c,v 1.10 2006/04/12 14:54:28 ballen4705 Exp $"
-DAEMON_WIN32_H_CVSID;
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-#define ARGUSED(x) ((void)(x))
-
-// Prevent spawning of child process if debugging
-#ifdef _DEBUG
-#define debugging() IsDebuggerPresent()
-#else
-#define debugging() FALSE
-#endif
-
-
-#define EVT_NAME_LEN 260
-
-// Internal events (must be > SIGUSRn)
-#define EVT_RUNNING   100 // Exists when running, signaled on creation
-#define EVT_DETACHED  101 // Signaled when child detaches from console
-#define EVT_RESTART   102 // Signaled when child should restart
-
-static void make_name(char * name, int sig)
-{
-       int i;
-       if (!GetModuleFileNameA(NULL, name, EVT_NAME_LEN-10))
-               strcpy(name, "DaemonEvent");
-       for (i = 0; name[i]; i++) {
-               char c = name[i];
-               if (!(   ('0' <= c && c <= '9')
-                     || ('A' <= c && c <= 'Z')
-                     || ('a' <= c && c <= 'z')))
-                         name[i] = '_';
-       }
-       sprintf(name+strlen(name), "-%d", sig);
-}
-
-
-static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists)
-{
-       char name[EVT_NAME_LEN];
-       HANDLE h;
-       if (sig >= 0)
-               make_name(name, sig);
-       else
-               name[0] = 0;
-       if (exists)
-               *exists = FALSE;
-       if (!(h = CreateEventA(NULL, FALSE, initial, (name[0] ? name : NULL)))) {
-               if (errmsg)
-                       fprintf(stderr, "CreateEvent(.,\"%s\"): Error=%ld\n", name, GetLastError());
-               return 0;
-       }
-
-       if (GetLastError() == ERROR_ALREADY_EXISTS) {
-               if (!exists) {
-                       if (errmsg)
-                               fprintf(stderr, "CreateEvent(.,\"%s\"): Exists\n", name);
-                       CloseHandle(h);
-                       return 0;
-               }
-               *exists = TRUE;
-       }
-       return h;
-}
-
-
-static HANDLE open_event(int sig)
-{
-       char name[EVT_NAME_LEN];
-       make_name(name, sig);
-       return OpenEventA(EVENT_MODIFY_STATE, FALSE, name);
-}
-
-
-static int event_exists(int sig)
-{
-       char name[EVT_NAME_LEN];
-       HANDLE h;
-       make_name(name, sig);
-       if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name)))
-               return 0;
-       CloseHandle(h);
-       return 1;
-}
-
-
-static int sig_event(int sig)
-{
-       char name[EVT_NAME_LEN];
-       HANDLE h;
-       make_name(name, sig);
-       if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) {
-               make_name(name, EVT_RUNNING);
-               if (!(h = OpenEvent(EVENT_MODIFY_STATE, FALSE, name)))
-                       return -1;
-               CloseHandle(h);
-               return 0;
-       }
-       SetEvent(h);
-       CloseHandle(h);
-       return 1;
-}
-
-
-static void daemon_help(FILE * f, const char * ident, const char * message)
-{
-       fprintf(f,
-               "%s: %s.\n"
-               "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n",
-               ident, message, ident);
-       fflush(f);
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// Parent Process
-
-
-static BOOL WINAPI parent_console_handler(DWORD event)
-{
-       switch (event) {
-               case CTRL_C_EVENT:
-               case CTRL_BREAK_EVENT:
-                       return TRUE; // Ignore
-       }
-       return FALSE; // continue with next handler ...
-}
-
-
-static int parent_main(HANDLE rev)
-{
-       HANDLE dev;
-       HANDLE ht[2];
-       char * cmdline;
-       STARTUPINFO si;
-       PROCESS_INFORMATION pi;
-       DWORD rc, exitcode;
-
-       // Ignore ^C, ^BREAK in parent
-       SetConsoleCtrlHandler(parent_console_handler, TRUE/*add*/);
-
-       // Create event used by child to signal daemon_detach()
-       if (!(dev = create_event(EVT_DETACHED, FALSE/*not signaled*/, TRUE, NULL/*must not exist*/))) {
-               CloseHandle(rev);
-               return 101;
-       }
-
-       // Restart process with same args
-       cmdline = GetCommandLineA();
-       memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
-       
-       if (!CreateProcessA(
-               NULL, cmdline,
-               NULL, NULL, TRUE/*inherit*/,
-               0, NULL, NULL, &si, &pi)) {
-               fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError());
-               CloseHandle(rev); CloseHandle(dev);
-               return 101;
-       }
-       CloseHandle(pi.hThread);
-
-       // Wait for daemon_detach() or exit()
-       ht[0] = dev; ht[1] = pi.hProcess;
-       rc = WaitForMultipleObjects(2, ht, FALSE/*or*/, INFINITE);
-       if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+2)) {
-               fprintf(stderr, "WaitForMultipleObjects returns %lX\n", rc);
-               TerminateProcess(pi.hProcess, 200);
-       }
-       CloseHandle(rev); CloseHandle(dev);
-
-       // Get exit code
-       if (!GetExitCodeProcess(pi.hProcess, &exitcode))
-               exitcode = 201;
-       else if (exitcode == STILL_ACTIVE) // detach()ed, assume OK
-               exitcode = 0;
-
-       CloseHandle(pi.hProcess);
-       return exitcode;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// Child Process
-
-
-static int svc_mode;   // Running as service?
-static int svc_paused; // Service paused?
-
-static void service_report_status(int state, int waithint);
-
-
-// Tables of signal handler and corresponding events
-typedef void (*sigfunc_t)(int);
-
-#define MAX_SIG_HANDLERS 8
-
-static int num_sig_handlers = 0;
-static sigfunc_t sig_handlers[MAX_SIG_HANDLERS];
-static int sig_numbers[MAX_SIG_HANDLERS];
-static HANDLE sig_events[MAX_SIG_HANDLERS];
-
-static HANDLE sighup_handle, sigint_handle, sigbreak_handle;
-static HANDLE sigterm_handle, sigusr1_handle;
-
-static HANDLE running_event;
-
-static int reopen_stdin, reopen_stdout, reopen_stderr;
-
-
-// Handler for windows console events
-
-static BOOL WINAPI child_console_handler(DWORD event)
-{
-       // Caution: runs in a new thread
-       // TODO: Guard with a mutex
-       HANDLE h = 0;
-       switch (event) {
-               case CTRL_C_EVENT: // <CONTROL-C> (SIGINT)
-                       h = sigint_handle; break;
-               case CTRL_BREAK_EVENT: // <CONTROL-Break> (SIGBREAK/SIGQUIT)
-               case CTRL_CLOSE_EVENT: // User closed console or abort via task manager
-                       h = sigbreak_handle; break;
-               case CTRL_LOGOFF_EVENT: // Logout/Shutdown (SIGTERM)
-               case CTRL_SHUTDOWN_EVENT:
-                       h = sigterm_handle; break;
-       }
-       if (!h)
-               return FALSE; // continue with next handler
-       // Signal event
-       if (!SetEvent(h))
-               return FALSE;
-       return TRUE;
-}
-
-
-static void child_exit(void)
-{
-       int i;
-       char * cmdline;
-       HANDLE rst;
-       STARTUPINFO si;
-       PROCESS_INFORMATION pi;
-
-       for (i = 0; i < num_sig_handlers; i++)
-               CloseHandle(sig_events[i]);
-       num_sig_handlers = 0;
-       CloseHandle(running_event); running_event = 0;
-
-       // Restart?
-       if (!(rst = open_event(EVT_RESTART)))
-               return; // No => normal exit
-
-       // Yes => Signal exit and restart process
-       Sleep(500);
-       SetEvent(rst);
-       CloseHandle(rst);
-       Sleep(500);
-
-       cmdline = GetCommandLineA();
-       memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
-       si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE;
-
-       if (!CreateProcessA(
-               NULL, cmdline,
-               NULL, NULL, TRUE/*inherit*/,
-               0, NULL, NULL, &si, &pi)) {
-               fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError());
-       }
-       CloseHandle(pi.hThread); CloseHandle(pi.hProcess);
-}
-
-static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv)
-{
-       // Keep EVT_RUNNING open until exit
-       running_event = hev;
-
-       // Install console handler
-       SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
-
-       // Install restart handler
-       atexit(child_exit);
-
-       // Continue in main_func() to do the real work
-       return main_func(argc, argv);
-}
-
-
-// Simulate signal()
-
-sigfunc_t daemon_signal(int sig, sigfunc_t func)
-{
-       int i;
-       HANDLE h;
-       if (func == SIG_DFL || func == SIG_IGN)
-               return func; // TODO
-       for (i = 0; i < num_sig_handlers; i++) {
-               if (sig_numbers[i] == sig) {
-                       sigfunc_t old = sig_handlers[i];
-                       sig_handlers[i] = func;
-                       return old;
-               }
-       }
-       if (num_sig_handlers >= MAX_SIG_HANDLERS)
-               return SIG_ERR;
-       if (!(h = create_event((!svc_mode ? sig : -1), FALSE, TRUE, NULL)))
-               return SIG_ERR;
-       sig_events[num_sig_handlers]   = h;
-       sig_numbers[num_sig_handlers]  = sig;
-       sig_handlers[num_sig_handlers] = func;
-       switch (sig) {
-               case SIGHUP:   sighup_handle   = h; break;
-               case SIGINT:   sigint_handle   = h; break;
-               case SIGTERM:  sigterm_handle  = h; break;
-               case SIGBREAK: sigbreak_handle = h; break;
-               case SIGUSR1:  sigusr1_handle  = h; break;
-       }
-       num_sig_handlers++;
-       return SIG_DFL;
-}
-
-
-// strsignal()
-
-const char * daemon_strsignal(int sig)
-{
-       switch (sig) {
-               case SIGHUP:  return "SIGHUP";
-               case SIGINT:  return "SIGINT";
-               case SIGTERM: return "SIGTERM";
-               case SIGBREAK:return "SIGBREAK";
-               case SIGUSR1: return "SIGUSR1";
-               case SIGUSR2: return "SIGUSR2";
-               default:      return "*UNKNOWN*";
-       }
-}
-
-
-// Simulate sleep()
-
-void daemon_sleep(int seconds)
-{
-       do {
-               if (num_sig_handlers <= 0) {
-                       Sleep(seconds*1000L);
-               }
-               else {
-                       // Wait for any signal or timeout
-                       DWORD rc = WaitForMultipleObjects(num_sig_handlers, sig_events,
-                               FALSE/*OR*/, seconds*1000L);
-                       if (rc != WAIT_TIMEOUT) {
-                               if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+(unsigned)num_sig_handlers)) {
-                                       fprintf(stderr,"WaitForMultipleObjects returns %lu\n", rc);
-                                       Sleep(seconds*1000L);
-                                       return;
-                               }
-                               // Call Handler
-                               sig_handlers[rc-WAIT_OBJECT_0](sig_numbers[rc-WAIT_OBJECT_0]);
-                               break;
-                       }
-               }
-       } while (svc_paused);
-}
-
-
-// Disable/Enable console
-
-void daemon_disable_console()
-{
-       SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/);
-       reopen_stdin = reopen_stdout = reopen_stderr = 0;
-       if (isatty(fileno(stdin))) {
-               fclose(stdin); reopen_stdin = 1;
-       }
-       if (isatty(fileno(stdout))) {
-               fclose(stdout); reopen_stdout = 1;
-       }
-       if (isatty(fileno(stderr))) {
-               fclose(stderr); reopen_stderr = 1;
-       }
-       FreeConsole();
-       SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
-}
-
-int daemon_enable_console(const char * title)
-{
-       BOOL ok;
-       SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/);
-       ok = AllocConsole();
-       SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
-       if (!ok)
-               return -1;
-       if (title)
-               SetConsoleTitleA(title);
-       if (reopen_stdin)
-               freopen("conin$",  "r", stdin);
-       if (reopen_stdout)
-               freopen("conout$", "w", stdout);
-       if (reopen_stderr)
-               freopen("conout$", "w", stderr);
-       reopen_stdin = reopen_stdout = reopen_stderr = 0;
-       return 0;
-}
-
-
-// Detach daemon from console & parent
-
-int daemon_detach(const char * ident)
-{
-       if (!svc_mode) {
-               if (ident) {
-                       // Print help
-                       FILE * f = ( isatty(fileno(stdout)) ? stdout
-                                          : isatty(fileno(stderr)) ? stderr : NULL);
-                       if (f)
-                               daemon_help(f, ident, "now detaches from console into background mode");
-               }
-               // Signal detach to parent
-               if (sig_event(EVT_DETACHED) != 1) {
-                       if (!debugging())
-                               return -1;
-               }
-               daemon_disable_console();
-       }
-       else {
-               // Signal end of initialization to service control manager
-               service_report_status(SERVICE_RUNNING, 0);
-               reopen_stdin = reopen_stdout = reopen_stderr = 1;
-       }
-
-       return 0;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// MessageBox
-
-#ifndef _MT
-//MT runtime not necessary, because mbox_thread uses no unsafe lib functions
-//#error Program must be linked with multithreaded runtime library
-#endif
-
-static LONG mbox_count; // # mbox_thread()s
-static HANDLE mbox_mutex; // Show 1 box at a time (not necessary for service)
-
-typedef struct mbox_args_s {
-       HANDLE taken; const char * title, * text; int mode;
-} mbox_args;
-
-
-// Thread to display one message box
-
-static ULONG WINAPI mbox_thread(LPVOID arg)
-{
-       // Take args
-       mbox_args * mb = (mbox_args *)arg;
-       char title[100]; char text[1000]; int mode;
-       strncpy(title, mb->title, sizeof(title)-1); title[sizeof(title)-1] = 0;
-       strncpy(text , mb->text , sizeof(text )-1); text [sizeof(text )-1] = 0;
-       mode = mb->mode;
-       SetEvent(mb->taken);
-
-       // Show only one box at a time
-       WaitForSingleObject(mbox_mutex, INFINITE);
-       MessageBoxA(NULL, text, title, mode);
-       ReleaseMutex(mbox_mutex);
-
-       InterlockedDecrement(&mbox_count);
-       return 0;
-}
-
-
-// Display a message box
-int daemon_messagebox(int system, const char * title, const char * text)
-{
-       mbox_args mb;
-       HANDLE ht; DWORD tid;
-
-       // Create mutex during first call
-       if (!mbox_mutex)
-               mbox_mutex = CreateMutex(NULL/*!inherit*/, FALSE/*!owned*/, NULL/*unnamed*/);
-
-       // Allow at most 10 threads
-       if (InterlockedIncrement(&mbox_count) > 10) {
-               InterlockedDecrement(&mbox_count);
-               return -1;
-       }
-
-       // Create thread
-       mb.taken = CreateEvent(NULL/*!inherit*/, FALSE, FALSE/*!signaled*/, NULL/*unnamed*/);
-       mb.mode = MB_OK|MB_ICONWARNING
-                |(svc_mode?MB_SERVICE_NOTIFICATION:0)
-                |(system?MB_SYSTEMMODAL:MB_APPLMODAL);
-       mb.title = title; mb.text = text;
-       mb.text = text;
-       if (!(ht = CreateThread(NULL, 0, mbox_thread, &mb, 0, &tid)))
-               return -1;
-
-       // Wait for args taken
-       if (WaitForSingleObject(mb.taken, 10000) != WAIT_OBJECT_0)
-               TerminateThread(ht, 0);
-       CloseHandle(mb.taken);
-       CloseHandle(ht);
-       return 0;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-// Spawn a command and redirect <inpbuf >outbuf
-// return command's exitcode or -1 on error
-
-int daemon_spawn(const char * cmd,
-                 const char * inpbuf, int inpsize,
-                 char *       outbuf, int outsize )
-{
-       HANDLE pipe_inp_r, pipe_inp_w, pipe_out_r, pipe_out_w, pipe_err_w, h;
-       char temp_path[MAX_PATH];
-       DWORD flags, num_io, exitcode;
-       int use_file, state, i;
-       SECURITY_ATTRIBUTES sa;
-       STARTUPINFO si; PROCESS_INFORMATION pi;
-       HANDLE self = GetCurrentProcess();
-
-       if (GetVersion() & 0x80000000L) {
-               // Win9x/ME: A calling process never receives EOF if output of COMMAND.COM or
-               // any other DOS program is redirected via a pipe. Using a temp file instead.
-               use_file = 1; flags = DETACHED_PROCESS;
-       }
-       else {
-               // NT4/2000/XP: If DETACHED_PROCESS is used, CMD.EXE opens a new console window
-               // for each external command in a redirected .BAT file.
-               // Even (DETACHED_PROCESS|CREATE_NO_WINDOW) does not work.
-               use_file = 0; flags = CREATE_NO_WINDOW;
-       }
-
-       // Create stdin pipe with inheritable read side
-       memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa);
-       sa.bInheritHandle = TRUE;
-       if (!CreatePipe(&pipe_inp_r, &h, &sa/*inherit*/, inpsize*2+13))
-               return -1;
-       if (!DuplicateHandle(self, h, self, &pipe_inp_w,
-               0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) {
-               CloseHandle(pipe_inp_r);
-               return -1;
-       }
-
-       memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa);
-       sa.bInheritHandle = TRUE;
-       if (!use_file) {
-               // Create stdout pipe with inheritable write side
-               if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize)) {
-                       CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
-                       return -1;
-               }
-       }
-       else {
-               // Create temp file with inheritable write handle
-               char temp_dir[MAX_PATH];
-               if (!GetTempPathA(sizeof(temp_dir), temp_dir))
-                       strcpy(temp_dir, ".");
-               if (!GetTempFileNameA(temp_dir, "out"/*prefix*/, 0/*create unique*/, temp_path)) {
-                       CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
-                       return -1;
-               }
-               if ((h = CreateFileA(temp_path, GENERIC_READ|GENERIC_WRITE,
-                       0/*no sharing*/, &sa/*inherit*/, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) {
-                       CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
-                       return -1;
-               }
-               if (!DuplicateHandle(self, h, self, &pipe_out_w,
-                       GENERIC_WRITE, TRUE/*inherit*/, 0)) {
-                       CloseHandle(h); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
-                       return -1;
-               }
-       }
-
-       if (!DuplicateHandle(self, h, self, &pipe_out_r,
-               GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
-               CloseHandle(pipe_out_w); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
-               return -1;
-       }
-
-       // Create stderr handle as dup of stdout write side
-       if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
-               0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
-               CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
-               CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
-               return -1;
-       }
-
-       // Create process with pipes/file as stdio
-       memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
-       si.hStdInput  = pipe_inp_r;
-       si.hStdOutput = pipe_out_w;
-       si.hStdError  = pipe_err_w;
-       si.dwFlags = STARTF_USESTDHANDLES;
-       if (!CreateProcessA(
-               NULL, (char*)cmd,
-               NULL, NULL, TRUE/*inherit*/,
-               flags/*DETACHED_PROCESS or CREATE_NO_WINDOW*/,
-               NULL, NULL, &si, &pi)) {
-               CloseHandle(pipe_err_w);
-               CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
-               CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
-               return -1;
-       }
-       CloseHandle(pi.hThread);
-       // Close inherited handles
-       CloseHandle(pipe_inp_r);
-       CloseHandle(pipe_out_w);
-       CloseHandle(pipe_err_w);
-
-       // Copy inpbuf to stdin
-       // convert \n => \r\n 
-       for (i = 0; i < inpsize; ) {
-               int len = 0;
-               while (i+len < inpsize && inpbuf[i+len] != '\n')
-                       len++;
-               if (len > 0)
-                       WriteFile(pipe_inp_w, inpbuf+i, len, &num_io, NULL);
-               i += len;
-               if (i < inpsize) {
-                       WriteFile(pipe_inp_w, "\r\n", 2, &num_io, NULL);
-                       i++;
-               }
-       }
-       CloseHandle(pipe_inp_w);
-
-       exitcode = 42;
-       for (state = 0; state < 2; state++) {
-               // stdout pipe: read pipe first
-               // stdout file: wait for process first
-               if (state == use_file) {
-                       // Copy stdout to output buffer until full, rest to /dev/null
-                       // convert \r\n => \n
-                       if (use_file)
-                               SetFilePointer(pipe_out_r, 0, NULL, FILE_BEGIN);
-                       for (i = 0; ; ) {
-                               char buf[256];
-                               int j;
-                               if (!ReadFile(pipe_out_r, buf, sizeof(buf), &num_io, NULL) || num_io == 0)
-                                       break;
-                               for (j = 0; i < outsize-1 && j < (int)num_io; j++) {
-                                       if (buf[j] != '\r')
-                                               outbuf[i++] = buf[j];
-                               }
-                       }
-                       outbuf[i] = 0;
-                       CloseHandle(pipe_out_r);
-                       if (use_file)
-                               DeleteFileA(temp_path);
-               }
-               else {
-                       // Wait for process exitcode
-                       WaitForSingleObject(pi.hProcess, INFINITE);
-                       GetExitCodeProcess(pi.hProcess, &exitcode);
-                       CloseHandle(pi.hProcess);
-               }
-       }
-       return exitcode;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// Initd Functions
-
-static int wait_signaled(HANDLE h, int seconds)
-{
-       int i;
-       for (i = 0; ; ) {
-               if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0)
-                       return 0;
-               if (++i >= seconds)
-                       return -1;
-               fputchar('.'); fflush(stdout);
-       }
-}
-
-
-static int wait_evt_running(int seconds, int exists)
-{
-       int i;
-       if (event_exists(EVT_RUNNING) == exists)
-               return 0;
-       for (i = 0; ; ) {
-               Sleep(1000);
-               if (event_exists(EVT_RUNNING) == exists)
-                       return 0;
-               if (++i >= seconds)
-                       return -1;
-               fputchar('.'); fflush(stdout);
-       }
-}
-
-
-static int is_initd_command(char * s)
-{
-       if (!strcmp(s, "status"))
-               return EVT_RUNNING;
-       if (!strcmp(s, "stop"))
-               return SIGTERM;
-       if (!strcmp(s, "reload"))
-               return SIGHUP;
-       if (!strcmp(s, "sigusr1"))
-               return SIGUSR1;
-       if (!strcmp(s, "sigusr2"))
-               return SIGUSR2;
-       if (!strcmp(s, "restart"))
-               return EVT_RESTART;
-       return -1;
-}
-
-
-static int initd_main(const char * ident, int argc, char **argv)
-{
-       int rc;
-       if (argc < 2)
-               return -1;
-       if ((rc = is_initd_command(argv[1])) < 0)
-               return -1;
-       if (argc != 2) {
-               printf("%s: no arguments allowed for command %s\n", ident, argv[1]);
-               return 1;
-       }
-
-       switch (rc) {
-               default:
-               case EVT_RUNNING:
-                       printf("Checking for %s:", ident); fflush(stdout);
-                       rc = event_exists(EVT_RUNNING);
-                       puts(rc ? " running" : " not running");
-                       return (rc ? 0 : 1);
-
-               case SIGTERM:
-                       printf("Stopping %s:", ident); fflush(stdout);
-                       rc = sig_event(SIGTERM);
-                       if (rc <= 0) {
-                               puts(rc < 0 ? " not running" : " error");
-                               return (rc < 0 ? 0 : 1);
-                       }
-                       rc = wait_evt_running(10, 0);
-                       puts(!rc ? " done" : " timeout");
-                       return (!rc ? 0 : 1);
-
-               case SIGHUP:
-                       printf("Reloading %s:", ident); fflush(stdout);
-                       rc = sig_event(SIGHUP);
-                       puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running");
-                       return (rc > 0 ? 0 : 1);
-
-               case SIGUSR1:
-               case SIGUSR2:
-                       printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout);
-                       rc = sig_event(rc);
-                       puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running");
-                       return (rc > 0 ? 0 : 1);
-
-               case EVT_RESTART:
-                       {
-                               HANDLE rst;
-                               printf("Stopping %s:", ident); fflush(stdout);
-                               if (event_exists(EVT_DETACHED)) {
-                                       puts(" not detached, cannot restart");
-                                       return 1;
-                               }
-                               if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) {
-                                       puts(" error");
-                                       return 1;
-                               }
-                               rc = sig_event(SIGTERM);
-                               if (rc <= 0) {
-                                       puts(rc < 0 ? " not running" : " error");
-                                       CloseHandle(rst);
-                                       return 1;
-                               }
-                               rc = wait_signaled(rst, 10);
-                               CloseHandle(rst);
-                               if (rc) {
-                                       puts(" timeout");
-                                       return 1;
-                               }
-                               puts(" done");
-                               Sleep(100);
-
-                               printf("Starting %s:", ident); fflush(stdout);
-                               rc = wait_evt_running(10, 1);
-                               puts(!rc ? " done" : " error");
-                               return (!rc ? 0 : 1);
-                       }
-       }
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// Windows Service Functions
-
-int daemon_winsvc_exitcode; // Set by app to exit(code)
-
-static SERVICE_STATUS_HANDLE svc_handle;
-static SERVICE_STATUS svc_status;
-
-
-// Report status to SCM
-
-static void service_report_status(int state, int seconds)
-{
-       // TODO: Avoid race
-       static DWORD checkpoint = 1;
-       static DWORD accept_more = SERVICE_ACCEPT_PARAMCHANGE; // Win2000/XP
-       svc_status.dwCurrentState = state;
-       svc_status.dwWaitHint = seconds*1000;
-       switch (state) {
-               default:
-                       svc_status.dwCheckPoint = checkpoint++;
-                       break;
-               case SERVICE_RUNNING:
-               case SERVICE_STOPPED:
-                       svc_status.dwCheckPoint = 0;
-       }
-       switch (state) {
-               case SERVICE_START_PENDING:
-               case SERVICE_STOP_PENDING:
-                       svc_status.dwControlsAccepted = 0;
-                       break;
-               default:
-                       svc_status.dwControlsAccepted =
-                               SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
-                               SERVICE_ACCEPT_PAUSE_CONTINUE|accept_more;
-                       break;
-       }
-       if (!SetServiceStatus(svc_handle, &svc_status)) {
-               if (svc_status.dwControlsAccepted & accept_more) {
-                       // Retry without SERVICE_ACCEPT_PARAMCHANGE (WinNT4)
-                       svc_status.dwControlsAccepted &= ~accept_more;
-                       accept_more = 0;
-                       SetServiceStatus(svc_handle, &svc_status);
-               }
-       }
-}
-
-
-// Control the service, called by SCM
-
-static void WINAPI service_control(DWORD ctrlcode)
-{
-       switch (ctrlcode) {
-               case SERVICE_CONTROL_STOP:
-               case SERVICE_CONTROL_SHUTDOWN:
-                       service_report_status(SERVICE_STOP_PENDING, 30);
-                       svc_paused = 0;
-                       SetEvent(sigterm_handle);
-                       break;
-               case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP
-                       service_report_status(svc_status.dwCurrentState, 0);
-                       svc_paused = 0;
-                       SetEvent(sighup_handle); // reload
-                       break;
-               case SERVICE_CONTROL_PAUSE:
-                       service_report_status(SERVICE_PAUSED, 0);
-                       svc_paused = 1;
-                       break;
-               case SERVICE_CONTROL_CONTINUE:
-                       service_report_status(SERVICE_RUNNING, 0);
-                       {
-                               int was_paused = svc_paused;
-                               svc_paused = 0;
-                               SetEvent(was_paused ? sighup_handle : sigusr1_handle); // reload:recheck
-                       }
-                       break;
-               case SERVICE_CONTROL_INTERROGATE:
-               default: // unknown
-                       service_report_status(svc_status.dwCurrentState, 0);
-                       break;
-       }
-}
-
-
-// Exit handler for service
-
-static void service_exit(void)
-{
-       // Close signal events
-       int i;
-       for (i = 0; i < num_sig_handlers; i++)
-               CloseHandle(sig_events[i]);
-       num_sig_handlers = 0;
-
-       // Set exitcode
-       if (daemon_winsvc_exitcode) {
-               svc_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
-               svc_status.dwServiceSpecificExitCode = daemon_winsvc_exitcode;
-       }
-       // Report stopped
-       service_report_status(SERVICE_STOPPED, 0);
-}
-
-
-// Variables for passing main(argc, argv) from daemon_main to service_main()
-static int (*svc_main_func)(int, char **);
-static int svc_main_argc;
-static char ** svc_main_argv;
-
-// Main function for service, called by service dispatcher
-
-static void WINAPI service_main(DWORD argc, LPSTR * argv)
-{
-       char path[MAX_PATH], *p;
-       ARGUSED(argc);
-
-       // Register control handler
-       svc_handle = RegisterServiceCtrlHandler(argv[0], service_control);
-
-       // Init service status
-       svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
-       service_report_status(SERVICE_START_PENDING, 10);
-
-       // Service started in \windows\system32, change to .exe directory
-       if (GetModuleFileNameA(NULL, path, sizeof(path)) && (p = strrchr(path, '\\'))) {
-               *p = 0; SetCurrentDirectoryA(path);
-       }
-       
-       // Install exit handler
-       atexit(service_exit);
-
-       // Do the real work, service status later updated by daemon_detach()
-       daemon_winsvc_exitcode = svc_main_func(svc_main_argc, svc_main_argv);
-
-       exit(daemon_winsvc_exitcode);
-       // ... continued in service_exit()
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// Windows Service Admin Functions
-
-// Set Service description (Win2000/XP)
-
-static int svcadm_setdesc(SC_HANDLE hs, const char * desc)
-{
-       HANDLE hdll;
-       BOOL (WINAPI * ChangeServiceConfig2A_p)(SC_HANDLE, DWORD, LPVOID);
-       BOOL ret;
-       if (!(hdll = LoadLibraryA("ADVAPI32.DLL")))
-               return FALSE;
-       if (!((ChangeServiceConfig2A_p = (BOOL (WINAPI *)(SC_HANDLE, DWORD, LPVOID))GetProcAddress(hdll, "ChangeServiceConfig2A"))))
-               ret = FALSE;
-       else {
-               SERVICE_DESCRIPTIONA sd = { (char *)desc };
-               ret = ChangeServiceConfig2A_p(hs, SERVICE_CONFIG_DESCRIPTION, &sd);
-       }
-       FreeLibrary(hdll);
-       return ret;
-}
-
-
-// Service install/remove commands
-
-static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts,
-                       int argc, char **argv                                      )
-{
-       int remove; long err;
-       SC_HANDLE hm, hs;
-
-       if (argc < 2)
-               return -1;
-       if (!strcmp(argv[1], "install"))
-               remove = 0;
-       else if (!strcmp(argv[1], "remove")) {
-               if (argc != 2) {
-                       printf("%s: no arguments allowed for command remove\n", ident);
-                       return 1;
-               }
-               remove = 1;
-       }
-       else
-               return -1;
-
-       printf("%s service %s:", (!remove?"Installing":"Removing"), ident); fflush(stdout);
-
-       // Open SCM
-       if (!(hm = OpenSCManager(NULL/*local*/, NULL/*default*/, SC_MANAGER_ALL_ACCESS))) {
-               if ((err = GetLastError()) == ERROR_ACCESS_DENIED)
-                       puts(" access to SCManager denied");
-               else if (err == ERROR_CALL_NOT_IMPLEMENTED)
-                       puts(" services not implemented on this version of Windows");
-               else
-                       printf(" cannot open SCManager, Error=%ld\n", err);
-               return 1;
-       }
-
-       if (!remove) {
-               char path[MAX_PATH+100];
-               int i;
-               // Get program path
-               if (!GetModuleFileNameA(NULL, path, MAX_PATH)) {
-                       printf(" unknown program path, Error=%ld\n", GetLastError());
-                       CloseServiceHandle(hm);
-                       return 1;
-               }
-               // Append options
-               strcat(path, " "); strcat(path, svc_opts->cmd_opt);
-               for (i = 2; i < argc; i++) {
-                       const char * s = argv[i];
-                       if (strlen(path)+strlen(s)+1 >= sizeof(path))
-                               break;
-                       strcat(path, " "); strcat(path, s);
-               }
-               // Create
-               if (!(hs = CreateService(hm,
-                       svc_opts->svcname, svc_opts->dispname,
-                       SERVICE_ALL_ACCESS,
-                       SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,
-                       SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
-                       NULL/*no load ordering*/, NULL/*no tag id*/,
-                       ""/*no depedencies*/, NULL/*local system account*/, NULL/*no pw*/))) {
-                       if ((err = GetLastError()) == ERROR_SERVICE_EXISTS)
-                               puts(" the service is already installed");
-                       else if (err == ERROR_SERVICE_MARKED_FOR_DELETE)
-                               puts(" service is still running and marked for deletion\n"
-                                    "Stop the service and retry install");
-                       else
-                               printf(" failed, Error=%ld\n", err);
-                       CloseServiceHandle(hm);
-                       return 1;
-               }
-               // Set optional description
-               if (svc_opts->descript)
-                       svcadm_setdesc(hs, svc_opts->descript);
-       }
-       else {
-               // Open
-               if (!(hs = OpenService(hm, svc_opts->svcname, SERVICE_ALL_ACCESS))) {
-                       puts(" not found");
-                       CloseServiceHandle(hm);
-                       return 1;
-               }
-               // TODO: Stop service if running
-               // Remove
-               if (!DeleteService(hs)) {
-                       if ((err = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE)
-                               puts(" service is still running and marked for deletion\n"
-                                    "Stop the service to remove it");
-                       else
-                               printf(" failed, Error=%ld\n", err);
-                       CloseServiceHandle(hs); CloseServiceHandle(hm);
-                       return 1;
-               }
-       }
-       puts(" done");
-       CloseServiceHandle(hs); CloseServiceHandle(hm);
-       return 0;
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// Main Function
-
-// This function must be called from main()
-// main_func is the function doing the real work
-
-int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts,
-                int (*main_func)(int, char **), int argc, char **argv      )
-{
-       int rc;
-#ifdef _DEBUG
-       // Enable Debug heap checks
-       _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
-               |_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF);
-#endif
-
-       // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters
-       if ((rc = initd_main(ident, argc, argv)) >= 0)
-               return rc;
-       // Check for [install|remove] parameters
-       if (svc_opts && (rc = svcadm_main(ident, svc_opts, argc, argv)) >= 0)
-               return rc;
-
-       // Run as service if svc_opts.cmd_opt is given as first(!) argument
-       svc_mode = (svc_opts && argc >= 2 && !strcmp(argv[1], svc_opts->cmd_opt));
-
-       if (!svc_mode) {
-               // Daemon: Try to simulate a Unix-like daemon
-               HANDLE rev;
-               BOOL exists;
-
-               // Create main event to detect process type:
-               // 1. new: parent process => start child and wait for detach() or exit() of child.
-               // 2. exists && signaled: child process => do the real work, signal detach() to parent
-               // 3. exists && !signaled: already running => exit()
-               if (!(rev = create_event(EVT_RUNNING, TRUE/*signaled*/, TRUE, &exists)))
-                       return 100;
-
-               if (!exists && !debugging()) {
-                       // Event new => parent process
-                       return parent_main(rev);
-               }
-
-               if (WaitForSingleObject(rev, 0) == WAIT_OBJECT_0) {
-                       // Event was signaled => In child process
-                       return child_main(rev, main_func, argc, argv);
-               }
-
-               // Event no longer signaled => Already running!
-               daemon_help(stdout, ident, "already running");
-               CloseHandle(rev);
-               return 1;
-       }
-       else {
-               // Service: Start service_main() via SCM
-               SERVICE_TABLE_ENTRY service_table[] = {
-                       { (char*)svc_opts->svcname, service_main }, { NULL, NULL }
-               };
-
-               svc_main_func = main_func;
-               svc_main_argc = argc;
-               svc_main_argv = argv;
-               if (!StartServiceCtrlDispatcher(service_table)) {
-                       printf("%s: cannot dispatch service, Error=%ld\n"
-                               "Option \"%s\" cannot be used to start %s as a service from console.\n"
-                               "Use \"%s install ...\" to install the service\n"
-                               "and \"net start %s\" to start it.\n",
-                               ident, GetLastError(), svc_opts->cmd_opt, ident, ident, ident);
-
-#ifdef _DEBUG
-                       if (debugging())
-                               service_main(argc, argv);
-#endif
-                       return 100;
-               }
-               Sleep(1000);
-               ExitThread(0); // Do not redo exit() processing
-               /*NOTREACHED*/
-               return 0;
-       }
-}
-
-
-/////////////////////////////////////////////////////////////////////////////
-// Test Program
-
-#ifdef TEST
-
-static volatile sig_atomic_t caughtsig = 0;
-
-static void sig_handler(int sig)
-{
-       caughtsig = sig;
-}
-
-static void test_exit(void)
-{
-       printf("Main exit\n");
-}
-
-int test_main(int argc, char **argv)
-{
-       int i;
-       int debug = 0;
-       char * cmd = 0;
-
-       printf("PID=%ld\n", GetCurrentProcessId());
-       for (i = 0; i < argc; i++) {
-               printf("%d: \"%s\"\n", i, argv[i]);
-               if (!strcmp(argv[i],"-d"))
-                       debug = 1;
-       }
-       if (argc > 1 && argv[argc-1][0] != '-')
-               cmd = argv[argc-1];
-
-       daemon_signal(SIGINT, sig_handler);
-       daemon_signal(SIGBREAK, sig_handler);
-       daemon_signal(SIGTERM, sig_handler);
-       daemon_signal(SIGHUP, sig_handler);
-       daemon_signal(SIGUSR1, sig_handler);
-       daemon_signal(SIGUSR2, sig_handler);
-
-       atexit(test_exit);
-
-       if (!debug) {
-               printf("Preparing to detach...\n");
-               Sleep(2000);
-               daemon_detach("test");
-               printf("Detached!\n");
-       }
-
-       for (;;) {
-               daemon_sleep(1);
-               printf("."); fflush(stdout);
-               if (caughtsig) {
-                       if (caughtsig == SIGUSR2) {
-                               debug ^= 1;
-                               if (debug)
-                                       daemon_enable_console("Daemon[Debug]");
-                               else
-                                       daemon_disable_console();
-                       }
-                       else if (caughtsig == SIGUSR1 && cmd) {
-                               char inpbuf[200], outbuf[1000]; int rc;
-                               strcpy(inpbuf, "Hello\nWorld!\n");
-                               rc = daemon_spawn(cmd, inpbuf, strlen(inpbuf), outbuf, sizeof(outbuf));
-                               if (!debug)
-                                       daemon_enable_console("Command output");
-                               printf("\"%s\" returns %d\n", cmd, rc);
-                               if (rc >= 0)
-                                       printf("output:\n%s.\n", outbuf);
-                               fflush(stdout);
-                               if (!debug) {
-                                       Sleep(10000); daemon_disable_console();
-                               }
-                       }
-                       printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig); fflush(stdout);
-                       if (caughtsig == SIGTERM || caughtsig == SIGBREAK)
-                               break;
-                       caughtsig = 0;
-               }
-       }
-       printf("\nExiting on signal %d\n", caughtsig);
-       return 0;
-}
-
-
-int main(int argc, char **argv)
-{
-       static const daemon_winsvc_options svc_opts = {
-       "-s", "test", "Test Service", "Service to test daemon_win32.c Module"
-       };
-
-       return daemon_main("testd", &svc_opts, test_main, argc, argv);
-}
-
-#endif
diff --git a/os_win32/daemon_win32.cpp b/os_win32/daemon_win32.cpp
new file mode 100644 (file)
index 0000000..9b6c403
--- /dev/null
@@ -0,0 +1,1266 @@
+/*
+ * os_win32/daemon_win32.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <io.h>
+
+#define WIN32_LEAN_AND_MEAN
+// Need MB_SERVICE_NOTIFICATION (NT4/2000/XP), IsDebuggerPresent() (Win98/ME/NT4/2000/XP)
+#define _WIN32_WINNT 0x0400 
+#include <windows.h>
+#ifdef _DEBUG
+#include <crtdbg.h>
+#endif
+
+#include "daemon_win32.h"
+
+const char *daemon_win32_c_cvsid = "$Id: daemon_win32.cpp,v 1.11 2006/08/09 20:40:20 chrfranke Exp $"
+DAEMON_WIN32_H_CVSID;
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define ARGUSED(x) ((void)(x))
+
+// Prevent spawning of child process if debugging
+#ifdef _DEBUG
+#define debugging() IsDebuggerPresent()
+#else
+#define debugging() FALSE
+#endif
+
+
+#define EVT_NAME_LEN 260
+
+// Internal events (must be > SIGUSRn)
+#define EVT_RUNNING   100 // Exists when running, signaled on creation
+#define EVT_DETACHED  101 // Signaled when child detaches from console
+#define EVT_RESTART   102 // Signaled when child should restart
+
+static void make_name(char * name, int sig)
+{
+       int i;
+       if (!GetModuleFileNameA(NULL, name, EVT_NAME_LEN-10))
+               strcpy(name, "DaemonEvent");
+       for (i = 0; name[i]; i++) {
+               char c = name[i];
+               if (!(   ('0' <= c && c <= '9')
+                     || ('A' <= c && c <= 'Z')
+                     || ('a' <= c && c <= 'z')))
+                         name[i] = '_';
+       }
+       sprintf(name+strlen(name), "-%d", sig);
+}
+
+
+static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists)
+{
+       char name[EVT_NAME_LEN];
+       HANDLE h;
+       if (sig >= 0)
+               make_name(name, sig);
+       else
+               name[0] = 0;
+       if (exists)
+               *exists = FALSE;
+       if (!(h = CreateEventA(NULL, FALSE, initial, (name[0] ? name : NULL)))) {
+               if (errmsg)
+                       fprintf(stderr, "CreateEvent(.,\"%s\"): Error=%ld\n", name, GetLastError());
+               return 0;
+       }
+
+       if (GetLastError() == ERROR_ALREADY_EXISTS) {
+               if (!exists) {
+                       if (errmsg)
+                               fprintf(stderr, "CreateEvent(.,\"%s\"): Exists\n", name);
+                       CloseHandle(h);
+                       return 0;
+               }
+               *exists = TRUE;
+       }
+       return h;
+}
+
+
+static HANDLE open_event(int sig)
+{
+       char name[EVT_NAME_LEN];
+       make_name(name, sig);
+       return OpenEventA(EVENT_MODIFY_STATE, FALSE, name);
+}
+
+
+static int event_exists(int sig)
+{
+       char name[EVT_NAME_LEN];
+       HANDLE h;
+       make_name(name, sig);
+       if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name)))
+               return 0;
+       CloseHandle(h);
+       return 1;
+}
+
+
+static int sig_event(int sig)
+{
+       char name[EVT_NAME_LEN];
+       HANDLE h;
+       make_name(name, sig);
+       if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) {
+               make_name(name, EVT_RUNNING);
+               if (!(h = OpenEvent(EVENT_MODIFY_STATE, FALSE, name)))
+                       return -1;
+               CloseHandle(h);
+               return 0;
+       }
+       SetEvent(h);
+       CloseHandle(h);
+       return 1;
+}
+
+
+static void daemon_help(FILE * f, const char * ident, const char * message)
+{
+       fprintf(f,
+               "%s: %s.\n"
+               "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n",
+               ident, message, ident);
+       fflush(f);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Parent Process
+
+
+static BOOL WINAPI parent_console_handler(DWORD event)
+{
+       switch (event) {
+               case CTRL_C_EVENT:
+               case CTRL_BREAK_EVENT:
+                       return TRUE; // Ignore
+       }
+       return FALSE; // continue with next handler ...
+}
+
+
+static int parent_main(HANDLE rev)
+{
+       HANDLE dev;
+       HANDLE ht[2];
+       char * cmdline;
+       STARTUPINFO si;
+       PROCESS_INFORMATION pi;
+       DWORD rc, exitcode;
+
+       // Ignore ^C, ^BREAK in parent
+       SetConsoleCtrlHandler(parent_console_handler, TRUE/*add*/);
+
+       // Create event used by child to signal daemon_detach()
+       if (!(dev = create_event(EVT_DETACHED, FALSE/*not signaled*/, TRUE, NULL/*must not exist*/))) {
+               CloseHandle(rev);
+               return 101;
+       }
+
+       // Restart process with same args
+       cmdline = GetCommandLineA();
+       memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
+       
+       if (!CreateProcessA(
+               NULL, cmdline,
+               NULL, NULL, TRUE/*inherit*/,
+               0, NULL, NULL, &si, &pi)) {
+               fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError());
+               CloseHandle(rev); CloseHandle(dev);
+               return 101;
+       }
+       CloseHandle(pi.hThread);
+
+       // Wait for daemon_detach() or exit()
+       ht[0] = dev; ht[1] = pi.hProcess;
+       rc = WaitForMultipleObjects(2, ht, FALSE/*or*/, INFINITE);
+       if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+2)) {
+               fprintf(stderr, "WaitForMultipleObjects returns %lX\n", rc);
+               TerminateProcess(pi.hProcess, 200);
+       }
+       CloseHandle(rev); CloseHandle(dev);
+
+       // Get exit code
+       if (!GetExitCodeProcess(pi.hProcess, &exitcode))
+               exitcode = 201;
+       else if (exitcode == STILL_ACTIVE) // detach()ed, assume OK
+               exitcode = 0;
+
+       CloseHandle(pi.hProcess);
+       return exitcode;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Child Process
+
+
+static int svc_mode;   // Running as service?
+static int svc_paused; // Service paused?
+
+static void service_report_status(int state, int waithint);
+
+
+// Tables of signal handler and corresponding events
+typedef void (*sigfunc_t)(int);
+
+#define MAX_SIG_HANDLERS 8
+
+static int num_sig_handlers = 0;
+static sigfunc_t sig_handlers[MAX_SIG_HANDLERS];
+static int sig_numbers[MAX_SIG_HANDLERS];
+static HANDLE sig_events[MAX_SIG_HANDLERS];
+
+static HANDLE sighup_handle, sigint_handle, sigbreak_handle;
+static HANDLE sigterm_handle, sigusr1_handle;
+
+static HANDLE running_event;
+
+static int reopen_stdin, reopen_stdout, reopen_stderr;
+
+
+// Handler for windows console events
+
+static BOOL WINAPI child_console_handler(DWORD event)
+{
+       // Caution: runs in a new thread
+       // TODO: Guard with a mutex
+       HANDLE h = 0;
+       switch (event) {
+               case CTRL_C_EVENT: // <CONTROL-C> (SIGINT)
+                       h = sigint_handle; break;
+               case CTRL_BREAK_EVENT: // <CONTROL-Break> (SIGBREAK/SIGQUIT)
+               case CTRL_CLOSE_EVENT: // User closed console or abort via task manager
+                       h = sigbreak_handle; break;
+               case CTRL_LOGOFF_EVENT: // Logout/Shutdown (SIGTERM)
+               case CTRL_SHUTDOWN_EVENT:
+                       h = sigterm_handle; break;
+       }
+       if (!h)
+               return FALSE; // continue with next handler
+       // Signal event
+       if (!SetEvent(h))
+               return FALSE;
+       return TRUE;
+}
+
+
+static void child_exit(void)
+{
+       int i;
+       char * cmdline;
+       HANDLE rst;
+       STARTUPINFO si;
+       PROCESS_INFORMATION pi;
+
+       for (i = 0; i < num_sig_handlers; i++)
+               CloseHandle(sig_events[i]);
+       num_sig_handlers = 0;
+       CloseHandle(running_event); running_event = 0;
+
+       // Restart?
+       if (!(rst = open_event(EVT_RESTART)))
+               return; // No => normal exit
+
+       // Yes => Signal exit and restart process
+       Sleep(500);
+       SetEvent(rst);
+       CloseHandle(rst);
+       Sleep(500);
+
+       cmdline = GetCommandLineA();
+       memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
+       si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE;
+
+       if (!CreateProcessA(
+               NULL, cmdline,
+               NULL, NULL, TRUE/*inherit*/,
+               0, NULL, NULL, &si, &pi)) {
+               fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError());
+       }
+       CloseHandle(pi.hThread); CloseHandle(pi.hProcess);
+}
+
+static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv)
+{
+       // Keep EVT_RUNNING open until exit
+       running_event = hev;
+
+       // Install console handler
+       SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
+
+       // Install restart handler
+       atexit(child_exit);
+
+       // Continue in main_func() to do the real work
+       return main_func(argc, argv);
+}
+
+
+// Simulate signal()
+
+sigfunc_t daemon_signal(int sig, sigfunc_t func)
+{
+       int i;
+       HANDLE h;
+       if (func == SIG_DFL || func == SIG_IGN)
+               return func; // TODO
+       for (i = 0; i < num_sig_handlers; i++) {
+               if (sig_numbers[i] == sig) {
+                       sigfunc_t old = sig_handlers[i];
+                       sig_handlers[i] = func;
+                       return old;
+               }
+       }
+       if (num_sig_handlers >= MAX_SIG_HANDLERS)
+               return SIG_ERR;
+       if (!(h = create_event((!svc_mode ? sig : -1), FALSE, TRUE, NULL)))
+               return SIG_ERR;
+       sig_events[num_sig_handlers]   = h;
+       sig_numbers[num_sig_handlers]  = sig;
+       sig_handlers[num_sig_handlers] = func;
+       switch (sig) {
+               case SIGHUP:   sighup_handle   = h; break;
+               case SIGINT:   sigint_handle   = h; break;
+               case SIGTERM:  sigterm_handle  = h; break;
+               case SIGBREAK: sigbreak_handle = h; break;
+               case SIGUSR1:  sigusr1_handle  = h; break;
+       }
+       num_sig_handlers++;
+       return SIG_DFL;
+}
+
+
+// strsignal()
+
+const char * daemon_strsignal(int sig)
+{
+       switch (sig) {
+               case SIGHUP:  return "SIGHUP";
+               case SIGINT:  return "SIGINT";
+               case SIGTERM: return "SIGTERM";
+               case SIGBREAK:return "SIGBREAK";
+               case SIGUSR1: return "SIGUSR1";
+               case SIGUSR2: return "SIGUSR2";
+               default:      return "*UNKNOWN*";
+       }
+}
+
+
+// Simulate sleep()
+
+void daemon_sleep(int seconds)
+{
+       do {
+               if (num_sig_handlers <= 0) {
+                       Sleep(seconds*1000L);
+               }
+               else {
+                       // Wait for any signal or timeout
+                       DWORD rc = WaitForMultipleObjects(num_sig_handlers, sig_events,
+                               FALSE/*OR*/, seconds*1000L);
+                       if (rc != WAIT_TIMEOUT) {
+                               if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+(unsigned)num_sig_handlers)) {
+                                       fprintf(stderr,"WaitForMultipleObjects returns %lu\n", rc);
+                                       Sleep(seconds*1000L);
+                                       return;
+                               }
+                               // Call Handler
+                               sig_handlers[rc-WAIT_OBJECT_0](sig_numbers[rc-WAIT_OBJECT_0]);
+                               break;
+                       }
+               }
+       } while (svc_paused);
+}
+
+
+// Disable/Enable console
+
+void daemon_disable_console()
+{
+       SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/);
+       reopen_stdin = reopen_stdout = reopen_stderr = 0;
+       if (isatty(fileno(stdin))) {
+               fclose(stdin); reopen_stdin = 1;
+       }
+       if (isatty(fileno(stdout))) {
+               fclose(stdout); reopen_stdout = 1;
+       }
+       if (isatty(fileno(stderr))) {
+               fclose(stderr); reopen_stderr = 1;
+       }
+       FreeConsole();
+       SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
+}
+
+int daemon_enable_console(const char * title)
+{
+       BOOL ok;
+       SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/);
+       ok = AllocConsole();
+       SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
+       if (!ok)
+               return -1;
+       if (title)
+               SetConsoleTitleA(title);
+       if (reopen_stdin)
+               freopen("conin$",  "r", stdin);
+       if (reopen_stdout)
+               freopen("conout$", "w", stdout);
+       if (reopen_stderr)
+               freopen("conout$", "w", stderr);
+       reopen_stdin = reopen_stdout = reopen_stderr = 0;
+       return 0;
+}
+
+
+// Detach daemon from console & parent
+
+int daemon_detach(const char * ident)
+{
+       if (!svc_mode) {
+               if (ident) {
+                       // Print help
+                       FILE * f = ( isatty(fileno(stdout)) ? stdout
+                                          : isatty(fileno(stderr)) ? stderr : NULL);
+                       if (f)
+                               daemon_help(f, ident, "now detaches from console into background mode");
+               }
+               // Signal detach to parent
+               if (sig_event(EVT_DETACHED) != 1) {
+                       if (!debugging())
+                               return -1;
+               }
+               daemon_disable_console();
+       }
+       else {
+               // Signal end of initialization to service control manager
+               service_report_status(SERVICE_RUNNING, 0);
+               reopen_stdin = reopen_stdout = reopen_stderr = 1;
+       }
+
+       return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// MessageBox
+
+#ifndef _MT
+//MT runtime not necessary, because mbox_thread uses no unsafe lib functions
+//#error Program must be linked with multithreaded runtime library
+#endif
+
+static LONG mbox_count; // # mbox_thread()s
+static HANDLE mbox_mutex; // Show 1 box at a time (not necessary for service)
+
+typedef struct mbox_args_s {
+       HANDLE taken; const char * title, * text; int mode;
+} mbox_args;
+
+
+// Thread to display one message box
+
+static ULONG WINAPI mbox_thread(LPVOID arg)
+{
+       // Take args
+       mbox_args * mb = (mbox_args *)arg;
+       char title[100]; char text[1000]; int mode;
+       strncpy(title, mb->title, sizeof(title)-1); title[sizeof(title)-1] = 0;
+       strncpy(text , mb->text , sizeof(text )-1); text [sizeof(text )-1] = 0;
+       mode = mb->mode;
+       SetEvent(mb->taken);
+
+       // Show only one box at a time
+       WaitForSingleObject(mbox_mutex, INFINITE);
+       MessageBoxA(NULL, text, title, mode);
+       ReleaseMutex(mbox_mutex);
+
+       InterlockedDecrement(&mbox_count);
+       return 0;
+}
+
+
+// Display a message box
+int daemon_messagebox(int system, const char * title, const char * text)
+{
+       mbox_args mb;
+       HANDLE ht; DWORD tid;
+
+       // Create mutex during first call
+       if (!mbox_mutex)
+               mbox_mutex = CreateMutex(NULL/*!inherit*/, FALSE/*!owned*/, NULL/*unnamed*/);
+
+       // Allow at most 10 threads
+       if (InterlockedIncrement(&mbox_count) > 10) {
+               InterlockedDecrement(&mbox_count);
+               return -1;
+       }
+
+       // Create thread
+       mb.taken = CreateEvent(NULL/*!inherit*/, FALSE, FALSE/*!signaled*/, NULL/*unnamed*/);
+       mb.mode = MB_OK|MB_ICONWARNING
+                |(svc_mode?MB_SERVICE_NOTIFICATION:0)
+                |(system?MB_SYSTEMMODAL:MB_APPLMODAL);
+       mb.title = title; mb.text = text;
+       mb.text = text;
+       if (!(ht = CreateThread(NULL, 0, mbox_thread, &mb, 0, &tid)))
+               return -1;
+
+       // Wait for args taken
+       if (WaitForSingleObject(mb.taken, 10000) != WAIT_OBJECT_0)
+               TerminateThread(ht, 0);
+       CloseHandle(mb.taken);
+       CloseHandle(ht);
+       return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Spawn a command and redirect <inpbuf >outbuf
+// return command's exitcode or -1 on error
+
+int daemon_spawn(const char * cmd,
+                 const char * inpbuf, int inpsize,
+                 char *       outbuf, int outsize )
+{
+       HANDLE pipe_inp_r, pipe_inp_w, pipe_out_r, pipe_out_w, pipe_err_w, h;
+       char temp_path[MAX_PATH];
+       DWORD flags, num_io, exitcode;
+       int use_file, state, i;
+       SECURITY_ATTRIBUTES sa;
+       STARTUPINFO si; PROCESS_INFORMATION pi;
+       HANDLE self = GetCurrentProcess();
+
+       if (GetVersion() & 0x80000000L) {
+               // Win9x/ME: A calling process never receives EOF if output of COMMAND.COM or
+               // any other DOS program is redirected via a pipe. Using a temp file instead.
+               use_file = 1; flags = DETACHED_PROCESS;
+       }
+       else {
+               // NT4/2000/XP: If DETACHED_PROCESS is used, CMD.EXE opens a new console window
+               // for each external command in a redirected .BAT file.
+               // Even (DETACHED_PROCESS|CREATE_NO_WINDOW) does not work.
+               use_file = 0; flags = CREATE_NO_WINDOW;
+       }
+
+       // Create stdin pipe with inheritable read side
+       memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa);
+       sa.bInheritHandle = TRUE;
+       if (!CreatePipe(&pipe_inp_r, &h, &sa/*inherit*/, inpsize*2+13))
+               return -1;
+       if (!DuplicateHandle(self, h, self, &pipe_inp_w,
+               0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) {
+               CloseHandle(pipe_inp_r);
+               return -1;
+       }
+
+       memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa);
+       sa.bInheritHandle = TRUE;
+       if (!use_file) {
+               // Create stdout pipe with inheritable write side
+               if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize)) {
+                       CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+                       return -1;
+               }
+       }
+       else {
+               // Create temp file with inheritable write handle
+               char temp_dir[MAX_PATH];
+               if (!GetTempPathA(sizeof(temp_dir), temp_dir))
+                       strcpy(temp_dir, ".");
+               if (!GetTempFileNameA(temp_dir, "out"/*prefix*/, 0/*create unique*/, temp_path)) {
+                       CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+                       return -1;
+               }
+               if ((h = CreateFileA(temp_path, GENERIC_READ|GENERIC_WRITE,
+                       0/*no sharing*/, &sa/*inherit*/, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) {
+                       CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+                       return -1;
+               }
+               if (!DuplicateHandle(self, h, self, &pipe_out_w,
+                       GENERIC_WRITE, TRUE/*inherit*/, 0)) {
+                       CloseHandle(h); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+                       return -1;
+               }
+       }
+
+       if (!DuplicateHandle(self, h, self, &pipe_out_r,
+               GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
+               CloseHandle(pipe_out_w); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+               return -1;
+       }
+
+       // Create stderr handle as dup of stdout write side
+       if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
+               0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
+               CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
+               CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+               return -1;
+       }
+
+       // Create process with pipes/file as stdio
+       memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
+       si.hStdInput  = pipe_inp_r;
+       si.hStdOutput = pipe_out_w;
+       si.hStdError  = pipe_err_w;
+       si.dwFlags = STARTF_USESTDHANDLES;
+       if (!CreateProcessA(
+               NULL, (char*)cmd,
+               NULL, NULL, TRUE/*inherit*/,
+               flags/*DETACHED_PROCESS or CREATE_NO_WINDOW*/,
+               NULL, NULL, &si, &pi)) {
+               CloseHandle(pipe_err_w);
+               CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
+               CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+               return -1;
+       }
+       CloseHandle(pi.hThread);
+       // Close inherited handles
+       CloseHandle(pipe_inp_r);
+       CloseHandle(pipe_out_w);
+       CloseHandle(pipe_err_w);
+
+       // Copy inpbuf to stdin
+       // convert \n => \r\n 
+       for (i = 0; i < inpsize; ) {
+               int len = 0;
+               while (i+len < inpsize && inpbuf[i+len] != '\n')
+                       len++;
+               if (len > 0)
+                       WriteFile(pipe_inp_w, inpbuf+i, len, &num_io, NULL);
+               i += len;
+               if (i < inpsize) {
+                       WriteFile(pipe_inp_w, "\r\n", 2, &num_io, NULL);
+                       i++;
+               }
+       }
+       CloseHandle(pipe_inp_w);
+
+       exitcode = 42;
+       for (state = 0; state < 2; state++) {
+               // stdout pipe: read pipe first
+               // stdout file: wait for process first
+               if (state == use_file) {
+                       // Copy stdout to output buffer until full, rest to /dev/null
+                       // convert \r\n => \n
+                       if (use_file)
+                               SetFilePointer(pipe_out_r, 0, NULL, FILE_BEGIN);
+                       for (i = 0; ; ) {
+                               char buf[256];
+                               int j;
+                               if (!ReadFile(pipe_out_r, buf, sizeof(buf), &num_io, NULL) || num_io == 0)
+                                       break;
+                               for (j = 0; i < outsize-1 && j < (int)num_io; j++) {
+                                       if (buf[j] != '\r')
+                                               outbuf[i++] = buf[j];
+                               }
+                       }
+                       outbuf[i] = 0;
+                       CloseHandle(pipe_out_r);
+                       if (use_file)
+                               DeleteFileA(temp_path);
+               }
+               else {
+                       // Wait for process exitcode
+                       WaitForSingleObject(pi.hProcess, INFINITE);
+                       GetExitCodeProcess(pi.hProcess, &exitcode);
+                       CloseHandle(pi.hProcess);
+               }
+       }
+       return exitcode;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Initd Functions
+
+static int wait_signaled(HANDLE h, int seconds)
+{
+       int i;
+       for (i = 0; ; ) {
+               if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0)
+                       return 0;
+               if (++i >= seconds)
+                       return -1;
+               fputchar('.'); fflush(stdout);
+       }
+}
+
+
+static int wait_evt_running(int seconds, int exists)
+{
+       int i;
+       if (event_exists(EVT_RUNNING) == exists)
+               return 0;
+       for (i = 0; ; ) {
+               Sleep(1000);
+               if (event_exists(EVT_RUNNING) == exists)
+                       return 0;
+               if (++i >= seconds)
+                       return -1;
+               fputchar('.'); fflush(stdout);
+       }
+}
+
+
+static int is_initd_command(char * s)
+{
+       if (!strcmp(s, "status"))
+               return EVT_RUNNING;
+       if (!strcmp(s, "stop"))
+               return SIGTERM;
+       if (!strcmp(s, "reload"))
+               return SIGHUP;
+       if (!strcmp(s, "sigusr1"))
+               return SIGUSR1;
+       if (!strcmp(s, "sigusr2"))
+               return SIGUSR2;
+       if (!strcmp(s, "restart"))
+               return EVT_RESTART;
+       return -1;
+}
+
+
+static int initd_main(const char * ident, int argc, char **argv)
+{
+       int rc;
+       if (argc < 2)
+               return -1;
+       if ((rc = is_initd_command(argv[1])) < 0)
+               return -1;
+       if (argc != 2) {
+               printf("%s: no arguments allowed for command %s\n", ident, argv[1]);
+               return 1;
+       }
+
+       switch (rc) {
+               default:
+               case EVT_RUNNING:
+                       printf("Checking for %s:", ident); fflush(stdout);
+                       rc = event_exists(EVT_RUNNING);
+                       puts(rc ? " running" : " not running");
+                       return (rc ? 0 : 1);
+
+               case SIGTERM:
+                       printf("Stopping %s:", ident); fflush(stdout);
+                       rc = sig_event(SIGTERM);
+                       if (rc <= 0) {
+                               puts(rc < 0 ? " not running" : " error");
+                               return (rc < 0 ? 0 : 1);
+                       }
+                       rc = wait_evt_running(10, 0);
+                       puts(!rc ? " done" : " timeout");
+                       return (!rc ? 0 : 1);
+
+               case SIGHUP:
+                       printf("Reloading %s:", ident); fflush(stdout);
+                       rc = sig_event(SIGHUP);
+                       puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running");
+                       return (rc > 0 ? 0 : 1);
+
+               case SIGUSR1:
+               case SIGUSR2:
+                       printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout);
+                       rc = sig_event(rc);
+                       puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running");
+                       return (rc > 0 ? 0 : 1);
+
+               case EVT_RESTART:
+                       {
+                               HANDLE rst;
+                               printf("Stopping %s:", ident); fflush(stdout);
+                               if (event_exists(EVT_DETACHED)) {
+                                       puts(" not detached, cannot restart");
+                                       return 1;
+                               }
+                               if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) {
+                                       puts(" error");
+                                       return 1;
+                               }
+                               rc = sig_event(SIGTERM);
+                               if (rc <= 0) {
+                                       puts(rc < 0 ? " not running" : " error");
+                                       CloseHandle(rst);
+                                       return 1;
+                               }
+                               rc = wait_signaled(rst, 10);
+                               CloseHandle(rst);
+                               if (rc) {
+                                       puts(" timeout");
+                                       return 1;
+                               }
+                               puts(" done");
+                               Sleep(100);
+
+                               printf("Starting %s:", ident); fflush(stdout);
+                               rc = wait_evt_running(10, 1);
+                               puts(!rc ? " done" : " error");
+                               return (!rc ? 0 : 1);
+                       }
+       }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Windows Service Functions
+
+int daemon_winsvc_exitcode; // Set by app to exit(code)
+
+static SERVICE_STATUS_HANDLE svc_handle;
+static SERVICE_STATUS svc_status;
+
+
+// Report status to SCM
+
+static void service_report_status(int state, int seconds)
+{
+       // TODO: Avoid race
+       static DWORD checkpoint = 1;
+       static DWORD accept_more = SERVICE_ACCEPT_PARAMCHANGE; // Win2000/XP
+       svc_status.dwCurrentState = state;
+       svc_status.dwWaitHint = seconds*1000;
+       switch (state) {
+               default:
+                       svc_status.dwCheckPoint = checkpoint++;
+                       break;
+               case SERVICE_RUNNING:
+               case SERVICE_STOPPED:
+                       svc_status.dwCheckPoint = 0;
+       }
+       switch (state) {
+               case SERVICE_START_PENDING:
+               case SERVICE_STOP_PENDING:
+                       svc_status.dwControlsAccepted = 0;
+                       break;
+               default:
+                       svc_status.dwControlsAccepted =
+                               SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
+                               SERVICE_ACCEPT_PAUSE_CONTINUE|accept_more;
+                       break;
+       }
+       if (!SetServiceStatus(svc_handle, &svc_status)) {
+               if (svc_status.dwControlsAccepted & accept_more) {
+                       // Retry without SERVICE_ACCEPT_PARAMCHANGE (WinNT4)
+                       svc_status.dwControlsAccepted &= ~accept_more;
+                       accept_more = 0;
+                       SetServiceStatus(svc_handle, &svc_status);
+               }
+       }
+}
+
+
+// Control the service, called by SCM
+
+static void WINAPI service_control(DWORD ctrlcode)
+{
+       switch (ctrlcode) {
+               case SERVICE_CONTROL_STOP:
+               case SERVICE_CONTROL_SHUTDOWN:
+                       service_report_status(SERVICE_STOP_PENDING, 30);
+                       svc_paused = 0;
+                       SetEvent(sigterm_handle);
+                       break;
+               case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP
+                       service_report_status(svc_status.dwCurrentState, 0);
+                       svc_paused = 0;
+                       SetEvent(sighup_handle); // reload
+                       break;
+               case SERVICE_CONTROL_PAUSE:
+                       service_report_status(SERVICE_PAUSED, 0);
+                       svc_paused = 1;
+                       break;
+               case SERVICE_CONTROL_CONTINUE:
+                       service_report_status(SERVICE_RUNNING, 0);
+                       {
+                               int was_paused = svc_paused;
+                               svc_paused = 0;
+                               SetEvent(was_paused ? sighup_handle : sigusr1_handle); // reload:recheck
+                       }
+                       break;
+               case SERVICE_CONTROL_INTERROGATE:
+               default: // unknown
+                       service_report_status(svc_status.dwCurrentState, 0);
+                       break;
+       }
+}
+
+
+// Exit handler for service
+
+static void service_exit(void)
+{
+       // Close signal events
+       int i;
+       for (i = 0; i < num_sig_handlers; i++)
+               CloseHandle(sig_events[i]);
+       num_sig_handlers = 0;
+
+       // Set exitcode
+       if (daemon_winsvc_exitcode) {
+               svc_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
+               svc_status.dwServiceSpecificExitCode = daemon_winsvc_exitcode;
+       }
+       // Report stopped
+       service_report_status(SERVICE_STOPPED, 0);
+}
+
+
+// Variables for passing main(argc, argv) from daemon_main to service_main()
+static int (*svc_main_func)(int, char **);
+static int svc_main_argc;
+static char ** svc_main_argv;
+
+// Main function for service, called by service dispatcher
+
+static void WINAPI service_main(DWORD argc, LPSTR * argv)
+{
+       char path[MAX_PATH], *p;
+       ARGUSED(argc);
+
+       // Register control handler
+       svc_handle = RegisterServiceCtrlHandler(argv[0], service_control);
+
+       // Init service status
+       svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
+       service_report_status(SERVICE_START_PENDING, 10);
+
+       // Service started in \windows\system32, change to .exe directory
+       if (GetModuleFileNameA(NULL, path, sizeof(path)) && (p = strrchr(path, '\\'))) {
+               *p = 0; SetCurrentDirectoryA(path);
+       }
+       
+       // Install exit handler
+       atexit(service_exit);
+
+       // Do the real work, service status later updated by daemon_detach()
+       daemon_winsvc_exitcode = svc_main_func(svc_main_argc, svc_main_argv);
+
+       exit(daemon_winsvc_exitcode);
+       // ... continued in service_exit()
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Windows Service Admin Functions
+
+// Set Service description (Win2000/XP)
+
+static int svcadm_setdesc(SC_HANDLE hs, const char * desc)
+{
+       HINSTANCE hdll;
+       BOOL (WINAPI * ChangeServiceConfig2A_p)(SC_HANDLE, DWORD, LPVOID);
+       BOOL ret;
+       if (!(hdll = LoadLibraryA("ADVAPI32.DLL")))
+               return FALSE;
+       if (!((ChangeServiceConfig2A_p = (BOOL (WINAPI *)(SC_HANDLE, DWORD, LPVOID))GetProcAddress(hdll, "ChangeServiceConfig2A"))))
+               ret = FALSE;
+       else {
+               SERVICE_DESCRIPTIONA sd = { (char *)desc };
+               ret = ChangeServiceConfig2A_p(hs, SERVICE_CONFIG_DESCRIPTION, &sd);
+       }
+       FreeLibrary(hdll);
+       return ret;
+}
+
+
+// Service install/remove commands
+
+static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts,
+                       int argc, char **argv                                      )
+{
+       int remove; long err;
+       SC_HANDLE hm, hs;
+
+       if (argc < 2)
+               return -1;
+       if (!strcmp(argv[1], "install"))
+               remove = 0;
+       else if (!strcmp(argv[1], "remove")) {
+               if (argc != 2) {
+                       printf("%s: no arguments allowed for command remove\n", ident);
+                       return 1;
+               }
+               remove = 1;
+       }
+       else
+               return -1;
+
+       printf("%s service %s:", (!remove?"Installing":"Removing"), ident); fflush(stdout);
+
+       // Open SCM
+       if (!(hm = OpenSCManager(NULL/*local*/, NULL/*default*/, SC_MANAGER_ALL_ACCESS))) {
+               if ((err = GetLastError()) == ERROR_ACCESS_DENIED)
+                       puts(" access to SCManager denied");
+               else if (err == ERROR_CALL_NOT_IMPLEMENTED)
+                       puts(" services not implemented on this version of Windows");
+               else
+                       printf(" cannot open SCManager, Error=%ld\n", err);
+               return 1;
+       }
+
+       if (!remove) {
+               char path[MAX_PATH+100];
+               int i;
+               // Get program path
+               if (!GetModuleFileNameA(NULL, path, MAX_PATH)) {
+                       printf(" unknown program path, Error=%ld\n", GetLastError());
+                       CloseServiceHandle(hm);
+                       return 1;
+               }
+               // Append options
+               strcat(path, " "); strcat(path, svc_opts->cmd_opt);
+               for (i = 2; i < argc; i++) {
+                       const char * s = argv[i];
+                       if (strlen(path)+strlen(s)+1 >= sizeof(path))
+                               break;
+                       strcat(path, " "); strcat(path, s);
+               }
+               // Create
+               if (!(hs = CreateService(hm,
+                       svc_opts->svcname, svc_opts->dispname,
+                       SERVICE_ALL_ACCESS,
+                       SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,
+                       SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
+                       NULL/*no load ordering*/, NULL/*no tag id*/,
+                       ""/*no depedencies*/, NULL/*local system account*/, NULL/*no pw*/))) {
+                       if ((err = GetLastError()) == ERROR_SERVICE_EXISTS)
+                               puts(" the service is already installed");
+                       else if (err == ERROR_SERVICE_MARKED_FOR_DELETE)
+                               puts(" service is still running and marked for deletion\n"
+                                    "Stop the service and retry install");
+                       else
+                               printf(" failed, Error=%ld\n", err);
+                       CloseServiceHandle(hm);
+                       return 1;
+               }
+               // Set optional description
+               if (svc_opts->descript)
+                       svcadm_setdesc(hs, svc_opts->descript);
+       }
+       else {
+               // Open
+               if (!(hs = OpenService(hm, svc_opts->svcname, SERVICE_ALL_ACCESS))) {
+                       puts(" not found");
+                       CloseServiceHandle(hm);
+                       return 1;
+               }
+               // TODO: Stop service if running
+               // Remove
+               if (!DeleteService(hs)) {
+                       if ((err = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE)
+                               puts(" service is still running and marked for deletion\n"
+                                    "Stop the service to remove it");
+                       else
+                               printf(" failed, Error=%ld\n", err);
+                       CloseServiceHandle(hs); CloseServiceHandle(hm);
+                       return 1;
+               }
+       }
+       puts(" done");
+       CloseServiceHandle(hs); CloseServiceHandle(hm);
+       return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Main Function
+
+// This function must be called from main()
+// main_func is the function doing the real work
+
+int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts,
+                int (*main_func)(int, char **), int argc, char **argv      )
+{
+       int rc;
+#ifdef _DEBUG
+       // Enable Debug heap checks
+       _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
+               |_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF);
+#endif
+
+       // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters
+       if ((rc = initd_main(ident, argc, argv)) >= 0)
+               return rc;
+       // Check for [install|remove] parameters
+       if (svc_opts && (rc = svcadm_main(ident, svc_opts, argc, argv)) >= 0)
+               return rc;
+
+       // Run as service if svc_opts.cmd_opt is given as first(!) argument
+       svc_mode = (svc_opts && argc >= 2 && !strcmp(argv[1], svc_opts->cmd_opt));
+
+       if (!svc_mode) {
+               // Daemon: Try to simulate a Unix-like daemon
+               HANDLE rev;
+               BOOL exists;
+
+               // Create main event to detect process type:
+               // 1. new: parent process => start child and wait for detach() or exit() of child.
+               // 2. exists && signaled: child process => do the real work, signal detach() to parent
+               // 3. exists && !signaled: already running => exit()
+               if (!(rev = create_event(EVT_RUNNING, TRUE/*signaled*/, TRUE, &exists)))
+                       return 100;
+
+               if (!exists && !debugging()) {
+                       // Event new => parent process
+                       return parent_main(rev);
+               }
+
+               if (WaitForSingleObject(rev, 0) == WAIT_OBJECT_0) {
+                       // Event was signaled => In child process
+                       return child_main(rev, main_func, argc, argv);
+               }
+
+               // Event no longer signaled => Already running!
+               daemon_help(stdout, ident, "already running");
+               CloseHandle(rev);
+               return 1;
+       }
+       else {
+               // Service: Start service_main() via SCM
+               SERVICE_TABLE_ENTRY service_table[] = {
+                       { (char*)svc_opts->svcname, service_main }, { NULL, NULL }
+               };
+
+               svc_main_func = main_func;
+               svc_main_argc = argc;
+               svc_main_argv = argv;
+               if (!StartServiceCtrlDispatcher(service_table)) {
+                       printf("%s: cannot dispatch service, Error=%ld\n"
+                               "Option \"%s\" cannot be used to start %s as a service from console.\n"
+                               "Use \"%s install ...\" to install the service\n"
+                               "and \"net start %s\" to start it.\n",
+                               ident, GetLastError(), svc_opts->cmd_opt, ident, ident, ident);
+
+#ifdef _DEBUG
+                       if (debugging())
+                               service_main(argc, argv);
+#endif
+                       return 100;
+               }
+               Sleep(1000);
+               ExitThread(0); // Do not redo exit() processing
+               /*NOTREACHED*/
+               return 0;
+       }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Test Program
+
+#ifdef TEST
+
+static volatile sig_atomic_t caughtsig = 0;
+
+static void sig_handler(int sig)
+{
+       caughtsig = sig;
+}
+
+static void test_exit(void)
+{
+       printf("Main exit\n");
+}
+
+int test_main(int argc, char **argv)
+{
+       int i;
+       int debug = 0;
+       char * cmd = 0;
+
+       printf("PID=%ld\n", GetCurrentProcessId());
+       for (i = 0; i < argc; i++) {
+               printf("%d: \"%s\"\n", i, argv[i]);
+               if (!strcmp(argv[i],"-d"))
+                       debug = 1;
+       }
+       if (argc > 1 && argv[argc-1][0] != '-')
+               cmd = argv[argc-1];
+
+       daemon_signal(SIGINT, sig_handler);
+       daemon_signal(SIGBREAK, sig_handler);
+       daemon_signal(SIGTERM, sig_handler);
+       daemon_signal(SIGHUP, sig_handler);
+       daemon_signal(SIGUSR1, sig_handler);
+       daemon_signal(SIGUSR2, sig_handler);
+
+       atexit(test_exit);
+
+       if (!debug) {
+               printf("Preparing to detach...\n");
+               Sleep(2000);
+               daemon_detach("test");
+               printf("Detached!\n");
+       }
+
+       for (;;) {
+               daemon_sleep(1);
+               printf("."); fflush(stdout);
+               if (caughtsig) {
+                       if (caughtsig == SIGUSR2) {
+                               debug ^= 1;
+                               if (debug)
+                                       daemon_enable_console("Daemon[Debug]");
+                               else
+                                       daemon_disable_console();
+                       }
+                       else if (caughtsig == SIGUSR1 && cmd) {
+                               char inpbuf[200], outbuf[1000]; int rc;
+                               strcpy(inpbuf, "Hello\nWorld!\n");
+                               rc = daemon_spawn(cmd, inpbuf, strlen(inpbuf), outbuf, sizeof(outbuf));
+                               if (!debug)
+                                       daemon_enable_console("Command output");
+                               printf("\"%s\" returns %d\n", cmd, rc);
+                               if (rc >= 0)
+                                       printf("output:\n%s.\n", outbuf);
+                               fflush(stdout);
+                               if (!debug) {
+                                       Sleep(10000); daemon_disable_console();
+                               }
+                       }
+                       printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig); fflush(stdout);
+                       if (caughtsig == SIGTERM || caughtsig == SIGBREAK)
+                               break;
+                       caughtsig = 0;
+               }
+       }
+       printf("\nExiting on signal %d\n", caughtsig);
+       return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+       static const daemon_winsvc_options svc_opts = {
+       "-s", "test", "Test Service", "Service to test daemon_win32.c Module"
+       };
+
+       return daemon_main("testd", &svc_opts, test_main, argc, argv);
+}
+
+#endif
diff --git a/os_win32/hostname_win32.c b/os_win32/hostname_win32.c
deleted file mode 100644 (file)
index 6202eff..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * os_win32/hostname_win32.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-#include "hostname_win32.h"
-
-const char * hostname_win32_c_cvsid = "$Id: hostname_win32.c,v 1.4 2006/04/12 14:54:28 ballen4705 Exp $" HOSTNAME_WIN32_H_CVSID;
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <string.h>
-
-#ifndef MAX_HOSTNAME_LEN
-
-// From IPHlpApi.dll:
-
-#define MAX_HOSTNAME_LEN    132
-#define MAX_DOMAIN_NAME_LEN 132
-#define MAX_SCOPE_ID_LEN    260
-
-typedef struct {
-  char String[4 * 4];
-} IP_ADDRESS_STRING, 
-*PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
-
-typedef struct _IP_ADDR_STRING {
-  struct _IP_ADDR_STRING* Next;
-  IP_ADDRESS_STRING IpAddress;
-  IP_MASK_STRING IpMask;
-  DWORD Context;
-} IP_ADDR_STRING, 
-*PIP_ADDR_STRING;
-
-typedef struct {
-  char HostName[MAX_HOSTNAME_LEN];
-  char DomainName[MAX_DOMAIN_NAME_LEN];
-  PIP_ADDR_STRING CurrentDnsServer;
-  IP_ADDR_STRING DnsServerList;
-  UINT NodeType;
-  char ScopeId[MAX_SCOPE_ID_LEN];
-  UINT EnableRouting;
-  UINT EnableProxy;
-  UINT EnableDns;
-} FIXED_INFO,
-*PFIXED_INFO;
-
-DWORD WINAPI GetNetworkParams(PFIXED_INFO info, PULONG size);
-
-#endif // MAX_HOSTNAME_LEN
-
-
-// Call GetComputerNameEx() if available (Win2000/XP)
-
-static BOOL CallGetComputerNameExA(int type, LPSTR name, LPDWORD size)
-{
-       HANDLE hdll;
-       BOOL (WINAPI * GetComputerNameExA_p)(int/*enum COMPUTER_NAME_FORMAT*/, LPSTR, LPDWORD);
-       BOOL ret;
-       if (!(hdll = LoadLibraryA("KERNEL32.DLL")))
-               return FALSE;
-       if (!(GetComputerNameExA_p = (BOOL (WINAPI *)(int, LPSTR, LPDWORD))GetProcAddress(hdll, "GetComputerNameExA")))
-               ret = FALSE;
-       else
-               ret = GetComputerNameExA_p(type, name, size);
-       FreeLibrary(hdll);
-       return ret;
-}
-
-
-// Call GetNetworkParams() if available (Win98/ME/2000/XP)
-
-static DWORD CallGetNetworkParams(PFIXED_INFO info, PULONG size)
-{
-       HANDLE hdll;
-       DWORD (WINAPI * GetNetworkParams_p)(PFIXED_INFO, PULONG);
-       DWORD ret;
-       if (!(hdll = LoadLibraryA("IPHlpApi.dll")))
-               return ERROR_NOT_SUPPORTED;
-       if (!(GetNetworkParams_p = (DWORD (WINAPI *)(PFIXED_INFO, PULONG))GetProcAddress(hdll, "GetNetworkParams")))
-               ret = ERROR_NOT_SUPPORTED;
-       else
-               ret = GetNetworkParams_p(info, size);
-       FreeLibrary(hdll);
-       return ret;
-}
-
-
-// Get host/domainname from registry (Win98/ME/NT4/2000/XP)
-
-static DWORD GetNamesFromRegistry(BOOL domain, char * name, int len)
-{
-       HKEY hk; DWORD size, type;
-       if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
-           (GetVersion() & 0x80000000
-            ? "System\\CurrentControlSet\\Services\\VxD\\MSTCP" //Win9x/ME
-            : "System\\CurrentControlSet\\Services\\Tcpip\\Parameters"),
-           0, KEY_READ, &hk) != ERROR_SUCCESS)
-               return 0;
-       size = len-1;
-       if (!(RegQueryValueExA(hk, (!domain?"HostName":"Domain"), 0, &type, name, &size) == ERROR_SUCCESS && type == REG_SZ))
-               size = 0;
-       if (size == 0 && domain) {
-               size = len-1;
-               if (!(RegQueryValueExA(hk, "DhcpDomain", 0, &type, name, &size) == ERROR_SUCCESS && type == REG_SZ))
-                       size = 0;
-       }
-       RegCloseKey(hk);
-       return size;
-}
-
-
-static int gethostdomname(int domain, char * name, int len)
-{
-       DWORD size; FIXED_INFO info;
-
-       // try KERNEL32.dll::GetComputerNameEx()
-       size = len - 1;
-       if (CallGetComputerNameExA((!domain ? 1:2/*ComputerNameDnsHost:Domain*/), name, &size))
-               return 0;
-
-       // try IPHlpApi.dll::GetNetworkParams() 
-       size = sizeof(info);
-       if (CallGetNetworkParams(&info, &size) == ERROR_SUCCESS) {
-               strncpy(name, (!domain?info.HostName:info.DomainName), len-1); name[len-1] = 0;
-               return 0;
-       }
-
-       // try registry
-       if (GetNamesFromRegistry(domain, name, len))
-               return 0;
-
-       if (domain)
-               return -1;
-
-       // last resort: get NETBIOS name
-       size = len - 1;
-       if (GetComputerNameA(name, &size))
-               return 0;
-
-       return -1;
-}
-
-
-int gethostname(char * name, int len)
-{
-       return gethostdomname(0, name, len);
-}
-
-
-int getdomainname(char * name, int len)
-{
-       return gethostdomname(1, name, len);
-}
-
-
-#ifdef TEST
-
-#include <stdio.h>
-
-main()
-{
-       char name[256];
-       if (gethostname(name, sizeof(name)))
-               strcpy(name, "Error");
-       printf("hostname=\"%s\"\n", name);
-       if (getdomainname(name, sizeof(name)))
-               strcpy(name, "Error");
-       printf("domainname=\"%s\"\n", name);
-       return 0;
-}
-
-#endif
diff --git a/os_win32/hostname_win32.cpp b/os_win32/hostname_win32.cpp
new file mode 100644 (file)
index 0000000..b939935
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * os_win32/hostname_win32.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "hostname_win32.h"
+
+const char * hostname_win32_c_cvsid = "$Id: hostname_win32.cpp,v 1.5 2006/08/09 20:40:20 chrfranke Exp $" HOSTNAME_WIN32_H_CVSID;
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <string.h>
+
+#ifndef MAX_HOSTNAME_LEN
+
+// From IPHlpApi.dll:
+
+#define MAX_HOSTNAME_LEN    132
+#define MAX_DOMAIN_NAME_LEN 132
+#define MAX_SCOPE_ID_LEN    260
+
+typedef struct {
+  char String[4 * 4];
+} IP_ADDRESS_STRING, 
+*PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
+
+typedef struct _IP_ADDR_STRING {
+  struct _IP_ADDR_STRING* Next;
+  IP_ADDRESS_STRING IpAddress;
+  IP_MASK_STRING IpMask;
+  DWORD Context;
+} IP_ADDR_STRING, 
+*PIP_ADDR_STRING;
+
+typedef struct {
+  char HostName[MAX_HOSTNAME_LEN];
+  char DomainName[MAX_DOMAIN_NAME_LEN];
+  PIP_ADDR_STRING CurrentDnsServer;
+  IP_ADDR_STRING DnsServerList;
+  UINT NodeType;
+  char ScopeId[MAX_SCOPE_ID_LEN];
+  UINT EnableRouting;
+  UINT EnableProxy;
+  UINT EnableDns;
+} FIXED_INFO,
+*PFIXED_INFO;
+
+DWORD WINAPI GetNetworkParams(PFIXED_INFO info, PULONG size);
+
+#endif // MAX_HOSTNAME_LEN
+
+
+// Call GetComputerNameEx() if available (Win2000/XP)
+
+static BOOL CallGetComputerNameExA(int type, LPSTR name, LPDWORD size)
+{
+       HINSTANCE hdll;
+       BOOL (WINAPI * GetComputerNameExA_p)(int/*enum COMPUTER_NAME_FORMAT*/, LPSTR, LPDWORD);
+       BOOL ret;
+       if (!(hdll = LoadLibraryA("KERNEL32.DLL")))
+               return FALSE;
+       if (!(GetComputerNameExA_p = (BOOL (WINAPI *)(int, LPSTR, LPDWORD))GetProcAddress(hdll, "GetComputerNameExA")))
+               ret = FALSE;
+       else
+               ret = GetComputerNameExA_p(type, name, size);
+       FreeLibrary(hdll);
+       return ret;
+}
+
+
+// Call GetNetworkParams() if available (Win98/ME/2000/XP)
+
+static DWORD CallGetNetworkParams(PFIXED_INFO info, PULONG size)
+{
+       HINSTANCE hdll;
+       DWORD (WINAPI * GetNetworkParams_p)(PFIXED_INFO, PULONG);
+       DWORD ret;
+       if (!(hdll = LoadLibraryA("IPHlpApi.dll")))
+               return ERROR_NOT_SUPPORTED;
+       if (!(GetNetworkParams_p = (DWORD (WINAPI *)(PFIXED_INFO, PULONG))GetProcAddress(hdll, "GetNetworkParams")))
+               ret = ERROR_NOT_SUPPORTED;
+       else
+               ret = GetNetworkParams_p(info, size);
+       FreeLibrary(hdll);
+       return ret;
+}
+
+
+// Get host/domainname from registry (Win98/ME/NT4/2000/XP)
+
+static DWORD GetNamesFromRegistry(BOOL domain, char * name, int len)
+{
+       HKEY hk; DWORD size, type;
+       if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+           (GetVersion() & 0x80000000
+            ? "System\\CurrentControlSet\\Services\\VxD\\MSTCP" //Win9x/ME
+            : "System\\CurrentControlSet\\Services\\Tcpip\\Parameters"),
+           0, KEY_READ, &hk) != ERROR_SUCCESS)
+               return 0;
+       size = len-1;
+       if (!(RegQueryValueExA(hk, (!domain?"HostName":"Domain"), 0, &type, (unsigned char *)name, &size) == ERROR_SUCCESS && type == REG_SZ))
+               size = 0;
+       if (size == 0 && domain) {
+               size = len-1;
+               if (!(RegQueryValueExA(hk, "DhcpDomain", 0, &type, (unsigned char *)name, &size) == ERROR_SUCCESS && type == REG_SZ))
+                       size = 0;
+       }
+       RegCloseKey(hk);
+       return size;
+}
+
+
+static int gethostdomname(int domain, char * name, int len)
+{
+       DWORD size; FIXED_INFO info;
+
+       // try KERNEL32.dll::GetComputerNameEx()
+       size = len - 1;
+       if (CallGetComputerNameExA((!domain ? 1:2/*ComputerNameDnsHost:Domain*/), name, &size))
+               return 0;
+
+       // try IPHlpApi.dll::GetNetworkParams() 
+       size = sizeof(info);
+       if (CallGetNetworkParams(&info, &size) == ERROR_SUCCESS) {
+               strncpy(name, (!domain?info.HostName:info.DomainName), len-1); name[len-1] = 0;
+               return 0;
+       }
+
+       // try registry
+       if (GetNamesFromRegistry(domain, name, len))
+               return 0;
+
+       if (domain)
+               return -1;
+
+       // last resort: get NETBIOS name
+       size = len - 1;
+       if (GetComputerNameA(name, &size))
+               return 0;
+
+       return -1;
+}
+
+
+int gethostname(char * name, int len)
+{
+       return gethostdomname(0, name, len);
+}
+
+
+int getdomainname(char * name, int len)
+{
+       return gethostdomname(1, name, len);
+}
+
+
+#ifdef TEST
+
+#include <stdio.h>
+
+main()
+{
+       char name[256];
+       if (gethostname(name, sizeof(name)))
+               strcpy(name, "Error");
+       printf("hostname=\"%s\"\n", name);
+       if (getdomainname(name, sizeof(name)))
+               strcpy(name, "Error");
+       printf("domainname=\"%s\"\n", name);
+       return 0;
+}
+
+#endif
index 259735fc8d4d0f5d5953e846309209c820b04a6c..2332947216367b6ab5c3065c611a01100becdd79 100644 (file)
 #ifndef HOSTNAME_WIN32_H
 #define HOSTNAME_WIN32_H
 
-#define HOSTNAME_WIN32_H_CVSID "$Id: hostname_win32.h,v 1.3 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define HOSTNAME_WIN32_H_CVSID "$Id: hostname_win32.h,v 1.4 2006/08/09 20:40:20 chrfranke Exp $\n"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 int gethostname(char * name, int len);
 int getdomainname(char * name, int len);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif // HOSTNAME_WIN32_H
diff --git a/os_win32/installer.nsi b/os_win32/installer.nsi
new file mode 100644 (file)
index 0000000..870a2e6
--- /dev/null
@@ -0,0 +1,466 @@
+;
+; installer.nsi - NSIS install script for smartmontools
+;
+; Copyright (C) 2006 Christian Franke <smartmontools-support@lists.sourceforge.net>
+;
+; Project home page is: http://smartmontools.sourceforge.net
+;
+; Download and install NSIS from: http://nsis.sourceforge.net/Download
+; Process with makensis to create installer (tested with NSIS 2.17).
+;
+; $Id: installer.nsi,v 1.2 2006/07/17 20:51:22 chrfranke Exp $
+;
+
+
+;--------------------------------------------------------------------
+; Command line arguments:
+; makensis /DINPDIR=<input-dir> /DOUTFILE=<output-file> installer.nsi
+
+!ifndef INPDIR
+  !define INPDIR "."
+!endif
+
+!ifndef OUTFILE
+  !define OUTFILE "smartmontools.win32-setup.exe"
+!endif
+
+;--------------------------------------------------------------------
+; General
+
+Name "smartmontools"
+OutFile "${OUTFILE}"
+
+SetCompressor /solid lzma
+
+XPStyle on
+InstallColors /windows
+
+InstallDir "$PROGRAMFILES\smartmontools"
+InstallDirRegKey HKLM "Software\smartmontools" "Install_Dir"
+
+LicenseData "${INPDIR}\doc\COPYING.txt"
+
+;--------------------------------------------------------------------
+; Pages
+
+Page license
+Page components
+Page directory
+Page instfiles
+
+UninstPage uninstConfirm
+UninstPage instfiles
+
+InstType "Full"
+InstType "Extract files only"
+
+
+;--------------------------------------------------------------------
+; Sections
+
+SectionGroup "Program files"
+
+  Section "smartctl"
+
+    SectionIn 1 2
+
+    SetOutPath "$INSTDIR\bin"
+    File "${INPDIR}\bin\smartctl.exe"
+
+  SectionEnd
+
+  Section "smartd"
+
+    SectionIn 1 2
+
+    SetOutPath "$INSTDIR\bin"
+    File "${INPDIR}\bin\smartd.exe"
+
+    IfFileExists "$INSTDIR\bin\smartd.conf" 0 +2
+      MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2 "Replace existing configuration file$\n$INSTDIR\bin\smartd.conf ?" IDYES 0 IDNO +2
+        File "${INPDIR}\doc\smartd.conf"
+
+    IfFileExists "$WINDIR\system32\cmd.exe" 0 +2
+      File /nonfatal "${INPDIR}\bin\syslogevt.exe"
+
+  SectionEnd
+
+SectionGroupEnd
+
+Section "Documentation"
+
+  SectionIn 1 2
+
+  SetOutPath "$INSTDIR\doc"
+  File "${INPDIR}\doc\AUTHORS.txt"
+  File "${INPDIR}\doc\CHANGELOG.txt"
+  File "${INPDIR}\doc\COPYING.txt"
+  File "${INPDIR}\doc\INSTALL.txt"
+  File "${INPDIR}\doc\NEWS.txt"
+  File "${INPDIR}\doc\README.txt"
+  File "${INPDIR}\doc\TODO.txt"
+  File "${INPDIR}\doc\WARNINGS.txt"
+  File "${INPDIR}\doc\smartctl.8.html"
+  File "${INPDIR}\doc\smartctl.8.txt"
+  File "${INPDIR}\doc\smartd.8.html"
+  File "${INPDIR}\doc\smartd.8.txt"
+  File "${INPDIR}\doc\smartd.conf"
+  File "${INPDIR}\doc\smartd.conf.5.html"
+  File "${INPDIR}\doc\smartd.conf.5.txt"
+
+SectionEnd
+
+Section "Uninstaller"
+
+  SectionIn 1
+  AddSize 35
+
+  CreateDirectory "$INSTDIR"
+
+  ; Save installation location
+  WriteRegStr HKLM "Software\smartmontools" "Install_Dir" "$INSTDIR"
+
+  ; Write uninstall keys and program
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "DisplayName" "smartmontools"
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "UninstallString" '"$INSTDIR\uninst-smartmontools.exe"'
+  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "NoModify" 1
+  WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "NoRepair" 1
+  WriteUninstaller "uninst-smartmontools.exe"
+
+SectionEnd
+
+Section "Start Menu Shortcuts"
+
+  SectionIn 1
+
+  CreateDirectory "$SMPROGRAMS\smartmontools"
+
+  ; smartctl
+  IfFileExists "$INSTDIR\bin\smartctl.exe" 0 noctl
+    SetOutPath "$INSTDIR\bin"
+    DetailPrint "Create file: $INSTDIR\bin\smartctl-run.bat"
+    FileOpen $0 "$INSTDIR\bin\smartctl-run.bat" "w"
+    FileWrite $0 "@echo off$\r$\necho smartctl %1 %2 %3 %4 %5$\r$\nsmartctl %1 %2 %3 %4 %5$\r$\npause$\r$\n"
+    FileClose $0
+    CreateDirectory "$SMPROGRAMS\smartmontools\smartctl Examples"
+    FileOpen $0 "$SMPROGRAMS\smartmontools\smartctl Examples\!Read this first!.txt" "w"
+    FileWrite $0 "All the example commands in this directory$\r$\napply to the first IDE/ATA/SATA drive (hda).$\r$\n"
+    FileClose $0
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\All info (-a).lnk"                    "$INSTDIR\bin\smartctl-run.bat" "-a hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Identify drive (-i).lnk"              "$INSTDIR\bin\smartctl-run.bat" "-i hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART attributes (-A).lnk"            "$INSTDIR\bin\smartctl-run.bat" "-A hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART capabilities (-c).lnk"          "$INSTDIR\bin\smartctl-run.bat" "-c hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART health status (-H).lnk"         "$INSTDIR\bin\smartctl-run.bat" "-H hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART error log (-l error).lnk"       "$INSTDIR\bin\smartctl-run.bat" "-l error hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\SMART selftest log (-l selftest).lnk" "$INSTDIR\bin\smartctl-run.bat" "-l selftest hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Start long selftest (-t long).lnk"    "$INSTDIR\bin\smartctl-run.bat" "-t long hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Start offline test (-t offline).lnk"  "$INSTDIR\bin\smartctl-run.bat" "-t offline hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Start short selftest (-t short).lnk"  "$INSTDIR\bin\smartctl-run.bat" "-t short hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Stop(Abort) selftest (-X).lnk"        "$INSTDIR\bin\smartctl-run.bat" "-X hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Turn SMART off (-s off).lnk"          "$INSTDIR\bin\smartctl-run.bat" "-s off hda"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartctl Examples\Turn SMART on (-s on).lnk"            "$INSTDIR\bin\smartctl-run.bat" "-s on hda"  
+  noctl:
+
+  ; smartd
+  IfFileExists "$INSTDIR\bin\smartd.exe" 0 nod
+    SetOutPath "$INSTDIR\bin"
+    DetailPrint "Create file: $INSTDIR\bin\smartd-run.bat"
+    FileOpen $0 "$INSTDIR\bin\smartd-run.bat" "w"
+    FileWrite $0 "@echo off$\r$\necho smartd %1 %2 %3 %4 %5$\r$\nsmartd %1 %2 %3 %4 %5$\r$\npause$\r$\n"
+    FileClose $0
+    CreateDirectory "$SMPROGRAMS\smartmontools\smartd Examples"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Daemon start, log to smartd.log.lnk" "$INSTDIR\bin\smartd-run.bat" "-l local0"
+    IfFileExists "$WINDIR\system32\cmd.exe" 0 +2
+      CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Daemon start, log to eventlog.lnk" "$INSTDIR\bin\smartd-run.bat" ""
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Daemon stop.lnk" "$INSTDIR\bin\smartd-run.bat" "stop"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Do all tests once (-q onecheck).lnk" "$INSTDIR\bin\smartd-run.bat" "-q onecheck"
+    CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Debug mode (-d).lnk" "$INSTDIR\bin\smartd-run.bat" "-d"
+    IfFileExists "$WINDIR\notepad.exe" 0 nopad
+      CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Edit smartd.conf.lnk" "$WINDIR\notepad.exe" "$INSTDIR\bin\smartd.conf"
+      CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\View smartd.log.lnk" "$WINDIR\notepad.exe" "$INSTDIR\bin\smartd.log"
+    nopad:
+
+    ; smartd service (not on 9x/ME)
+    IfFileExists "$WINDIR\system32\cmd.exe" 0 nosvc
+      CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service install, log to eventlog.lnk" "$INSTDIR\bin\smartd-run.bat" "install"
+      CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service install, log to smartd.log.lnk" "$INSTDIR\bin\smartd-run.bat" "install -l local0"
+      CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service remove.lnk" "$INSTDIR\bin\smartd-run.bat" "remove"
+      DetailPrint "Create file: $INSTDIR\bin\net-run.bat"
+      FileOpen $0 "$INSTDIR\bin\net-run.bat" "w"
+      FileWrite $0 "@echo off$\r$\necho net %1 %2 %3 %4 %5$\r$\nnet %1 %2 %3 %4 %5$\r$\npause$\r$\n"
+      FileClose $0
+      CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service start.lnk" "$INSTDIR\bin\net-run.bat" "start smartd"
+      CreateShortCut "$SMPROGRAMS\smartmontools\smartd Examples\Service stop.lnk" "$INSTDIR\bin\net-run.bat" "stop smartd"
+    nosvc:
+  nod:
+
+  ; Documentation
+  IfFileExists "$INSTDIR\doc\README.TXT" 0 nodoc
+    SetOutPath "$INSTDIR\doc"
+    CreateDirectory "$SMPROGRAMS\smartmontools\Documentation"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartctl manual page (html).lnk"    "$INSTDIR\doc\smartctl.8.html"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd manual page (html).lnk"      "$INSTDIR\doc\smartd.8.html"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd.conf manual page (html).lnk" "$INSTDIR\doc\smartd.conf.5.html"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartctl manual page (txt).lnk"     "$INSTDIR\doc\smartctl.8.txt"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd manual page (txt).lnk"       "$INSTDIR\doc\smartd.8.txt"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd.conf manual page (txt).lnk"  "$INSTDIR\doc\smartd.conf.5.txt"
+    IfFileExists "$WINDIR\notepad.exe" 0 +2
+      CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\smartd.conf sample.lnk" "$WINDIR\notepad.exe" "$INSTDIR\doc\smartd.conf"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\AUTHORS.lnk"   "$INSTDIR\doc\AUTHORS.txt"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\CHANGELOG.lnk" "$INSTDIR\doc\CHANGELOG.txt"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\COPYING.lnk"   "$INSTDIR\doc\COPYING.txt"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\INSTALL.lnk"   "$INSTDIR\doc\INSTALL.txt"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\NEWS.lnk"      "$INSTDIR\doc\NEWS.txt"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\README.lnk"    "$INSTDIR\doc\README.txt"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\TODO.lnk"      "$INSTDIR\doc\TODO.txt"
+    CreateShortCut "$SMPROGRAMS\smartmontools\Documentation\WARNINGS.lnk"  "$INSTDIR\doc\WARNINGS.txt"
+  nodoc:
+
+  ; Homepage
+  CreateShortCut "$SMPROGRAMS\smartmontools\smartmontools Home Page.lnk" "http://smartmontools.sourceforge.net/"
+
+  ; Uninstall
+  IfFileExists "$INSTDIR\uninst-smartmontools.exe" 0 +2
+    CreateShortCut "$SMPROGRAMS\smartmontools\Uninstall smartmontools.lnk" "$INSTDIR\uninst-smartmontools.exe"
+
+SectionEnd
+
+Section "Add install dir to PATH" PATH_IDX
+
+  SectionIn 1
+
+  IfFileExists "$WINDIR\system32\cmd.exe" 0 +3
+    Push "$INSTDIR\bin"
+    Call AddToPath
+SectionEnd
+
+;--------------------------------------------------------------------
+
+Section "Uninstall"
+  
+  ; Remove registry keys
+  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools"
+  DeleteRegKey HKLM "Software\smartmontools"
+
+  IfFileExists "$INSTDIR\bin\smartd.conf" 0 noconf
+    ; Assume unchanged if timestamp is equal to sample file
+    GetFileTime "$INSTDIR\bin\smartd.conf" $0 $1
+    GetFileTime "$INSTDIR\doc\smartd.conf" $2 $3
+    StrCmp "$0:$1" "$2:$3" +2 0
+      MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2  "Delete configuration file$\n$INSTDIR\bin\smartd.conf ?" IDYES 0 IDNO noconf
+        Delete "$INSTDIR\bin\smartd.conf"
+  noconf:
+
+  IfFileExists "$INSTDIR\bin\smartd.log" 0 +3
+    MessageBox MB_YESNO|MB_ICONQUESTION|MB_DEFBUTTON2  "Delete log file$\n$INSTDIR\bin\smartd.log ?" IDYES 0 IDNO +2
+      Delete "$INSTDIR\bin\smartd.log"
+
+  ; Remove files
+  Delete "$INSTDIR\bin\smartctl.exe"
+  Delete "$INSTDIR\bin\smartd.exe"
+  Delete "$INSTDIR\bin\syslogevt.exe"
+  Delete "$INSTDIR\bin\smartctl-run.bat"
+  Delete "$INSTDIR\bin\smartd-run.bat"
+  Delete "$INSTDIR\bin\net-run.bat"
+  Delete "$INSTDIR\doc\AUTHORS.txt"
+  Delete "$INSTDIR\doc\CHANGELOG.txt"
+  Delete "$INSTDIR\doc\COPYING.txt"
+  Delete "$INSTDIR\doc\INSTALL.txt"
+  Delete "$INSTDIR\doc\NEWS.txt"
+  Delete "$INSTDIR\doc\README.txt"
+  Delete "$INSTDIR\doc\TODO.txt"
+  Delete "$INSTDIR\doc\WARNINGS.txt"
+  Delete "$INSTDIR\doc\smartctl.8.html"
+  Delete "$INSTDIR\doc\smartctl.8.txt"
+  Delete "$INSTDIR\doc\smartd.8.html"
+  Delete "$INSTDIR\doc\smartd.8.txt"
+  Delete "$INSTDIR\doc\smartd.conf"
+  Delete "$INSTDIR\doc\smartd.conf.5.html"
+  Delete "$INSTDIR\doc\smartd.conf.5.txt"
+  Delete "$INSTDIR\uninst-smartmontools.exe"
+
+  ; Remove shortcuts
+  Delete "$SMPROGRAMS\smartmontools\*.*"
+  Delete "$SMPROGRAMS\smartmontools\Documentation\*.*"
+  Delete "$SMPROGRAMS\smartmontools\smartctl Examples\*.*"
+  Delete "$SMPROGRAMS\smartmontools\smartd Examples\*.*"
+
+  ; Remove folders
+  RMDir  "$SMPROGRAMS\smartmontools\Documentation"
+  RMDir  "$SMPROGRAMS\smartmontools\smartctl Examples\"
+  RMDir  "$SMPROGRAMS\smartmontools\smartd Examples\"
+  RMDir  "$SMPROGRAMS\smartmontools"
+  RMDir  "$INSTDIR\bin"
+  RMDir  "$INSTDIR\doc"
+  RMDir  "$INSTDIR"
+
+  ; Remove install dir from PATH
+  IfFileExists "$WINDIR\system32\cmd.exe" 0 +3
+    Push "$INSTDIR\bin"
+    Call un.RemoveFromPath
+
+  ; Check for still existing files
+  IfFileExists "$INSTDIR\bin\smartd.exe" 0 +3
+    MessageBox MB_OK|MB_ICONEXCLAMATION "$INSTDIR\bin\smartd.exe could not be removed.$\nsmartd is possibly still running."
+    Goto +3
+  IfFileExists "$INSTDIR" 0 +2
+    MessageBox MB_OK "Note: $INSTDIR could not be removed."
+
+SectionEnd
+
+;--------------------------------------------------------------------
+
+Function .onInit
+
+  ; Hide "Add install dir to PATH" on 9x/ME
+  IfFileExists "$WINDIR\system32\cmd.exe" +2 0
+    SectionSetText ${PATH_IDX} ""
+
+FunctionEnd
+
+
+;--------------------------------------------------------------------
+; Utility functions
+;
+; Based on example from:
+; http://nsis.sourceforge.net/Path_Manipulation
+;
+
+
+!include "WinMessages.nsh"
+
+; Registry Entry for environment (NT4,2000,XP)
+; All users:
+;!define Environ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
+; Current user only:
+!define Environ 'HKCU "Environment"'
+
+
+; AddToPath - Appends dir to PATH
+;   (does not work on Win9x/ME)
+;
+; Usage:
+;   Push "dir"
+;   Call AddToPath
+
+Function AddToPath
+  Exch $0
+  Push $1
+  Push $2
+  Push $3
+  ReadRegStr $1 ${Environ} "PATH"
+  Push "$1;"
+  Push "$0;"
+  Call StrStr
+  Pop $2
+  StrCmp $2 "" "" done
+  Push "$1;"
+  Push "$0\;"
+  Call StrStr
+  Pop $2
+  StrCmp $2 "" "" done
+  DetailPrint "Add to PATH: $0"
+  StrCpy $2 $1 1 -1
+  StrCmp $2 ";" 0 +2
+    StrCpy $1 $1 -1 ; remove trailing ';'
+  StrCmp $1 "" +2   ; no leading ';'
+    StrCpy $0 "$1;$0"
+  WriteRegExpandStr ${Environ} "PATH" $0
+  SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
+done:
+  Pop $3
+  Pop $2
+  Pop $1
+  Pop $0
+FunctionEnd
+
+
+; RemoveFromPath - Removes dir from PATH
+;
+; Usage:
+;   Push "dir"
+;   Call RemoveFromPath
+Function un.RemoveFromPath
+  Exch $0
+  Push $1
+  Push $2
+  Push $3
+  Push $4
+  Push $5
+  Push $6
+  ReadRegStr $1 ${Environ} "PATH"
+  StrCpy $5 $1 1 -1
+  StrCmp $5 ";" +2
+    StrCpy $1 "$1;" ; ensure trailing ';'
+  Push $1
+  Push "$0;"
+  Call un.StrStr
+  Pop $2 ; pos of our dir
+  StrCmp $2 "" done
+
+  DetailPrint "Remove from PATH: $0"
+  StrLen $3 "$0;"
+  StrLen $4 $2
+  StrCpy $5 $1 -$4 ; $5 is now the part before the path to remove
+  StrCpy $6 $2 "" $3 ; $6 is now the part after the path to remove
+  StrCpy $3 "$5$6"
+  StrCpy $5 $3 1 -1
+  StrCmp $5 ";" 0 +2
+    StrCpy $3 $3 -1 ; remove trailing ';'
+  WriteRegExpandStr ${Environ} "PATH" $3
+  SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
+
+done:
+  Pop $6
+  Pop $5
+  Pop $4
+  Pop $3
+  Pop $2
+  Pop $1
+  Pop $0
+FunctionEnd
+
+; StrStr - find substring in a string
+;
+; Usage:
+;   Push "this is some string"
+;   Push "some"
+;   Call StrStr
+;   Pop $0 ; "some string"
+!macro StrStr un
+Function ${un}StrStr
+  Exch $R1 ; $R1=substring, stack=[old$R1,string,...]
+  Exch     ;                stack=[string,old$R1,...]
+  Exch $R2 ; $R2=string,    stack=[old$R2,old$R1,...]
+  Push $R3
+  Push $R4
+  Push $R5
+  StrLen $R3 $R1
+  StrCpy $R4 0
+  ; $R1=substring, $R2=string, $R3=strlen(substring)
+  ; $R4=count, $R5=tmp
+  loop:
+    StrCpy $R5 $R2 $R3 $R4
+    StrCmp $R5 $R1 done
+    StrCmp $R5 "" done
+    IntOp $R4 $R4 + 1
+    Goto loop
+done:
+  StrCpy $R1 $R2 "" $R4
+  Pop $R5
+  Pop $R4
+  Pop $R3
+  Pop $R2
+  Exch $R1 ; $R1=old$R1, stack=[result,...]
+FunctionEnd
+!macroend
+!insertmacro StrStr ""
+!insertmacro StrStr "un."
diff --git a/os_win32/smartctl_vc6.dsp b/os_win32/smartctl_vc6.dsp
new file mode 100644 (file)
index 0000000..516623b
--- /dev/null
@@ -0,0 +1,244 @@
+# Microsoft Developer Studio Project File - Name="smartctl_vc6" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** NICHT BEARBEITEN **\r
+\r
+# TARGTYPE "Win32 (x86) Console Application" 0x0103\r
+\r
+CFG=smartctl_vc6 - Win32 Debug\r
+!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE\r
+!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "smartctl_vc6.mak".\r
+!MESSAGE \r
+!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben\r
+!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "smartctl_vc6.mak" CFG="smartctl_vc6 - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Für die Konfiguration stehen zur Auswahl:\r
+!MESSAGE \r
+!MESSAGE "smartctl_vc6 - Win32 Release" (basierend auf  "Win32 (x86) Console Application")\r
+!MESSAGE "smartctl_vc6 - Win32 Debug" (basierend auf  "Win32 (x86) Console Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "smartctl_vc6 - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "smartctl.r"\r
+# PROP Intermediate_Dir "smartctl.r"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /W3 /GX /O1 /I "." /I ".." /I "..\posix" /D "NDEBUG" /D "HAVE_CONFIG_H" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD BASE RSC /l 0x407 /d "NDEBUG"\r
+# ADD RSC /l 0x407 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"smartctl.exe"\r
+\r
+!ELSEIF  "$(CFG)" == "smartctl_vc6 - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "smartctl.d"\r
+# PROP Intermediate_Dir "smartctl.d"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "." /I ".." /I "..\posix" /D "_DEBUG" /D "HAVE_CONFIG_H" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD BASE RSC /l 0x407 /d "_DEBUG"\r
+# ADD RSC /l 0x407 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "smartctl_vc6 - Win32 Release"\r
+# Name "smartctl_vc6 - Win32 Debug"\r
+# Begin Group "posix"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\getopt.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\getopt.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\getopt1.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regcomp.c\r
+# PROP Exclude_From_Build 1\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regex.c\r
+# ADD CPP /w /W0\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regex.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regex_internal.c\r
+# PROP Exclude_From_Build 1\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regex_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regexec.c\r
+# PROP Exclude_From_Build 1\r
+# End Source File\r
+# End Group\r
+# Begin Source File\r
+\r
+SOURCE=..\atacmdnames.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\atacmdnames.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\atacmds.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\atacmds.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\ataprint.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\ataprint.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\config_vc6.h\r
+\r
+!IF  "$(CFG)" == "smartctl_vc6 - Win32 Release"\r
+\r
+# Begin Custom Build - Copy $(InputPath) config.h\r
+InputPath=.\config_vc6.h\r
+\r
+"config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\r
+       copy $(InputPath) config.h\r
+\r
+# End Custom Build\r
+\r
+!ELSEIF  "$(CFG)" == "smartctl_vc6 - Win32 Debug"\r
+\r
+# Begin Custom Build - Copy $(InputPath) config.h\r
+InputPath=.\config_vc6.h\r
+\r
+"config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\r
+       copy $(InputPath) config.h\r
+\r
+# End Custom Build\r
+\r
+!ENDIF \r
+\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\extern.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\int64.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\knowndrives.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\knowndrives.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\os_win32.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsiata.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsiata.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsicmds.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsicmds.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsiprint.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsiprint.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\smartctl.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\smartctl.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\syslog.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\utility.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\utility.h\r
+# End Source File\r
+# End Target\r
+# End Project\r
diff --git a/os_win32/smartd_vc6.dsp b/os_win32/smartd_vc6.dsp
new file mode 100644 (file)
index 0000000..6610ae6
--- /dev/null
@@ -0,0 +1,268 @@
+# Microsoft Developer Studio Project File - Name="smartd_vc6" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** NICHT BEARBEITEN **\r
+\r
+# TARGTYPE "Win32 (x86) Console Application" 0x0103\r
+\r
+CFG=smartd_vc6 - Win32 Debug\r
+!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE\r
+!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "smartd_vc6.mak".\r
+!MESSAGE \r
+!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben\r
+!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "smartd_vc6.mak" CFG="smartd_vc6 - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Für die Konfiguration stehen zur Auswahl:\r
+!MESSAGE \r
+!MESSAGE "smartd_vc6 - Win32 Release" (basierend auf  "Win32 (x86) Console Application")\r
+!MESSAGE "smartd_vc6 - Win32 Debug" (basierend auf  "Win32 (x86) Console Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "smartd_vc6 - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "smartd.r"\r
+# PROP Intermediate_Dir "smartd.r"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /W3 /GX /O1 /I "." /I ".." /I "..\posix" /D "NDEBUG" /D "HAVE_CONFIG_H" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD BASE RSC /l 0x407 /d "NDEBUG"\r
+# ADD RSC /l 0x407 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"smartd.exe"\r
+\r
+!ELSEIF  "$(CFG)" == "smartd_vc6 - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "smartd.d"\r
+# PROP Intermediate_Dir "smartd.d"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "." /I ".." /I "..\posix" /D "_DEBUG" /D "HAVE_CONFIG_H" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD BASE RSC /l 0x407 /d "_DEBUG"\r
+# ADD RSC /l 0x407 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "smartd_vc6 - Win32 Release"\r
+# Name "smartd_vc6 - Win32 Debug"\r
+# Begin Group "posix"\r
+\r
+# PROP Default_Filter ""\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\getopt.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\getopt.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\getopt1.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regcomp.c\r
+# PROP Exclude_From_Build 1\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regex.c\r
+# ADD CPP /w /W0\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regex.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regex_internal.c\r
+# PROP Exclude_From_Build 1\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regex_internal.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\posix\regexec.c\r
+# PROP Exclude_From_Build 1\r
+# End Source File\r
+# End Group\r
+# Begin Source File\r
+\r
+SOURCE=..\atacmdnames.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\atacmdnames.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\atacmds.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\atacmds.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\ataprint.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\ataprint.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\config_vc6.h\r
+\r
+!IF  "$(CFG)" == "smartd_vc6 - Win32 Release"\r
+\r
+# Begin Custom Build - Copy $(InputPath) config.h\r
+InputPath=.\config_vc6.h\r
+\r
+"config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\r
+       copy $(InputPath) config.h\r
+\r
+# End Custom Build\r
+\r
+!ELSEIF  "$(CFG)" == "smartd_vc6 - Win32 Debug"\r
+\r
+# Begin Custom Build - Copy $(InputPath) config.h\r
+InputPath=.\config_vc6.h\r
+\r
+"config.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\r
+       copy $(InputPath) config.h\r
+\r
+# End Custom Build\r
+\r
+!ENDIF \r
+\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\daemon_win32.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\daemon_win32.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\extern.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\hostname_win32.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\hostname_win32.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\int64.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\knowndrives.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\knowndrives.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\os_win32.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsiata.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsiata.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsicmds.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsicmds.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsiprint.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\scsiprint.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\smartctl.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\smartd.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\smartd.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\syslog.h\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\syslog_win32.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\utility.cpp\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\utility.h\r
+# End Source File\r
+# End Target\r
+# End Project\r
diff --git a/os_win32/smartmontools_vc6.dsw b/os_win32/smartmontools_vc6.dsw
new file mode 100644 (file)
index 0000000..76e451e
--- /dev/null
@@ -0,0 +1,53 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00\r
+# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELÖSCHT WERDEN!\r
+\r
+###############################################################################\r
+\r
+Project: "smartctl_vc6"=.\smartctl_vc6.dsp - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Project: "smartd_vc6"=.\smartd_vc6.dsp - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Project: "syslogevt_vc6"=.\syslogevt_vc6.dsp - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Global:\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<3>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
index 7ef23f0caededc4c8b61ef3197afb3a0b0b3c16e..12bf1463c9532affedd7e35c642a64e80083bbbc 100644 (file)
 #ifndef SYSLOG_H
 #define SYSLOG_H
 
-#define SYSLOG_H_CVSID "$Id: syslog.h,v 1.4 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define SYSLOG_H_CVSID "$Id: syslog.h,v 1.5 2006/08/09 20:40:20 chrfranke Exp $\n"
 
 #include <stdarg.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 /* EVENTLOG_ERROR_TYPE: */
 #define LOG_EMERG       0
 #define LOG_ALERT       1
@@ -59,4 +63,8 @@ void closelog(void);
 
 void vsyslog(int priority, const char * message, va_list args);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* SYSLOG_H */
diff --git a/os_win32/syslog_win32.c b/os_win32/syslog_win32.c
deleted file mode 100644 (file)
index 7ed039f..0000000
+++ /dev/null
@@ -1,435 +0,0 @@
-/*
- * os_win32/syslog_win32.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
-
-// Win32 Emulation of syslog() for smartd
-// Writes to windows event log on NT4/2000/XP
-// (Register syslogevt.exe as event message file)
-// Writes to file "<ident>.log" on 9x/ME.
-// If facility is set to LOG_LOCAL[0-7], log is written to
-// file "<ident>.log", stdout, stderr, "<ident>[1-5].log".
-
-
-#include "syslog.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <time.h>
-#include <errno.h>
-#include <process.h> // getpid()
-
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h> // RegisterEventSourceA(), ReportEventA(), ...
-
-const char *syslog_win32_c_cvsid = "$Id: syslog_win32.c,v 1.6 2006/04/12 14:54:29 ballen4705 Exp $"
-SYSLOG_H_CVSID;
-
-#ifdef _MSC_VER
-// MSVC
-#define snprintf _snprintf
-#define vsnprintf _vsnprintf
-#endif
-
-#define ARGUSED(x) ((void)(x))
-
-
-#ifndef _MT
-//MT runtime not necessary, because thread uses no unsafe lib functions
-//#error Program must be linked with multithreaded runtime library
-#endif
-
-
-#ifdef TESTEVT
-// Redirect event log to stdout for testing
-
-static BOOL Test_ReportEventA(HANDLE h, WORD type, WORD cat, DWORD id, PSID usid,
-                                               WORD nstrings, WORD datasize, LPCTSTR * strings, LPVOID data)
-{
-       int i;
-       printf("%u %lu:%s", type, id, nstrings != 1?"\n":"");
-       for (i = 0; i < nstrings; i++)
-               printf(" \"%s\"\n", strings[i]);
-       fflush(stdout);
-       return TRUE;
-}
-
-HANDLE Test_RegisterEventSourceA(LPCTSTR server, LPCTSTR source)
-{
-       return (HANDLE)42;
-}
-
-#define ReportEventA Test_ReportEventA
-#define RegisterEventSourceA Test_RegisterEventSourceA
-#endif // TESTEVT
-
-
-// Event message ids,
-// should be identical to MSG_SYSLOG* in "syslogevt.h"
-// (generated by "mc" from "syslogevt.mc")
-#define MSG_SYSLOG                       0x00000000L
-#define MSG_SYSLOG_01                    0x00000001L
-// ...
-#define MSG_SYSLOG_10                    0x0000000AL
-
-static char sl_ident[100];
-static char sl_logpath[sizeof(sl_ident) + sizeof("0.log")-1];
-static FILE * sl_logfile;
-static char sl_pidstr[16];
-static HANDLE sl_hevtsrc;
-
-
-// Ring buffer for event log output via thread
-#define MAXLINES 10
-#define LINELEN 200
-
-static HANDLE evt_hthread;
-static char evt_lines[MAXLINES][LINELEN+1];
-static int evt_priorities[MAXLINES];
-static volatile int evt_timeout;
-static int evt_index_in, evt_index_out;
-static HANDLE evt_wait_in, evt_wait_out;
-
-
-// Map syslog priority to event type
-
-static WORD pri2evtype(int priority)
-{
-       switch (priority) {
-               default:
-               case LOG_EMERG: case LOG_ALERT:
-               case LOG_CRIT:  case LOG_ERR:
-                       return EVENTLOG_ERROR_TYPE;
-               case LOG_WARNING:
-                       return EVENTLOG_WARNING_TYPE;
-               case LOG_NOTICE: case LOG_INFO:
-               case LOG_DEBUG:
-                       return EVENTLOG_INFORMATION_TYPE;
-       }
-}
-
-
-// Map syslog priority to string
-
-static const char * pri2text(int priority)
-{
-       switch (priority) {
-               case LOG_EMERG:   return "EMERG";
-               case LOG_ALERT:   return "ALERT";
-               case LOG_CRIT:    return "CRIT";
-               default:
-               case LOG_ERR:     return "ERROR";
-               case LOG_WARNING: return "Warn";
-               case LOG_NOTICE:  return "Note";
-               case LOG_INFO:    return "Info";
-               case LOG_DEBUG:   return "Debug";
-       }
-}
-
-
-// Output cnt events from ring buffer
-
-static void report_events(int cnt)
-{
-       const char * msgs[3+MAXLINES];
-
-       int i, pri;
-       if (cnt <= 0)
-               return;
-       if (cnt > MAXLINES)
-               cnt = MAXLINES;
-
-       pri = evt_priorities[evt_index_out];
-
-       msgs[0] = sl_ident;
-       msgs[1] = sl_pidstr;
-       msgs[2] = pri2text(pri);
-       for (i = 0; i < cnt; i++) {
-               //assert(evt_priorities[evt_index_out] == pri);
-               msgs[3+i] = evt_lines[evt_index_out];
-               if (++evt_index_out >= MAXLINES)
-                       evt_index_out = 0;
-       }
-       ReportEventA(sl_hevtsrc,
-               pri2evtype(pri), // type
-               0, MSG_SYSLOG+cnt,    // category, message id
-               NULL,                 // no security id
-               (WORD)(3+cnt), 0,     // 3+cnt strings, ...
-               msgs, NULL);          // ...          , no data
-}
-
-
-// Thread to combine several syslog lines into one event log entry
-
-static ULONG WINAPI event_logger_thread(LPVOID arg)
-{
-       int cnt;
-       ARGUSED(arg);
-
-       cnt = 0;
-       for (;;) {
-               // Wait for first line ...
-               int prior, i, rest;
-               if (cnt == 0) {
-                       if (WaitForSingleObject(evt_wait_out, (evt_timeout? INFINITE : 0)) != WAIT_OBJECT_0)
-                               break;
-                       cnt = 1;
-               }
-
-               // ... wait some time for more lines with same prior
-               i = evt_index_out;
-               prior = evt_priorities[i];
-               rest = 0;
-               while (cnt < MAXLINES) {
-                       long timeout =
-                               evt_timeout * ((1000L * (MAXLINES-cnt+1))/MAXLINES);
-                       if (WaitForSingleObject(evt_wait_out, timeout) != WAIT_OBJECT_0)
-                               break;
-                       if (++i >= MAXLINES)
-                               i = 0;
-                       if (evt_priorities[i] != prior) {
-                               rest = 1;
-                               break;
-                       }
-                       cnt++;
-               }
-
-               // Output all in one event log entry
-               report_events(cnt);
-
-               // Signal space
-               if (!ReleaseSemaphore(evt_wait_in, cnt, NULL))
-                       break;
-               cnt = rest;
-       }
-       return 0;
-}
-
-
-static void on_exit_event_logger(void)
-{
-       // Output lines immediate if exiting
-       evt_timeout = 0; 
-       // Wait for thread to finish
-       WaitForSingleObject(evt_hthread, 1000L);
-       CloseHandle(evt_hthread);
-#if 0
-       if (sl_hevtsrc) {
-               DeregisterEventSource(sl_hevtsrc); sl_hevtsrc = 0;
-       }
-#else
-       // Leave event message source open to prevent losing messages during shutdown
-#endif
-}
-
-
-static int start_event_logger()
-{
-       DWORD tid;
-       evt_timeout = 1;
-       if (   !(evt_wait_in  = CreateSemaphore(NULL,  MAXLINES, MAXLINES, NULL))
-               || !(evt_wait_out = CreateSemaphore(NULL,         0, MAXLINES, NULL))) {
-               fprintf(stderr,"CreateSemaphore failed, Error=%ld\n", GetLastError());
-               return -1;
-       }
-       if (!(evt_hthread = CreateThread(NULL, 0, event_logger_thread, NULL, 0, &tid))) {
-               fprintf(stderr,"CreateThread failed, Error=%ld\n", GetLastError());
-               return -1;
-       }
-       atexit(on_exit_event_logger);
-       return 0;
-}
-
-
-// Write lines to event log ring buffer
-
-static void write_event_log(int priority, const char * lines)
-{
-       int cnt = 0;
-       int i;
-       for (i = 0; lines[i]; i++) {
-               int len = 0;
-               while (lines[i+len] && lines[i+len] != '\n')
-                       len++;
-                       ;
-               if (len > 0) {
-                       // Wait for space
-                       if (WaitForSingleObject(evt_wait_in, INFINITE) != WAIT_OBJECT_0)
-                               return;
-                       // Copy line
-                       evt_priorities[evt_index_in] = priority;
-                       memcpy(evt_lines[evt_index_in], lines+i, (len < LINELEN ? len : LINELEN));
-                       if (len < LINELEN)
-                               evt_lines[evt_index_in][len] = 0;
-                       if (++evt_index_in >= MAXLINES)
-                               evt_index_in = 0;
-                       // Signal avail if ring buffer full
-                       if (++cnt >= MAXLINES) {
-                               ReleaseSemaphore(evt_wait_out, cnt, NULL);
-                               cnt = 0;
-                       }
-                       i += len;
-               }
-               if (!lines[i])
-                       break;
-       }
-
-       // Signal avail
-       if (cnt > 0)
-               ReleaseSemaphore(evt_wait_out, cnt, NULL);
-       Sleep(1);
-}
-
-
-// Write lines to logfile
-
-static void write_logfile(FILE * f, int priority, const char * lines)
-{
-       time_t now; char stamp[sizeof("2004-04-04 10:00:00")+13];
-       int i;
-
-       now = time((time_t*)0);
-       if (!strftime(stamp, sizeof(stamp)-1, "%Y-%m-%d %H:%M:%S", localtime(&now)))
-               strcpy(stamp,"?");
-
-       for (i = 0; lines[i]; i++) {
-               int len = 0;
-               while (lines[i+len] && lines[i+len] != '\n')
-                       len++;
-               if (len > 0) {
-                       fprintf(f, "%s %s[%s]: %-5s: ",
-                               stamp, sl_ident, sl_pidstr, pri2text(priority));
-                       fwrite(lines+i, len, 1, f);
-                       fputc('\n', f);
-                       i += len;
-               }
-               if (!lines[i])
-                       break;
-       }
-}
-
-
-void openlog(const char *ident, int logopt, int facility)
-{
-       int pid;
-       if (sl_logpath[0] || sl_logfile || sl_hevtsrc)
-               return; // Already open
-
-       strncpy(sl_ident, ident, sizeof(sl_ident)-1);
-       // logopt==LOG_PID assumed
-       ARGUSED(logopt);
-       pid = getpid();
-       if (snprintf(sl_pidstr, sizeof(sl_pidstr)-1, (pid >= 0 ? "%d" : "0x%X"), pid) < 0)
-               strcpy(sl_pidstr,"?");
-
-       if (facility == LOG_LOCAL0) // "ident.log"
-               strcat(strcpy(sl_logpath, sl_ident), ".log");
-       else if (facility == LOG_LOCAL1) // stdout
-               sl_logfile = stdout;
-       else if (facility == LOG_LOCAL2) // stderr
-               sl_logfile = stderr;
-       else if (LOG_LOCAL2 < facility && facility <= LOG_LOCAL7) { // "ident[1-5].log"
-               snprintf(sl_logpath, sizeof(sl_logpath)-1, "%s%d.log",
-                       sl_ident, LOG_FAC(facility)-LOG_FAC(LOG_LOCAL2));
-       }
-       else // Assume LOG_DAEMON, use event log if possible, else "ident.log"
-       if (!(sl_hevtsrc = RegisterEventSourceA(NULL/*localhost*/, sl_ident))) {
-               // Cannot open => Use logfile
-               long err = GetLastError();
-               strcat(strcpy(sl_logpath, sl_ident), ".log");
-               if (GetVersion() & 0x80000000)
-                       fprintf(stderr, "%s: No event log on Win9x/ME, writing to %s\n",
-                               sl_ident, sl_logpath);
-               else
-                       fprintf(stderr, "%s: Cannot register event source (Error=%ld), writing to %s\n",
-                               sl_ident, err, sl_logpath);
-       }
-       else {
-               // Start event log thread
-               start_event_logger();
-       }
-       //assert(sl_logpath[0] || sl_logfile || sl_hevtsrc);
-
-}
-
-
-void closelog()
-{
-}
-
-
-void vsyslog(int priority, const char * message, va_list args)
-{
-       char buffer[1000];
-
-       // Translation of %m to error text not supported yet
-       if (strstr(message, "%m"))
-               message = "Internal error: \"%%m\" in log message";
-
-       // Format message
-       if (vsnprintf(buffer, sizeof(buffer)-1, message, args) < 0)
-               strcpy(buffer, "Internal Error: buffer overflow");
-
-       if (sl_hevtsrc) {
-               // Write to event log
-               write_event_log(priority, buffer);
-       }
-       else if (sl_logfile) {
-               // Write to stdout/err
-               write_logfile(sl_logfile, priority, buffer);
-               fflush(sl_logfile);
-       }
-       else if (sl_logpath[0]) {
-               // Append to logfile
-               FILE * f;
-               if (!(f = fopen(sl_logpath, "a")))
-                       return;
-               write_logfile(f, priority, buffer);
-               fclose(f);
-       }
-}
-
-
-#ifdef TEST
-// Test program
-
-void syslog(int priority, const char *message, ...)
-{
-       va_list args;
-       va_start(args, message);
-       vsyslog(priority, message, args);
-       va_end(args);
-}
-
-int main(int argc, char* argv[])
-{
-       int i;
-       openlog(argc < 2 ? "test" : argv[1], LOG_PID, (argc < 3 ? LOG_DAEMON : LOG_LOCAL1));
-       syslog(LOG_INFO,    "Info\n");
-       syslog(LOG_WARNING, "Warning %d\n\n", 42);
-       syslog(LOG_ERR,     "Error %s", "Fatal");
-       for (i = 0; i < 100; i++) {
-               char buf[LINELEN];
-               if (i % 13 == 0)
-                       Sleep(1000L);
-               sprintf(buf, "Log Line %d\n", i);
-               syslog(i % 17 ? LOG_INFO : LOG_ERR, buf);
-       }
-       closelog();
-       return 0;
-}
-
-#endif
diff --git a/os_win32/syslog_win32.cpp b/os_win32/syslog_win32.cpp
new file mode 100644 (file)
index 0000000..4a15c37
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * os_win32/syslog_win32.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+// Win32 Emulation of syslog() for smartd
+// Writes to windows event log on NT4/2000/XP
+// (Register syslogevt.exe as event message file)
+// Writes to file "<ident>.log" on 9x/ME.
+// If facility is set to LOG_LOCAL[0-7], log is written to
+// file "<ident>.log", stdout, stderr, "<ident>[1-5].log".
+
+
+#include "syslog.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <process.h> // getpid()
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h> // RegisterEventSourceA(), ReportEventA(), ...
+
+const char *syslog_win32_c_cvsid = "$Id: syslog_win32.cpp,v 1.7 2006/08/09 20:40:20 chrfranke Exp $"
+SYSLOG_H_CVSID;
+
+#ifdef _MSC_VER
+// MSVC
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+
+#define ARGUSED(x) ((void)(x))
+
+
+#ifndef _MT
+//MT runtime not necessary, because thread uses no unsafe lib functions
+//#error Program must be linked with multithreaded runtime library
+#endif
+
+
+#ifdef TESTEVT
+// Redirect event log to stdout for testing
+
+static BOOL Test_ReportEventA(HANDLE h, WORD type, WORD cat, DWORD id, PSID usid,
+                                               WORD nstrings, WORD datasize, LPCTSTR * strings, LPVOID data)
+{
+       int i;
+       printf("%u %lu:%s", type, id, nstrings != 1?"\n":"");
+       for (i = 0; i < nstrings; i++)
+               printf(" \"%s\"\n", strings[i]);
+       fflush(stdout);
+       return TRUE;
+}
+
+HANDLE Test_RegisterEventSourceA(LPCTSTR server, LPCTSTR source)
+{
+       return (HANDLE)42;
+}
+
+#define ReportEventA Test_ReportEventA
+#define RegisterEventSourceA Test_RegisterEventSourceA
+#endif // TESTEVT
+
+
+// Event message ids,
+// should be identical to MSG_SYSLOG* in "syslogevt.h"
+// (generated by "mc" from "syslogevt.mc")
+#define MSG_SYSLOG                       0x00000000L
+#define MSG_SYSLOG_01                    0x00000001L
+// ...
+#define MSG_SYSLOG_10                    0x0000000AL
+
+static char sl_ident[100];
+static char sl_logpath[sizeof(sl_ident) + sizeof("0.log")-1];
+static FILE * sl_logfile;
+static char sl_pidstr[16];
+static HANDLE sl_hevtsrc;
+
+
+// Ring buffer for event log output via thread
+#define MAXLINES 10
+#define LINELEN 200
+
+static HANDLE evt_hthread;
+static char evt_lines[MAXLINES][LINELEN+1];
+static int evt_priorities[MAXLINES];
+static volatile int evt_timeout;
+static int evt_index_in, evt_index_out;
+static HANDLE evt_wait_in, evt_wait_out;
+
+
+// Map syslog priority to event type
+
+static WORD pri2evtype(int priority)
+{
+       switch (priority) {
+               default:
+               case LOG_EMERG: case LOG_ALERT:
+               case LOG_CRIT:  case LOG_ERR:
+                       return EVENTLOG_ERROR_TYPE;
+               case LOG_WARNING:
+                       return EVENTLOG_WARNING_TYPE;
+               case LOG_NOTICE: case LOG_INFO:
+               case LOG_DEBUG:
+                       return EVENTLOG_INFORMATION_TYPE;
+       }
+}
+
+
+// Map syslog priority to string
+
+static const char * pri2text(int priority)
+{
+       switch (priority) {
+               case LOG_EMERG:   return "EMERG";
+               case LOG_ALERT:   return "ALERT";
+               case LOG_CRIT:    return "CRIT";
+               default:
+               case LOG_ERR:     return "ERROR";
+               case LOG_WARNING: return "Warn";
+               case LOG_NOTICE:  return "Note";
+               case LOG_INFO:    return "Info";
+               case LOG_DEBUG:   return "Debug";
+       }
+}
+
+
+// Output cnt events from ring buffer
+
+static void report_events(int cnt)
+{
+       const char * msgs[3+MAXLINES];
+
+       int i, pri;
+       if (cnt <= 0)
+               return;
+       if (cnt > MAXLINES)
+               cnt = MAXLINES;
+
+       pri = evt_priorities[evt_index_out];
+
+       msgs[0] = sl_ident;
+       msgs[1] = sl_pidstr;
+       msgs[2] = pri2text(pri);
+       for (i = 0; i < cnt; i++) {
+               //assert(evt_priorities[evt_index_out] == pri);
+               msgs[3+i] = evt_lines[evt_index_out];
+               if (++evt_index_out >= MAXLINES)
+                       evt_index_out = 0;
+       }
+       ReportEventA(sl_hevtsrc,
+               pri2evtype(pri), // type
+               0, MSG_SYSLOG+cnt,    // category, message id
+               NULL,                 // no security id
+               (WORD)(3+cnt), 0,     // 3+cnt strings, ...
+               msgs, NULL);          // ...          , no data
+}
+
+
+// Thread to combine several syslog lines into one event log entry
+
+static ULONG WINAPI event_logger_thread(LPVOID arg)
+{
+       int cnt;
+       ARGUSED(arg);
+
+       cnt = 0;
+       for (;;) {
+               // Wait for first line ...
+               int prior, i, rest;
+               if (cnt == 0) {
+                       if (WaitForSingleObject(evt_wait_out, (evt_timeout? INFINITE : 0)) != WAIT_OBJECT_0)
+                               break;
+                       cnt = 1;
+               }
+
+               // ... wait some time for more lines with same prior
+               i = evt_index_out;
+               prior = evt_priorities[i];
+               rest = 0;
+               while (cnt < MAXLINES) {
+                       long timeout =
+                               evt_timeout * ((1000L * (MAXLINES-cnt+1))/MAXLINES);
+                       if (WaitForSingleObject(evt_wait_out, timeout) != WAIT_OBJECT_0)
+                               break;
+                       if (++i >= MAXLINES)
+                               i = 0;
+                       if (evt_priorities[i] != prior) {
+                               rest = 1;
+                               break;
+                       }
+                       cnt++;
+               }
+
+               // Output all in one event log entry
+               report_events(cnt);
+
+               // Signal space
+               if (!ReleaseSemaphore(evt_wait_in, cnt, NULL))
+                       break;
+               cnt = rest;
+       }
+       return 0;
+}
+
+
+static void on_exit_event_logger(void)
+{
+       // Output lines immediate if exiting
+       evt_timeout = 0; 
+       // Wait for thread to finish
+       WaitForSingleObject(evt_hthread, 1000L);
+       CloseHandle(evt_hthread);
+#if 0
+       if (sl_hevtsrc) {
+               DeregisterEventSource(sl_hevtsrc); sl_hevtsrc = 0;
+       }
+#else
+       // Leave event message source open to prevent losing messages during shutdown
+#endif
+}
+
+
+static int start_event_logger()
+{
+       DWORD tid;
+       evt_timeout = 1;
+       if (   !(evt_wait_in  = CreateSemaphore(NULL,  MAXLINES, MAXLINES, NULL))
+               || !(evt_wait_out = CreateSemaphore(NULL,         0, MAXLINES, NULL))) {
+               fprintf(stderr,"CreateSemaphore failed, Error=%ld\n", GetLastError());
+               return -1;
+       }
+       if (!(evt_hthread = CreateThread(NULL, 0, event_logger_thread, NULL, 0, &tid))) {
+               fprintf(stderr,"CreateThread failed, Error=%ld\n", GetLastError());
+               return -1;
+       }
+       atexit(on_exit_event_logger);
+       return 0;
+}
+
+
+// Write lines to event log ring buffer
+
+static void write_event_log(int priority, const char * lines)
+{
+       int cnt = 0;
+       int i;
+       for (i = 0; lines[i]; i++) {
+               int len = 0;
+               while (lines[i+len] && lines[i+len] != '\n')
+                       len++;
+                       ;
+               if (len > 0) {
+                       // Wait for space
+                       if (WaitForSingleObject(evt_wait_in, INFINITE) != WAIT_OBJECT_0)
+                               return;
+                       // Copy line
+                       evt_priorities[evt_index_in] = priority;
+                       memcpy(evt_lines[evt_index_in], lines+i, (len < LINELEN ? len : LINELEN));
+                       if (len < LINELEN)
+                               evt_lines[evt_index_in][len] = 0;
+                       if (++evt_index_in >= MAXLINES)
+                               evt_index_in = 0;
+                       // Signal avail if ring buffer full
+                       if (++cnt >= MAXLINES) {
+                               ReleaseSemaphore(evt_wait_out, cnt, NULL);
+                               cnt = 0;
+                       }
+                       i += len;
+               }
+               if (!lines[i])
+                       break;
+       }
+
+       // Signal avail
+       if (cnt > 0)
+               ReleaseSemaphore(evt_wait_out, cnt, NULL);
+       Sleep(1);
+}
+
+
+// Write lines to logfile
+
+static void write_logfile(FILE * f, int priority, const char * lines)
+{
+       time_t now; char stamp[sizeof("2004-04-04 10:00:00")+13];
+       int i;
+
+       now = time((time_t*)0);
+       if (!strftime(stamp, sizeof(stamp)-1, "%Y-%m-%d %H:%M:%S", localtime(&now)))
+               strcpy(stamp,"?");
+
+       for (i = 0; lines[i]; i++) {
+               int len = 0;
+               while (lines[i+len] && lines[i+len] != '\n')
+                       len++;
+               if (len > 0) {
+                       fprintf(f, "%s %s[%s]: %-5s: ",
+                               stamp, sl_ident, sl_pidstr, pri2text(priority));
+                       fwrite(lines+i, len, 1, f);
+                       fputc('\n', f);
+                       i += len;
+               }
+               if (!lines[i])
+                       break;
+       }
+}
+
+
+void openlog(const char *ident, int logopt, int facility)
+{
+       int pid;
+       if (sl_logpath[0] || sl_logfile || sl_hevtsrc)
+               return; // Already open
+
+       strncpy(sl_ident, ident, sizeof(sl_ident)-1);
+       // logopt==LOG_PID assumed
+       ARGUSED(logopt);
+       pid = getpid();
+       if (snprintf(sl_pidstr, sizeof(sl_pidstr)-1, (pid >= 0 ? "%d" : "0x%X"), pid) < 0)
+               strcpy(sl_pidstr,"?");
+
+       if (facility == LOG_LOCAL0) // "ident.log"
+               strcat(strcpy(sl_logpath, sl_ident), ".log");
+       else if (facility == LOG_LOCAL1) // stdout
+               sl_logfile = stdout;
+       else if (facility == LOG_LOCAL2) // stderr
+               sl_logfile = stderr;
+       else if (LOG_LOCAL2 < facility && facility <= LOG_LOCAL7) { // "ident[1-5].log"
+               snprintf(sl_logpath, sizeof(sl_logpath)-1, "%s%d.log",
+                       sl_ident, LOG_FAC(facility)-LOG_FAC(LOG_LOCAL2));
+       }
+       else // Assume LOG_DAEMON, use event log if possible, else "ident.log"
+       if (!(sl_hevtsrc = RegisterEventSourceA(NULL/*localhost*/, sl_ident))) {
+               // Cannot open => Use logfile
+               long err = GetLastError();
+               strcat(strcpy(sl_logpath, sl_ident), ".log");
+               if (GetVersion() & 0x80000000)
+                       fprintf(stderr, "%s: No event log on Win9x/ME, writing to %s\n",
+                               sl_ident, sl_logpath);
+               else
+                       fprintf(stderr, "%s: Cannot register event source (Error=%ld), writing to %s\n",
+                               sl_ident, err, sl_logpath);
+       }
+       else {
+               // Start event log thread
+               start_event_logger();
+       }
+       //assert(sl_logpath[0] || sl_logfile || sl_hevtsrc);
+
+}
+
+
+void closelog()
+{
+}
+
+
+void vsyslog(int priority, const char * message, va_list args)
+{
+       char buffer[1000];
+
+       // Translation of %m to error text not supported yet
+       if (strstr(message, "%m"))
+               message = "Internal error: \"%%m\" in log message";
+
+       // Format message
+       if (vsnprintf(buffer, sizeof(buffer)-1, message, args) < 0)
+               strcpy(buffer, "Internal Error: buffer overflow");
+
+       if (sl_hevtsrc) {
+               // Write to event log
+               write_event_log(priority, buffer);
+       }
+       else if (sl_logfile) {
+               // Write to stdout/err
+               write_logfile(sl_logfile, priority, buffer);
+               fflush(sl_logfile);
+       }
+       else if (sl_logpath[0]) {
+               // Append to logfile
+               FILE * f;
+               if (!(f = fopen(sl_logpath, "a")))
+                       return;
+               write_logfile(f, priority, buffer);
+               fclose(f);
+       }
+}
+
+
+#ifdef TEST
+// Test program
+
+void syslog(int priority, const char *message, ...)
+{
+       va_list args;
+       va_start(args, message);
+       vsyslog(priority, message, args);
+       va_end(args);
+}
+
+int main(int argc, char* argv[])
+{
+       int i;
+       openlog(argc < 2 ? "test" : argv[1], LOG_PID, (argc < 3 ? LOG_DAEMON : LOG_LOCAL1));
+       syslog(LOG_INFO,    "Info\n");
+       syslog(LOG_WARNING, "Warning %d\n\n", 42);
+       syslog(LOG_ERR,     "Error %s", "Fatal");
+       for (i = 0; i < 100; i++) {
+               char buf[LINELEN];
+               if (i % 13 == 0)
+                       Sleep(1000L);
+               sprintf(buf, "Log Line %d\n", i);
+               syslog(i % 17 ? LOG_INFO : LOG_ERR, buf);
+       }
+       closelog();
+       return 0;
+}
+
+#endif
diff --git a/os_win32/syslogevt.c b/os_win32/syslogevt.c
new file mode 100644 (file)
index 0000000..016bf98
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * os_win32/syslogevt.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+static char rcsid[] = "$Id: syslogevt.c,v 1.4 2006/04/12 14:54:29 ballen4705 Exp $";
+
+#include <stdio.h>
+#include <string.h>
+#include <process.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#ifdef _DEBUG
+#include "syslogevt.h"
+#endif
+
+
+static int usage()
+{
+       puts(
+               "syslogevt $Revision: 1.4 $ Copyright (C) 2004-6 Christian Franke\n"
+               "Home page is http://smartmontools.sourceforge.net/\n"
+               "\n"
+               "Usage: syslogevt [-ru] name [ident ...]\n"
+               "\n"
+               "Creates registry files \"name-r.reg\" and \"name-u.reg\" to (un)register\n"
+               "this program as an event message file for message source(s) \"ident\".\n"
+               "If \"ident\" is ommited, \"name\" is used. Options:\n"
+               "\n"
+               "    -r    run \"regedit name-r.reg\" after creating files\n"
+               "    -u    run \"regedit name-u.reg\" after creating files\n"
+               "\n"
+               "Examples:\n"
+               "\n"
+               "syslogevt smartd                     (Create smartd-r.reg and smartd-u.reg)\n"
+               "regedit smartd-r.reg           (Register syslogevt.exe for smartd messages)\n"
+               "\n"
+               "syslogevt -r smartd                             (Same as above in one step)\n"
+               "\n"
+               "regedit smartd-u.reg                                (Undo the registration)\n"
+               "\n"
+               "CAUTION: A registry entry of an existing event source with the same \"ident\"\n"
+               "         will be overwritten by regedit without notice."
+       );
+       return 1;
+}
+
+main(int argc, char ** argv)
+{
+       int regedit, a1, ai;
+       char name1[30+1], name2[30+1], mypath[MAX_PATH+1];
+       const char * ident;
+       FILE * f1, * f2;
+
+#ifdef _DEBUG
+       if (!(MSG_SYSLOG == 0 && MSG_SYSLOG_01 == 1 && MSG_SYSLOG_10 == 10)) {
+               puts("Internal error: MSG_SYSLOG_n != n"); return 1;
+       }
+#endif
+
+       if (argc < 2)
+               return usage();
+
+       a1 = 1;
+       regedit = 0;
+       if (!strcmp(argv[a1], "-r")) {
+               regedit = 1; a1++;
+       }
+       else if (!strcmp(argv[a1], "-u")) {
+               regedit = -1; a1++;
+       }
+
+       for (ai = a1; ai < argc; ai++) {
+               ident = argv[ai];
+               if (!(ident[0] && strlen(ident) < sizeof(name1)-10
+                         && strcspn(ident, "-.:/\\") == strlen(ident) )) {
+                       return usage();
+               }
+       }
+
+       if (!GetModuleFileName(NULL, mypath, sizeof(mypath)-1)) {
+               fputs("GetModuleFileName failed\n", stderr);
+               return 1;
+       }
+
+       ident = argv[a1];
+       strcpy(name1, ident); strcat(name1, "-r.reg");
+       strcpy(name2, ident); strcat(name2, "-u.reg");
+
+       if (!(f1 = fopen(name1, "w"))) {
+               perror(name1); return 1;
+       }
+       if (!(f2 = fopen(name2, "w"))) {
+               perror(name2); unlink(name1); return 1;
+       }
+
+       fputs("REGEDIT4\n\n", f1);
+       fputs("REGEDIT4\n\n", f2);
+
+       for (ai = (argc > a1+1 ? a1+1 : a1); ai < argc; ai++) {
+               int i;
+               ident = argv[ai];
+               fputs("[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\", f1);
+               fputs(ident, f1); fputs("]\n\"EventMessageFile\"=\"", f1);
+               for (i = 0; mypath[i]; i++) {
+                       if (mypath[i] == '\\')
+                               fputc('\\', f1);
+                       fputc(mypath[i], f1);
+               }
+               fputs("\"\n\"TypesSupported\"=dword:00000007\n\n", f1);
+
+               fputs("[-HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\", f2);
+               fputs(ident, f2); fputs("]\n\n", f2);
+       }
+
+       fclose(f1);
+       fclose(f2);
+
+       if (GetVersion() & 0x80000000) {
+               puts("Warning: Event log not supported on Win9x/ME\n");
+               if (regedit)
+                       return 1;
+       }
+
+       if (regedit) {
+               if (spawnlp(P_WAIT, "regedit", "regedit", (regedit > 0 ? name1 : name2), (const char *)0) == -1) {
+                       fputs("regedit: cannot execute\n", stderr);
+                       return 1;
+               }
+       }
+       else {
+               fputs("Files generated. Use\n\n    regedit ", stdout);
+               puts(name1);
+               fputs("\nto register event message file, and\n\n    regedit ", stdout);
+               puts(name2);
+               fputs("\nto remove registration later.\n\n"
+                         "Do not remove this program when registered.\n", stdout);
+       }
+
+       return 0;
+}
diff --git a/os_win32/syslogevt.mc b/os_win32/syslogevt.mc
new file mode 100644 (file)
index 0000000..41675a1
--- /dev/null
@@ -0,0 +1,161 @@
+;/*
+; * os_win32/syslogevt.mc
+; *
+; * Home page of code is: http://smartmontools.sourceforge.net
+; *
+; * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+; *
+; * 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, or (at your option)
+; * any later version.
+; *
+; * You should have received a copy of the GNU General Public License
+; * (for example COPYING); if not, write to the Free
+; * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+; *
+; */
+;
+;// $Id: syslogevt.mc,v 1.4 2006/04/12 14:54:29 ballen4705 Exp $
+;
+;// Use message compiler "mc" to generate
+;//   syslogevt.rc, syslogevt.h, msg00001.bin
+;// from this file.
+;// MSG_SYSLOG in syslogmsg.h must be zero
+;// MSG_SYSLOG_nn must be == nn
+;
+;
+
+MessageId=0x0
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG
+Language=English
+%1
+.
+;// 1-10 Line SYSLOG Messages
+;// %1=Ident, %2=PID, %3=Severity, %[4-13]=Line 1-10
+MessageId=0x1
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_01
+Language=English
+%1[%2]:%3: %4
+.
+MessageId=0x2
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_02
+Language=English
+%1[%2]:%3%n
+%4%n
+%5
+.
+MessageId=0x3
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_03
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6
+.
+MessageId=0x4
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_04
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7
+.
+MessageId=0x5
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_05
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8
+.
+MessageId=0x6
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_06
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8%n
+%9
+.
+MessageId=0x7
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_07
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8%n
+%9%n
+%10
+.
+MessageId=0x8
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_08
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8%n
+%9%n
+%10%n
+%11
+.
+MessageId=0x9
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_09
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8%n
+%9%n
+%10%n
+%11%n
+%12
+.
+MessageId=0xa
+Severity=Success
+Facility=Application
+SymbolicName=MSG_SYSLOG_10
+Language=English
+%1[%2]:%3%n
+%4%n
+%5%n
+%6%n
+%7%n
+%8%n
+%9%n
+%10%n
+%11%n
+%12%n
+%13
+.
diff --git a/os_win32/syslogevt_vc6.dsp b/os_win32/syslogevt_vc6.dsp
new file mode 100644 (file)
index 0000000..3f8a8af
--- /dev/null
@@ -0,0 +1,148 @@
+# Microsoft Developer Studio Project File - Name="syslogevt_vc6" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** NICHT BEARBEITEN **\r
+\r
+# TARGTYPE "Win32 (x86) Console Application" 0x0103\r
+\r
+CFG=syslogevt_vc6 - Win32 Debug\r
+!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE\r
+!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "syslogevt_vc6.mak".\r
+!MESSAGE \r
+!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben\r
+!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "syslogevt_vc6.mak" CFG="syslogevt_vc6 - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Für die Konfiguration stehen zur Auswahl:\r
+!MESSAGE \r
+!MESSAGE "syslogevt_vc6 - Win32 Release" (basierend auf  "Win32 (x86) Console Application")\r
+!MESSAGE "syslogevt_vc6 - Win32 Debug" (basierend auf  "Win32 (x86) Console Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "syslogevt_vc6 - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "syslogevt.r"\r
+# PROP Intermediate_Dir "syslogevt.r"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /GX /O1 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD BASE RSC /l 0x407 /d "NDEBUG"\r
+# ADD RSC /l 0x407 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 $(IntDir)\syslogevt.res kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 /out:"syslogevt.exe"\r
+# SUBTRACT LINK32 /pdb:none\r
+# Begin Special Build Tool\r
+IntDir=.\syslogevt.r\r
+SOURCE="$(InputPath)"\r
+PreLink_Desc=Compiling Resources\r
+PreLink_Cmds=rc $(IntDir)\syslogevt.rc\r
+# End Special Build Tool\r
+\r
+!ELSEIF  "$(CFG)" == "syslogevt_vc6 - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "syslogevt.d"\r
+# PROP Intermediate_Dir "syslogevt.d"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c\r
+# ADD BASE RSC /l 0x407 /d "_DEBUG"\r
+# ADD RSC /l 0x407 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 $(IntDir)\syslogevt.res kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# Begin Special Build Tool\r
+IntDir=.\syslogevt.d\r
+SOURCE="$(InputPath)"\r
+PreLink_Desc=Compiling Resources\r
+PreLink_Cmds=rc $(IntDir)\syslogevt.rc\r
+# End Special Build Tool\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "syslogevt_vc6 - Win32 Release"\r
+# Name "syslogevt_vc6 - Win32 Debug"\r
+# Begin Source File\r
+\r
+SOURCE=.\syslogevt.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=.\syslogevt.mc\r
+\r
+!IF  "$(CFG)" == "syslogevt_vc6 - Win32 Release"\r
+\r
+# Begin Custom Build - Compiling Messages\r
+IntDir=.\syslogevt.r\r
+InputPath=.\syslogevt.mc\r
+\r
+BuildCmds= \\r
+       mc -r $(IntDir) syslogevt.mc\r
+\r
+"$(IntDir)\syslogevt.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\r
+   $(BuildCmds)\r
+\r
+"$(IntDir)\msg00001.bin" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\r
+   $(BuildCmds)\r
+\r
+"syslogevt.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\r
+   $(BuildCmds)\r
+# End Custom Build\r
+\r
+!ELSEIF  "$(CFG)" == "syslogevt_vc6 - Win32 Debug"\r
+\r
+# Begin Custom Build - Compiling Messages\r
+IntDir=.\syslogevt.d\r
+InputPath=.\syslogevt.mc\r
+\r
+BuildCmds= \\r
+       mc -r $(IntDir) syslogevt.mc\r
+\r
+"$(IntDir)\syslogevt.rc" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\r
+   $(BuildCmds)\r
+\r
+"$(IntDir)\msg00001.bin" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\r
+   $(BuildCmds)\r
+\r
+"syslogevt.h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"\r
+   $(BuildCmds)\r
+# End Custom Build\r
+\r
+!ENDIF \r
+\r
+# End Source File\r
+# End Target\r
+# End Project\r
diff --git a/posix/getopt.c b/posix/getopt.c
new file mode 100644 (file)
index 0000000..289d137
--- /dev/null
@@ -0,0 +1,1277 @@
+/* Getopt for GNU.
+   NOTE: getopt is now part of the C library, so if you don't know what
+   "Keep this file name-space clean" means, talk to drepper@gnu.org
+   before changing it!
+   Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002
+       Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+\f
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+   Ditto for AIX 3.2 and <stdlib.h>.  */
+#ifndef _NO_PROTO
+# define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+# ifndef const
+#  define const
+# endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+# include <gnu-versions.h>
+# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#  define ELIDE_CODE
+# endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+   contain conflicting prototypes for getopt.  */
+# include <stdlib.h>
+# include <unistd.h>
+#endif /* GNU C library.  */
+
+#ifdef VMS
+# include <unixlib.h>
+# if HAVE_STRING_H - 0
+#  include <string.h>
+# endif
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.  */
+# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
+#  include <libintl.h>
+#  ifndef _
+#   define _(msgid)    gettext (msgid)
+#  endif
+# else
+#  define _(msgid)     (msgid)
+# endif
+# if defined _LIBC && defined USE_IN_LIBIO
+#  include <wchar.h>
+# endif
+#endif
+
+#ifndef attribute_hidden
+# define attribute_hidden
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+   but it behaves differently for the user, since it allows the user
+   to intersperse the options with the other arguments.
+
+   As `getopt' works, it permutes the elements of ARGV so that,
+   when it is done, all the options precede everything else.  Thus
+   all application programs are extended to handle flexible argument order.
+
+   Setting the environment variable POSIXLY_CORRECT disables permutation.
+   Then the behavior is completely standard.
+
+   GNU application programs can use a third alternative mode in which
+   they can distinguish the relative order of options and other arguments.  */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+/* 1003.2 says this must be 1 before any call.  */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+   causes problems with re-calling getopt as programs generally don't
+   know that. */
+
+int __getopt_initialized attribute_hidden;
+
+/* The next char to be scanned in the option-element
+   in which the last option character we returned was found.
+   This allows us to pick up the scan where we left off.
+
+   If this is zero, or a null string, it means resume the scan
+   by advancing to the next ARGV-element.  */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+   for unrecognized options.  */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+   This must be initialized on some systems to avoid linking in the
+   system's own getopt implementation.  */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   If the caller did not specify anything,
+   the default is REQUIRE_ORDER if the environment variable
+   POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+   REQUIRE_ORDER means don't recognize them as options;
+   stop option processing when the first non-option is seen.
+   This is what Unix does.
+   This mode of operation is selected by either setting the environment
+   variable POSIXLY_CORRECT, or using `+' as the first character
+   of the list of option characters.
+
+   PERMUTE is the default.  We permute the contents of ARGV as we scan,
+   so that eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written to
+   expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were written
+   to expect options and other ARGV-elements in any order and that care about
+   the ordering of the two.  We describe each non-option ARGV-element
+   as if it were the argument of an option with character code 1.
+   Using `-' as the first character of the list of option characters
+   selects this mode of operation.
+
+   The special argument `--' forces an end of option-scanning regardless
+   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
+   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
+
+static enum
+{
+  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable.  */
+static char *posixly_correct;
+\f
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+   because there are many ways it can cause trouble.
+   On some systems, it contains special magic macros that don't work
+   in GCC.  */
+# include <string.h>
+# define my_index      strchr
+#else
+
+# if HAVE_STRING_H
+#  include <string.h>
+# else
+#  include <strings.h>
+# endif
+
+/* Avoid depending on library functions or files
+   whose names are inconsistent.  */
+
+#ifndef getenv
+extern char *getenv ();
+#endif
+
+static char *
+my_index (str, chr)
+     const char *str;
+     int chr;
+{
+  while (*str)
+    {
+      if (*str == chr)
+       return (char *) str;
+      str++;
+    }
+  return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+   If not using GCC, it is ok not to declare it.  */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+   That was relevant to code that was here before.  */
+# if (!defined __STDC__ || !__STDC__) && !defined strlen
+/* gcc with -traditional declares the built-in strlen to return int,
+   and has done so at least since version 2.4.5. -- rms.  */
+extern int strlen (const char *);
+# endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+\f
+/* Handle permutation of arguments.  */
+
+/* Describe the part of ARGV that contains non-options that have
+   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
+   `last_nonopt' is the index after the last of them.  */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Stored original parameters.
+   XXX This is no good solution.  We should rather copy the args so
+   that we can compare them later.  But we must not use malloc(3).  */
+extern int __libc_argc;
+extern char **__libc_argv;
+
+/* Bash 2.0 gives us an environment variable containing flags
+   indicating ARGV elements that should not be considered arguments.  */
+
+# ifdef USE_NONOPTION_FLAGS
+/* Defined in getopt_init.c  */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+# endif
+
+# ifdef USE_NONOPTION_FLAGS
+#  define SWAP_FLAGS(ch1, ch2) \
+  if (nonoption_flags_len > 0)                                               \
+    {                                                                        \
+      char __tmp = __getopt_nonoption_flags[ch1];                            \
+      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];         \
+      __getopt_nonoption_flags[ch2] = __tmp;                                 \
+    }
+# else
+#  define SWAP_FLAGS(ch1, ch2)
+# endif
+#else  /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+   One subsequence is elements [first_nonopt,last_nonopt)
+   which contains all the non-options that have been skipped so far.
+   The other is elements [last_nonopt,optind), which contains all
+   the options processed since those non-options were skipped.
+
+   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   the new indices of the non-options in ARGV after they are moved.  */
+
+#if defined __STDC__ && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+     char **argv;
+{
+  int bottom = first_nonopt;
+  int middle = last_nonopt;
+  int top = optind;
+  char *tem;
+
+  /* Exchange the shorter segment with the far end of the longer segment.
+     That puts the shorter segment into the right place.
+     It leaves the longer segment in the right place overall,
+     but it consists of two parts that need to be swapped next.  */
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+  /* First make sure the handling of the `__getopt_nonoption_flags'
+     string can work normally.  Our top argument must be in the range
+     of the string.  */
+  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+    {
+      /* We must extend the array.  The user plays games with us and
+        presents new arguments.  */
+      char *new_str = malloc (top + 1);
+      if (new_str == NULL)
+       nonoption_flags_len = nonoption_flags_max_len = 0;
+      else
+       {
+         memset (__mempcpy (new_str, __getopt_nonoption_flags,
+                            nonoption_flags_max_len),
+                 '\0', top + 1 - nonoption_flags_max_len);
+         nonoption_flags_max_len = top + 1;
+         __getopt_nonoption_flags = new_str;
+       }
+    }
+#endif
+
+  while (top > middle && middle > bottom)
+    {
+      if (top - middle > middle - bottom)
+       {
+         /* Bottom segment is the short one.  */
+         int len = middle - bottom;
+         register int i;
+
+         /* Swap it with the top part of the top segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[top - (middle - bottom) + i];
+             argv[top - (middle - bottom) + i] = tem;
+             SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+           }
+         /* Exclude the moved bottom segment from further swapping.  */
+         top -= len;
+       }
+      else
+       {
+         /* Top segment is the short one.  */
+         int len = top - middle;
+         register int i;
+
+         /* Swap it with the bottom part of the bottom segment.  */
+         for (i = 0; i < len; i++)
+           {
+             tem = argv[bottom + i];
+             argv[bottom + i] = argv[middle + i];
+             argv[middle + i] = tem;
+             SWAP_FLAGS (bottom + i, middle + i);
+           }
+         /* Exclude the moved top segment from further swapping.  */
+         bottom += len;
+       }
+    }
+
+  /* Update records for the slots the non-options now occupy.  */
+
+  first_nonopt += (optind - last_nonopt);
+  last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made.  */
+
+#if defined __STDC__ && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  /* Start processing options with ARGV-element 1 (since ARGV-element 0
+     is the program name); the sequence of previously skipped
+     non-option ARGV-elements is empty.  */
+
+  first_nonopt = last_nonopt = optind;
+
+  nextchar = NULL;
+
+  posixly_correct = getenv ("POSIXLY_CORRECT");
+
+  /* Determine how to handle the ordering of options and nonoptions.  */
+
+  if (optstring[0] == '-')
+    {
+      ordering = RETURN_IN_ORDER;
+      ++optstring;
+    }
+  else if (optstring[0] == '+')
+    {
+      ordering = REQUIRE_ORDER;
+      ++optstring;
+    }
+  else if (posixly_correct != NULL)
+    ordering = REQUIRE_ORDER;
+  else
+    ordering = PERMUTE;
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+  if (posixly_correct == NULL
+      && argc == __libc_argc && argv == __libc_argv)
+    {
+      if (nonoption_flags_max_len == 0)
+       {
+         if (__getopt_nonoption_flags == NULL
+             || __getopt_nonoption_flags[0] == '\0')
+           nonoption_flags_max_len = -1;
+         else
+           {
+             const char *orig_str = __getopt_nonoption_flags;
+             int len = nonoption_flags_max_len = strlen (orig_str);
+             if (nonoption_flags_max_len < argc)
+               nonoption_flags_max_len = argc;
+             __getopt_nonoption_flags =
+               (char *) malloc (nonoption_flags_max_len);
+             if (__getopt_nonoption_flags == NULL)
+               nonoption_flags_max_len = -1;
+             else
+               memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+                       '\0', nonoption_flags_max_len - len);
+           }
+       }
+      nonoption_flags_len = nonoption_flags_max_len;
+    }
+  else
+    nonoption_flags_len = 0;
+#endif
+
+  return optstring;
+}
+\f
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+   given in OPTSTRING.
+
+   If an element of ARGV starts with '-', and is not exactly "-" or "--",
+   then it is an option element.  The characters of this element
+   (aside from the initial '-') are option characters.  If `getopt'
+   is called repeatedly, it returns successively each of the option characters
+   from each of the option elements.
+
+   If `getopt' finds another option character, it returns that character,
+   updating `optind' and `nextchar' so that the next call to `getopt' can
+   resume the scan with the following option character or ARGV-element.
+
+   If there are no more option characters, `getopt' returns -1.
+   Then `optind' is the index in ARGV of the first ARGV-element
+   that is not an option.  (The ARGV-elements have been permuted
+   so that those that are not options now come last.)
+
+   OPTSTRING is a string containing the legitimate option characters.
+   If an option character is seen that is not listed in OPTSTRING,
+   return '?' after printing an error message.  If you set `opterr' to
+   zero, the error message is suppressed but we still return '?'.
+
+   If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+   so the following text in the same ARGV-element, or the text of the following
+   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   wants an optional arg; if there is text in the current ARGV-element,
+   it is returned in `optarg', otherwise `optarg' is set to zero.
+
+   If OPTSTRING starts with `-' or `+', it requests different methods of
+   handling the non-option ARGV-elements.
+   See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+   Long-named options begin with `--' instead of `-'.
+   Their names may be abbreviated as long as the abbreviation is unique
+   or is an exact match for some defined option.  If they have an
+   argument, it follows the option name in the same ARGV-element, separated
+   from the option name by a `=', or else the in next ARGV-element.
+   When `getopt' finds a long-named option, it returns 0 if that option's
+   `flag' field is nonzero, the value of the option's `val' field
+   if the `flag' field is zero.
+
+   The elements of ARGV aren't really const, because we permute them.
+   But we pretend they're const in the prototype to be compatible
+   with other systems.
+
+   LONGOPTS is a vector of `struct option' terminated by an
+   element containing a name which is zero.
+
+   LONGIND returns the index in LONGOPT of the long-named option found.
+   It is only valid when a long-named option has been found by the most
+   recent call.
+
+   If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+   long-named options.  */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+     const struct option *longopts;
+     int *longind;
+     int long_only;
+{
+  int print_errors = opterr;
+  if (optstring[0] == ':')
+    print_errors = 0;
+
+  if (argc < 1)
+    return -1;
+
+  optarg = NULL;
+
+  if (optind == 0 || !__getopt_initialized)
+    {
+      if (optind == 0)
+       optind = 1;     /* Don't scan ARGV[0], the program name.  */
+      optstring = _getopt_initialize (argc, argv, optstring);
+      __getopt_initialized = 1;
+    }
+
+  /* Test whether ARGV[optind] points to a non-option argument.
+     Either it does not have option syntax, or there is an environment flag
+     from the shell indicating it is not an option.  The later information
+     is only used when the used in the GNU libc.  */
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'              \
+                     || (optind < nonoption_flags_len                        \
+                         && __getopt_nonoption_flags[optind] == '1'))
+#else
+# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+  if (nextchar == NULL || *nextchar == '\0')
+    {
+      /* Advance to the next ARGV-element.  */
+
+      /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+        moved back by the user (who may also have changed the arguments).  */
+      if (last_nonopt > optind)
+       last_nonopt = optind;
+      if (first_nonopt > optind)
+       first_nonopt = optind;
+
+      if (ordering == PERMUTE)
+       {
+         /* If we have just processed some options following some non-options,
+            exchange them so that the options come first.  */
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (last_nonopt != optind)
+           first_nonopt = optind;
+
+         /* Skip any additional non-options
+            and extend the range of non-options previously skipped.  */
+
+         while (optind < argc && NONOPTION_P)
+           optind++;
+         last_nonopt = optind;
+       }
+
+      /* The special ARGV-element `--' means premature end of options.
+        Skip it like a null option,
+        then exchange with previous non-options as if it were an option,
+        then skip everything else like a non-option.  */
+
+      if (optind != argc && !strcmp (argv[optind], "--"))
+       {
+         optind++;
+
+         if (first_nonopt != last_nonopt && last_nonopt != optind)
+           exchange ((char **) argv);
+         else if (first_nonopt == last_nonopt)
+           first_nonopt = optind;
+         last_nonopt = argc;
+
+         optind = argc;
+       }
+
+      /* If we have done all the ARGV-elements, stop the scan
+        and back over any non-options that we skipped and permuted.  */
+
+      if (optind == argc)
+       {
+         /* Set the next-arg-index to point at the non-options
+            that we previously skipped, so the caller will digest them.  */
+         if (first_nonopt != last_nonopt)
+           optind = first_nonopt;
+         return -1;
+       }
+
+      /* If we have come to a non-option and did not permute it,
+        either stop the scan or describe it to the caller and pass it by.  */
+
+      if (NONOPTION_P)
+       {
+         if (ordering == REQUIRE_ORDER)
+           return -1;
+         optarg = argv[optind++];
+         return 1;
+       }
+
+      /* We have found another option-ARGV-element.
+        Skip the initial punctuation.  */
+
+      nextchar = (argv[optind] + 1
+                 + (longopts != NULL && argv[optind][1] == '-'));
+    }
+
+  /* Decode the current option-ARGV-element.  */
+
+  /* Check whether the ARGV-element is a long option.
+
+     If long_only and the ARGV-element has the form "-f", where f is
+     a valid short option, don't consider it an abbreviated form of
+     a long option that starts with f.  Otherwise there would be no
+     way to give the -f short option.
+
+     On the other hand, if there's a long option "fubar" and
+     the ARGV-element is "-fu", do consider that an abbreviation of
+     the long option, just like "--fu", and not "-f" with arg "u".
+
+     This distinction seems to be the most useful approach.  */
+
+  if (longopts != NULL
+      && (argv[optind][1] == '-'
+         || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+    {
+      char *nameend;
+      const struct option *p;
+      const struct option *pfound = NULL;
+      int exact = 0;
+      int ambig = 0;
+      int indfound = -1;
+      int option_index;
+
+      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+       /* Do nothing.  */ ;
+
+      /* Test all long options for either exact match
+        or abbreviated matches.  */
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+       if (!strncmp (p->name, nextchar, nameend - nextchar))
+         {
+           if ((unsigned int) (nameend - nextchar)
+               == (unsigned int) strlen (p->name))
+             {
+               /* Exact match found.  */
+               pfound = p;
+               indfound = option_index;
+               exact = 1;
+               break;
+             }
+           else if (pfound == NULL)
+             {
+               /* First nonexact match found.  */
+               pfound = p;
+               indfound = option_index;
+             }
+           else if (long_only
+                    || pfound->has_arg != p->has_arg
+                    || pfound->flag != p->flag
+                    || pfound->val != p->val)
+             /* Second or later nonexact match found.  */
+             ambig = 1;
+         }
+
+      if (ambig && !exact)
+       {
+         if (print_errors)
+           {
+#if defined _LIBC && defined USE_IN_LIBIO
+             char *buf;
+
+             if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"),
+                             argv[0], argv[optind]) >= 0)
+               {
+
+                 if (_IO_fwide (stderr, 0) > 0)
+                   __fwprintf (stderr, L"%s", buf);
+                 else
+                   fputs (buf, stderr);
+
+                 free (buf);
+               }
+#else
+             fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+                      argv[0], argv[optind]);
+#endif
+           }
+         nextchar += strlen (nextchar);
+         optind++;
+         optopt = 0;
+         return '?';
+       }
+
+      if (pfound != NULL)
+       {
+         option_index = indfound;
+         optind++;
+         if (*nameend)
+           {
+             /* Don't test has_arg with >, because some C compilers don't
+                allow it to be used on enums.  */
+             if (pfound->has_arg)
+               optarg = nameend + 1;
+             else
+               {
+                 if (print_errors)
+                   {
+#if defined _LIBC && defined USE_IN_LIBIO
+                     char *buf;
+                     int n;
+#endif
+
+                     if (argv[optind - 1][1] == '-')
+                       {
+                         /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+                         n = __asprintf (&buf, _("\
+%s: option `--%s' doesn't allow an argument\n"),
+                                         argv[0], pfound->name);
+#else
+                         fprintf (stderr, _("\
+%s: option `--%s' doesn't allow an argument\n"),
+                                  argv[0], pfound->name);
+#endif
+                       }
+                     else
+                       {
+                         /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+                         n = __asprintf (&buf, _("\
+%s: option `%c%s' doesn't allow an argument\n"),
+                                         argv[0], argv[optind - 1][0],
+                                         pfound->name);
+#else
+                         fprintf (stderr, _("\
+%s: option `%c%s' doesn't allow an argument\n"),
+                                  argv[0], argv[optind - 1][0], pfound->name);
+#endif
+                       }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+                     if (n >= 0)
+                       {
+                         if (_IO_fwide (stderr, 0) > 0)
+                           __fwprintf (stderr, L"%s", buf);
+                         else
+                           fputs (buf, stderr);
+
+                         free (buf);
+                       }
+#endif
+                   }
+
+                 nextchar += strlen (nextchar);
+
+                 optopt = pfound->val;
+                 return '?';
+               }
+           }
+         else if (pfound->has_arg == 1)
+           {
+             if (optind < argc)
+               optarg = argv[optind++];
+             else
+               {
+                 if (print_errors)
+                   {
+#if defined _LIBC && defined USE_IN_LIBIO
+                     char *buf;
+
+                     if (__asprintf (&buf, _("\
+%s: option `%s' requires an argument\n"),
+                                     argv[0], argv[optind - 1]) >= 0)
+                       {
+                         if (_IO_fwide (stderr, 0) > 0)
+                           __fwprintf (stderr, L"%s", buf);
+                         else
+                           fputs (buf, stderr);
+
+                         free (buf);
+                       }
+#else
+                     fprintf (stderr,
+                              _("%s: option `%s' requires an argument\n"),
+                              argv[0], argv[optind - 1]);
+#endif
+                   }
+                 nextchar += strlen (nextchar);
+                 optopt = pfound->val;
+                 return optstring[0] == ':' ? ':' : '?';
+               }
+           }
+         nextchar += strlen (nextchar);
+         if (longind != NULL)
+           *longind = option_index;
+         if (pfound->flag)
+           {
+             *(pfound->flag) = pfound->val;
+             return 0;
+           }
+         return pfound->val;
+       }
+
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+        or the option starts with '--' or is not a valid short
+        option, then it's an error.
+        Otherwise interpret it as a short option.  */
+      if (!long_only || argv[optind][1] == '-'
+         || my_index (optstring, *nextchar) == NULL)
+       {
+         if (print_errors)
+           {
+#if defined _LIBC && defined USE_IN_LIBIO
+             char *buf;
+             int n;
+#endif
+
+             if (argv[optind][1] == '-')
+               {
+                 /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+                 n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"),
+                                 argv[0], nextchar);
+#else
+                 fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+                          argv[0], nextchar);
+#endif
+               }
+             else
+               {
+                 /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+                 n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"),
+                                 argv[0], argv[optind][0], nextchar);
+#else
+                 fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+                          argv[0], argv[optind][0], nextchar);
+#endif
+               }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+             if (n >= 0)
+               {
+                 if (_IO_fwide (stderr, 0) > 0)
+                   __fwprintf (stderr, L"%s", buf);
+                 else
+                   fputs (buf, stderr);
+
+                 free (buf);
+               }
+#endif
+           }
+         nextchar = (char *) "";
+         optind++;
+         optopt = 0;
+         return '?';
+       }
+    }
+
+  /* Look at and handle the next short option-character.  */
+
+  {
+    char c = *nextchar++;
+    char *temp = my_index (optstring, c);
+
+    /* Increment `optind' when we start to process its last character.  */
+    if (*nextchar == '\0')
+      ++optind;
+
+    if (temp == NULL || c == ':')
+      {
+       if (print_errors)
+         {
+#if defined _LIBC && defined USE_IN_LIBIO
+             char *buf;
+             int n;
+#endif
+
+           if (posixly_correct)
+             {
+               /* 1003.2 specifies the format of this message.  */
+#if defined _LIBC && defined USE_IN_LIBIO
+               n = __asprintf (&buf, _("%s: illegal option -- %c\n"),
+                               argv[0], c);
+#else
+               fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
+#endif
+             }
+           else
+             {
+#if defined _LIBC && defined USE_IN_LIBIO
+               n = __asprintf (&buf, _("%s: invalid option -- %c\n"),
+                               argv[0], c);
+#else
+               fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
+#endif
+             }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+           if (n >= 0)
+             {
+               if (_IO_fwide (stderr, 0) > 0)
+                 __fwprintf (stderr, L"%s", buf);
+               else
+                 fputs (buf, stderr);
+
+               free (buf);
+             }
+#endif
+         }
+       optopt = c;
+       return '?';
+      }
+    /* Convenience. Treat POSIX -W foo same as long option --foo */
+    if (temp[0] == 'W' && temp[1] == ';')
+      {
+       char *nameend;
+       const struct option *p;
+       const struct option *pfound = NULL;
+       int exact = 0;
+       int ambig = 0;
+       int indfound = 0;
+       int option_index;
+
+       /* This is an option that requires an argument.  */
+       if (*nextchar != '\0')
+         {
+           optarg = nextchar;
+           /* If we end this ARGV-element by taking the rest as an arg,
+              we must advance to the next element now.  */
+           optind++;
+         }
+       else if (optind == argc)
+         {
+           if (print_errors)
+             {
+               /* 1003.2 specifies the format of this message.  */
+#if defined _LIBC && defined USE_IN_LIBIO
+               char *buf;
+
+               if (__asprintf (&buf,
+                               _("%s: option requires an argument -- %c\n"),
+                               argv[0], c) >= 0)
+                 {
+                   if (_IO_fwide (stderr, 0) > 0)
+                     __fwprintf (stderr, L"%s", buf);
+                   else
+                     fputs (buf, stderr);
+
+                   free (buf);
+                 }
+#else
+               fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+                        argv[0], c);
+#endif
+             }
+           optopt = c;
+           if (optstring[0] == ':')
+             c = ':';
+           else
+             c = '?';
+           return c;
+         }
+       else
+         /* We already incremented `optind' once;
+            increment it again when taking next ARGV-elt as argument.  */
+         optarg = argv[optind++];
+
+       /* optarg is now the argument, see if it's in the
+          table of longopts.  */
+
+       for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+         /* Do nothing.  */ ;
+
+       /* Test all long options for either exact match
+          or abbreviated matches.  */
+       for (p = longopts, option_index = 0; p->name; p++, option_index++)
+         if (!strncmp (p->name, nextchar, nameend - nextchar))
+           {
+             if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+               {
+                 /* Exact match found.  */
+                 pfound = p;
+                 indfound = option_index;
+                 exact = 1;
+                 break;
+               }
+             else if (pfound == NULL)
+               {
+                 /* First nonexact match found.  */
+                 pfound = p;
+                 indfound = option_index;
+               }
+             else
+               /* Second or later nonexact match found.  */
+               ambig = 1;
+           }
+       if (ambig && !exact)
+         {
+           if (print_errors)
+             {
+#if defined _LIBC && defined USE_IN_LIBIO
+               char *buf;
+
+               if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"),
+                               argv[0], argv[optind]) >= 0)
+                 {
+                   if (_IO_fwide (stderr, 0) > 0)
+                     __fwprintf (stderr, L"%s", buf);
+                   else
+                     fputs (buf, stderr);
+
+                   free (buf);
+                 }
+#else
+               fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+                        argv[0], argv[optind]);
+#endif
+             }
+           nextchar += strlen (nextchar);
+           optind++;
+           return '?';
+         }
+       if (pfound != NULL)
+         {
+           option_index = indfound;
+           if (*nameend)
+             {
+               /* Don't test has_arg with >, because some C compilers don't
+                  allow it to be used on enums.  */
+               if (pfound->has_arg)
+                 optarg = nameend + 1;
+               else
+                 {
+                   if (print_errors)
+                     {
+#if defined _LIBC && defined USE_IN_LIBIO
+                       char *buf;
+
+                       if (__asprintf (&buf, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+                                       argv[0], pfound->name) >= 0)
+                         {
+                           if (_IO_fwide (stderr, 0) > 0)
+                             __fwprintf (stderr, L"%s", buf);
+                           else
+                             fputs (buf, stderr);
+
+                           free (buf);
+                         }
+#else
+                       fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+                                argv[0], pfound->name);
+#endif
+                     }
+
+                   nextchar += strlen (nextchar);
+                   return '?';
+                 }
+             }
+           else if (pfound->has_arg == 1)
+             {
+               if (optind < argc)
+                 optarg = argv[optind++];
+               else
+                 {
+                   if (print_errors)
+                     {
+#if defined _LIBC && defined USE_IN_LIBIO
+                       char *buf;
+
+                       if (__asprintf (&buf, _("\
+%s: option `%s' requires an argument\n"),
+                                       argv[0], argv[optind - 1]) >= 0)
+                         {
+                           if (_IO_fwide (stderr, 0) > 0)
+                             __fwprintf (stderr, L"%s", buf);
+                           else
+                             fputs (buf, stderr);
+
+                           free (buf);
+                         }
+#else
+                       fprintf (stderr,
+                                _("%s: option `%s' requires an argument\n"),
+                                argv[0], argv[optind - 1]);
+#endif
+                     }
+                   nextchar += strlen (nextchar);
+                   return optstring[0] == ':' ? ':' : '?';
+                 }
+             }
+           nextchar += strlen (nextchar);
+           if (longind != NULL)
+             *longind = option_index;
+           if (pfound->flag)
+             {
+               *(pfound->flag) = pfound->val;
+               return 0;
+             }
+           return pfound->val;
+         }
+         nextchar = NULL;
+         return 'W';   /* Let the application handle it.   */
+      }
+    if (temp[1] == ':')
+      {
+       if (temp[2] == ':')
+         {
+           /* This is an option that accepts an argument optionally.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               optind++;
+             }
+           else
+             optarg = NULL;
+           nextchar = NULL;
+         }
+       else
+         {
+           /* This is an option that requires an argument.  */
+           if (*nextchar != '\0')
+             {
+               optarg = nextchar;
+               /* If we end this ARGV-element by taking the rest as an arg,
+                  we must advance to the next element now.  */
+               optind++;
+             }
+           else if (optind == argc)
+             {
+               if (print_errors)
+                 {
+                   /* 1003.2 specifies the format of this message.  */
+#if defined _LIBC && defined USE_IN_LIBIO
+                   char *buf;
+
+                   if (__asprintf (&buf, _("\
+%s: option requires an argument -- %c\n"),
+                                   argv[0], c) >= 0)
+                     {
+                       if (_IO_fwide (stderr, 0) > 0)
+                         __fwprintf (stderr, L"%s", buf);
+                       else
+                         fputs (buf, stderr);
+
+                       free (buf);
+                     }
+#else
+                   fprintf (stderr,
+                            _("%s: option requires an argument -- %c\n"),
+                            argv[0], c);
+#endif
+                 }
+               optopt = c;
+               if (optstring[0] == ':')
+                 c = ':';
+               else
+                 c = '?';
+             }
+           else
+             /* We already incremented `optind' once;
+                increment it again when taking next ARGV-elt as argument.  */
+             optarg = argv[optind++];
+           nextchar = NULL;
+         }
+      }
+    return c;
+  }
+}
+
+int
+getopt (argc, argv, optstring)
+     int argc;
+     char *const *argv;
+     const char *optstring;
+{
+  return _getopt_internal (argc, argv, optstring,
+                          (const struct option *) 0,
+                          (int *) 0,
+                          0);
+}
+
+#endif /* Not ELIDE_CODE.  */
+\f
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+   the above definition of `getopt'.  */
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+
+      c = getopt (argc, argv, "abc:d:0123456789");
+      if (c == -1)
+       break;
+
+      switch (c)
+       {
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
diff --git a/posix/getopt.h b/posix/getopt.h
new file mode 100644 (file)
index 0000000..4283c35
--- /dev/null
@@ -0,0 +1,181 @@
+/* Declarations for getopt.
+   Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _GETOPT_H
+
+#ifndef __need_getopt
+# define _GETOPT_H 1
+#endif
+
+/* If __GNU_LIBRARY__ is not already defined, either we are being used
+   standalone, or this is the first header included in the source file.
+   If we are being used with glibc, we need to include <features.h>, but
+   that does not exist if we are standalone.  So: if __GNU_LIBRARY__ is
+   not defined, include <ctype.h>, which will pull in <features.h> for us
+   if it's from glibc.  (Why ctype.h?  It's guaranteed to exist and it
+   doesn't flood the namespace with stuff the way some other headers do.)  */
+#if !defined __GNU_LIBRARY__
+# include <ctype.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+   When `getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when `ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to `getopt'.
+
+   On entry to `getopt', zero means this is the first call; initialize.
+
+   When `getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, `optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+#ifndef __need_getopt
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of `struct option' terminated by an element containing a name which is
+   zero.
+
+   The field `has_arg' is:
+   no_argument         (or 0) if the option does not take an argument,
+   required_argument   (or 1) if the option requires an argument,
+   optional_argument   (or 2) if the option takes an optional argument.
+
+   If the field `flag' is not NULL, it points to a variable that is set
+   to the value given in the field `val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an `int' to
+   a compiled-in constant, such as set a value from `optarg', set the
+   option's `flag' field to zero and its `val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero `flag' field, `getopt'
+   returns the contents of the `val' field.  */
+
+struct option
+{
+# if (defined __STDC__ && __STDC__) || defined __cplusplus
+  const char *name;
+# else
+  char *name;
+# endif
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'.  */
+
+# define no_argument           0
+# define required_argument     1
+# define optional_argument     2
+#endif /* need getopt */
+
+
+/* Get definitions and prototypes for functions to process the
+   arguments in ARGV (ARGC of them, minus the program name) for
+   options given in OPTS.
+
+   Return the option character from OPTS just read.  Return -1 when
+   there are no more options.  For unrecognized options, or options
+   missing arguments, `optopt' is set to the option letter, and '?' is
+   returned.
+
+   The OPTS string is a list of characters which are recognized option
+   letters, optionally followed by colons, specifying that that letter
+   takes an argument, to be placed in `optarg'.
+
+   If a letter in OPTS is followed by two colons, its argument is
+   optional.  This behavior is specific to the GNU `getopt'.
+
+   The argument `--' causes premature termination of argument
+   scanning, explicitly telling `getopt' that there are no more
+   options.
+
+   If OPTS begins with `--', then non-option arguments are treated as
+   arguments to the option '\0'.  This behavior is specific to the GNU
+   `getopt'.  */
+
+#if (defined __STDC__ && __STDC__) || defined __cplusplus
+# ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+   differences in the consts, in stdlib.h.  To avoid compilation
+   errors, only prototype getopt for the GNU C library.  */
+extern int getopt (int ___argc, char *const *___argv, const char *__shortopts);
+# else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+# endif /* __GNU_LIBRARY__ */
+
+# ifndef __need_getopt
+extern int getopt_long (int ___argc, char *const *___argv,
+                       const char *__shortopts,
+                       const struct option *__longopts, int *__longind);
+extern int getopt_long_only (int ___argc, char *const *___argv,
+                            const char *__shortopts,
+                            const struct option *__longopts, int *__longind);
+
+/* Internal only.  Users should not call this directly.  */
+extern int _getopt_internal (int ___argc, char *const *___argv,
+                            const char *__shortopts,
+                            const struct option *__longopts, int *__longind,
+                            int __long_only);
+# endif
+#else /* not __STDC__ */
+extern int getopt ();
+# ifndef __need_getopt
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+# endif
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Make sure we later can get all the definitions and declarations.  */
+#undef __need_getopt
+
+#endif /* getopt.h */
diff --git a/posix/getopt1.c b/posix/getopt1.c
new file mode 100644 (file)
index 0000000..ad06cc7
--- /dev/null
@@ -0,0 +1,196 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+   Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
+     Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+\f
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef _LIBC
+# include <getopt.h>
+#else
+# include "getopt.h"
+#endif
+
+#if !defined __STDC__ || !__STDC__
+/* This is a separate conditional since some stdc systems
+   reject `defined (const)'.  */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+   actually compiling the library itself.  This code is part of the GNU C
+   Library, but also included in many other GNU distributions.  Compiling
+   and linking in this code is a waste when using the GNU C library
+   (especially if it is a shared library).  Rather than having every GNU
+   program understand `configure --with-gnu-libc' and omit the object files,
+   it is simpler to just do this in the source for each such file.  */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+   to get __GNU_LIBRARY__ defined.  */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef        NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+   If an option that starts with '-' (not '--') doesn't match a long option,
+   but does match a short option, it is parsed as a short option
+   instead.  */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+     int argc;
+     char *const *argv;
+     const char *options;
+     const struct option *long_options;
+     int *opt_index;
+{
+  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+# ifdef _LIBC
+libc_hidden_def (getopt_long)
+libc_hidden_def (getopt_long_only)
+# endif
+
+#endif /* Not ELIDE_CODE.  */
+\f
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int c;
+  int digit_optind = 0;
+
+  while (1)
+    {
+      int this_option_optind = optind ? optind : 1;
+      int option_index = 0;
+      static struct option long_options[] =
+      {
+       {"add", 1, 0, 0},
+       {"append", 0, 0, 0},
+       {"delete", 1, 0, 0},
+       {"verbose", 0, 0, 0},
+       {"create", 0, 0, 0},
+       {"file", 1, 0, 0},
+       {0, 0, 0, 0}
+      };
+
+      c = getopt_long (argc, argv, "abc:d:0123456789",
+                      long_options, &option_index);
+      if (c == -1)
+       break;
+
+      switch (c)
+       {
+       case 0:
+         printf ("option %s", long_options[option_index].name);
+         if (optarg)
+           printf (" with arg %s", optarg);
+         printf ("\n");
+         break;
+
+       case '0':
+       case '1':
+       case '2':
+       case '3':
+       case '4':
+       case '5':
+       case '6':
+       case '7':
+       case '8':
+       case '9':
+         if (digit_optind != 0 && digit_optind != this_option_optind)
+           printf ("digits occur in two different argv-elements.\n");
+         digit_optind = this_option_optind;
+         printf ("option %c\n", c);
+         break;
+
+       case 'a':
+         printf ("option a\n");
+         break;
+
+       case 'b':
+         printf ("option b\n");
+         break;
+
+       case 'c':
+         printf ("option c with value `%s'\n", optarg);
+         break;
+
+       case 'd':
+         printf ("option d with value `%s'\n", optarg);
+         break;
+
+       case '?':
+         break;
+
+       default:
+         printf ("?? getopt returned character code 0%o ??\n", c);
+       }
+    }
+
+  if (optind < argc)
+    {
+      printf ("non-option ARGV-elements: ");
+      while (optind < argc)
+       printf ("%s ", argv[optind++]);
+      printf ("\n");
+    }
+
+  exit (0);
+}
+
+#endif /* TEST */
index 314292b69645111a0a46e89a5ccf1a830ccd9b64..9575857216509503a7a9730b7fa5315336f1a35c 100644 (file)
@@ -439,7 +439,7 @@ typedef struct
    unfortunately clutters up the declarations a bit, but I think it's
    worth it.  */
 
-#if __STDC__
+#if defined(__STDC__) || defined(__cplusplus)
 
 # define _RE_ARGS(args) args
 
@@ -536,7 +536,7 @@ extern int re_exec _RE_ARGS ((const char *));
 #endif
 /* gcc 3.1 and up support the [restrict] syntax.  */
 #ifndef __restrict_arr
-# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) && !defined(__WIN32__)
 #  define __restrict_arr __restrict
 # else
 #  define __restrict_arr
diff --git a/scsiata.cpp b/scsiata.cpp
new file mode 100644 (file)
index 0000000..3a838bf
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ * scsiata.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2006 Douglas Gilbert <dougg@torque.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * The code in this file is based on the SCSI to ATA Translation (SAT)
+ * draft found at http://www.t10.org . The original draft used for this
+ * code is sat-r08.pdf which is not too far away from becoming a
+ * standard. The SAT commands of interest to smartmontools are the
+ * ATA PASS THROUGH SCSI (16) and ATA PASS THROUGH SCSI (12) defined in
+ * section 12 of that document.
+ *
+ * With more transports "hiding" SATA disks (and other S-ATAPI devices)
+ * behind a SCSI command set, accessing special features like SMART
+ * information becomes a challenge. The SAT standard offers ATA PASS
+ * THROUGH commands for special usages. Note that the SAT layer may
+ * be inside a generic OS layer (e.g. libata in linux), in a host
+ * adapter (HA or HBA) firmware, or somewhere on the interconnect
+ * between the host computer and the SATA devices (e.g. a RAID made
+ * of SATA disks and the RAID talks "SCSI" to the host computer).
+ * Note that in the latter case, this code does not solve the
+ * addressing issue (i.e. which SATA disk to address behind the logical
+ * SCSI (RAID) interface).
+ * 
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+#include "int64.h"
+#include "extern.h"
+#include "scsicmds.h"
+#include "scsiata.h"
+#include "utility.h"
+
+const char *scsiata_c_cvsid="$Id: scsiata.cpp,v 1.7 2006/08/09 20:40:19 chrfranke Exp $"
+CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIATA_H_CVSID UTILITY_H_CVSID;
+
+/* for passing global control variables */
+extern smartmonctrl *con;
+
+#define DEF_SAT_ATA_PASSTHRU_SIZE 16
+
+
+// cdb[0]: ATA PASS THROUGH (16) SCSI command opcode byte (0x85)
+// cdb[1]: multiple_count, protocol + extend
+// cdb[2]: offline, ck_cond, t_dir, byte_block + t_length
+// cdb[3]: features (15:8)
+// cdb[4]: features (7:0)
+// cdb[5]: sector_count (15:8)
+// cdb[6]: sector_count (7:0)
+// cdb[7]: lba_low (15:8)
+// cdb[8]: lba_low (7:0)
+// cdb[9]: lba_mid (15:8)
+// cdb[10]: lba_mid (7:0)
+// cdb[11]: lba_high (15:8)
+// cdb[12]: lba_high (7:0)
+// cdb[13]: device
+// cdb[14]: (ata) command
+// cdb[15]: control (SCSI, leave as zero)
+//
+// 24 bit lba (from MSB): cdb[12] cdb[10] cdb[8]
+// 48 bit lba (from MSB): cdb[11] cdb[9] cdb[7] cdb[12] cdb[10] cdb[8]
+//
+//
+// cdb[0]: ATA PASS THROUGH (12) SCSI command opcode byte (0xa1)
+// cdb[1]: multiple_count, protocol + extend
+// cdb[2]: offline, ck_cond, t_dir, byte_block + t_length
+// cdb[3]: features (7:0)
+// cdb[4]: sector_count (7:0)
+// cdb[5]: lba_low (7:0)
+// cdb[6]: lba_mid (7:0)
+// cdb[7]: lba_high (7:0)
+// cdb[8]: device
+// cdb[9]: (ata) command
+// cdb[10]: reserved
+// cdb[11]: control (SCSI, leave as zero)
+//
+//
+// ATA status return descriptor (component of descriptor sense data)
+// des[0]: descriptor code (0x9)
+// des[1]: additional descriptor length (0xc)
+// des[2]: extend (bit 0)
+// des[3]: error
+// des[4]: sector_count (15:8)
+// des[5]: sector_count (7:0)
+// des[6]: lba_low (15:8)
+// des[7]: lba_low (7:0)
+// des[8]: lba_mid (15:8)
+// des[9]: lba_mid (7:0)
+// des[10]: lba_high (15:8)
+// des[11]: lba_high (7:0)
+// des[12]: device
+// des[13]: status
+
+
+
+// PURPOSE
+//   This interface routine takes ATA SMART commands and packages
+//   them in the SAT-defined ATA PASS THROUGH SCSI commands. There are
+//   two available SCSI commands: a 12 byte and 16 byte variant; the
+//   one used is chosen via con->satpassthrulen .
+// DETAILED DESCRIPTION OF ARGUMENTS
+//   device: is the file descriptor provided by (a SCSI dvice type) open()
+//   command: defines the different ATA operations.
+//   select: additional input data if needed (which log, which type of
+//           self-test).
+//   data:   location to write output data, if needed (512 bytes).
+//     Note: not all commands use all arguments.
+// RETURN VALUES
+//  -1 if the command failed
+//   0 if the command succeeded,
+//   STATUS_CHECK routine: 
+//  -1 if the command failed
+//   0 if the command succeeded and disk SMART status is "OK"
+//   1 if the command succeeded and disk SMART status is "FAILING"
+
+int sat_command_interface(int device, smart_command_set command, int select,
+                          char *data)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    struct sg_scsi_sense_hdr ssh;
+    unsigned char cdb[SAT_ATA_PASSTHROUGH_16LEN];
+    unsigned char sense[32];
+    const unsigned char * ucp;
+    int status, len;
+    int copydata = 0;
+    int outlen = 0;
+    int extend = 0;
+    int chk_cond = 0;   /* set to 1 to read register(s) back */
+    int protocol = 3;   /* non-data */
+    int t_dir = 1;      /* 0 -> to device, 1 -> from device */
+    int byte_block = 1; /* 0 -> bytes, 1 -> 512 byte blocks */
+    int t_length = 0;   /* 0 -> no data transferred */
+    int feature = 0;
+    int ata_command = 0;
+    int sector_count = 0;
+    int lba_low = 0;
+    int lba_mid = 0;
+    int lba_high = 0;
+    int passthru_size = DEF_SAT_ATA_PASSTHRU_SIZE;
+
+    memset(cdb, 0, sizeof(cdb));
+    memset(sense, 0, sizeof(sense));
+
+    ata_command = ATA_SMART_CMD;
+    switch (command) {
+    case CHECK_POWER_MODE:
+        ata_command = ATA_CHECK_POWER_MODE;
+        chk_cond = 1;
+        copydata = 1;
+        break;
+    case READ_VALUES:           /* READ DATA */
+        feature = ATA_SMART_READ_VALUES;
+        sector_count = 1;     /* one (512 byte) block */
+        protocol = 4;   /* PIO data-in */
+        t_length = 2;   /* sector count holds count */
+        copydata = 512;
+        break;
+    case READ_THRESHOLDS:       /* obsolete */
+        feature = ATA_SMART_READ_THRESHOLDS;
+        sector_count = 1;     /* one (512 byte) block */
+        lba_low = 1;
+        protocol = 4;   /* PIO data-in */
+        t_length = 2;   /* sector count holds count */
+        copydata=512;
+        break;
+    case READ_LOG:
+        feature = ATA_SMART_READ_LOG_SECTOR;
+        sector_count = 1;     /* one (512 byte) block */
+        lba_low = select;
+        protocol = 4;   /* PIO data-in */
+        t_length = 2;   /* sector count holds count */
+        copydata = 512;
+        break;
+    case WRITE_LOG:
+        feature = ATA_SMART_WRITE_LOG_SECTOR;
+        sector_count = 1;     /* one (512 byte) block */
+        lba_low = select;
+        protocol = 5;   /* PIO data-out */
+        t_length = 2;   /* sector count holds count */
+        t_dir = 0;      /* to device */
+        outlen = 512;
+        break;
+    case IDENTIFY:
+        ata_command = ATA_IDENTIFY_DEVICE;
+        sector_count = 1;     /* one (512 byte) block */
+        protocol = 4;   /* PIO data-in */
+        t_length = 2;   /* sector count holds count */
+        copydata = 512;
+        break;
+    case PIDENTIFY:
+        ata_command = ATA_IDENTIFY_PACKET_DEVICE;
+        sector_count = 1;     /* one (512 byte) block */
+        protocol = 4;   /* PIO data-in */
+        t_length = 2;   /* sector count (7:0) holds count */
+        copydata = 512;
+        break;
+    case ENABLE:
+        feature = ATA_SMART_ENABLE;
+        lba_low = 1;
+        break;
+    case DISABLE:
+        feature = ATA_SMART_DISABLE;
+        lba_low = 1;
+        break;
+    case STATUS:
+        // this command only says if SMART is working.  It could be
+        // replaced with STATUS_CHECK below.
+        feature = ATA_SMART_STATUS;
+        chk_cond = 1;
+        break;
+    case AUTO_OFFLINE:
+        feature = ATA_SMART_AUTO_OFFLINE;
+        sector_count = select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+        break;
+    case AUTOSAVE:
+        feature = ATA_SMART_AUTOSAVE;
+        sector_count = select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+        break;
+    case IMMEDIATE_OFFLINE:
+        feature = ATA_SMART_IMMEDIATE_OFFLINE;
+        lba_low = select;
+        break;
+    case STATUS_CHECK:
+        // This command uses HDIO_DRIVE_TASK and has different syntax than
+        // the other commands.
+        feature = ATA_SMART_STATUS;      /* SMART RETURN STATUS */
+        chk_cond = 1;
+        break;
+    default:
+        pout("Unrecognized command %d in sat_command_interface()\n"
+             "Please contact " PACKAGE_BUGREPORT "\n", command);
+        errno=ENOSYS;
+        return -1;
+    }
+    if (ATA_SMART_CMD == ata_command) {
+        lba_mid = 0x4f;
+        lba_high = 0xc2;
+    }
+
+    if ((SAT_ATA_PASSTHROUGH_12LEN == con->satpassthrulen) ||
+        (SAT_ATA_PASSTHROUGH_16LEN == con->satpassthrulen))
+        passthru_size = con->satpassthrulen;
+    cdb[0] = (SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ?
+             SAT_ATA_PASSTHROUGH_12 : SAT_ATA_PASSTHROUGH_16;
+
+    cdb[1] = (protocol << 1) | extend;
+    cdb[2] = (chk_cond << 5) | (t_dir << 3) |
+             (byte_block << 2) | t_length;
+    cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 3 : 4] = feature;
+    cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 4 : 6] = sector_count;
+    cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 5 : 8] = lba_low;
+    cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 6 : 10] = lba_mid;
+    cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 7 : 12] = lba_high;
+    cdb[(SAT_ATA_PASSTHROUGH_12LEN == passthru_size) ? 9 : 14] = ata_command;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    if (0 == t_length) {
+        io_hdr.dxfer_dir = DXFER_NONE;
+        io_hdr.dxfer_len = 0;
+    } else if (t_dir) {         /* from device */
+        io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+        io_hdr.dxfer_len = copydata;
+        io_hdr.dxferp = (unsigned char *)data;
+        memset(data, 0, copydata); /* prefill with zeroes */
+    } else {                    /* to device */
+        io_hdr.dxfer_dir = DXFER_TO_DEVICE;
+        io_hdr.dxfer_len = outlen;
+        io_hdr.dxferp = (unsigned char *)data;
+    }
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = passthru_size;
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status) {
+        if (con->reportscsiioctl > 0)
+            pout("sat_command_interface: do_scsi_cmnd_io() failed, "
+                 "status=%d\n", status);
+        return -1;
+    }
+    if (chk_cond) {     /* expecting SAT specific sense data */
+        ucp = NULL;
+        if (sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len,
+                                    &ssh)) {
+            /* look for SAT extended ATA status return descriptor (9) */
+            ucp = sg_scsi_sense_desc_find(io_hdr.sensep,
+                                          io_hdr.resp_sense_len, 9);
+            if (ucp) {
+                len = ucp[1] + 2;
+                if (len < 12)
+                    len = 12;
+                else if (len > 14)
+                    len = 14;
+                if (con->reportscsiioctl > 1) {
+                    pout("Values from ATA status return descriptor are:\n");
+                    dStrHex((const char *)ucp, len, 1);
+                }
+                if (ATA_CHECK_POWER_MODE == cdb[14])
+                    data[0] = ucp[5];      /* sector count (0:7) */
+                else if (STATUS_CHECK == command) {
+                    if ((ucp[9] == 0x4f) && (ucp[11] == 0xc2))
+                        return 0;    /* GOOD smart status */
+                    if ((ucp[9] == 0xf4) && (ucp[11] == 0x2c))
+                        return 1;    // smart predicting failure, "bad" status
+                    // We haven't gotten output that makes sense so
+                    // print out some debugging info
+                    syserror("Error SMART Status command failed");
+                    pout("Please get assistance from " PACKAGE_HOMEPAGE "\n");
+                    pout("Values from ATA status return descriptor are:\n");
+                    dStrHex((const char *)ucp, len, 1);
+                    return -1;
+                }
+            }
+        }
+        if (ucp == NULL) {
+            chk_cond = 0;       /* not the type of sense data expected */
+            if (t_dir && (t_length > 0))
+                data[0] = 0;
+        }
+    }
+    if (0 == chk_cond) {
+        ucp = NULL;
+        if (sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len,
+                                    &ssh)) {
+            if ((ssh.response_code >= 0x72) &&
+                ((SCSI_SK_NO_SENSE == ssh.sense_key) ||
+                 (SCSI_SK_RECOVERED_ERR == ssh.sense_key)) &&
+                (0 == ssh.asc) &&
+                (SCSI_ASCQ_ATA_PASS_THROUGH == ssh.ascq)) {
+                /* look for SAT extended ATA status return descriptor (9) */
+                ucp = sg_scsi_sense_desc_find(io_hdr.sensep,
+                                              io_hdr.resp_sense_len, 9);
+                if (ucp) {
+                    if (con->reportscsiioctl > 0) {
+                        pout("Values from ATA status return descriptor are:\n");
+                        len = ucp[1] + 2;
+                        if (len < 12)
+                            len = 12;
+                        else if (len > 14)
+                            len = 14;
+                        dStrHex((const char *)ucp, len, 1);
+                    }
+                    return -1;
+                }
+            }
+            scsi_do_sense_disect(&io_hdr, &sinfo);
+            status = scsiSimpleSenseFilter(&sinfo);
+            if (0 != status) {
+                if (con->reportscsiioctl > 0)
+                    pout("sat_command_interface: scsi error: %s\n",
+                         scsiErrString(status));
+                return -1;
+            }
+        }
+    }
+    return 0;
+}
+
+/* Attempt an IDENTIFY DEVICE ATA command via SATL when packet_interface
+   is 0 otherwise attempt IDENTIFY PACKET DEVICE. If successful
+   return 1, else 0 */
+int has_sat_pass_through(int device, int packet_interface)
+{
+    char data[512];
+    smart_command_set command;
+
+    command = packet_interface ? PIDENTIFY : IDENTIFY;
+    if (0 == sat_command_interface(device, command, 0, data))
+        return 1;
+    else
+        return 0;
+}
+
+/* Next two functions are borrowed from sg_lib.c in the sg3_utils
+   package. Same copyrght owner, same license as this file. */
+int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len,
+                            struct sg_scsi_sense_hdr * sshp)
+{
+    if (sshp)
+        memset(sshp, 0, sizeof(struct sg_scsi_sense_hdr));
+    if ((NULL == sensep) || (0 == sb_len) || (0x70 != (0x70 & sensep[0])))
+        return 0;
+    if (sshp) {
+        sshp->response_code = (0x7f & sensep[0]);
+        if (sshp->response_code >= 0x72) {  /* descriptor format */
+            if (sb_len > 1)
+                sshp->sense_key = (0xf & sensep[1]);
+            if (sb_len > 2)
+                sshp->asc = sensep[2];
+            if (sb_len > 3)
+                sshp->ascq = sensep[3];
+            if (sb_len > 7)
+                sshp->additional_length = sensep[7];
+        } else {                              /* fixed format */
+            if (sb_len > 2)
+                sshp->sense_key = (0xf & sensep[2]);
+            if (sb_len > 7) {
+                sb_len = (sb_len < (sensep[7] + 8)) ? sb_len :
+                                                      (sensep[7] + 8);
+                if (sb_len > 12)
+                    sshp->asc = sensep[12];
+                if (sb_len > 13)
+                    sshp->ascq = sensep[13];
+            }
+        }
+    }
+    return 1;
+}
+
+
+const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
+                                              int sense_len, int desc_type)
+{
+    int add_sen_len, add_len, desc_len, k;
+    const unsigned char * descp;
+
+    if ((sense_len < 8) || (0 == (add_sen_len = sensep[7])))
+        return NULL;
+    if ((sensep[0] < 0x72) || (sensep[0] > 0x73))
+        return NULL;
+    add_sen_len = (add_sen_len < (sense_len - 8)) ?
+                         add_sen_len : (sense_len - 8);
+    descp = &sensep[8];
+    for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
+        descp += desc_len;
+        add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
+        desc_len = add_len + 2;
+        if (descp[0] == desc_type)
+            return descp;
+        if (add_len < 0) /* short descriptor ?? */
+            break;
+    }
+    return NULL;
+}
diff --git a/scsiata.h b/scsiata.h
new file mode 100644 (file)
index 0000000..4ff2e19
--- /dev/null
+++ b/scsiata.h
@@ -0,0 +1,78 @@
+/*
+ * scsiata.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2006 Douglas Gilbert <dougg@torque.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#ifndef SCSIATA_H_
+#define SCSIATA_H_
+
+#define SCSIATA_H_CVSID "$Id: scsiata.h,v 1.2 2006/07/01 21:29:31 dpgilbert Exp $\n"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "atacmds.h"
+
+#define SAT_ATA_PASSTHROUGH_12LEN 12
+#define SAT_ATA_PASSTHROUGH_16LEN 16
+
+extern int sat_command_interface(int device, smart_command_set command,
+                                 int select, char *data);
+
+/* Attempt an IDENTIFY DEVICE ATA command via SATL when packet_interface
+   is 0 otherwise attempt IDENTIFY PACKET DEVICE. If successful
+   return 1, else 0 */
+extern int has_sat_pass_through(int device, int packet_interface);
+
+/* This is a slightly stretched SCSI sense "descriptor" format header.
+   The addition is to allow the 0x70 and 0x71 response codes. The idea
+   is to place the salient data of both "fixed" and "descriptor" sense
+   format into one structure to ease application processing.
+   The original sense buffer should be kept around for those cases
+   in which more information is required (e.g. the LBA of a MEDIUM ERROR). */
+struct sg_scsi_sense_hdr {
+    unsigned char response_code; /* permit: 0x0, 0x70, 0x71, 0x72, 0x73 */
+    unsigned char sense_key;
+    unsigned char asc;
+    unsigned char ascq;
+    unsigned char byte4;
+    unsigned char byte5;
+    unsigned char byte6;
+    unsigned char additional_length;
+};
+
+/* Maps the salient data from a sense buffer which is in either fixed or
+   descriptor format into a structure mimicking a descriptor format
+   header (i.e. the first 8 bytes of sense descriptor format).
+   If zero response code returns 0. Otherwise returns 1 and if 'sshp' is
+   non-NULL then zero all fields and then set the appropriate fields in
+   that structure. sshp::additional_length is always 0 for response
+   codes 0x70 and 0x71 (fixed format). */
+extern int sg_scsi_normalize_sense(const unsigned char * sensep,
+                                   int sense_len,
+                                   struct sg_scsi_sense_hdr * sshp);
+
+/* Attempt to find the first SCSI sense data descriptor that matches the
+   given 'desc_type'. If found return pointer to start of sense data
+   descriptor; otherwise (including fixed format sense data) returns NULL. */
+extern const unsigned char * sg_scsi_sense_desc_find(
+                const unsigned char * sensep, int sense_len, int desc_type);
+
+#endif
+
diff --git a/scsicmds.c b/scsicmds.c
deleted file mode 100644 (file)
index 36ae6dc..0000000
+++ /dev/null
@@ -1,2097 +0,0 @@
-/*
- * scsicmds.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
- *
- * Additional SCSI work:
- * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
- *
- * In the SCSI world "SMART" is a dead or withdrawn standard. In recent
- * SCSI standards (since SCSI-3) it goes under the awkward name of
- * "Informational Exceptions" ["IE" or "IEC" (with the "C" for "control")].
- * The relevant information is spread around several SCSI draft
- * standards available at http://www.t10.org . Reference is made in the
- * code to the following acronyms:
- *      - SAM [SCSI Architectural model, versions 2 or 3]
- *      - SPC [SCSI Primary commands, versions 2 or 3]
- *      - SBC [SCSI Block commands, versions 2]
- *
- * Some SCSI disk vendors have snippets of "SMART" information in their
- * product manuals.
- */
-
-#include <stdio.h>
-#include <string.h>
-
-#include "config.h"
-#include "int64.h"
-#include "extern.h"
-#include "scsicmds.h"
-#include "utility.h"
-
-const char *scsicmds_c_cvsid="$Id: scsicmds.c,v 1.85 2006/04/12 14:54:28 ballen4705 Exp $"
-CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
-
-/* for passing global control variables */
-extern smartmonctrl *con;
-
-/* output binary in hex and optionally ascii */
-void dStrHex(const char* str, int len, int no_ascii)
-{
-    const char* p = str;
-    unsigned char c;
-    char buff[82];
-    int a = 0;
-    const int bpstart = 5;
-    const int cpstart = 60;
-    int cpos = cpstart;
-    int bpos = bpstart;
-    int i, k;
-    
-    if (len <= 0) return;
-    memset(buff,' ',80);
-    buff[80]='\0';
-    k = sprintf(buff + 1, "%.2x", a);
-    buff[k + 1] = ' ';
-    if (bpos >= ((bpstart + (9 * 3))))
-        bpos++;
-
-    for(i = 0; i < len; i++)
-    {
-        c = *p++;
-        bpos += 3;
-        if (bpos == (bpstart + (9 * 3)))
-            bpos++;
-        sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c);
-        buff[bpos + 2] = ' ';
-        if (no_ascii)
-            buff[cpos++] = ' ';
-        else {
-            if ((c < ' ') || (c >= 0x7f))
-                c='.';
-            buff[cpos++] = c;
-        }
-        if (cpos > (cpstart+15))
-        {
-            pout("%s\n", buff);
-            bpos = bpstart;
-            cpos = cpstart;
-            a += 16;
-            memset(buff,' ',80);
-            k = sprintf(buff + 1, "%.2x", a);
-            buff[k + 1] = ' ';
-        }
-    }
-    if (cpos > cpstart)
-    {
-        pout("%s\n", buff);
-    }
-}
-
-struct scsi_opcode_name {
-    UINT8 opcode;
-    const char * name;
-};
-
-static struct scsi_opcode_name opcode_name_arr[] = {
-    /* in ascending opcode order */
-    {TEST_UNIT_READY, "test unit ready"},       /* 0x00 */
-    {REQUEST_SENSE, "request sense"},           /* 0x03 */
-    {INQUIRY, "inquiry"},                       /* 0x12 */
-    {MODE_SELECT, "mode select"},               /* 0x15 */
-    {MODE_SENSE, "mode sense"},                 /* 0x1a */
-    {RECEIVE_DIAGNOSTIC, "receive diagnostic"}, /* 0x1c */
-    {SEND_DIAGNOSTIC, "send diagnostic"},       /* 0x1d */
-    {READ_DEFECT_10, "read defect list(10)"},   /* 0x37 */
-    {LOG_SENSE, "log sense"},                   /* 0x4d */
-    {MODE_SELECT_10, "mode select(10)"},        /* 0x55 */
-    {MODE_SENSE_10, "mode sense(10)"},          /* 0x5a */
-};
-
-const char * scsi_get_opcode_name(UINT8 opcode)
-{
-    int k;
-    int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]);
-    struct scsi_opcode_name * onp;
-
-    for (k = 0; k < len; ++k) {
-        onp = &opcode_name_arr[k];
-        if (opcode == onp->opcode)
-            return onp->name;
-        else if (opcode < onp->opcode)
-            return NULL;
-    }
-    return NULL;
-}
-
-
-void scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf,
-                          struct scsi_sense_disect * out)
-{
-    memset(out, 0, sizeof(struct scsi_sense_disect));
-    if ((SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) && 
-        (io_buf->resp_sense_len > 7)) {  
-        out->error_code = (io_buf->sensep[0] & 0x7f);
-        out->sense_key = (io_buf->sensep[2] & 0xf);
-        if (io_buf->resp_sense_len > 13) {
-            out->asc = io_buf->sensep[12];
-            out->ascq = io_buf->sensep[13];
-        }
-    }
-}
-
-static int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo)
-{
-    switch (sinfo->sense_key) {
-    case SCSI_SK_NOT_READY:
-        if (SCSI_ASC_NO_MEDIUM == sinfo->asc) 
-            return SIMPLE_ERR_NO_MEDIUM;
-        else if (SCSI_ASC_NOT_READY == sinfo->asc) {
-            if (0x1 == sinfo->ascq)
-                return SIMPLE_ERR_BECOMING_READY;
-            else
-                return SIMPLE_ERR_NOT_READY;
-        } else
-            return SIMPLE_ERR_NOT_READY;
-    case SCSI_SK_MEDIUM_ERROR:
-    case SCSI_SK_HARDWARE_ERROR:
-        return SIMPLE_ERR_MEDIUM_HARDWARE;
-    case SCSI_SK_ILLEGAL_REQUEST:
-        if (SCSI_ASC_UNKNOWN_OPCODE == sinfo->asc)
-            return SIMPLE_ERR_BAD_OPCODE;
-        else if (SCSI_ASC_UNKNOWN_FIELD == sinfo->asc)
-            return SIMPLE_ERR_BAD_FIELD;
-        else if (SCSI_ASC_UNKNOWN_PARAM == sinfo->asc)
-            return SIMPLE_ERR_BAD_PARAM;
-        else
-            return SIMPLE_ERR_BAD_PARAM;    /* all other illegal request */
-    case SCSI_SK_UNIT_ATTENTION:
-        return SIMPLE_ERR_TRY_AGAIN;
-    default:
-        return SIMPLE_NO_ERROR;
-    }
-}
-
-const char * scsiErrString(int scsiErr)
-{
-    if (scsiErr < 0)
-        return strerror(-scsiErr);
-    switch (scsiErr) {
-        case SIMPLE_NO_ERROR: 
-            return "no error";
-        case SIMPLE_ERR_NOT_READY: 
-            return "device not ready";
-        case SIMPLE_ERR_BAD_OPCODE: 
-            return "unsupported scsi opcode";
-        case SIMPLE_ERR_BAD_FIELD: 
-            return "unsupported field in scsi command";
-        case SIMPLE_ERR_BAD_PARAM: 
-            return "badly formed scsi parameters";
-        case SIMPLE_ERR_BAD_RESP: 
-            return "scsi response fails sanity test";
-        case SIMPLE_ERR_NO_MEDIUM: 
-            return "no medium present";
-        case SIMPLE_ERR_BECOMING_READY: 
-            return "device will be ready soon";
-        case SIMPLE_ERR_TRY_AGAIN: 
-            return "unit attention reported, try again";
-        case SIMPLE_ERR_MEDIUM_HARDWARE: 
-            return "medium or hardware error (serious)";
-        default:
-            return "unknown error";
-    }
-}
-
-/* Sends LOG SENSE command. Returns 0 if ok, 1 if device NOT READY, 2 if
-   command not supported, 3 if field (within command) not supported or
-   returns negated errno.  SPC-3 sections 6.6 and 7.2 (rec 22a).
-   N.B. Sets PC==1 to fetch "current cumulative" log pages.
-   If known_resp_len > 0 then a single fetch is done for this response
-   length. If known_resp_len == 0 then twin fetches are performed, the
-   first to deduce the response length, then send the same command again
-   requesting the deduced response length. This protects certain fragile 
-   HBAs. The twin fetch technique should not be used with the TapeAlert
-   log page since it clears its state flags after each fetch. */
-int scsiLogSense(int device, int pagenum, UINT8 *pBuf, int bufLen,
-                 int known_resp_len)
-{
-    struct scsi_cmnd_io io_hdr;
-    struct scsi_sense_disect sinfo;
-    UINT8 cdb[10];
-    UINT8 sense[32];
-    int pageLen;
-    int status, res;
-
-    if (known_resp_len > bufLen)
-        return -EIO;
-    if (known_resp_len > 0)
-        pageLen = known_resp_len;
-    else {
-        /* Starting twin fetch strategy: first fetch to find respone length */
-        pageLen = 4;
-        if (pageLen > bufLen)
-            return -EIO;
-        else
-            memset(pBuf, 0, pageLen);
-
-        memset(&io_hdr, 0, sizeof(io_hdr));
-        memset(cdb, 0, sizeof(cdb));
-        io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
-        io_hdr.dxfer_len = pageLen;
-        io_hdr.dxferp = pBuf;
-        cdb[0] = LOG_SENSE;
-        cdb[2] = 0x40 | (pagenum & 0x3f);  /* Page control (PC)==1 */
-        cdb[7] = (pageLen >> 8) & 0xff;
-        cdb[8] = pageLen & 0xff;
-        io_hdr.cmnd = cdb;
-        io_hdr.cmnd_len = sizeof(cdb);
-        io_hdr.sensep = sense;
-        io_hdr.max_sense_len = sizeof(sense);
-        io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-    
-        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-        if (0 != status)
-            return status;
-        scsi_do_sense_disect(&io_hdr, &sinfo);
-        if ((res = scsiSimpleSenseFilter(&sinfo)))
-            return res;
-        /* sanity check on response */
-        if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum))
-            return SIMPLE_ERR_BAD_RESP;
-        if (0 == ((pBuf[2] << 8) + pBuf[3]))
-            return SIMPLE_ERR_BAD_RESP;
-        pageLen = (pBuf[2] << 8) + pBuf[3] + 4;
-        /* some SCSI HBA don't like "odd" length transfers */
-        if (pageLen % 2)
-            pageLen += 1;   
-        if (pageLen > bufLen)
-            pageLen = bufLen;
-    }
-    memset(pBuf, 0, 4);
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
-    io_hdr.dxfer_len = pageLen;
-    io_hdr.dxferp = pBuf;
-    cdb[0] = LOG_SENSE;
-    cdb[2] = 0x40 | (pagenum & 0x3f);  /* Page control (PC)==1 */
-    cdb[7] = (pageLen >> 8) & 0xff;
-    cdb[8] = pageLen & 0xff;
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
-    scsi_do_sense_disect(&io_hdr, &sinfo);
-    status = scsiSimpleSenseFilter(&sinfo);
-    if (0 != status)
-        return status;
-    /* sanity check on response */
-    if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum))
-        return SIMPLE_ERR_BAD_RESP;
-    if (0 == ((pBuf[2] << 8) + pBuf[3]))
-        return SIMPLE_ERR_BAD_RESP;
-    return 0;
-}
-
-/* Send MODE SENSE (6 byte) command. Returns 0 if ok, 1 if NOT READY,
- * 2 if command not supported (then MODE SENSE(10) should be supported),
- * 3 if field in command not supported or returns negated errno. 
- * SPC-3 sections 6.9 and 7.4 (rev 22a) [mode subpage==0] */
-int scsiModeSense(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen)
-{
-    struct scsi_cmnd_io io_hdr;
-    struct scsi_sense_disect sinfo;
-    UINT8 cdb[6];
-    UINT8 sense[32];
-    int status;
-
-    if ((bufLen < 0) || (bufLen > 255))
-        return -EINVAL;
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
-    io_hdr.dxfer_len = bufLen;
-    io_hdr.dxferp = pBuf;
-    cdb[0] = MODE_SENSE;
-    cdb[2] = (pc << 6) | (pagenum & 0x3f);
-    cdb[4] = bufLen;
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
-    scsi_do_sense_disect(&io_hdr, &sinfo);
-    status = scsiSimpleSenseFilter(&sinfo);
-    if (SIMPLE_ERR_TRY_AGAIN == status) {
-        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-        if (0 != status)
-            return status;
-        scsi_do_sense_disect(&io_hdr, &sinfo);
-        status = scsiSimpleSenseFilter(&sinfo);
-    }
-    if ((0 == status) && (ALL_MODE_PAGES != pagenum)) {
-        int offset;
-
-        offset = scsiModePageOffset(pBuf, bufLen, 0);
-        if (offset < 0)
-            return SIMPLE_ERR_BAD_RESP;
-        else if (pagenum != (pBuf[offset] & 0x3f))
-            return SIMPLE_ERR_BAD_RESP;
-    }
-    return status;
-}
-
-/* Sends a 6 byte MODE SELECT command. Assumes given pBuf is the response
- * from a corresponding 6 byte MODE SENSE command. Such a response should
- * have a 4 byte header followed by 0 or more 8 byte block descriptors
- * (normally 1) and then 1 mode page. Returns 0 if ok, 1 if NOT READY,
- * 2 if command not supported (then MODE SELECT(10) may be supported), 
- * 3 if field in command not supported, 4 if bad parameter to command
- * or returns negated errno. SPC-3 sections 6.7 and 7.4 (rev 22a) */
-int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen)
-{
-    struct scsi_cmnd_io io_hdr;
-    struct scsi_sense_disect sinfo;
-    UINT8 cdb[6];
-    UINT8 sense[32];
-    int status, pg_offset, pg_len, hdr_plus_1_pg;
-
-    pg_offset = 4 + pBuf[3];
-    if (pg_offset + 2 >= bufLen)
-        return -EINVAL;
-    pg_len = pBuf[pg_offset + 1] + 2;
-    hdr_plus_1_pg = pg_offset + pg_len;
-    if (hdr_plus_1_pg > bufLen)
-        return -EINVAL;
-    pBuf[0] = 0;    /* Length of returned mode sense data reserved for SELECT */
-    pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = DXFER_TO_DEVICE;
-    io_hdr.dxfer_len = hdr_plus_1_pg;
-    io_hdr.dxferp = pBuf;
-    cdb[0] = MODE_SELECT;
-    cdb[1] = 0x10 | (sp & 1);      /* set PF (page format) bit always */
-    cdb[4] = hdr_plus_1_pg; /* make sure only one page sent */
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
-    scsi_do_sense_disect(&io_hdr, &sinfo);
-    return scsiSimpleSenseFilter(&sinfo);
-}
-
-/* MODE SENSE (10 byte). Returns 0 if ok, 1 if NOT READY, 2 if command 
- * not supported (then MODE SENSE(6) might be supported), 3 if field in
- * command not supported or returns negated errno.  
- * SPC-3 sections 6.10 and 7.4 (rev 22a) [mode subpage==0] */
-int scsiModeSense10(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen)
-{
-    struct scsi_cmnd_io io_hdr;
-    struct scsi_sense_disect sinfo;
-    UINT8 cdb[10];
-    UINT8 sense[32];
-    int status;
-
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
-    io_hdr.dxfer_len = bufLen;
-    io_hdr.dxferp = pBuf;
-    cdb[0] = MODE_SENSE_10;
-    cdb[2] = (pc << 6) | (pagenum & 0x3f);
-    cdb[7] = (bufLen >> 8) & 0xff;
-    cdb[8] = bufLen & 0xff;
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
-    scsi_do_sense_disect(&io_hdr, &sinfo);
-    status = scsiSimpleSenseFilter(&sinfo);
-    if (SIMPLE_ERR_TRY_AGAIN == status) {
-        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-        if (0 != status)
-            return status;
-        scsi_do_sense_disect(&io_hdr, &sinfo);
-        status = scsiSimpleSenseFilter(&sinfo);
-    }
-    if ((0 == status) && (ALL_MODE_PAGES != pagenum)) {
-        int offset;
-
-        offset = scsiModePageOffset(pBuf, bufLen, 1);
-        if (offset < 0)
-            return SIMPLE_ERR_BAD_RESP;
-        else if (pagenum != (pBuf[offset] & 0x3f))
-            return SIMPLE_ERR_BAD_RESP;
-    }
-    return status;
-}
-
-/* Sends a 10 byte MODE SELECT command. Assumes given pBuf is the response
- * from a corresponding 10 byte MODE SENSE command. Such a response should
- * have a 8 byte header followed by 0 or more 8 byte block descriptors
- * (normally 1) and then 1 mode page. Returns 0 if ok, 1 NOT REAFY, 2 if 
- * command not supported (then MODE SELECT(6) may be supported), 3 if field
- * in command not supported, 4 if bad parameter to command or returns
- * negated errno. SPC-3 sections 6.8 and 7.4 (rev 22a) */
-int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen)
-{
-    struct scsi_cmnd_io io_hdr;
-    struct scsi_sense_disect sinfo;
-    UINT8 cdb[10];
-    UINT8 sense[32];
-    int status, pg_offset, pg_len, hdr_plus_1_pg;
-
-    pg_offset = 8 + (pBuf[6] << 8) + pBuf[7];
-    if (pg_offset + 2 >= bufLen)
-        return -EINVAL;
-    pg_len = pBuf[pg_offset + 1] + 2;
-    hdr_plus_1_pg = pg_offset + pg_len;
-    if (hdr_plus_1_pg > bufLen)
-        return -EINVAL;
-    pBuf[0] = 0;    
-    pBuf[1] = 0; /* Length of returned mode sense data reserved for SELECT */
-    pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = DXFER_TO_DEVICE;
-    io_hdr.dxfer_len = hdr_plus_1_pg;
-    io_hdr.dxferp = pBuf;
-    cdb[0] = MODE_SELECT_10;
-    cdb[1] = 0x10 | (sp & 1);      /* set PF (page format) bit always */
-    cdb[8] = hdr_plus_1_pg; /* make sure only one page sent */
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
-    scsi_do_sense_disect(&io_hdr, &sinfo);
-    return scsiSimpleSenseFilter(&sinfo);
-}
-
-/* Standard INQUIRY returns 0 for ok, anything else is a major problem.
- * bufLen should be 36 for unsafe devices (like USB mass storage stuff)
- * otherwise they can lock up! SPC-3 sections 6.4 and 7.6 (rev 22a) */
-int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen)
-{
-    struct scsi_sense_disect sinfo;
-    struct scsi_cmnd_io io_hdr;
-    UINT8 cdb[6];
-    UINT8 sense[32];
-    int status;
-
-    if ((bufLen < 0) || (bufLen > 255))
-        return -EINVAL;
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
-    io_hdr.dxfer_len = bufLen;
-    io_hdr.dxferp = pBuf;
-    cdb[0] = INQUIRY;
-    cdb[4] = bufLen;
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
-    scsi_do_sense_disect(&io_hdr, &sinfo);
-    return scsiSimpleSenseFilter(&sinfo);
-}
-
-/* INQUIRY to fetch Vital Page Data.  Returns 0 if ok, 1 if NOT READY
- * (unlikely), 2 if command not supported, 3 if field in command not 
- * supported, 5 if response indicates that EVPD bit ignored or returns
- * negated errno. SPC-3 section 6.4 and 7.6 (rev 22a) */
-int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen)
-{
-    struct scsi_cmnd_io io_hdr;
-    struct scsi_sense_disect sinfo;
-    UINT8 cdb[6];
-    UINT8 sense[32];
-    int status, res;
-
-    if ((bufLen < 0) || (bufLen > 255))
-        return -EINVAL;
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    if (bufLen > 1)
-        pBuf[1] = 0x0;
-    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
-    io_hdr.dxfer_len = bufLen;
-    io_hdr.dxferp = pBuf;
-    cdb[0] = INQUIRY;
-    cdb[1] = 0x1;       /* set EVPD bit (enable Vital Product Data) */
-    cdb[2] = vpd_page;
-    cdb[4] = bufLen;
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
-    scsi_do_sense_disect(&io_hdr, &sinfo);
-    if ((res = scsiSimpleSenseFilter(&sinfo)))
-        return res;
-    /* Guard against devices that ignore EVPD bit and do standard INQUIRY */
-    if (bufLen > 1) {
-        if (vpd_page == pBuf[1]) {
-            if ((0x80 == vpd_page) && (bufLen > 2) && (0x0 != pBuf[2]))
-                return SIMPLE_ERR_BAD_RESP;
-        } else
-            return SIMPLE_ERR_BAD_RESP;
-    }
-    return 0;
-}
-
-/* REQUEST SENSE command. Returns 0 if ok, anything else major problem.
- * SPC-3 section 6.27 (rev 22a) */
-int scsiRequestSense(int device, struct scsi_sense_disect * sense_info)
-{
-    struct scsi_cmnd_io io_hdr;
-    UINT8 cdb[6];
-    UINT8 sense[32];
-    UINT8 buff[18];
-    int status, len;
-    UINT8 ecode;
-
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
-    io_hdr.dxfer_len = sizeof(buff);
-    io_hdr.dxferp = buff;
-    cdb[0] = REQUEST_SENSE;
-    cdb[4] = sizeof(buff);
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if ((0 == status) && (sense_info)) {
-        ecode = buff[0] & 0x7f;
-        sense_info->error_code = ecode;
-        sense_info->sense_key = buff[2] & 0xf;
-        sense_info->asc = 0;
-        sense_info->ascq = 0;
-        if ((0x70 == ecode) || (0x71 == ecode)) {
-            len = buff[7] + 8;
-            if (len > 13) {
-                sense_info->asc = buff[12];
-                sense_info->ascq = buff[13];
-            }
-        }
-    }
-    return status;
-}
-
-/* SEND DIAGNOSTIC command.  Returns 0 if ok, 1 if NOT READY, 2 if command
- * not supported, 3 if field in command not supported or returns negated
- * errno. SPC-3 section 6.28 (rev 22a) */
-int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen)
-{
-    struct scsi_cmnd_io io_hdr;
-    struct scsi_sense_disect sinfo;
-    UINT8 cdb[6];
-    UINT8 sense[32];
-    int status;
-
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = bufLen ? DXFER_TO_DEVICE: DXFER_NONE;
-    io_hdr.dxfer_len = bufLen;
-    io_hdr.dxferp = pBuf;
-    cdb[0] = SEND_DIAGNOSTIC;
-    if (SCSI_DIAG_DEF_SELF_TEST == functioncode)
-        cdb[1] = 0x4;  /* SelfTest bit */
-    else if (SCSI_DIAG_NO_SELF_TEST != functioncode)
-        cdb[1] = (functioncode & 0x7) << 5; /* SelfTest _code_ */
-    else   /* SCSI_DIAG_NO_SELF_TEST == functioncode */
-        cdb[1] = 0x10;  /* PF bit */
-    cdb[3] = (bufLen >> 8) & 0xff;
-    cdb[4] = bufLen & 0xff;
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    /* worst case is an extended foreground self test on a big disk */
-    io_hdr.timeout = SCSI_TIMEOUT_SELF_TEST;
-    
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
-    scsi_do_sense_disect(&io_hdr, &sinfo);
-    return scsiSimpleSenseFilter(&sinfo);
-}
-
-/* RECEIVE DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if
- * command not supported, 3 if field in command not supported or returns
- * negated errno. SPC-3 section 6.18 (rev 22a) */
-int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf, 
-                      int bufLen)
-{
-    struct scsi_cmnd_io io_hdr;
-    struct scsi_sense_disect sinfo;
-    UINT8 cdb[6];
-    UINT8 sense[32];
-    int status;
-
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
-    io_hdr.dxfer_len = bufLen;
-    io_hdr.dxferp = pBuf;
-    cdb[0] = RECEIVE_DIAGNOSTIC;
-    cdb[1] = pcv;
-    cdb[2] = pagenum;
-    cdb[3] = (bufLen >> 8) & 0xff;
-    cdb[4] = bufLen & 0xff;
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
-    scsi_do_sense_disect(&io_hdr, &sinfo);
-    return scsiSimpleSenseFilter(&sinfo);
-}
-
-/* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */
-static int _testunitready(int device, struct scsi_sense_disect * sinfo)
-{
-    struct scsi_cmnd_io io_hdr;
-    UINT8 cdb[6];
-    UINT8 sense[32];
-    int status;
-
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = DXFER_NONE;
-    io_hdr.dxfer_len = 0;
-    io_hdr.dxferp = NULL;
-    cdb[0] = TEST_UNIT_READY;
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
-    scsi_do_sense_disect(&io_hdr, sinfo);
-    return 0;
-}
-
-/* Returns 0 for device responds and media ready, 1 for device responds and
-   media not ready, or returns a negated errno value */
-int scsiTestUnitReady(int device)
-{
-    struct scsi_sense_disect sinfo;
-    int status;
-
-    status = _testunitready(device, &sinfo);
-    if (0 != status)
-        return status;
-    status = scsiSimpleSenseFilter(&sinfo);
-    if (SIMPLE_ERR_TRY_AGAIN == status) {
-        /* power on reset, media changed, ok ... try again */
-        status = _testunitready(device, &sinfo);        
-        if (0 != status)
-            return status;
-        status = scsiSimpleSenseFilter(&sinfo);
-    }
-    return status;
-}
-
-/* READ DEFECT (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
- * command not supported, 3 if field in command not supported or returns
- * negated errno. SBC-2 section 5.12 (rev 16) */
-int scsiReadDefect10(int device, int req_plist, int req_glist, int dl_format,
-                     UINT8 *pBuf, int bufLen)
-{
-    struct scsi_cmnd_io io_hdr;
-    struct scsi_sense_disect sinfo;
-    UINT8 cdb[10];
-    UINT8 sense[32];
-    int status;
-
-    memset(&io_hdr, 0, sizeof(io_hdr));
-    memset(cdb, 0, sizeof(cdb));
-    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
-    io_hdr.dxfer_len = bufLen;
-    io_hdr.dxferp = pBuf;
-    cdb[0] = READ_DEFECT_10;
-    cdb[2] = (unsigned char)(((req_plist << 4) & 0x10) |
-               ((req_glist << 3) & 0x8) | (dl_format & 0x7));
-    cdb[7] = (bufLen >> 8) & 0xff;
-    cdb[8] = bufLen & 0xff;
-    io_hdr.cmnd = cdb;
-    io_hdr.cmnd_len = sizeof(cdb);
-    io_hdr.sensep = sense;
-    io_hdr.max_sense_len = sizeof(sense);
-    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
-
-    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
-    if (0 != status)
-        return status;
-    scsi_do_sense_disect(&io_hdr, &sinfo);
-    return scsiSimpleSenseFilter(&sinfo);
-}
-
-/* Offset into mode sense (6 or 10 byte) response that actual mode page
- * starts at (relative to resp[0]). Returns -1 if problem */
-int scsiModePageOffset(const UINT8 * resp, int len, int modese_len)
-{
-    int resp_len, bd_len;
-    int offset = -1;
-
-    if (resp) {
-        if (10 == modese_len) {
-            resp_len = (resp[0] << 8) + resp[1] + 2;
-            bd_len = (resp[6] << 8) + resp[7];
-            offset = bd_len + 8;
-        } else {
-            resp_len = resp[0] + 1;
-            bd_len = resp[3];
-            offset = bd_len + 4;
-        }
-        if ((offset + 2) > len) {
-            pout("scsiModePageOffset: raw_curr too small, offset=%d "
-                 "resp_len=%d bd_len=%d\n", offset, resp_len, bd_len);
-            offset = -1;
-        } else if ((offset + 2) > resp_len) {
-             if ((resp_len > 2) || con->reportscsiioctl)
-                pout("scsiModePageOffset: response length too short, "
-                     "resp_len=%d offset=%d bd_len=%d\n", resp_len,
-                     offset, bd_len);
-            offset = -1;
-        }
-    }
-    return offset;
-}
-
-/* IEC mode page byte 2 bit masks */
-#define DEXCPT_ENABLE   0x08
-#define EWASC_ENABLE    0x10
-#define DEXCPT_DISABLE  0xf7
-#define EWASC_DISABLE   0xef
-#define TEST_DISABLE    0xfb
-
-/* Fetches the Informational Exceptions Control mode page. First tries
- * the 6 byte MODE SENSE command and if that fails with an illegal opcode
- * tries a 10 byte MODE SENSE command. Returns 0 if successful, a positive
- * number if a known error (see  SIMPLE_ERR_ ...) or a negative errno
- * value. */
-int scsiFetchIECmpage(int device, struct scsi_iec_mode_page *iecp, int modese_len)
-{
-    int err = 0;
-
-    memset(iecp, 0, sizeof(*iecp));
-    iecp->modese_len = modese_len;
-    iecp->requestedCurrent = 1;
-    if (iecp->modese_len <= 6) {
-        if ((err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, 
-                                 MPAGE_CONTROL_CURRENT, 
-                                 iecp->raw_curr, sizeof(iecp->raw_curr)))) {
-            if (SIMPLE_ERR_BAD_OPCODE == err)
-                iecp->modese_len = 10;
-            else {
-                iecp->modese_len = 0;
-                return err;
-            }
-        } else if (0 == iecp->modese_len)
-            iecp->modese_len = 6;
-    }
-    if (10 == iecp->modese_len) {
-        err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
-                              MPAGE_CONTROL_CURRENT, 
-                              iecp->raw_curr, sizeof(iecp->raw_curr));
-        if (err) {
-            iecp->modese_len = 0;
-            return err;
-        }
-    } 
-    iecp->gotCurrent = 1;
-    iecp->requestedChangeable = 1;
-    if (10 == iecp->modese_len)
-        err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
-                                 MPAGE_CONTROL_CHANGEABLE,
-                                 iecp->raw_chg, sizeof(iecp->raw_chg));
-    else if (6 == iecp->modese_len)
-        err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, 
-                            MPAGE_CONTROL_CHANGEABLE, 
-                            iecp->raw_chg, sizeof(iecp->raw_chg));
-    if (err)
-        return err;
-    iecp->gotChangeable = 1;
-    return 0;
-}
-
-int scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp)
-{
-    int offset;
-
-    if (iecp && iecp->gotCurrent) {
-        offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
-                                    iecp->modese_len);
-        if (offset >= 0)
-            return (iecp->raw_curr[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
-        else
-            return 0;
-    } else
-        return 0;
-}
-
-int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp)
-{
-    int offset;
-
-    if (iecp && iecp->gotCurrent) {
-        offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
-                                    iecp->modese_len);
-        if (offset >= 0)
-            return (iecp->raw_curr[offset + 2] & EWASC_ENABLE) ? 1 : 0;
-        else
-            return 0;
-    } else
-        return 0;
-}
-
-/* set EWASC and clear PERF, EBF, DEXCPT TEST and LOGERR */
-#define SCSI_IEC_MP_BYTE2_ENABLED 0x10 
-#define SCSI_IEC_MP_BYTE2_TEST_MASK 0x4
-/* exception/warning via an unrequested REQUEST SENSE command */
-#define SCSI_IEC_MP_MRIE 6      
-#define SCSI_IEC_MP_INTERVAL_T 0
-#define SCSI_IEC_MP_REPORT_COUNT 1
-
-/* Try to set (or clear) both Exception Control and Warning in the IE
- * mode page subject to the "changeable" mask. The object pointed to
- * by iecp is (possibly) inaccurate after this call, therefore
- * scsiFetchIECmpage() should be called again if the IEC mode page
- * is to be re-examined.
- * When -r ioctl is invoked 3 or more time on 'smartctl -s on ...'
- * then set the TEST bit (causes asc,ascq pair of 0x5d,0xff). */
-int scsiSetExceptionControlAndWarning(int device, int enabled,
-                                      const struct scsi_iec_mode_page *iecp)
-{
-    int k, offset, resp_len;
-    int err = 0;
-    UINT8 rout[SCSI_IECMP_RAW_LEN];
-    int sp, eCEnabled, wEnabled;
-
-    if ((! iecp) || (! iecp->gotCurrent))
-        return -EINVAL;
-    offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
-                                iecp->modese_len);
-    if (offset < 0)
-        return -EINVAL;
-    memcpy(rout, iecp->raw_curr, SCSI_IECMP_RAW_LEN);
-    if (10 == iecp->modese_len) {
-        resp_len = (rout[0] << 8) + rout[1] + 2;
-        rout[3] &= 0xef;    /* for disks mask out DPOFUA bit */
-    } else {
-        resp_len = rout[0] + 1;
-        rout[2] &= 0xef;    /* for disks mask out DPOFUA bit */
-    }
-    sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
-    if (enabled) {
-        rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED;
-        if (con->reportscsiioctl > 2)
-            rout[offset + 2] |= SCSI_IEC_MP_BYTE2_TEST_MASK;
-        rout[offset + 3] = SCSI_IEC_MP_MRIE;
-        rout[offset + 4] = (SCSI_IEC_MP_INTERVAL_T >> 24) & 0xff;
-        rout[offset + 5] = (SCSI_IEC_MP_INTERVAL_T >> 16) & 0xff;
-        rout[offset + 6] = (SCSI_IEC_MP_INTERVAL_T >> 8) & 0xff;
-        rout[offset + 7] = SCSI_IEC_MP_INTERVAL_T & 0xff;
-        rout[offset + 8] = (SCSI_IEC_MP_REPORT_COUNT >> 24) & 0xff;
-        rout[offset + 9] = (SCSI_IEC_MP_REPORT_COUNT >> 16) & 0xff;
-        rout[offset + 10] = (SCSI_IEC_MP_REPORT_COUNT >> 8) & 0xff;
-        rout[offset + 11] = SCSI_IEC_MP_REPORT_COUNT & 0xff;
-        if (iecp->gotChangeable) {
-            UINT8 chg2 = iecp->raw_chg[offset + 2];
-
-            rout[offset + 2] = chg2 ? (rout[offset + 2] & chg2) :
-                                      iecp->raw_curr[offset + 2];
-            for (k = 3; k < 12; ++k) {
-                if (0 == iecp->raw_chg[offset + k])
-                    rout[offset + k] = iecp->raw_curr[offset + k];
-            }
-        }
-        if (0 == memcmp(&rout[offset + 2], &iecp->raw_chg[offset + 2], 10)) {
-            if (con->reportscsiioctl > 0)
-                pout("scsiSetExceptionControlAndWarning: already enabled\n");
-            return 0;
-        }
-    } else { /* disabling Exception Control and (temperature) Warnings */
-        eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
-        wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0;
-        if ((! eCEnabled) && (! wEnabled)) {
-            if (con->reportscsiioctl > 0)
-                pout("scsiSetExceptionControlAndWarning: already disabled\n");
-            return 0;   /* nothing to do, leave other setting alone */
-        }
-        if (wEnabled) 
-            rout[offset + 2] &= EWASC_DISABLE;
-        if (eCEnabled) {
-            if (iecp->gotChangeable && 
-                (iecp->raw_chg[offset + 2] & DEXCPT_ENABLE))
-                rout[offset + 2] |= DEXCPT_ENABLE;
-                rout[offset + 2] &= TEST_DISABLE;/* clear TEST bit for spec */
-        }
-    }
-    if (10 == iecp->modese_len)
-        err = scsiModeSelect10(device, sp, rout, resp_len);
-    else if (6 == iecp->modese_len)
-        err = scsiModeSelect(device, sp, rout, resp_len);
-    return err;
-}
-
-int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp)
-{
-    UINT8 tBuf[252];
-    int err;
-
-    memset(tBuf, 0, sizeof(tBuf));
-    if ((err = scsiLogSense(device, TEMPERATURE_LPAGE, tBuf, 
-                            sizeof(tBuf), 0))) {
-        *currenttemp = 0;
-        *triptemp = 0;
-        pout("Log Sense for temperature failed [%s]\n", scsiErrString(err));
-        return err;
-    }
-    *currenttemp = tBuf[9];
-    *triptemp = tBuf[15];
-    return 0;
-}
-
-/* Read informational exception log page or Request Sense response.
- * Fetching asc/ascq code potentially flagging an exception or warning.
- * Returns 0 if ok, else error number. A current temperature of 255
- * (Celsius) implies that the temperature not available. */
-int scsiCheckIE(int device, int hasIELogPage, int hasTempLogPage,
-                UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp,
-                UINT8 *triptemp)
-{
-    UINT8 tBuf[252];
-    struct scsi_sense_disect sense_info;
-    int err;
-    int temperatureSet = 0;
-    unsigned short pagesize;
-    UINT8 currTemp, trTemp;
-    *asc = 0;
-    *ascq = 0;
-    *currenttemp = 0;
-    *triptemp = 0;
-    memset(tBuf,0,sizeof(tBuf)); // need to clear stack space of junk
-    memset(&sense_info, 0, sizeof(sense_info));
-    if (hasIELogPage) {
-        if ((err = scsiLogSense(device, IE_LPAGE, tBuf, 
-                                sizeof(tBuf), 0))) {
-            pout("Log Sense failed, IE page [%s]\n", scsiErrString(err));
-            return err;
-        }
-        // pull out page size from response, don't forget to add 4
-        pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4; 
-        if ((pagesize < 4) || tBuf[4] || tBuf[5]) {
-            pout("Log Sense failed, IE page, bad parameter code or length\n");
-            return SIMPLE_ERR_BAD_PARAM;
-        }
-        if (tBuf[7] > 1) {
-            sense_info.asc = tBuf[8]; 
-            sense_info.ascq = tBuf[9];
-            if (! hasTempLogPage) {
-                if (tBuf[7] > 2) 
-                    *currenttemp = tBuf[10];
-                if (tBuf[7] > 3)        /* IBM extension in SMART (IE) lpage */
-                    *triptemp = tBuf[11];
-            }
-        } 
-    }
-    if (0 == sense_info.asc) {    
-        /* ties in with MRIE field of 6 in IEC mode page (0x1c) */
-        if ((err = scsiRequestSense(device, &sense_info))) {
-            pout("Request Sense failed, [%s]\n", scsiErrString(err));
-            return err;
-        }
-    }
-    *asc = sense_info.asc;
-    *ascq = sense_info.ascq;
-    if ((! temperatureSet) && hasTempLogPage) {
-        if (0 == scsiGetTemp(device, &currTemp, &trTemp)) {
-            *currenttemp = currTemp;
-            *triptemp = trTemp;
-        }
-    }
-    return 0;
-}
-
-// The first character (W, C, I) tells the severity
-static const char * TapeAlertsMessageTable[]= {  
-    " ",
-    /* 0x01 */
-   "W: The tape drive is having problems reading data. No data has been lost,\n"
-       "  but there has been a reduction in the performance of the tape.",
-    /* 0x02 */
-   "W: The tape drive is having problems writing data. No data has been lost,\n"
-       "  but there has been a reduction in the capacity of the tape.",
-    /* 0x03 */
-   "W: The operation has stopped because an error has occurred while reading\n"
-       "  or writing data that the drive cannot correct.",
-    /* 0x04 */
-   "C: Your data is at risk:\n"
-       "  1. Copy any data you require from this tape. \n"
-       "  2. Do not use this tape again.\n"
-       "  3. Restart the operation with a different tape.",
-    /* 0x05 */
-   "C: The tape is damaged or the drive is faulty. Call the tape drive\n"
-       "  supplier helpline.",
-    /* 0x06 */
-   "C: The tape is from a faulty batch or the tape drive is faulty:\n"
-       "  1. Use a good tape to test the drive.\n"
-       "  2. If problem persists, call the tape drive supplier helpline.",
-    /* 0x07 */
-   "W: The tape cartridge has reached the end of its calculated useful life:\n"
-       "  1. Copy data you need to another tape.\n"
-       "  2. Discard the old tape.",
-    /* 0x08 */
-   "W: The tape cartridge is not data-grade. Any data you back up to the tape\n"
-       "  is at risk. Replace the cartridge with a data-grade tape.",
-    /* 0x09 */
-   "C: You are trying to write to a write-protected cartridge. Remove the\n"
-       "  write-protection or use another tape.",
-    /* 0x0a */
-   "I: You cannot eject the cartridge because the tape drive is in use. Wait\n"
-       "  until the operation is complete before ejecting the cartridge.",
-    /* 0x0b */
-   "I: The tape in the drive is a cleaning cartridge.",
-    /* 0x0c */
-   "I: You have tried to load a cartridge of a type which is not supported\n"
-       "  by this drive.",
-    /* 0x0d */
-   "C: The operation has failed because the tape in the drive has experienced\n"
-       "  a mechanical failure:\n"
-       "  1. Discard the old tape.\n"
-       "  2. Restart the operation with a different tape.",
-    /* 0x0e */
-   "C: The operation has failed because the tape in the drive has experienced\n"
-       "  a mechanical failure:\n"
-       "  1. Do not attempt to extract the tape cartridge\n"
-       "  2. Call the tape drive supplier helpline.",
-    /* 0x0f */
-   "W: The memory in the tape cartridge has failed, which reduces\n"
-       "  performance. Do not use the cartridge for further write operations.",
-    /* 0x10 */
-   "C: The operation has failed because the tape cartridge was manually\n"
-       "  de-mounted while the tape drive was actively writing or reading.",
-    /* 0x11 */
-   "W: You have loaded a cartridge of a type that is read-only in this drive.\n"
-       "  The cartridge will appear as write-protected.",
-    /* 0x12 */
-   "W: The tape directory on the tape cartridge has been corrupted. File\n"
-       "  search performance will be degraded. The tape directory can be rebuilt\n"
-       "  by reading all the data on the cartridge.",
-    /* 0x13 */
-   "I: The tape cartridge is nearing the end of its calculated life. It is\n"
-       "  recommended that you:\n"
-       "  1. Use another tape cartridge for your next backup.\n"
-       "  2. Store this tape in a safe place in case you need to restore "
-       "  data from it.",
-    /* 0x14 */
-   "C: The tape drive needs cleaning:\n"
-       "  1. If the operation has stopped, eject the tape and clean the drive.\n"
-       "  2. If the operation has not stopped, wait for it to finish and then\n"
-       "  clean the drive.\n"
-       "  Check the tape drive users manual for device specific cleaning instructions.",
-    /* 0x15 */
-   "W: The tape drive is due for routine cleaning:\n"
-       "  1. Wait for the current operation to finish.\n"
-       "  2. The use a cleaning cartridge.\n"
-       "  Check the tape drive users manual for device specific cleaning instructions.",
-    /* 0x16 */
-   "C: The last cleaning cartridge used in the tape drive has worn out:\n"
-       "  1. Discard the worn out cleaning cartridge.\n"
-       "  2. Wait for the current operation to finish.\n"
-       "  3. Then use a new cleaning cartridge.",
-    /* 0x17 */
-   "C: The last cleaning cartridge used in the tape drive was an invalid\n"
-       "  type:\n"
-       "  1. Do not use this cleaning cartridge in this drive.\n"
-       "  2. Wait for the current operation to finish.\n"
-       "  3. Then use a new cleaning cartridge.",
-    /* 0x18 */
-   "W: The tape drive has requested a retention operation",
-    /* 0x19 */
-   "W: A redundant interface port on the tape drive has failed",
-    /* 0x1a */
-   "W: A tape drive cooling fan has failed",
-    /* 0x1b */
-   "W: A redundant power supply has failed inside the tape drive enclosure.\n"
-       "  Check the enclosure users manual for instructions on replacing the\n"
-       "  failed power supply.",
-    /* 0x1c */
-   "W: The tape drive power consumption is outside the specified range.",
-    /* 0x1d */
-   "W: Preventive maintenance of the tape drive is required. Check the tape\n"
-       "  drive users manual for device specific preventive maintenance\n"
-       "  tasks or call the tape drive supplier helpline.",
-    /* 0x1e */
-   "C: The tape drive has a hardware fault:\n"
-       "  1. Eject the tape or magazine.\n"
-       "  2. Reset the drive.\n"
-       "  3. Restart the operation.",
-    /* 0x1f */
-   "C: The tape drive has a hardware fault:\n"
-       "  1. Turn the tape drive off and then on again.\n"
-       "  2. Restart the operation.\n"
-    "  3. If the problem persists, call the tape drive supplier helpline.",
-    /* 0x20 */
-   "W: The tape drive has a problem with the application client interface:\n"
-       "  1. Check the cables and cable connections.\n"
-       "  2. Restart the operation.",
-    /* 0x21 */
-   "C: The operation has failed:\n"
-       "  1. Eject the tape or magazine.\n"
-       "  2. Insert the tape or magazine again.\n"
-       "  3. Restart the operation.",
-    /* 0x22 */
-   "W: The firmware download has failed because you have tried to use the\n"
-       "  incorrect firmware for this tape drive. Obtain the correct\n"
-       "  firmware and try again.",
-    /* 0x23 */
-   "W: Environmental conditions inside the tape drive are outside the\n"
-       "  specified humidity range.",
-    /* 0x24 */
-   "W: Environmental conditions inside the tape drive are outside the\n"
-       "  specified temperature range.",
-    /* 0x25 */
-   "W: The voltage supply to the tape drive is outside the specified range.",
-    /* 0x26 */
-   "C: A hardware failure of the tape drive is predicted. Call the tape\n"
-       "  drive supplier helpline.",
-    /* 0x27 */
-   "W: The tape drive may have a hardware fault. Run extended diagnostics to\n"
-       "  verify and diagnose the problem. Check the tape drive users manual for\n"
-       "  device specific instructions on running extended diagnostic tests.",
-    /* 0x28 */
-   "C: The changer mechanism is having difficulty communicating with the tape\n"
-       "  drive:\n"
-       "  1. Turn the autoloader off then on.\n"
-       "  2. Restart the operation.\n"
-       "  3. If problem persists, call the tape drive supplier helpline.",
-    /* 0x29 */
-   "C: A tape has been left in the autoloader by a previous hardware fault:\n"
-       "  1. Insert an empty magazine to clear the fault.\n"
-       "  2. If the fault does not clear, turn the autoloader off and then\n"
-       "  on again.\n"
-       "  3. If the problem persists, call the tape drive supplier helpline.",
-    /* 0x2a */
-   "W: There is a problem with the autoloader mechanism.",
-    /* 0x2b */
-   "C: The operation has failed because the autoloader door is open:\n"
-       "  1. Clear any obstructions from the autoloader door.\n"
-       "  2. Eject the magazine and then insert it again.\n"
-       "  3. If the fault does not clear, turn the autoloader off and then\n"
-       "  on again.\n"
-       "  4. If the problem persists, call the tape drive supplier helpline.",
-    /* 0x2c */
-   "C: The autoloader has a hardware fault:\n"
-       "  1. Turn the autoloader off and then on again.\n"
-       "  2. Restart the operation.\n"
-       "  3. If the problem persists, call the tape drive supplier helpline.\n"
-       "  Check the autoloader users manual for device specific instructions\n"
-       "  on turning the device power on and off.",
-    /* 0x2d */
-   "C: The autoloader cannot operate without the magazine,\n"
-       "  1. Insert the magazine into the autoloader.\n"
-       "  2. Restart the operation.",
-    /* 0x2e */
-   "W: A hardware failure of the changer mechanism is predicted. Call the\n"
-       "  tape drive supplier helpline.",
-    /* 0x2f */
-   "I: Reserved.",
-    /* 0x30 */
-   "I: Reserved.",
-    /* 0x31 */
-   "I: Reserved.",
-    /* 0x32 */
-   "W: Media statistics have been lost at some time in the past",
-    /* 0x33 */
-   "W: The tape directory on the tape cartridge just unloaded has been\n"
-       "  corrupted. File search performance will be degraded. The tape\n"
-       "  directory can be rebuilt by reading all the data.",
-    /* 0x34 */
-   "C: The tape just unloaded could not write its system area successfully:\n"
-       "  1. Copy data to another tape cartridge.\n"
-       "  2. Discard the old cartridge.",
-    /* 0x35 */
-   "C: The tape system are could not be read successfully at load time:\n"
-    "  1. Copy data to another tape cartridge.\n",
-    /* 0x36 */
-   "C: The start or data could not be found on the tape:\n"
-       "  1. Check you are using the correct format tape.\n"
-       "  2. Discard the tape or return the tape to your supplier",
-    /* 0x37 */
-    "C: The operation has failed because the media cannot be loaded\n"
-        "  and threaded.\n"
-        "  1. Remove the cartridge, inspect it as specified in the product\n"
-        "  manual, and retry the operation.\n"
-        "  2. If the problem persists, call the tape drive supplier help line.",
-    /* 0x38 */
-    "C: The operation has failed because the medium cannot be unloaded:\n"
-        "  1. Do not attempt to extract the tape cartridge.\n"
-        "  2. Call the tape driver supplier help line.",
-    /* 0x39 */
-    "C: The tape drive has a problem with the automation interface:\n"
-        "  1. Check the power to the automation system.\n"
-        "  2. Check the cables and cable connections.\n"
-        "  3. Call the supplier help line if problem persists.",
-    /* 0x3a */
-    "W: The tape drive has reset itself due to a detected firmware\n"
-        "  fault. If problem persists, call the supplier help line.",
-    };
-
-const char * scsiTapeAlertsTapeDevice(unsigned short code)
-{
-    const int num = sizeof(TapeAlertsMessageTable) /
-                        sizeof(TapeAlertsMessageTable[0]);
-
-    return (code < num) ?  TapeAlertsMessageTable[code] : "Unknown Alert"; 
-}
-
-// The first character (W, C, I) tells the severity
-static const char * ChangerTapeAlertsMessageTable[]= {  
-    " ",
-    /* 0x01 */
-    "C: The library mechanism is having difficulty communicating with the\n"
-        "  drive:\n"
-        "  1. Turn the library off then on.\n"
-        "  2. Restart the operation.\n"
-        "  3. If the problem persists, call the library supplier help line.",
-    /* 0x02 */
-    "W: There is a problem with the library mechanism. If problem persists,\n"
-        "  call the library supplier help line.",
-    /* 0x03 */
-    "C: The library has a hardware fault:\n"
-        "  1. Reset the library.\n"
-        "  2. Restart the operation.\n"
-        "  Check the library users manual for device specific instructions on resetting\n"
-        "  the device.",
-    /* 0x04 */
-    "C: The library has a hardware fault:\n"
-        "  1. Turn the library off then on again.\n"
-        "  2. Restart the operation.\n"
-        "  3. If the problem persists, call the library supplier help line.\n"
-        "  Check the library users manual for device specific instructions on turning the\n"
-        "  device power on and off.",
-    /* 0x05 */
-    "W: The library mechanism may have a hardware fault.\n"
-        "  Run extended diagnostics to verify and diagnose the problem. Check the library\n"
-        "  users manual for device specific instructions on running extended diagnostic\n"
-        "  tests.",
-    /* 0x06 */
-    "C: The library has a problem with the host interface:\n"
-        "  1. Check the cables and connections.\n"
-        "  2. Restart the operation.",
-    /* 0x07 */
-    "W: A hardware failure of the library is predicted. Call the library\n"
-        "  supplier help line.",
-    /* 0x08 */
-    "W: Preventive maintenance of the library is required.\n"
-        "  Check the library users manual for device specific preventative maintenance\n"
-        "  tasks, or call your library supplier help line.",
-    /* 0x09 */
-    "C: General environmental conditions inside the library are outside the\n"
-        "  specified humidity range.",
-    /* 0x0a */
-    "C: General environmental conditions inside the library are outside the\n"
-        "  specified temperature range.",
-    /* 0x0b */
-    "C: The voltage supply to the library is outside the specified range.\n"
-        "  There is a potential problem with the power supply or failure of\n"
-        "  a redundant power supply.",
-    /* 0x0c */
-    "C: A cartridge has been left inside the library by a previous hardware\n"
-        "  fault:\n"
-        "  1. Insert an empty magazine to clear the fault.\n"
-        "  2. If the fault does not clear, turn the library off and then on again.\n"
-        "  3. If the problem persists, call the library supplier help line.",
-    /* 0x0d */
-    "W: There is a potential problem with the drive ejecting cartridges or\n"
-        "  with the library mechanism picking a cartridge from a slot.\n"
-        "  1. No action needs to be taken at this time.\n"
-        "  2. If the problem persists, call the library supplier help line.",
-    /* 0x0e */
-    "W: There is a potential problem with the library mechanism placing a\n"
-        "  cartridge into a slot.\n"
-        "  1. No action needs to be taken at this time.\n"
-        "  2. If the problem persists, call the library supplier help line.",
-    /* 0x0f */
-    "W: There is a potential problem with the drive or the library mechanism\n"
-        "  loading cartridges, or an incompatible cartridge.",
-    /* 0x10 */
-    "C: The library has failed because the door is open:\n"
-        "  1. Clear any obstructions from the library door.\n"
-        "  2. Close the library door.\n"
-        "  3. If the problem persists, call the library supplier help line.",
-    /* 0x11 */
-    "C: There is a mechanical problem with the library media import/export\n"
-        "  mailslot.",
-    /* 0x12 */
-    "C: The library cannot operate without the magazine.\n"
-        "  1. Insert the magazine into the library.\n"
-        "  2. Restart the operation.",
-    /* 0x13 */
-    "W: Library security has been compromised.",
-    /* 0x14 */
-    "I: The library security mode has been changed.\n"
-        "  The library has either been put into secure mode, or the library has exited\n"
-        "  the secure mode.\n"
-        "  This is for information purposes only. No action is required.",
-    /* 0x15 */
-    "I: The library has been manually turned offline and is unavailable for use.",
-    /* 0x16 */
-    "I: A drive inside the library has been taken offline.\n"
-        "  This is for information purposes only. No action is required.",
-    /* 0x17 */
-    "W: There is a potential problem with the bar code label or the scanner\n"
-        "  hardware in the library mechanism.\n"
-        "  1. No action needs to be taken at this time.\n"
-        "  2. If the problem persists, call the library supplier help line.",
-    /* 0x18 */
-    "C: The library has detected an inconsistency in its inventory.\n"
-        "  1. Redo the library inventory to correct inconsistency.\n"
-        "  2. Restart the operation.\n"
-        "  Check the applications users manual or the hardware users manual for\n"
-        "  specific instructions on redoing the library inventory.",
-    /* 0x19 */
-    "W: A library operation has been attempted that is invalid at this time.",
-    /* 0x1a */
-    "W: A redundant interface port on the library has failed.",
-    /* 0x1b */
-    "W: A library cooling fan has failed.",
-    /* 0x1c */
-    "W: A redundant power supply has failed inside the library. Check the\n"
-        "  library users manual for instructions on replacing the failed power supply.",
-    /* 0x1d */
-    "W: The library power consumption is outside the specified range.",
-    /* 0x1e */
-    "C: A failure has occurred in the cartridge pass-through mechanism between\n"
-        "  two library modules.",
-    /* 0x1f */
-    "C: A cartridge has been left in the pass-through mechanism from a\n"
-        "  previous hardware fault. Check the library users guide for instructions on\n"
-        "  clearing this fault.",
-    /* 0x20 */
-    "I: The library was unable to read the bar code on a cartridge.",
-};
-
-const char * scsiTapeAlertsChangerDevice(unsigned short code)
-{
-    const int num = sizeof(ChangerTapeAlertsMessageTable) /
-                        sizeof(ChangerTapeAlertsMessageTable[0]);
-
-    return (code < num) ?  ChangerTapeAlertsMessageTable[code] : "Unknown Alert"; 
-}
-
-
-/* this is a subset of the SCSI additional sense code strings indexed
- * by "ascq" for the case when asc==SCSI_ASC_IMPENDING_FAILURE (0x5d)
- */
-static const char * strs_for_asc_5d[] = {
-   /* 0x00 */   "FAILURE PREDICTION THRESHOLD EXCEEDED",
-        "MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED",
-        "LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED",
-        "SPARE AREA EXHAUSTION PREDICTION THRESHOLD EXCEEDED",
-        "",
-        "",
-        "",
-        "",
-        "",
-        "",
-        "",
-        "",
-        "",
-        "",
-        "",
-        "",
-   /* 0x10 */   "HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
-        "HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
-        "HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
-        "HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
-        "HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
-        "HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
-        "HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
-        "HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
-        "HARDWARE IMPENDING FAILURE CONTROLLER DETECTED",
-        "HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
-        "HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
-        "HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
-        "HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
-        "",
-        "",
-        "",
-   /* 0x20 */   "CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
-        "CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
-        "CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
-        "CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
-        "CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
-        "CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH",
-        "CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH",
-        "CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS",
-        "CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED",
-        "CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE",
-        "CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE",
-        "CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT",
-        "CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
-        "",
-        "",
-        "",
-   /* 0x30 */   "DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
-        "DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
-        "DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
-        "DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
-        "DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
-        "DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH",
-        "DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH",
-        "DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS",
-        "DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED",
-        "DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE",
-        "DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE",
-        "DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT",
-        "DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
-        "",
-        "",
-        "",
-   /* 0x40 */   "SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
-        "SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
-        "SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
-        "SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
-        "SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
-        "SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH",
-        "SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH",
-        "SERVO IMPENDING FAILURE CHANNEL PARAMETRICS",
-        "SERVO IMPENDING FAILURE CONTROLLER DETECTED",
-        "SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE",
-        "SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE",
-        "SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT",
-        "SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
-        "",
-        "",
-        "",
-   /* 0x50 */   "SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
-        "SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
-        "SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
-        "SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
-        "SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
-        "SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
-        "SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
-        "SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS",
-        "SPINDLE IMPENDING FAILURE CONTROLLER DETECTED",
-        "SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
-        "SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE",
-        "SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT",
-        "SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
-        "",
-        "",
-        "",
-   /* 0x60 */   "FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
-        "FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
-        "FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
-        "FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
-        "FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
-        "FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
-        "FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
-        "FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
-        "FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED",
-        "FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
-        "FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
-        "FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
-   /* 0x6c */   "FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT"};
-
-
-/* this is a subset of the SCSI additional sense code strings indexed
- *  * by "ascq" for the case when asc==SCSI_ASC_WARNING (0xb)
- *   */
-static const char * strs_for_asc_b[] = {
-       /* 0x00 */   "WARNING",
-               "WARNING - SPECIFIED TEMPERATURE EXCEEDED",
-               "WARNING - ENCLOSURE DEGRADED"};
-
-static char spare_buff[128];
-
-const char * scsiGetIEString(UINT8 asc, UINT8 ascq)
-{
-    const char * rp;
-
-    if (SCSI_ASC_IMPENDING_FAILURE == asc) {
-        if (ascq == 0xff)
-            return "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)";
-        else if (ascq < 
-                 (sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) {
-            rp = strs_for_asc_5d[ascq];
-            if (strlen(rp) > 0)
-                return rp;
-        }
-        snprintf(spare_buff, sizeof(spare_buff),
-                 "FAILURE PREDICTION THRESHOLD EXCEEDED: ascq=0x%x", ascq);
-        return spare_buff;
-    } else if (SCSI_ASC_WARNING == asc) {
-        if (ascq < (sizeof(strs_for_asc_b) / sizeof(strs_for_asc_b[0]))) {
-            rp = strs_for_asc_b[ascq];
-            if (strlen(rp) > 0)
-                return rp;
-        }
-        snprintf(spare_buff, sizeof(spare_buff), "WARNING: ascq=0x%x", ascq);
-        return spare_buff;
-    }
-    return NULL;        /* not a IE additional sense code */
-}
-
-
-/* This is not documented in t10.org, page 0x80 is vendor specific */
-/* Some IBM disks do an offline read-scan when they get this command. */
-int scsiSmartIBMOfflineTest(int device)
-{       
-    UINT8 tBuf[256];
-    int res;
-        
-    memset(tBuf, 0, sizeof(tBuf));
-    /* Build SMART Off-line Immediate Diag Header */
-    tBuf[0] = 0x80; /* Page Code */
-    tBuf[1] = 0x00; /* Reserved */
-    tBuf[2] = 0x00; /* Page Length MSB */
-    tBuf[3] = 0x04; /* Page Length LSB */
-    tBuf[4] = 0x03; /* SMART Revision */
-    tBuf[5] = 0x00; /* Reserved */
-    tBuf[6] = 0x00; /* Off-line Immediate Time MSB */
-    tBuf[7] = 0x00; /* Off-line Immediate Time LSB */
-    res = scsiSendDiagnostic(device, SCSI_DIAG_NO_SELF_TEST, tBuf, 8);
-    if (res)
-        pout("IBM offline test failed [%s]\n", scsiErrString(res));
-    return res;
-}
-
-int scsiSmartDefaultSelfTest(int device)
-{       
-    int res;
-
-    res = scsiSendDiagnostic(device, SCSI_DIAG_DEF_SELF_TEST, NULL, 0);
-    if (res)
-        pout("Default self test failed [%s]\n", scsiErrString(res));
-    return res;
-}
-
-int scsiSmartShortSelfTest(int device)
-{       
-    int res;
-
-    res = scsiSendDiagnostic(device, SCSI_DIAG_BG_SHORT_SELF_TEST, NULL, 0);
-    if (res)
-        pout("Short offline self test failed [%s]\n", scsiErrString(res));
-    return res;
-}
-
-int scsiSmartExtendSelfTest(int device)
-{       
-    int res;
-
-    res = scsiSendDiagnostic(device, SCSI_DIAG_BG_EXTENDED_SELF_TEST, NULL, 0);
-    if (res)
-        pout("Long (extended) offline self test failed [%s]\n",
-             scsiErrString(res));
-    return res;
-}
-
-int scsiSmartShortCapSelfTest(int device)
-{       
-    int res;
-
-    res = scsiSendDiagnostic(device, SCSI_DIAG_FG_SHORT_SELF_TEST, NULL, 0);
-    if (res)
-        pout("Short foreground self test failed [%s]\n", scsiErrString(res));
-    return res;
-}
-
-int scsiSmartExtendCapSelfTest(int device)
-{
-    int res;
-
-    res = scsiSendDiagnostic(device, SCSI_DIAG_FG_EXTENDED_SELF_TEST, NULL, 0);
-    if (res)
-        pout("Long (extended) foreground self test failed [%s]\n",
-             scsiErrString(res));
-    return res;
-}
-
-int scsiSmartSelfTestAbort(int device)
-{
-    int res;
-
-    res = scsiSendDiagnostic(device, SCSI_DIAG_ABORT_SELF_TEST, NULL, 0);
-    if (res)
-        pout("Abort self test failed [%s]\n", scsiErrString(res));
-    return res;
-}
-
-/* Returns 0 and the expected duration of an extended self test (in seconds)
-   if successful; any other return value indicates a failure. */
-int scsiFetchExtendedSelfTestTime(int device, int * durationSec, int modese_len)
-{
-    int err, offset, res;
-    UINT8 buff[64];
-
-    memset(buff, 0, sizeof(buff));
-    if (modese_len <= 6) {
-        if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 
-                                 MPAGE_CONTROL_CURRENT, 
-                                 buff, sizeof(buff)))) {
-            if (SIMPLE_ERR_BAD_OPCODE == err)
-                modese_len = 10;
-            else
-                return err;
-        } else if (0 == modese_len)
-            modese_len = 6;
-    }
-    if (10 == modese_len) {
-        err = scsiModeSense10(device, CONTROL_MODE_PAGE, 
-                              MPAGE_CONTROL_CURRENT, 
-                              buff, sizeof(buff));
-        if (err)
-            return err;
-    } 
-    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
-    if (offset < 0)
-        return -EINVAL;
-    if (buff[offset + 1] >= 0xa) {
-        res = (buff[offset + 10] << 8) | buff[offset + 11];
-        *durationSec = res;
-        return 0;
-    }
-    else
-        return -EINVAL;
-}
-
-void scsiDecodeErrCounterPage(unsigned char * resp, 
-                              struct scsiErrorCounter *ecp)
-{
-    int k, j, num, pl, pc;
-    unsigned char * ucp;
-    unsigned char * xp;
-    uint64_t * ullp;
-
-    memset(ecp, 0, sizeof(*ecp));
-    num = (resp[2] << 8) | resp[3];
-    ucp = &resp[0] + 4;
-    while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
-        pl = ucp[3] + 4;
-        switch (pc) {
-            case 0: 
-            case 1: 
-            case 2: 
-            case 3: 
-            case 4: 
-            case 5: 
-            case 6: 
-                ecp->gotPC[pc] = 1;
-                ullp = &ecp->counter[pc];
-                break;
-        default: 
-                ecp->gotExtraPC = 1;
-                ullp = &ecp->counter[7];
-                break;
-        }
-        k = pl - 4;
-        xp = ucp + 4;
-        if (k > (int)sizeof(*ullp)) {
-            xp += (k - sizeof(*ullp));
-            k = sizeof(*ullp);
-        }
-        *ullp = 0;
-        for (j = 0; j < k; ++j) {
-            if (j > 0)
-                *ullp <<= 8;
-            *ullp |= xp[j];
-        }
-        num -= pl;
-        ucp += pl;
-    }
-}
-
-void scsiDecodeNonMediumErrPage(unsigned char *resp, 
-                                struct scsiNonMediumError *nmep)
-{
-    int k, j, num, pl, pc, szof;
-    unsigned char * ucp;
-    unsigned char * xp;
-
-    memset(nmep, 0, sizeof(*nmep));
-    num = (resp[2] << 8) | resp[3];
-    ucp = &resp[0] + 4;
-    szof = sizeof(nmep->counterPC0);
-    while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
-        pl = ucp[3] + 4;
-        switch (pc) {
-            case 0: 
-                nmep->gotPC0 = 1;
-                k = pl - 4;
-                xp = ucp + 4;
-                if (k > szof) {
-                    xp += (k - szof);
-                    k = szof;
-                }
-                nmep->counterPC0 = 0;
-                for (j = 0; j < k; ++j) {
-                    if (j > 0)
-                        nmep->counterPC0 <<= 8;
-                    nmep->counterPC0 |= xp[j];
-                }
-                break;
-            case 0x8009: 
-                nmep->gotTFE_H = 1;
-                k = pl - 4;
-                xp = ucp + 4;
-                if (k > szof) {
-                    xp += (k - szof);
-                    k = szof;
-                }
-                nmep->counterTFE_H = 0;
-                for (j = 0; j < k; ++j) {
-                    if (j > 0)
-                        nmep->counterTFE_H <<= 8;
-                    nmep->counterTFE_H |= xp[j];
-                }
-                break;
-            case 0x8015: 
-                nmep->gotPE_H = 1;
-                k = pl - 4;
-                xp = ucp + 4;
-                if (k > szof) {
-                    xp += (k - szof);
-                    k = szof;
-                }
-                nmep->counterPE_H = 0;
-                for (j = 0; j < k; ++j) {
-                    if (j > 0)
-                        nmep->counterPE_H <<= 8;
-                    nmep->counterPE_H |= xp[j];
-                }
-                break;
-        default: 
-                nmep->gotExtraPC = 1;
-                break;
-        }
-        num -= pl;
-        ucp += pl;
-    }
-}
-
-/* Counts number of failed self-tests. Also encodes the poweron_hour
-   of the most recent failed self-test. Return value is negative if
-   this function has a problem (typically -1), otherwise the bottom 8
-   bits are the number of failed self tests and the 16 bits above that
-   are the poweron hour of the most recent failure. Note: aborted self
-   tests (typically by the user) and self tests in progress are not 
-   considered failures. See Working Draft SCSI Primary Commands - 3 
-   (SPC-3) section 7.2.10 T10/1416-D (rev 22a) */
-int scsiCountFailedSelfTests(int fd, int noisy)
-{
-    int num, k, n, err, res, fails, fail_hour;
-    UINT8 * ucp;
-    unsigned char resp[LOG_RESP_SELF_TEST_LEN];
-
-    if ((err = scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, resp, 
-                            LOG_RESP_SELF_TEST_LEN, 0))) {
-        if (noisy)
-            pout("scsiCountSelfTests Failed [%s]\n", scsiErrString(err));
-        return -1;
-    }
-    if (resp[0] != SELFTEST_RESULTS_LPAGE) {
-        if (noisy)
-            pout("Self-test Log Sense Failed, page mismatch\n");
-        return -1;
-    }
-    // compute page length
-    num = (resp[2] << 8) + resp[3];
-    // Log sense page length 0x190 bytes
-    if (num != 0x190) {
-        if (noisy)
-            pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n", num);
-        return -1;
-    }
-    fails = 0;
-    fail_hour = 0;
-    // loop through the twenty possible entries
-    for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) {
-
-        // timestamp in power-on hours (or zero if test in progress)
-        n = (ucp[6] << 8) | ucp[7];
-
-        // The spec says "all 20 bytes will be zero if no test" but
-        // DG has found otherwise.  So this is a heuristic.
-        if ((0 == n) && (0 == ucp[4]))
-            break;
-        res = ucp[4] & 0xf;
-        if ((res > 2) && (res < 8)) {
-            fails++;
-            if (1 == fails) 
-                fail_hour = (ucp[6] << 8) + ucp[7];
-        }
-    }
-    return (fail_hour << 8) + fails;
-}
-
-/* Returns 0 if able to read self test log page; then outputs 1 into
-   *inProgress if self test still in progress, else outputs 0. */
-int scsiSelfTestInProgress(int fd, int * inProgress)
-{
-    int num;
-    UINT8 * ucp;
-    unsigned char resp[LOG_RESP_SELF_TEST_LEN];
-
-    if (scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, resp, 
-                     LOG_RESP_SELF_TEST_LEN, 0))
-        return -1;
-    if (resp[0] != SELFTEST_RESULTS_LPAGE)
-        return -1;
-    // compute page length
-    num = (resp[2] << 8) + resp[3];
-    // Log sense page length 0x190 bytes
-    if (num != 0x190) {
-        return -1;
-    }
-    ucp = resp + 4;
-    if (inProgress)
-        *inProgress = (0xf == (ucp[4] & 0xf)) ? 1 : 0;
-    return 0;
-}
-
-/* Returns a negative value if failed to fetch Contol mode page or it was
-   malformed. Returns 0 if GLTSD bit is zero and returns 1 if the GLTSD
-   bit is set. Examines default mode page when current==0 else examines
-   current mode page. */
-int scsiFetchControlGLTSD(int device, int modese_len, int current)
-{
-    int err, offset;
-    UINT8 buff[64];
-    int pc = current ? MPAGE_CONTROL_CURRENT : MPAGE_CONTROL_DEFAULT;
-
-    memset(buff, 0, sizeof(buff));
-    if (modese_len <= 6) {
-        if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, pc, 
-                                 buff, sizeof(buff)))) {
-            if (SIMPLE_ERR_BAD_OPCODE == err)
-                modese_len = 10;
-            else
-                return -EINVAL;
-        } else if (0 == modese_len)
-            modese_len = 6;
-    }
-    if (10 == modese_len) {
-        err = scsiModeSense10(device, CONTROL_MODE_PAGE, pc,
-                              buff, sizeof(buff));
-        if (err)
-            return -EINVAL;
-    } 
-    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
-    if ((offset >= 0) && (buff[offset + 1] >= 0xa))
-        return (buff[offset + 2] & 2) ? 1 : 0;
-    return -EINVAL;
-}
-
-/* Attempts to set or clear GLTSD bit in Control mode page. If enabled is
-   0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if
-   successful, negative if low level error, > 0 if higher level error (e.g.
-   SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */
-int scsiSetControlGLTSD(int device, int enabled, int modese_len)
-{
-    int err, offset, resp_len, sp;
-    UINT8 buff[64];
-    UINT8 ch_buff[64];
-
-    memset(buff, 0, sizeof(buff));
-    if (modese_len <= 6) {
-        if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 
-                                 MPAGE_CONTROL_CURRENT, 
-                                 buff, sizeof(buff)))) {
-            if (SIMPLE_ERR_BAD_OPCODE == err)
-                modese_len = 10;
-            else
-                return err;
-        } else if (0 == modese_len)
-            modese_len = 6;
-    }
-    if (10 == modese_len) {
-        err = scsiModeSense10(device, CONTROL_MODE_PAGE, 
-                              MPAGE_CONTROL_CURRENT, 
-                              buff, sizeof(buff));
-        if (err)
-            return err;
-    } 
-    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
-    if ((offset < 0) || (buff[offset + 1] < 0xa))
-        return SIMPLE_ERR_BAD_RESP;
-
-    if (enabled)
-        enabled = 2;
-    if (enabled == (buff[offset + 2] & 2))
-        return 0;       /* GLTSD already in wanted state so nothing to do */
-
-    if (modese_len == 6)
-        err = scsiModeSense(device, CONTROL_MODE_PAGE, 
-                            MPAGE_CONTROL_CHANGEABLE, 
-                            ch_buff, sizeof(ch_buff));
-    else
-        err = scsiModeSense10(device, CONTROL_MODE_PAGE, 
-                              MPAGE_CONTROL_CHANGEABLE, 
-                              ch_buff, sizeof(ch_buff));
-    if (err)
-        return err;
-    if (0 == (ch_buff[offset + 2] & 2))
-        return SIMPLE_ERR_BAD_PARAM;  /* GLTSD bit not chageable */
-    
-    if (10 == modese_len) {
-        resp_len = (buff[0] << 8) + buff[1] + 2;
-        buff[3] &= 0xef;    /* for disks mask out DPOFUA bit */
-    } else {
-        resp_len = buff[0] + 1;
-        buff[2] &= 0xef;    /* for disks mask out DPOFUA bit */
-    }
-    sp = (buff[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
-    if (enabled)
-        buff[offset + 2] |= 0x2;    /* set GLTSD bit */
-    else
-        buff[offset + 2] &= 0xfd;   /* clear GLTSD bit */
-    if (10 == modese_len)
-        err = scsiModeSelect10(device, sp, buff, resp_len);
-    else if (6 == modese_len)
-        err = scsiModeSelect(device, sp, buff, resp_len);
-    return err;
-}
-
-/* Returns a negative value if failed to fetch Protocol specific port mode 
-   page or it was malformed. Returns transport protocol identifier when
-   value >= 0 . */
-int scsiFetchTransportProtocol(int device, int modese_len)
-{
-    int err, offset;
-    UINT8 buff[64];
-
-    memset(buff, 0, sizeof(buff));
-    if (modese_len <= 6) {
-        if ((err = scsiModeSense(device, PROTOCOL_SPECIFIC_PORT_PAGE, 
-                                 MPAGE_CONTROL_CURRENT, 
-                                 buff, sizeof(buff)))) {
-            if (SIMPLE_ERR_BAD_OPCODE == err)
-                modese_len = 10;
-            else
-                return -EINVAL;
-        } else if (0 == modese_len)
-            modese_len = 6;
-    }
-    if (10 == modese_len) {
-        err = scsiModeSense10(device, PROTOCOL_SPECIFIC_PORT_PAGE, 
-                              MPAGE_CONTROL_CURRENT, 
-                              buff, sizeof(buff));
-        if (err)
-            return -EINVAL;
-    } 
-    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
-    if ((offset >= 0) && (buff[offset + 1] > 1)) {
-        if ((0 == (buff[offset] & 0x40)) &&       /* SPF==0 */
-            (PROTOCOL_SPECIFIC_PORT_PAGE == (buff[offset] & 0x3f))) 
-                return (buff[offset + 2] & 0xf);
-    }
-    return -EINVAL;
-}
-
-
-/* This is Linux specific code to look for the libata ATA-SCSI
-   simulator in the vendor device identification page.
-   Returns 1 if found else 0. */
-int isLinuxLibAta(unsigned char * vpd_di_buff, int len)
-{
-    int k, id_len, c_set, assoc, id_type, i_len;
-    unsigned char * ucp;
-    unsigned char * ip;
-
-    if (len < 4) {
-        /* Device identification VPD page length too short */
-        return 0;
-    }
-    len -= 4;
-    ucp = vpd_di_buff + 4;
-    for (k = 0; k < len; k += id_len, ucp += id_len) {
-        i_len = ucp[3];
-        id_len = i_len + 4;
-        if ((k + id_len) > len) {
-            /* short descriptor, badly formed */
-            return 0;
-        }
-        ip = ucp + 4;
-        c_set = (ucp[0] & 0xf);
-        assoc = ((ucp[1] >> 4) & 0x3);
-        id_type = (ucp[1] & 0xf);
-        if ((0 == id_type) && (2 == c_set) && (0 == assoc) &&
-            (0 == strncmp((const char *)ip,
-                          "Linux ATA-SCSI simulator", i_len))) {
-            return 1;
-        }
-    }
-    return 0;
-}
diff --git a/scsicmds.cpp b/scsicmds.cpp
new file mode 100644 (file)
index 0000000..afb7bdb
--- /dev/null
@@ -0,0 +1,2131 @@
+/*
+ * scsicmds.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * Additional SCSI work:
+ * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ *
+ * In the SCSI world "SMART" is a dead or withdrawn standard. In recent
+ * SCSI standards (since SCSI-3) it goes under the awkward name of
+ * "Informational Exceptions" ["IE" or "IEC" (with the "C" for "control")].
+ * The relevant information is spread around several SCSI draft
+ * standards available at http://www.t10.org . Reference is made in the
+ * code to the following acronyms:
+ *      - SAM [SCSI Architectural model, versions 2 or 3]
+ *      - SPC [SCSI Primary commands, versions 2 or 3]
+ *      - SBC [SCSI Block commands, versions 2]
+ *
+ * Some SCSI disk vendors have snippets of "SMART" information in their
+ * product manuals.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+#include "int64.h"
+#include "extern.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+const char *scsicmds_c_cvsid="$Id: scsicmds.cpp,v 1.91 2006/09/12 00:25:44 dpgilbert Exp $"
+CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+/* for passing global control variables */
+extern smartmonctrl *con;
+
+/* output binary in hex and optionally ascii */
+void dStrHex(const char* str, int len, int no_ascii)
+{
+    const char* p = str;
+    unsigned char c;
+    char buff[82];
+    int a = 0;
+    const int bpstart = 5;
+    const int cpstart = 60;
+    int cpos = cpstart;
+    int bpos = bpstart;
+    int i, k;
+    
+    if (len <= 0) return;
+    memset(buff,' ',80);
+    buff[80]='\0';
+    k = sprintf(buff + 1, "%.2x", a);
+    buff[k + 1] = ' ';
+    if (bpos >= ((bpstart + (9 * 3))))
+        bpos++;
+
+    for(i = 0; i < len; i++)
+    {
+        c = *p++;
+        bpos += 3;
+        if (bpos == (bpstart + (9 * 3)))
+            bpos++;
+        sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c);
+        buff[bpos + 2] = ' ';
+        if (no_ascii)
+            buff[cpos++] = ' ';
+        else {
+            if ((c < ' ') || (c >= 0x7f))
+                c='.';
+            buff[cpos++] = c;
+        }
+        if (cpos > (cpstart+15))
+        {
+            pout("%s\n", buff);
+            bpos = bpstart;
+            cpos = cpstart;
+            a += 16;
+            memset(buff,' ',80);
+            k = sprintf(buff + 1, "%.2x", a);
+            buff[k + 1] = ' ';
+        }
+    }
+    if (cpos > cpstart)
+    {
+        pout("%s\n", buff);
+    }
+}
+
+struct scsi_opcode_name {
+    UINT8 opcode;
+    const char * name;
+};
+
+static struct scsi_opcode_name opcode_name_arr[] = {
+    /* in ascending opcode order */
+    {TEST_UNIT_READY, "test unit ready"},       /* 0x00 */
+    {REQUEST_SENSE, "request sense"},           /* 0x03 */
+    {INQUIRY, "inquiry"},                       /* 0x12 */
+    {MODE_SELECT, "mode select"},               /* 0x15 */
+    {MODE_SENSE, "mode sense"},                 /* 0x1a */
+    {RECEIVE_DIAGNOSTIC, "receive diagnostic"}, /* 0x1c */
+    {SEND_DIAGNOSTIC, "send diagnostic"},       /* 0x1d */
+    {READ_DEFECT_10, "read defect list(10)"},   /* 0x37 */
+    {LOG_SENSE, "log sense"},                   /* 0x4d */
+    {MODE_SELECT_10, "mode select(10)"},        /* 0x55 */
+    {MODE_SENSE_10, "mode sense(10)"},          /* 0x5a */
+    {SAT_ATA_PASSTHROUGH_16, "ata pass-through(16)"}, /* 0x85 */
+    {SAT_ATA_PASSTHROUGH_12, "ata pass-through(12)"}, /* 0xa1 */
+};
+
+const char * scsi_get_opcode_name(UINT8 opcode)
+{
+    int k;
+    int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]);
+    struct scsi_opcode_name * onp;
+
+    for (k = 0; k < len; ++k) {
+        onp = &opcode_name_arr[k];
+        if (opcode == onp->opcode)
+            return onp->name;
+        else if (opcode < onp->opcode)
+            return NULL;
+    }
+    return NULL;
+}
+
+
+void scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf,
+                          struct scsi_sense_disect * out)
+{
+    int resp_code;
+
+    memset(out, 0, sizeof(struct scsi_sense_disect));
+    if (SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) {
+        resp_code = (io_buf->sensep[0] & 0x7f);
+        out->error_code = resp_code;
+        if (resp_code >= 0x72) {
+            out->sense_key = (io_buf->sensep[1] & 0xf);
+            out->asc = io_buf->sensep[2];
+            out->ascq = io_buf->sensep[3];
+        } else if (resp_code >= 0x70) {
+            out->sense_key = (io_buf->sensep[2] & 0xf);
+            if (io_buf->resp_sense_len > 13) {
+                out->asc = io_buf->sensep[12];
+                out->ascq = io_buf->sensep[13];
+            }
+        }
+    }
+}
+
+int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo)
+{
+    switch (sinfo->sense_key) {
+    case SCSI_SK_NOT_READY:
+        if (SCSI_ASC_NO_MEDIUM == sinfo->asc) 
+            return SIMPLE_ERR_NO_MEDIUM;
+        else if (SCSI_ASC_NOT_READY == sinfo->asc) {
+            if (0x1 == sinfo->ascq)
+                return SIMPLE_ERR_BECOMING_READY;
+            else
+                return SIMPLE_ERR_NOT_READY;
+        } else
+            return SIMPLE_ERR_NOT_READY;
+    case SCSI_SK_MEDIUM_ERROR:
+    case SCSI_SK_HARDWARE_ERROR:
+        return SIMPLE_ERR_MEDIUM_HARDWARE;
+    case SCSI_SK_ILLEGAL_REQUEST:
+        if (SCSI_ASC_UNKNOWN_OPCODE == sinfo->asc)
+            return SIMPLE_ERR_BAD_OPCODE;
+        else if (SCSI_ASC_UNKNOWN_FIELD == sinfo->asc)
+            return SIMPLE_ERR_BAD_FIELD;
+        else if (SCSI_ASC_UNKNOWN_PARAM == sinfo->asc)
+            return SIMPLE_ERR_BAD_PARAM;
+        else
+            return SIMPLE_ERR_BAD_PARAM;    /* all other illegal request */
+    case SCSI_SK_UNIT_ATTENTION:
+        return SIMPLE_ERR_TRY_AGAIN;
+    default:
+        return SIMPLE_NO_ERROR;
+    }
+}
+
+const char * scsiErrString(int scsiErr)
+{
+    if (scsiErr < 0)
+        return strerror(-scsiErr);
+    switch (scsiErr) {
+        case SIMPLE_NO_ERROR: 
+            return "no error";
+        case SIMPLE_ERR_NOT_READY: 
+            return "device not ready";
+        case SIMPLE_ERR_BAD_OPCODE: 
+            return "unsupported scsi opcode";
+        case SIMPLE_ERR_BAD_FIELD: 
+            return "unsupported field in scsi command";
+        case SIMPLE_ERR_BAD_PARAM: 
+            return "badly formed scsi parameters";
+        case SIMPLE_ERR_BAD_RESP: 
+            return "scsi response fails sanity test";
+        case SIMPLE_ERR_NO_MEDIUM: 
+            return "no medium present";
+        case SIMPLE_ERR_BECOMING_READY: 
+            return "device will be ready soon";
+        case SIMPLE_ERR_TRY_AGAIN: 
+            return "unit attention reported, try again";
+        case SIMPLE_ERR_MEDIUM_HARDWARE: 
+            return "medium or hardware error (serious)";
+        default:
+            return "unknown error";
+    }
+}
+
+/* Sends LOG SENSE command. Returns 0 if ok, 1 if device NOT READY, 2 if
+   command not supported, 3 if field (within command) not supported or
+   returns negated errno.  SPC-3 sections 6.6 and 7.2 (rec 22a).
+   N.B. Sets PC==1 to fetch "current cumulative" log pages.
+   If known_resp_len > 0 then a single fetch is done for this response
+   length. If known_resp_len == 0 then twin fetches are performed, the
+   first to deduce the response length, then send the same command again
+   requesting the deduced response length. This protects certain fragile 
+   HBAs. The twin fetch technique should not be used with the TapeAlert
+   log page since it clears its state flags after each fetch. */
+int scsiLogSense(int device, int pagenum, int subpagenum, UINT8 *pBuf,
+                 int bufLen, int known_resp_len)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[10];
+    UINT8 sense[32];
+    int pageLen;
+    int status, res;
+
+    if (known_resp_len > bufLen)
+        return -EIO;
+    if (known_resp_len > 0)
+        pageLen = known_resp_len;
+    else {
+        /* Starting twin fetch strategy: first fetch to find respone length */
+        pageLen = 4;
+        if (pageLen > bufLen)
+            return -EIO;
+        else
+            memset(pBuf, 0, pageLen);
+
+        memset(&io_hdr, 0, sizeof(io_hdr));
+        memset(cdb, 0, sizeof(cdb));
+        io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+        io_hdr.dxfer_len = pageLen;
+        io_hdr.dxferp = pBuf;
+        cdb[0] = LOG_SENSE;
+        cdb[2] = 0x40 | (pagenum & 0x3f);  /* Page control (PC)==1 */
+        cdb[3] = subpagenum;
+        cdb[7] = (pageLen >> 8) & 0xff;
+        cdb[8] = pageLen & 0xff;
+        io_hdr.cmnd = cdb;
+        io_hdr.cmnd_len = sizeof(cdb);
+        io_hdr.sensep = sense;
+        io_hdr.max_sense_len = sizeof(sense);
+        io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+    
+        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+        if (0 != status)
+            return status;
+        scsi_do_sense_disect(&io_hdr, &sinfo);
+        if ((res = scsiSimpleSenseFilter(&sinfo)))
+            return res;
+        /* sanity check on response */
+        if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum))
+            return SIMPLE_ERR_BAD_RESP;
+        if (0 == ((pBuf[2] << 8) + pBuf[3]))
+            return SIMPLE_ERR_BAD_RESP;
+        pageLen = (pBuf[2] << 8) + pBuf[3] + 4;
+        /* some SCSI HBA don't like "odd" length transfers */
+        if (pageLen % 2)
+            pageLen += 1;   
+        if (pageLen > bufLen)
+            pageLen = bufLen;
+    }
+    memset(pBuf, 0, 4);
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = pageLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = LOG_SENSE;
+    cdb[2] = 0x40 | (pagenum & 0x3f);  /* Page control (PC)==1 */
+    cdb[7] = (pageLen >> 8) & 0xff;
+    cdb[8] = pageLen & 0xff;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    status = scsiSimpleSenseFilter(&sinfo);
+    if (0 != status)
+        return status;
+    /* sanity check on response */
+    if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum))
+        return SIMPLE_ERR_BAD_RESP;
+    if (0 == ((pBuf[2] << 8) + pBuf[3]))
+        return SIMPLE_ERR_BAD_RESP;
+    return 0;
+}
+
+/* Send MODE SENSE (6 byte) command. Returns 0 if ok, 1 if NOT READY,
+ * 2 if command not supported (then MODE SENSE(10) should be supported),
+ * 3 if field in command not supported or returns negated errno. 
+ * SPC-3 sections 6.9 and 7.4 (rev 22a) [mode subpage==0] */
+int scsiModeSense(int device, int pagenum, int subpagenum, int pc,
+                  UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status;
+
+    if ((bufLen < 0) || (bufLen > 255))
+        return -EINVAL;
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = MODE_SENSE;
+    cdb[2] = (pc << 6) | (pagenum & 0x3f);
+    cdb[3] = subpagenum;
+    cdb[4] = bufLen;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    status = scsiSimpleSenseFilter(&sinfo);
+    if (SIMPLE_ERR_TRY_AGAIN == status) {
+        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+        if (0 != status)
+            return status;
+        scsi_do_sense_disect(&io_hdr, &sinfo);
+        status = scsiSimpleSenseFilter(&sinfo);
+    }
+    if ((0 == status) && (ALL_MODE_PAGES != pagenum)) {
+        int offset;
+
+        offset = scsiModePageOffset(pBuf, bufLen, 0);
+        if (offset < 0)
+            return SIMPLE_ERR_BAD_RESP;
+        else if (pagenum != (pBuf[offset] & 0x3f))
+            return SIMPLE_ERR_BAD_RESP;
+    }
+    return status;
+}
+
+/* Sends a 6 byte MODE SELECT command. Assumes given pBuf is the response
+ * from a corresponding 6 byte MODE SENSE command. Such a response should
+ * have a 4 byte header followed by 0 or more 8 byte block descriptors
+ * (normally 1) and then 1 mode page. Returns 0 if ok, 1 if NOT READY,
+ * 2 if command not supported (then MODE SELECT(10) may be supported), 
+ * 3 if field in command not supported, 4 if bad parameter to command
+ * or returns negated errno. SPC-3 sections 6.7 and 7.4 (rev 22a) */
+int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status, pg_offset, pg_len, hdr_plus_1_pg;
+
+    pg_offset = 4 + pBuf[3];
+    if (pg_offset + 2 >= bufLen)
+        return -EINVAL;
+    pg_len = pBuf[pg_offset + 1] + 2;
+    hdr_plus_1_pg = pg_offset + pg_len;
+    if (hdr_plus_1_pg > bufLen)
+        return -EINVAL;
+    pBuf[0] = 0;    /* Length of returned mode sense data reserved for SELECT */
+    pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_TO_DEVICE;
+    io_hdr.dxfer_len = hdr_plus_1_pg;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = MODE_SELECT;
+    cdb[1] = 0x10 | (sp & 1);      /* set PF (page format) bit always */
+    cdb[4] = hdr_plus_1_pg; /* make sure only one page sent */
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* MODE SENSE (10 byte). Returns 0 if ok, 1 if NOT READY, 2 if command 
+ * not supported (then MODE SENSE(6) might be supported), 3 if field in
+ * command not supported or returns negated errno.  
+ * SPC-3 sections 6.10 and 7.4 (rev 22a) [mode subpage==0] */
+int scsiModeSense10(int device, int pagenum, int subpagenum, int pc,
+                    UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[10];
+    UINT8 sense[32];
+    int status;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = MODE_SENSE_10;
+    cdb[2] = (pc << 6) | (pagenum & 0x3f);
+    cdb[3] = subpagenum;
+    cdb[7] = (bufLen >> 8) & 0xff;
+    cdb[8] = bufLen & 0xff;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    status = scsiSimpleSenseFilter(&sinfo);
+    if (SIMPLE_ERR_TRY_AGAIN == status) {
+        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+        if (0 != status)
+            return status;
+        scsi_do_sense_disect(&io_hdr, &sinfo);
+        status = scsiSimpleSenseFilter(&sinfo);
+    }
+    if ((0 == status) && (ALL_MODE_PAGES != pagenum)) {
+        int offset;
+
+        offset = scsiModePageOffset(pBuf, bufLen, 1);
+        if (offset < 0)
+            return SIMPLE_ERR_BAD_RESP;
+        else if (pagenum != (pBuf[offset] & 0x3f))
+            return SIMPLE_ERR_BAD_RESP;
+    }
+    return status;
+}
+
+/* Sends a 10 byte MODE SELECT command. Assumes given pBuf is the response
+ * from a corresponding 10 byte MODE SENSE command. Such a response should
+ * have a 8 byte header followed by 0 or more 8 byte block descriptors
+ * (normally 1) and then 1 mode page. Returns 0 if ok, 1 NOT REAFY, 2 if 
+ * command not supported (then MODE SELECT(6) may be supported), 3 if field
+ * in command not supported, 4 if bad parameter to command or returns
+ * negated errno. SPC-3 sections 6.8 and 7.4 (rev 22a) */
+int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[10];
+    UINT8 sense[32];
+    int status, pg_offset, pg_len, hdr_plus_1_pg;
+
+    pg_offset = 8 + (pBuf[6] << 8) + pBuf[7];
+    if (pg_offset + 2 >= bufLen)
+        return -EINVAL;
+    pg_len = pBuf[pg_offset + 1] + 2;
+    hdr_plus_1_pg = pg_offset + pg_len;
+    if (hdr_plus_1_pg > bufLen)
+        return -EINVAL;
+    pBuf[0] = 0;    
+    pBuf[1] = 0; /* Length of returned mode sense data reserved for SELECT */
+    pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_TO_DEVICE;
+    io_hdr.dxfer_len = hdr_plus_1_pg;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = MODE_SELECT_10;
+    cdb[1] = 0x10 | (sp & 1);      /* set PF (page format) bit always */
+    cdb[8] = hdr_plus_1_pg; /* make sure only one page sent */
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* Standard INQUIRY returns 0 for ok, anything else is a major problem.
+ * bufLen should be 36 for unsafe devices (like USB mass storage stuff)
+ * otherwise they can lock up! SPC-3 sections 6.4 and 7.6 (rev 22a) */
+int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_sense_disect sinfo;
+    struct scsi_cmnd_io io_hdr;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status;
+
+    if ((bufLen < 0) || (bufLen > 255))
+        return -EINVAL;
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = INQUIRY;
+    cdb[4] = bufLen;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* INQUIRY to fetch Vital Page Data.  Returns 0 if ok, 1 if NOT READY
+ * (unlikely), 2 if command not supported, 3 if field in command not 
+ * supported, 5 if response indicates that EVPD bit ignored or returns
+ * negated errno. SPC-3 section 6.4 and 7.6 (rev 22a) */
+int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status, res;
+
+    if ((bufLen < 0) || (bufLen > 255))
+        return -EINVAL;
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    if (bufLen > 1)
+        pBuf[1] = 0x0;
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = INQUIRY;
+    cdb[1] = 0x1;       /* set EVPD bit (enable Vital Product Data) */
+    cdb[2] = vpd_page;
+    cdb[4] = bufLen;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    if ((res = scsiSimpleSenseFilter(&sinfo)))
+        return res;
+    /* Guard against devices that ignore EVPD bit and do standard INQUIRY */
+    if (bufLen > 1) {
+        if (vpd_page == pBuf[1]) {
+            if ((0x80 == vpd_page) && (bufLen > 2) && (0x0 != pBuf[2]))
+                return SIMPLE_ERR_BAD_RESP;
+        } else
+            return SIMPLE_ERR_BAD_RESP;
+    }
+    return 0;
+}
+
+/* REQUEST SENSE command. Returns 0 if ok, anything else major problem.
+ * SPC-3 section 6.27 (rev 22a) */
+int scsiRequestSense(int device, struct scsi_sense_disect * sense_info)
+{
+    struct scsi_cmnd_io io_hdr;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    UINT8 buff[18];
+    int status, len;
+    UINT8 ecode;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = sizeof(buff);
+    io_hdr.dxferp = buff;
+    cdb[0] = REQUEST_SENSE;
+    cdb[4] = sizeof(buff);
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if ((0 == status) && (sense_info)) {
+        ecode = buff[0] & 0x7f;
+        sense_info->error_code = ecode;
+        sense_info->sense_key = buff[2] & 0xf;
+        sense_info->asc = 0;
+        sense_info->ascq = 0;
+        if ((0x70 == ecode) || (0x71 == ecode)) {
+            len = buff[7] + 8;
+            if (len > 13) {
+                sense_info->asc = buff[12];
+                sense_info->ascq = buff[13];
+            }
+        }
+    }
+    return status;
+}
+
+/* SEND DIAGNOSTIC command.  Returns 0 if ok, 1 if NOT READY, 2 if command
+ * not supported, 3 if field in command not supported or returns negated
+ * errno. SPC-3 section 6.28 (rev 22a) */
+int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = bufLen ? DXFER_TO_DEVICE: DXFER_NONE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = SEND_DIAGNOSTIC;
+    if (SCSI_DIAG_DEF_SELF_TEST == functioncode)
+        cdb[1] = 0x4;  /* SelfTest bit */
+    else if (SCSI_DIAG_NO_SELF_TEST != functioncode)
+        cdb[1] = (functioncode & 0x7) << 5; /* SelfTest _code_ */
+    else   /* SCSI_DIAG_NO_SELF_TEST == functioncode */
+        cdb[1] = 0x10;  /* PF bit */
+    cdb[3] = (bufLen >> 8) & 0xff;
+    cdb[4] = bufLen & 0xff;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    /* worst case is an extended foreground self test on a big disk */
+    io_hdr.timeout = SCSI_TIMEOUT_SELF_TEST;
+    
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* RECEIVE DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if
+ * command not supported, 3 if field in command not supported or returns
+ * negated errno. SPC-3 section 6.18 (rev 22a) */
+int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf, 
+                      int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = RECEIVE_DIAGNOSTIC;
+    cdb[1] = pcv;
+    cdb[2] = pagenum;
+    cdb[3] = (bufLen >> 8) & 0xff;
+    cdb[4] = bufLen & 0xff;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */
+static int _testunitready(int device, struct scsi_sense_disect * sinfo)
+{
+    struct scsi_cmnd_io io_hdr;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_NONE;
+    io_hdr.dxfer_len = 0;
+    io_hdr.dxferp = NULL;
+    cdb[0] = TEST_UNIT_READY;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, sinfo);
+    return 0;
+}
+
+/* Returns 0 for device responds and media ready, 1 for device responds and
+   media not ready, or returns a negated errno value */
+int scsiTestUnitReady(int device)
+{
+    struct scsi_sense_disect sinfo;
+    int status;
+
+    status = _testunitready(device, &sinfo);
+    if (0 != status)
+        return status;
+    status = scsiSimpleSenseFilter(&sinfo);
+    if (SIMPLE_ERR_TRY_AGAIN == status) {
+        /* power on reset, media changed, ok ... try again */
+        status = _testunitready(device, &sinfo);        
+        if (0 != status)
+            return status;
+        status = scsiSimpleSenseFilter(&sinfo);
+    }
+    return status;
+}
+
+/* READ DEFECT (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
+ * command not supported, 3 if field in command not supported or returns
+ * negated errno. SBC-2 section 5.12 (rev 16) */
+int scsiReadDefect10(int device, int req_plist, int req_glist, int dl_format,
+                     UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[10];
+    UINT8 sense[32];
+    int status;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = READ_DEFECT_10;
+    cdb[2] = (unsigned char)(((req_plist << 4) & 0x10) |
+               ((req_glist << 3) & 0x8) | (dl_format & 0x7));
+    cdb[7] = (bufLen >> 8) & 0xff;
+    cdb[8] = bufLen & 0xff;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* Offset into mode sense (6 or 10 byte) response that actual mode page
+ * starts at (relative to resp[0]). Returns -1 if problem */
+int scsiModePageOffset(const UINT8 * resp, int len, int modese_len)
+{
+    int resp_len, bd_len;
+    int offset = -1;
+
+    if (resp) {
+        if (10 == modese_len) {
+            resp_len = (resp[0] << 8) + resp[1] + 2;
+            bd_len = (resp[6] << 8) + resp[7];
+            offset = bd_len + 8;
+        } else {
+            resp_len = resp[0] + 1;
+            bd_len = resp[3];
+            offset = bd_len + 4;
+        }
+        if ((offset + 2) > len) {
+            pout("scsiModePageOffset: raw_curr too small, offset=%d "
+                 "resp_len=%d bd_len=%d\n", offset, resp_len, bd_len);
+            offset = -1;
+        } else if ((offset + 2) > resp_len) {
+             if ((resp_len > 2) || con->reportscsiioctl)
+                pout("scsiModePageOffset: response length too short, "
+                     "resp_len=%d offset=%d bd_len=%d\n", resp_len,
+                     offset, bd_len);
+            offset = -1;
+        }
+    }
+    return offset;
+}
+
+/* IEC mode page byte 2 bit masks */
+#define DEXCPT_ENABLE   0x08
+#define EWASC_ENABLE    0x10
+#define DEXCPT_DISABLE  0xf7
+#define EWASC_DISABLE   0xef
+#define TEST_DISABLE    0xfb
+
+/* Fetches the Informational Exceptions Control mode page. First tries
+ * the 6 byte MODE SENSE command and if that fails with an illegal opcode
+ * tries a 10 byte MODE SENSE command. Returns 0 if successful, a positive
+ * number if a known error (see  SIMPLE_ERR_ ...) or a negative errno
+ * value. */
+int scsiFetchIECmpage(int device, struct scsi_iec_mode_page *iecp, int modese_len)
+{
+    int err = 0;
+
+    memset(iecp, 0, sizeof(*iecp));
+    iecp->modese_len = modese_len;
+    iecp->requestedCurrent = 1;
+    if (iecp->modese_len <= 6) {
+        if ((err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
+                                 0, MPAGE_CONTROL_CURRENT,
+                                 iecp->raw_curr, sizeof(iecp->raw_curr)))) {
+            if (SIMPLE_ERR_BAD_OPCODE == err)
+                iecp->modese_len = 10;
+            else {
+                iecp->modese_len = 0;
+                return err;
+            }
+        } else if (0 == iecp->modese_len)
+            iecp->modese_len = 6;
+    }
+    if (10 == iecp->modese_len) {
+        err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
+                              0, MPAGE_CONTROL_CURRENT,
+                              iecp->raw_curr, sizeof(iecp->raw_curr));
+        if (err) {
+            iecp->modese_len = 0;
+            return err;
+        }
+    } 
+    iecp->gotCurrent = 1;
+    iecp->requestedChangeable = 1;
+    if (10 == iecp->modese_len)
+        err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
+                              0, MPAGE_CONTROL_CHANGEABLE,
+                              iecp->raw_chg, sizeof(iecp->raw_chg));
+    else if (6 == iecp->modese_len)
+        err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, 
+                            0, MPAGE_CONTROL_CHANGEABLE, 
+                            iecp->raw_chg, sizeof(iecp->raw_chg));
+    if (err)
+        return err;
+    iecp->gotChangeable = 1;
+    return 0;
+}
+
+int scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp)
+{
+    int offset;
+
+    if (iecp && iecp->gotCurrent) {
+        offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
+                                    iecp->modese_len);
+        if (offset >= 0)
+            return (iecp->raw_curr[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
+        else
+            return 0;
+    } else
+        return 0;
+}
+
+int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp)
+{
+    int offset;
+
+    if (iecp && iecp->gotCurrent) {
+        offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
+                                    iecp->modese_len);
+        if (offset >= 0)
+            return (iecp->raw_curr[offset + 2] & EWASC_ENABLE) ? 1 : 0;
+        else
+            return 0;
+    } else
+        return 0;
+}
+
+/* set EWASC and clear PERF, EBF, DEXCPT TEST and LOGERR */
+#define SCSI_IEC_MP_BYTE2_ENABLED 0x10 
+#define SCSI_IEC_MP_BYTE2_TEST_MASK 0x4
+/* exception/warning via an unrequested REQUEST SENSE command */
+#define SCSI_IEC_MP_MRIE 6      
+#define SCSI_IEC_MP_INTERVAL_T 0
+#define SCSI_IEC_MP_REPORT_COUNT 1
+
+/* Try to set (or clear) both Exception Control and Warning in the IE
+ * mode page subject to the "changeable" mask. The object pointed to
+ * by iecp is (possibly) inaccurate after this call, therefore
+ * scsiFetchIECmpage() should be called again if the IEC mode page
+ * is to be re-examined.
+ * When -r ioctl is invoked 3 or more time on 'smartctl -s on ...'
+ * then set the TEST bit (causes asc,ascq pair of 0x5d,0xff). */
+int scsiSetExceptionControlAndWarning(int device, int enabled,
+                                      const struct scsi_iec_mode_page *iecp)
+{
+    int k, offset, resp_len;
+    int err = 0;
+    UINT8 rout[SCSI_IECMP_RAW_LEN];
+    int sp, eCEnabled, wEnabled;
+
+    if ((! iecp) || (! iecp->gotCurrent))
+        return -EINVAL;
+    offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
+                                iecp->modese_len);
+    if (offset < 0)
+        return -EINVAL;
+    memcpy(rout, iecp->raw_curr, SCSI_IECMP_RAW_LEN);
+    if (10 == iecp->modese_len) {
+        resp_len = (rout[0] << 8) + rout[1] + 2;
+        rout[3] &= 0xef;    /* for disks mask out DPOFUA bit */
+    } else {
+        resp_len = rout[0] + 1;
+        rout[2] &= 0xef;    /* for disks mask out DPOFUA bit */
+    }
+    sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
+    if (enabled) {
+        rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED;
+        if (con->reportscsiioctl > 2)
+            rout[offset + 2] |= SCSI_IEC_MP_BYTE2_TEST_MASK;
+        rout[offset + 3] = SCSI_IEC_MP_MRIE;
+        rout[offset + 4] = (SCSI_IEC_MP_INTERVAL_T >> 24) & 0xff;
+        rout[offset + 5] = (SCSI_IEC_MP_INTERVAL_T >> 16) & 0xff;
+        rout[offset + 6] = (SCSI_IEC_MP_INTERVAL_T >> 8) & 0xff;
+        rout[offset + 7] = SCSI_IEC_MP_INTERVAL_T & 0xff;
+        rout[offset + 8] = (SCSI_IEC_MP_REPORT_COUNT >> 24) & 0xff;
+        rout[offset + 9] = (SCSI_IEC_MP_REPORT_COUNT >> 16) & 0xff;
+        rout[offset + 10] = (SCSI_IEC_MP_REPORT_COUNT >> 8) & 0xff;
+        rout[offset + 11] = SCSI_IEC_MP_REPORT_COUNT & 0xff;
+        if (iecp->gotChangeable) {
+            UINT8 chg2 = iecp->raw_chg[offset + 2];
+
+            rout[offset + 2] = chg2 ? (rout[offset + 2] & chg2) :
+                                      iecp->raw_curr[offset + 2];
+            for (k = 3; k < 12; ++k) {
+                if (0 == iecp->raw_chg[offset + k])
+                    rout[offset + k] = iecp->raw_curr[offset + k];
+            }
+        }
+        if (0 == memcmp(&rout[offset + 2], &iecp->raw_chg[offset + 2], 10)) {
+            if (con->reportscsiioctl > 0)
+                pout("scsiSetExceptionControlAndWarning: already enabled\n");
+            return 0;
+        }
+    } else { /* disabling Exception Control and (temperature) Warnings */
+        eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
+        wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0;
+        if ((! eCEnabled) && (! wEnabled)) {
+            if (con->reportscsiioctl > 0)
+                pout("scsiSetExceptionControlAndWarning: already disabled\n");
+            return 0;   /* nothing to do, leave other setting alone */
+        }
+        if (wEnabled) 
+            rout[offset + 2] &= EWASC_DISABLE;
+        if (eCEnabled) {
+            if (iecp->gotChangeable && 
+                (iecp->raw_chg[offset + 2] & DEXCPT_ENABLE))
+                rout[offset + 2] |= DEXCPT_ENABLE;
+                rout[offset + 2] &= TEST_DISABLE;/* clear TEST bit for spec */
+        }
+    }
+    if (10 == iecp->modese_len)
+        err = scsiModeSelect10(device, sp, rout, resp_len);
+    else if (6 == iecp->modese_len)
+        err = scsiModeSelect(device, sp, rout, resp_len);
+    return err;
+}
+
+int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp)
+{
+    UINT8 tBuf[252];
+    int err;
+
+    memset(tBuf, 0, sizeof(tBuf));
+    if ((err = scsiLogSense(device, TEMPERATURE_LPAGE, 0, tBuf,
+                            sizeof(tBuf), 0))) {
+        *currenttemp = 0;
+        *triptemp = 0;
+        pout("Log Sense for temperature failed [%s]\n", scsiErrString(err));
+        return err;
+    }
+    *currenttemp = tBuf[9];
+    *triptemp = tBuf[15];
+    return 0;
+}
+
+/* Read informational exception log page or Request Sense response.
+ * Fetching asc/ascq code potentially flagging an exception or warning.
+ * Returns 0 if ok, else error number. A current temperature of 255
+ * (Celsius) implies that the temperature not available. */
+int scsiCheckIE(int device, int hasIELogPage, int hasTempLogPage,
+                UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp,
+                UINT8 *triptemp)
+{
+    UINT8 tBuf[252];
+    struct scsi_sense_disect sense_info;
+    int err;
+    int temperatureSet = 0;
+    unsigned short pagesize;
+    UINT8 currTemp, trTemp;
+    *asc = 0;
+    *ascq = 0;
+    *currenttemp = 0;
+    *triptemp = 0;
+    memset(tBuf,0,sizeof(tBuf)); // need to clear stack space of junk
+    memset(&sense_info, 0, sizeof(sense_info));
+    if (hasIELogPage) {
+        if ((err = scsiLogSense(device, IE_LPAGE, 0, tBuf,
+                                sizeof(tBuf), 0))) {
+            pout("Log Sense failed, IE page [%s]\n", scsiErrString(err));
+            return err;
+        }
+        // pull out page size from response, don't forget to add 4
+        pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4; 
+        if ((pagesize < 4) || tBuf[4] || tBuf[5]) {
+            pout("Log Sense failed, IE page, bad parameter code or length\n");
+            return SIMPLE_ERR_BAD_PARAM;
+        }
+        if (tBuf[7] > 1) {
+            sense_info.asc = tBuf[8]; 
+            sense_info.ascq = tBuf[9];
+            if (! hasTempLogPage) {
+                if (tBuf[7] > 2) 
+                    *currenttemp = tBuf[10];
+                if (tBuf[7] > 3)        /* IBM extension in SMART (IE) lpage */
+                    *triptemp = tBuf[11];
+            }
+        } 
+    }
+    if (0 == sense_info.asc) {    
+        /* ties in with MRIE field of 6 in IEC mode page (0x1c) */
+        if ((err = scsiRequestSense(device, &sense_info))) {
+            pout("Request Sense failed, [%s]\n", scsiErrString(err));
+            return err;
+        }
+    }
+    *asc = sense_info.asc;
+    *ascq = sense_info.ascq;
+    if ((! temperatureSet) && hasTempLogPage) {
+        if (0 == scsiGetTemp(device, &currTemp, &trTemp)) {
+            *currenttemp = currTemp;
+            *triptemp = trTemp;
+        }
+    }
+    return 0;
+}
+
+// The first character (W, C, I) tells the severity
+static const char * TapeAlertsMessageTable[]= {  
+    " ",
+    /* 0x01 */
+   "W: The tape drive is having problems reading data. No data has been lost,\n"
+       "  but there has been a reduction in the performance of the tape.",
+    /* 0x02 */
+   "W: The tape drive is having problems writing data. No data has been lost,\n"
+       "  but there has been a reduction in the capacity of the tape.",
+    /* 0x03 */
+   "W: The operation has stopped because an error has occurred while reading\n"
+       "  or writing data that the drive cannot correct.",
+    /* 0x04 */
+   "C: Your data is at risk:\n"
+       "  1. Copy any data you require from this tape. \n"
+       "  2. Do not use this tape again.\n"
+       "  3. Restart the operation with a different tape.",
+    /* 0x05 */
+   "C: The tape is damaged or the drive is faulty. Call the tape drive\n"
+       "  supplier helpline.",
+    /* 0x06 */
+   "C: The tape is from a faulty batch or the tape drive is faulty:\n"
+       "  1. Use a good tape to test the drive.\n"
+       "  2. If problem persists, call the tape drive supplier helpline.",
+    /* 0x07 */
+   "W: The tape cartridge has reached the end of its calculated useful life:\n"
+       "  1. Copy data you need to another tape.\n"
+       "  2. Discard the old tape.",
+    /* 0x08 */
+   "W: The tape cartridge is not data-grade. Any data you back up to the tape\n"
+       "  is at risk. Replace the cartridge with a data-grade tape.",
+    /* 0x09 */
+   "C: You are trying to write to a write-protected cartridge. Remove the\n"
+       "  write-protection or use another tape.",
+    /* 0x0a */
+   "I: You cannot eject the cartridge because the tape drive is in use. Wait\n"
+       "  until the operation is complete before ejecting the cartridge.",
+    /* 0x0b */
+   "I: The tape in the drive is a cleaning cartridge.",
+    /* 0x0c */
+   "I: You have tried to load a cartridge of a type which is not supported\n"
+       "  by this drive.",
+    /* 0x0d */
+   "C: The operation has failed because the tape in the drive has experienced\n"
+       "  a mechanical failure:\n"
+       "  1. Discard the old tape.\n"
+       "  2. Restart the operation with a different tape.",
+    /* 0x0e */
+   "C: The operation has failed because the tape in the drive has experienced\n"
+       "  a mechanical failure:\n"
+       "  1. Do not attempt to extract the tape cartridge\n"
+       "  2. Call the tape drive supplier helpline.",
+    /* 0x0f */
+   "W: The memory in the tape cartridge has failed, which reduces\n"
+       "  performance. Do not use the cartridge for further write operations.",
+    /* 0x10 */
+   "C: The operation has failed because the tape cartridge was manually\n"
+       "  de-mounted while the tape drive was actively writing or reading.",
+    /* 0x11 */
+   "W: You have loaded a cartridge of a type that is read-only in this drive.\n"
+       "  The cartridge will appear as write-protected.",
+    /* 0x12 */
+   "W: The tape directory on the tape cartridge has been corrupted. File\n"
+       "  search performance will be degraded. The tape directory can be rebuilt\n"
+       "  by reading all the data on the cartridge.",
+    /* 0x13 */
+   "I: The tape cartridge is nearing the end of its calculated life. It is\n"
+       "  recommended that you:\n"
+       "  1. Use another tape cartridge for your next backup.\n"
+       "  2. Store this tape in a safe place in case you need to restore "
+       "  data from it.",
+    /* 0x14 */
+   "C: The tape drive needs cleaning:\n"
+       "  1. If the operation has stopped, eject the tape and clean the drive.\n"
+       "  2. If the operation has not stopped, wait for it to finish and then\n"
+       "  clean the drive.\n"
+       "  Check the tape drive users manual for device specific cleaning instructions.",
+    /* 0x15 */
+   "W: The tape drive is due for routine cleaning:\n"
+       "  1. Wait for the current operation to finish.\n"
+       "  2. The use a cleaning cartridge.\n"
+       "  Check the tape drive users manual for device specific cleaning instructions.",
+    /* 0x16 */
+   "C: The last cleaning cartridge used in the tape drive has worn out:\n"
+       "  1. Discard the worn out cleaning cartridge.\n"
+       "  2. Wait for the current operation to finish.\n"
+       "  3. Then use a new cleaning cartridge.",
+    /* 0x17 */
+   "C: The last cleaning cartridge used in the tape drive was an invalid\n"
+       "  type:\n"
+       "  1. Do not use this cleaning cartridge in this drive.\n"
+       "  2. Wait for the current operation to finish.\n"
+       "  3. Then use a new cleaning cartridge.",
+    /* 0x18 */
+   "W: The tape drive has requested a retention operation",
+    /* 0x19 */
+   "W: A redundant interface port on the tape drive has failed",
+    /* 0x1a */
+   "W: A tape drive cooling fan has failed",
+    /* 0x1b */
+   "W: A redundant power supply has failed inside the tape drive enclosure.\n"
+       "  Check the enclosure users manual for instructions on replacing the\n"
+       "  failed power supply.",
+    /* 0x1c */
+   "W: The tape drive power consumption is outside the specified range.",
+    /* 0x1d */
+   "W: Preventive maintenance of the tape drive is required. Check the tape\n"
+       "  drive users manual for device specific preventive maintenance\n"
+       "  tasks or call the tape drive supplier helpline.",
+    /* 0x1e */
+   "C: The tape drive has a hardware fault:\n"
+       "  1. Eject the tape or magazine.\n"
+       "  2. Reset the drive.\n"
+       "  3. Restart the operation.",
+    /* 0x1f */
+   "C: The tape drive has a hardware fault:\n"
+       "  1. Turn the tape drive off and then on again.\n"
+       "  2. Restart the operation.\n"
+    "  3. If the problem persists, call the tape drive supplier helpline.",
+    /* 0x20 */
+   "W: The tape drive has a problem with the application client interface:\n"
+       "  1. Check the cables and cable connections.\n"
+       "  2. Restart the operation.",
+    /* 0x21 */
+   "C: The operation has failed:\n"
+       "  1. Eject the tape or magazine.\n"
+       "  2. Insert the tape or magazine again.\n"
+       "  3. Restart the operation.",
+    /* 0x22 */
+   "W: The firmware download has failed because you have tried to use the\n"
+       "  incorrect firmware for this tape drive. Obtain the correct\n"
+       "  firmware and try again.",
+    /* 0x23 */
+   "W: Environmental conditions inside the tape drive are outside the\n"
+       "  specified humidity range.",
+    /* 0x24 */
+   "W: Environmental conditions inside the tape drive are outside the\n"
+       "  specified temperature range.",
+    /* 0x25 */
+   "W: The voltage supply to the tape drive is outside the specified range.",
+    /* 0x26 */
+   "C: A hardware failure of the tape drive is predicted. Call the tape\n"
+       "  drive supplier helpline.",
+    /* 0x27 */
+   "W: The tape drive may have a hardware fault. Run extended diagnostics to\n"
+       "  verify and diagnose the problem. Check the tape drive users manual for\n"
+       "  device specific instructions on running extended diagnostic tests.",
+    /* 0x28 */
+   "C: The changer mechanism is having difficulty communicating with the tape\n"
+       "  drive:\n"
+       "  1. Turn the autoloader off then on.\n"
+       "  2. Restart the operation.\n"
+       "  3. If problem persists, call the tape drive supplier helpline.",
+    /* 0x29 */
+   "C: A tape has been left in the autoloader by a previous hardware fault:\n"
+       "  1. Insert an empty magazine to clear the fault.\n"
+       "  2. If the fault does not clear, turn the autoloader off and then\n"
+       "  on again.\n"
+       "  3. If the problem persists, call the tape drive supplier helpline.",
+    /* 0x2a */
+   "W: There is a problem with the autoloader mechanism.",
+    /* 0x2b */
+   "C: The operation has failed because the autoloader door is open:\n"
+       "  1. Clear any obstructions from the autoloader door.\n"
+       "  2. Eject the magazine and then insert it again.\n"
+       "  3. If the fault does not clear, turn the autoloader off and then\n"
+       "  on again.\n"
+       "  4. If the problem persists, call the tape drive supplier helpline.",
+    /* 0x2c */
+   "C: The autoloader has a hardware fault:\n"
+       "  1. Turn the autoloader off and then on again.\n"
+       "  2. Restart the operation.\n"
+       "  3. If the problem persists, call the tape drive supplier helpline.\n"
+       "  Check the autoloader users manual for device specific instructions\n"
+       "  on turning the device power on and off.",
+    /* 0x2d */
+   "C: The autoloader cannot operate without the magazine,\n"
+       "  1. Insert the magazine into the autoloader.\n"
+       "  2. Restart the operation.",
+    /* 0x2e */
+   "W: A hardware failure of the changer mechanism is predicted. Call the\n"
+       "  tape drive supplier helpline.",
+    /* 0x2f */
+   "I: Reserved.",
+    /* 0x30 */
+   "I: Reserved.",
+    /* 0x31 */
+   "I: Reserved.",
+    /* 0x32 */
+   "W: Media statistics have been lost at some time in the past",
+    /* 0x33 */
+   "W: The tape directory on the tape cartridge just unloaded has been\n"
+       "  corrupted. File search performance will be degraded. The tape\n"
+       "  directory can be rebuilt by reading all the data.",
+    /* 0x34 */
+   "C: The tape just unloaded could not write its system area successfully:\n"
+       "  1. Copy data to another tape cartridge.\n"
+       "  2. Discard the old cartridge.",
+    /* 0x35 */
+   "C: The tape system are could not be read successfully at load time:\n"
+    "  1. Copy data to another tape cartridge.\n",
+    /* 0x36 */
+   "C: The start or data could not be found on the tape:\n"
+       "  1. Check you are using the correct format tape.\n"
+       "  2. Discard the tape or return the tape to your supplier",
+    /* 0x37 */
+    "C: The operation has failed because the media cannot be loaded\n"
+        "  and threaded.\n"
+        "  1. Remove the cartridge, inspect it as specified in the product\n"
+        "  manual, and retry the operation.\n"
+        "  2. If the problem persists, call the tape drive supplier help line.",
+    /* 0x38 */
+    "C: The operation has failed because the medium cannot be unloaded:\n"
+        "  1. Do not attempt to extract the tape cartridge.\n"
+        "  2. Call the tape driver supplier help line.",
+    /* 0x39 */
+    "C: The tape drive has a problem with the automation interface:\n"
+        "  1. Check the power to the automation system.\n"
+        "  2. Check the cables and cable connections.\n"
+        "  3. Call the supplier help line if problem persists.",
+    /* 0x3a */
+    "W: The tape drive has reset itself due to a detected firmware\n"
+        "  fault. If problem persists, call the supplier help line.",
+    };
+
+const char * scsiTapeAlertsTapeDevice(unsigned short code)
+{
+    const int num = sizeof(TapeAlertsMessageTable) /
+                        sizeof(TapeAlertsMessageTable[0]);
+
+    return (code < num) ?  TapeAlertsMessageTable[code] : "Unknown Alert"; 
+}
+
+// The first character (W, C, I) tells the severity
+static const char * ChangerTapeAlertsMessageTable[]= {  
+    " ",
+    /* 0x01 */
+    "C: The library mechanism is having difficulty communicating with the\n"
+        "  drive:\n"
+        "  1. Turn the library off then on.\n"
+        "  2. Restart the operation.\n"
+        "  3. If the problem persists, call the library supplier help line.",
+    /* 0x02 */
+    "W: There is a problem with the library mechanism. If problem persists,\n"
+        "  call the library supplier help line.",
+    /* 0x03 */
+    "C: The library has a hardware fault:\n"
+        "  1. Reset the library.\n"
+        "  2. Restart the operation.\n"
+        "  Check the library users manual for device specific instructions on resetting\n"
+        "  the device.",
+    /* 0x04 */
+    "C: The library has a hardware fault:\n"
+        "  1. Turn the library off then on again.\n"
+        "  2. Restart the operation.\n"
+        "  3. If the problem persists, call the library supplier help line.\n"
+        "  Check the library users manual for device specific instructions on turning the\n"
+        "  device power on and off.",
+    /* 0x05 */
+    "W: The library mechanism may have a hardware fault.\n"
+        "  Run extended diagnostics to verify and diagnose the problem. Check the library\n"
+        "  users manual for device specific instructions on running extended diagnostic\n"
+        "  tests.",
+    /* 0x06 */
+    "C: The library has a problem with the host interface:\n"
+        "  1. Check the cables and connections.\n"
+        "  2. Restart the operation.",
+    /* 0x07 */
+    "W: A hardware failure of the library is predicted. Call the library\n"
+        "  supplier help line.",
+    /* 0x08 */
+    "W: Preventive maintenance of the library is required.\n"
+        "  Check the library users manual for device specific preventative maintenance\n"
+        "  tasks, or call your library supplier help line.",
+    /* 0x09 */
+    "C: General environmental conditions inside the library are outside the\n"
+        "  specified humidity range.",
+    /* 0x0a */
+    "C: General environmental conditions inside the library are outside the\n"
+        "  specified temperature range.",
+    /* 0x0b */
+    "C: The voltage supply to the library is outside the specified range.\n"
+        "  There is a potential problem with the power supply or failure of\n"
+        "  a redundant power supply.",
+    /* 0x0c */
+    "C: A cartridge has been left inside the library by a previous hardware\n"
+        "  fault:\n"
+        "  1. Insert an empty magazine to clear the fault.\n"
+        "  2. If the fault does not clear, turn the library off and then on again.\n"
+        "  3. If the problem persists, call the library supplier help line.",
+    /* 0x0d */
+    "W: There is a potential problem with the drive ejecting cartridges or\n"
+        "  with the library mechanism picking a cartridge from a slot.\n"
+        "  1. No action needs to be taken at this time.\n"
+        "  2. If the problem persists, call the library supplier help line.",
+    /* 0x0e */
+    "W: There is a potential problem with the library mechanism placing a\n"
+        "  cartridge into a slot.\n"
+        "  1. No action needs to be taken at this time.\n"
+        "  2. If the problem persists, call the library supplier help line.",
+    /* 0x0f */
+    "W: There is a potential problem with the drive or the library mechanism\n"
+        "  loading cartridges, or an incompatible cartridge.",
+    /* 0x10 */
+    "C: The library has failed because the door is open:\n"
+        "  1. Clear any obstructions from the library door.\n"
+        "  2. Close the library door.\n"
+        "  3. If the problem persists, call the library supplier help line.",
+    /* 0x11 */
+    "C: There is a mechanical problem with the library media import/export\n"
+        "  mailslot.",
+    /* 0x12 */
+    "C: The library cannot operate without the magazine.\n"
+        "  1. Insert the magazine into the library.\n"
+        "  2. Restart the operation.",
+    /* 0x13 */
+    "W: Library security has been compromised.",
+    /* 0x14 */
+    "I: The library security mode has been changed.\n"
+        "  The library has either been put into secure mode, or the library has exited\n"
+        "  the secure mode.\n"
+        "  This is for information purposes only. No action is required.",
+    /* 0x15 */
+    "I: The library has been manually turned offline and is unavailable for use.",
+    /* 0x16 */
+    "I: A drive inside the library has been taken offline.\n"
+        "  This is for information purposes only. No action is required.",
+    /* 0x17 */
+    "W: There is a potential problem with the bar code label or the scanner\n"
+        "  hardware in the library mechanism.\n"
+        "  1. No action needs to be taken at this time.\n"
+        "  2. If the problem persists, call the library supplier help line.",
+    /* 0x18 */
+    "C: The library has detected an inconsistency in its inventory.\n"
+        "  1. Redo the library inventory to correct inconsistency.\n"
+        "  2. Restart the operation.\n"
+        "  Check the applications users manual or the hardware users manual for\n"
+        "  specific instructions on redoing the library inventory.",
+    /* 0x19 */
+    "W: A library operation has been attempted that is invalid at this time.",
+    /* 0x1a */
+    "W: A redundant interface port on the library has failed.",
+    /* 0x1b */
+    "W: A library cooling fan has failed.",
+    /* 0x1c */
+    "W: A redundant power supply has failed inside the library. Check the\n"
+        "  library users manual for instructions on replacing the failed power supply.",
+    /* 0x1d */
+    "W: The library power consumption is outside the specified range.",
+    /* 0x1e */
+    "C: A failure has occurred in the cartridge pass-through mechanism between\n"
+        "  two library modules.",
+    /* 0x1f */
+    "C: A cartridge has been left in the pass-through mechanism from a\n"
+        "  previous hardware fault. Check the library users guide for instructions on\n"
+        "  clearing this fault.",
+    /* 0x20 */
+    "I: The library was unable to read the bar code on a cartridge.",
+};
+
+const char * scsiTapeAlertsChangerDevice(unsigned short code)
+{
+    const int num = sizeof(ChangerTapeAlertsMessageTable) /
+                        sizeof(ChangerTapeAlertsMessageTable[0]);
+
+    return (code < num) ?  ChangerTapeAlertsMessageTable[code] : "Unknown Alert"; 
+}
+
+
+/* this is a subset of the SCSI additional sense code strings indexed
+ * by "ascq" for the case when asc==SCSI_ASC_IMPENDING_FAILURE (0x5d)
+ */
+static const char * strs_for_asc_5d[] = {
+   /* 0x00 */   "FAILURE PREDICTION THRESHOLD EXCEEDED",
+        "MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED",
+        "LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED",
+        "SPARE AREA EXHAUSTION PREDICTION THRESHOLD EXCEEDED",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+   /* 0x10 */   "HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "HARDWARE IMPENDING FAILURE CONTROLLER DETECTED",
+        "HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
+        "HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
+        "",
+        "",
+        "",
+   /* 0x20 */   "CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED",
+        "CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT",
+        "CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
+        "",
+        "",
+        "",
+   /* 0x30 */   "DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED",
+        "DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT",
+        "DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
+        "",
+        "",
+        "",
+   /* 0x40 */   "SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "SERVO IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "SERVO IMPENDING FAILURE CONTROLLER DETECTED",
+        "SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT",
+        "SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
+        "",
+        "",
+        "",
+   /* 0x50 */   "SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "SPINDLE IMPENDING FAILURE CONTROLLER DETECTED",
+        "SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT",
+        "SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
+        "",
+        "",
+        "",
+   /* 0x60 */   "FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED",
+        "FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
+   /* 0x6c */   "FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT"};
+
+
+/* this is a subset of the SCSI additional sense code strings indexed
+ *  * by "ascq" for the case when asc==SCSI_ASC_WARNING (0xb)
+ *   */
+static const char * strs_for_asc_b[] = {
+       /* 0x00 */   "WARNING",
+               "WARNING - SPECIFIED TEMPERATURE EXCEEDED",
+               "WARNING - ENCLOSURE DEGRADED"};
+
+static char spare_buff[128];
+
+const char * scsiGetIEString(UINT8 asc, UINT8 ascq)
+{
+    const char * rp;
+
+    if (SCSI_ASC_IMPENDING_FAILURE == asc) {
+        if (ascq == 0xff)
+            return "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)";
+        else if (ascq < 
+                 (sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) {
+            rp = strs_for_asc_5d[ascq];
+            if (strlen(rp) > 0)
+                return rp;
+        }
+        snprintf(spare_buff, sizeof(spare_buff),
+                 "FAILURE PREDICTION THRESHOLD EXCEEDED: ascq=0x%x", ascq);
+        return spare_buff;
+    } else if (SCSI_ASC_WARNING == asc) {
+        if (ascq < (sizeof(strs_for_asc_b) / sizeof(strs_for_asc_b[0]))) {
+            rp = strs_for_asc_b[ascq];
+            if (strlen(rp) > 0)
+                return rp;
+        }
+        snprintf(spare_buff, sizeof(spare_buff), "WARNING: ascq=0x%x", ascq);
+        return spare_buff;
+    }
+    return NULL;        /* not a IE additional sense code */
+}
+
+
+/* This is not documented in t10.org, page 0x80 is vendor specific */
+/* Some IBM disks do an offline read-scan when they get this command. */
+int scsiSmartIBMOfflineTest(int device)
+{       
+    UINT8 tBuf[256];
+    int res;
+        
+    memset(tBuf, 0, sizeof(tBuf));
+    /* Build SMART Off-line Immediate Diag Header */
+    tBuf[0] = 0x80; /* Page Code */
+    tBuf[1] = 0x00; /* Reserved */
+    tBuf[2] = 0x00; /* Page Length MSB */
+    tBuf[3] = 0x04; /* Page Length LSB */
+    tBuf[4] = 0x03; /* SMART Revision */
+    tBuf[5] = 0x00; /* Reserved */
+    tBuf[6] = 0x00; /* Off-line Immediate Time MSB */
+    tBuf[7] = 0x00; /* Off-line Immediate Time LSB */
+    res = scsiSendDiagnostic(device, SCSI_DIAG_NO_SELF_TEST, tBuf, 8);
+    if (res)
+        pout("IBM offline test failed [%s]\n", scsiErrString(res));
+    return res;
+}
+
+int scsiSmartDefaultSelfTest(int device)
+{       
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_DEF_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Default self test failed [%s]\n", scsiErrString(res));
+    return res;
+}
+
+int scsiSmartShortSelfTest(int device)
+{       
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_BG_SHORT_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Short offline self test failed [%s]\n", scsiErrString(res));
+    return res;
+}
+
+int scsiSmartExtendSelfTest(int device)
+{       
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_BG_EXTENDED_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Long (extended) offline self test failed [%s]\n",
+             scsiErrString(res));
+    return res;
+}
+
+int scsiSmartShortCapSelfTest(int device)
+{       
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_FG_SHORT_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Short foreground self test failed [%s]\n", scsiErrString(res));
+    return res;
+}
+
+int scsiSmartExtendCapSelfTest(int device)
+{
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_FG_EXTENDED_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Long (extended) foreground self test failed [%s]\n",
+             scsiErrString(res));
+    return res;
+}
+
+int scsiSmartSelfTestAbort(int device)
+{
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_ABORT_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Abort self test failed [%s]\n", scsiErrString(res));
+    return res;
+}
+
+/* Returns 0 and the expected duration of an extended self test (in seconds)
+   if successful; any other return value indicates a failure. */
+int scsiFetchExtendedSelfTestTime(int device, int * durationSec, int modese_len)
+{
+    int err, offset, res;
+    UINT8 buff[64];
+
+    memset(buff, 0, sizeof(buff));
+    if (modese_len <= 6) {
+        if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
+                                 MPAGE_CONTROL_CURRENT,
+                                 buff, sizeof(buff)))) {
+            if (SIMPLE_ERR_BAD_OPCODE == err)
+                modese_len = 10;
+            else
+                return err;
+        } else if (0 == modese_len)
+            modese_len = 6;
+    }
+    if (10 == modese_len) {
+        err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
+                              MPAGE_CONTROL_CURRENT, 
+                              buff, sizeof(buff));
+        if (err)
+            return err;
+    } 
+    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
+    if (offset < 0)
+        return -EINVAL;
+    if (buff[offset + 1] >= 0xa) {
+        res = (buff[offset + 10] << 8) | buff[offset + 11];
+        *durationSec = res;
+        return 0;
+    }
+    else
+        return -EINVAL;
+}
+
+void scsiDecodeErrCounterPage(unsigned char * resp, 
+                              struct scsiErrorCounter *ecp)
+{
+    int k, j, num, pl, pc;
+    unsigned char * ucp;
+    unsigned char * xp;
+    uint64_t * ullp;
+
+    memset(ecp, 0, sizeof(*ecp));
+    num = (resp[2] << 8) | resp[3];
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        switch (pc) {
+            case 0: 
+            case 1: 
+            case 2: 
+            case 3: 
+            case 4: 
+            case 5: 
+            case 6: 
+                ecp->gotPC[pc] = 1;
+                ullp = &ecp->counter[pc];
+                break;
+        default: 
+                ecp->gotExtraPC = 1;
+                ullp = &ecp->counter[7];
+                break;
+        }
+        k = pl - 4;
+        xp = ucp + 4;
+        if (k > (int)sizeof(*ullp)) {
+            xp += (k - sizeof(*ullp));
+            k = sizeof(*ullp);
+        }
+        *ullp = 0;
+        for (j = 0; j < k; ++j) {
+            if (j > 0)
+                *ullp <<= 8;
+            *ullp |= xp[j];
+        }
+        num -= pl;
+        ucp += pl;
+    }
+}
+
+void scsiDecodeNonMediumErrPage(unsigned char *resp, 
+                                struct scsiNonMediumError *nmep)
+{
+    int k, j, num, pl, pc, szof;
+    unsigned char * ucp;
+    unsigned char * xp;
+
+    memset(nmep, 0, sizeof(*nmep));
+    num = (resp[2] << 8) | resp[3];
+    ucp = &resp[0] + 4;
+    szof = sizeof(nmep->counterPC0);
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        switch (pc) {
+            case 0: 
+                nmep->gotPC0 = 1;
+                k = pl - 4;
+                xp = ucp + 4;
+                if (k > szof) {
+                    xp += (k - szof);
+                    k = szof;
+                }
+                nmep->counterPC0 = 0;
+                for (j = 0; j < k; ++j) {
+                    if (j > 0)
+                        nmep->counterPC0 <<= 8;
+                    nmep->counterPC0 |= xp[j];
+                }
+                break;
+            case 0x8009: 
+                nmep->gotTFE_H = 1;
+                k = pl - 4;
+                xp = ucp + 4;
+                if (k > szof) {
+                    xp += (k - szof);
+                    k = szof;
+                }
+                nmep->counterTFE_H = 0;
+                for (j = 0; j < k; ++j) {
+                    if (j > 0)
+                        nmep->counterTFE_H <<= 8;
+                    nmep->counterTFE_H |= xp[j];
+                }
+                break;
+            case 0x8015: 
+                nmep->gotPE_H = 1;
+                k = pl - 4;
+                xp = ucp + 4;
+                if (k > szof) {
+                    xp += (k - szof);
+                    k = szof;
+                }
+                nmep->counterPE_H = 0;
+                for (j = 0; j < k; ++j) {
+                    if (j > 0)
+                        nmep->counterPE_H <<= 8;
+                    nmep->counterPE_H |= xp[j];
+                }
+                break;
+        default: 
+                nmep->gotExtraPC = 1;
+                break;
+        }
+        num -= pl;
+        ucp += pl;
+    }
+}
+
+/* Counts number of failed self-tests. Also encodes the poweron_hour
+   of the most recent failed self-test. Return value is negative if
+   this function has a problem (typically -1), otherwise the bottom 8
+   bits are the number of failed self tests and the 16 bits above that
+   are the poweron hour of the most recent failure. Note: aborted self
+   tests (typically by the user) and self tests in progress are not 
+   considered failures. See Working Draft SCSI Primary Commands - 3 
+   (SPC-3) section 7.2.10 T10/1416-D (rev 22a) */
+int scsiCountFailedSelfTests(int fd, int noisy)
+{
+    int num, k, n, err, res, fails, fail_hour;
+    UINT8 * ucp;
+    unsigned char resp[LOG_RESP_SELF_TEST_LEN];
+
+    if ((err = scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp,
+                            LOG_RESP_SELF_TEST_LEN, 0))) {
+        if (noisy)
+            pout("scsiCountSelfTests Failed [%s]\n", scsiErrString(err));
+        return -1;
+    }
+    if ((resp[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
+        if (noisy)
+            pout("Self-test Log Sense Failed, page mismatch\n");
+        return -1;
+    }
+    // compute page length
+    num = (resp[2] << 8) + resp[3];
+    // Log sense page length 0x190 bytes
+    if (num != 0x190) {
+        if (noisy)
+            pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n", num);
+        return -1;
+    }
+    fails = 0;
+    fail_hour = 0;
+    // loop through the twenty possible entries
+    for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) {
+
+        // timestamp in power-on hours (or zero if test in progress)
+        n = (ucp[6] << 8) | ucp[7];
+
+        // The spec says "all 20 bytes will be zero if no test" but
+        // DG has found otherwise.  So this is a heuristic.
+        if ((0 == n) && (0 == ucp[4]))
+            break;
+        res = ucp[4] & 0xf;
+        if ((res > 2) && (res < 8)) {
+            fails++;
+            if (1 == fails) 
+                fail_hour = (ucp[6] << 8) + ucp[7];
+        }
+    }
+    return (fail_hour << 8) + fails;
+}
+
+/* Returns 0 if able to read self test log page; then outputs 1 into
+   *inProgress if self test still in progress, else outputs 0. */
+int scsiSelfTestInProgress(int fd, int * inProgress)
+{
+    int num;
+    UINT8 * ucp;
+    unsigned char resp[LOG_RESP_SELF_TEST_LEN];
+
+    if (scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp,
+                     LOG_RESP_SELF_TEST_LEN, 0))
+        return -1;
+    if (resp[0] != SELFTEST_RESULTS_LPAGE)
+        return -1;
+    // compute page length
+    num = (resp[2] << 8) + resp[3];
+    // Log sense page length 0x190 bytes
+    if (num != 0x190) {
+        return -1;
+    }
+    ucp = resp + 4;
+    if (inProgress)
+        *inProgress = (0xf == (ucp[4] & 0xf)) ? 1 : 0;
+    return 0;
+}
+
+/* Returns a negative value if failed to fetch Contol mode page or it was
+   malformed. Returns 0 if GLTSD bit is zero and returns 1 if the GLTSD
+   bit is set. Examines default mode page when current==0 else examines
+   current mode page. */
+int scsiFetchControlGLTSD(int device, int modese_len, int current)
+{
+    int err, offset;
+    UINT8 buff[64];
+    int pc = current ? MPAGE_CONTROL_CURRENT : MPAGE_CONTROL_DEFAULT;
+
+    memset(buff, 0, sizeof(buff));
+    if (modese_len <= 6) {
+        if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0, pc,
+                                 buff, sizeof(buff)))) {
+            if (SIMPLE_ERR_BAD_OPCODE == err)
+                modese_len = 10;
+            else
+                return -EINVAL;
+        } else if (0 == modese_len)
+            modese_len = 6;
+    }
+    if (10 == modese_len) {
+        err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0, pc,
+                              buff, sizeof(buff));
+        if (err)
+            return -EINVAL;
+    } 
+    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
+    if ((offset >= 0) && (buff[offset + 1] >= 0xa))
+        return (buff[offset + 2] & 2) ? 1 : 0;
+    return -EINVAL;
+}
+
+/* Attempts to set or clear GLTSD bit in Control mode page. If enabled is
+   0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if
+   successful, negative if low level error, > 0 if higher level error (e.g.
+   SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */
+int scsiSetControlGLTSD(int device, int enabled, int modese_len)
+{
+    int err, offset, resp_len, sp;
+    UINT8 buff[64];
+    UINT8 ch_buff[64];
+
+    memset(buff, 0, sizeof(buff));
+    if (modese_len <= 6) {
+        if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
+                                 MPAGE_CONTROL_CURRENT,
+                                 buff, sizeof(buff)))) {
+            if (SIMPLE_ERR_BAD_OPCODE == err)
+                modese_len = 10;
+            else
+                return err;
+        } else if (0 == modese_len)
+            modese_len = 6;
+    }
+    if (10 == modese_len) {
+        err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
+                              MPAGE_CONTROL_CURRENT,
+                              buff, sizeof(buff));
+        if (err)
+            return err;
+    } 
+    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
+    if ((offset < 0) || (buff[offset + 1] < 0xa))
+        return SIMPLE_ERR_BAD_RESP;
+
+    if (enabled)
+        enabled = 2;
+    if (enabled == (buff[offset + 2] & 2))
+        return 0;       /* GLTSD already in wanted state so nothing to do */
+
+    if (modese_len == 6)
+        err = scsiModeSense(device, CONTROL_MODE_PAGE, 0,
+                            MPAGE_CONTROL_CHANGEABLE,
+                            ch_buff, sizeof(ch_buff));
+    else
+        err = scsiModeSense10(device, CONTROL_MODE_PAGE, 0,
+                              MPAGE_CONTROL_CHANGEABLE,
+                              ch_buff, sizeof(ch_buff));
+    if (err)
+        return err;
+    if (0 == (ch_buff[offset + 2] & 2))
+        return SIMPLE_ERR_BAD_PARAM;  /* GLTSD bit not chageable */
+    
+    if (10 == modese_len) {
+        resp_len = (buff[0] << 8) + buff[1] + 2;
+        buff[3] &= 0xef;    /* for disks mask out DPOFUA bit */
+    } else {
+        resp_len = buff[0] + 1;
+        buff[2] &= 0xef;    /* for disks mask out DPOFUA bit */
+    }
+    sp = (buff[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
+    if (enabled)
+        buff[offset + 2] |= 0x2;    /* set GLTSD bit */
+    else
+        buff[offset + 2] &= 0xfd;   /* clear GLTSD bit */
+    if (10 == modese_len)
+        err = scsiModeSelect10(device, sp, buff, resp_len);
+    else if (6 == modese_len)
+        err = scsiModeSelect(device, sp, buff, resp_len);
+    return err;
+}
+
+/* Returns a negative value if failed to fetch Protocol specific port mode 
+   page or it was malformed. Returns transport protocol identifier when
+   value >= 0 . */
+int scsiFetchTransportProtocol(int device, int modese_len)
+{
+    int err, offset;
+    UINT8 buff[64];
+
+    memset(buff, 0, sizeof(buff));
+    if (modese_len <= 6) {
+        if ((err = scsiModeSense(device, PROTOCOL_SPECIFIC_PORT_PAGE, 0,
+                                 MPAGE_CONTROL_CURRENT,
+                                 buff, sizeof(buff)))) {
+            if (SIMPLE_ERR_BAD_OPCODE == err)
+                modese_len = 10;
+            else
+                return -EINVAL;
+        } else if (0 == modese_len)
+            modese_len = 6;
+    }
+    if (10 == modese_len) {
+        err = scsiModeSense10(device, PROTOCOL_SPECIFIC_PORT_PAGE, 0,
+                              MPAGE_CONTROL_CURRENT,
+                              buff, sizeof(buff));
+        if (err)
+            return -EINVAL;
+    } 
+    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
+    if ((offset >= 0) && (buff[offset + 1] > 1)) {
+        if ((0 == (buff[offset] & 0x40)) &&       /* SPF==0 */
+            (PROTOCOL_SPECIFIC_PORT_PAGE == (buff[offset] & 0x3f))) 
+                return (buff[offset + 2] & 0xf);
+    }
+    return -EINVAL;
+}
+
+
+/* This is Linux specific code to look for the libata ATA-SCSI
+   simulator in the vendor device identification page.
+   Returns 1 if found else 0. */
+int isLinuxLibAta(unsigned char * vpd_di_buff, int len)
+{
+    int k, id_len, c_set, assoc, id_type, i_len;
+    unsigned char * ucp;
+    unsigned char * ip;
+    unsigned char buff1[20];
+    unsigned char buff2[20];
+
+    if (len < 4) {
+        /* Device identification VPD page length too short */
+        return 0;
+    }
+    buff1[0] = '\0';
+    buff2[0] = '\0';
+    len -= 4;
+    ucp = vpd_di_buff + 4;
+    for (k = 0; k < len; k += id_len, ucp += id_len) {
+        i_len = ucp[3];
+        id_len = i_len + 4;
+        if ((k + id_len) > len) {
+            /* short descriptor, badly formed */
+            return 0;
+        }
+        ip = ucp + 4;
+        c_set = (ucp[0] & 0xf);
+        assoc = ((ucp[1] >> 4) & 0x3);
+        id_type = (ucp[1] & 0xf);
+        if ((0 == id_type) && (2 == c_set) && (0 == assoc)) {
+            /* assoc=lu, c_set=ascii, id_type=vendor */
+            if (0 == strncmp((const char *)ip,
+                             "Linux ATA-SCSI simulator", i_len)) {
+                /* until lk 2.6.16 */
+                return 1;
+            }
+            memcpy(buff1, ip, sizeof(buff1));
+        }
+        if ((1 == id_type) && (2 == c_set) && (0 == assoc)) {
+            /* assoc=lu, c_set=ascii, id_type=t10 vendor id */
+            if (0 == strncmp((const char *)ip, "ATA", 3))
+                memcpy(buff2, ip + 48, sizeof(buff2));
+        }
+    }
+    if (buff1[0] && buff2[0]) {
+        if (0 == memcmp(buff1, buff2, sizeof(buff1))) {
+            /* after lk 2.6.16, look for serial number match */
+            return 1;
+        }
+    }
+    return 0;
+}
index 11b50879c606b1d61fef62e02debccad13b5aef9..92b6307d98e19083047e65973c81e2002dacd31c 100644 (file)
@@ -32,7 +32,7 @@
 #ifndef SCSICMDS_H_
 #define SCSICMDS_H_
 
-#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.57 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.62 2006/09/12 00:22:49 dpgilbert Exp $\n"
 
 #include <stdio.h>
 #include <stdlib.h>
 #define READ_DEFECT_10  0x37
 #endif
 
+#ifndef SAT_ATA_PASSTHROUGH_12
+#define SAT_ATA_PASSTHROUGH_12 0xa1
+#endif
+#ifndef SAT_ATA_PASSTHROUGH_16
+#define SAT_ATA_PASSTHROUGH_16 0x85
+#endif
+
 typedef unsigned char UINT8;
 typedef char INT8;
 typedef unsigned int UINT32;
@@ -163,6 +170,7 @@ struct scsiNonMediumError {
 #define STARTSTOP_CYCLE_COUNTER_LPAGE           0x0e
 #define APPLICATION_CLIENT_LPAGE                0x0f
 #define SELFTEST_RESULTS_LPAGE                  0x10
+#define BACKGROUND_RESULTS_LPAGE                0x15   /* SBC-3 */
 #define IE_LPAGE                                0x2f
 
 /* Seagate vendor specific log pages. */
@@ -200,6 +208,9 @@ Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */
 #define INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE    0x1c
 #define FAULT_FAILURE_REPORTING_PAGE             0x1c
 
+/* Background control mode subpage is [0x1c,0x1] */
+#define BACKGROUND_CONTROL_M_SUBPAGE             0x1   /* SBC-2 */
+
 #define ALL_MODE_PAGES                           0x3f
 
 /* Mode page control field */
@@ -212,6 +223,8 @@ Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */
 #define SCSI_STATUS_CHECK_CONDITION     0x2
 
 /* defines for useful Sense Key codes */
+#define SCSI_SK_NO_SENSE                0x0
+#define SCSI_SK_RECOVERED_ERR           0x1
 #define SCSI_SK_NOT_READY               0x2
 #define SCSI_SK_MEDIUM_ERROR            0x3
 #define SCSI_SK_HARDWARE_ERROR          0x4
@@ -227,6 +240,8 @@ Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */
 #define SCSI_ASC_WARNING                0xb
 #define SCSI_ASC_IMPENDING_FAILURE      0x5d
 
+#define SCSI_ASCQ_ATA_PASS_THROUGH      0x1d
+
 /* Simplified error code (negative values as per errno) */
 #define SIMPLE_NO_ERROR                 0
 #define SIMPLE_ERR_NOT_READY            1
@@ -262,6 +277,8 @@ Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */
 void scsi_do_sense_disect(const struct scsi_cmnd_io * in,
                           struct scsi_sense_disect * out);
 
+int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo);
+
 const char * scsiErrString(int scsiErr);
 
 /* STANDARD SCSI Commands  */
@@ -271,14 +288,16 @@ int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen);
 
 int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen);
 
-int scsiLogSense(int device, int pagenum, UINT8 *pBuf, int bufLen,
-                 int known_resp_len);
+int scsiLogSense(int device, int pagenum, int subpagenum, UINT8 *pBuf,
+                int bufLen, int known_resp_len);
 
-int scsiModeSense(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen);
+int scsiModeSense(int device, int pagenum, int subpagenum, int pc,
+                  UINT8 *pBuf, int bufLen);
 
 int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen);
 
-int scsiModeSense10(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen);
+int scsiModeSense10(int device, int pagenum, int subpagenum, int pc,
+                   UINT8 *pBuf, int bufLen);
 
 int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen);
 
@@ -336,6 +355,9 @@ const char * scsiTapeAlertsChangerDevice(unsigned short code);
 
 const char * scsi_get_opcode_name(UINT8 opcode);
 void dStrHex(const char* str, int len, int no_ascii);
+inline void dStrHex(const unsigned char* str, int len, int no_ascii)
+  { dStrHex((const char *)str, len, no_ascii); }
+
 
 /* SCSI command transmission interface function declaration. Its
  * definition is target OS specific (see os_<OS>.c file).
diff --git a/scsiprint.c b/scsiprint.c
deleted file mode 100644 (file)
index 0b7c70b..0000000
+++ /dev/null
@@ -1,1233 +0,0 @@
-/*
- * scsiprint.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
- *
- * Additional SCSI work:
- * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
- */
-
-
-#include <stdio.h>
-#include <string.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include "config.h"
-#include "int64.h"
-#include "extern.h"
-#include "scsicmds.h"
-#include "scsiprint.h"
-#include "smartctl.h"
-#include "utility.h"
-
-#define GBUF_SIZE 65535
-
-const char* scsiprint_c_cvsid="$Id: scsiprint.c,v 1.107 2006/04/12 16:18:57 ballen4705 Exp $"
-CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
-
-// control block which points to external global control variables
-extern smartmonctrl *con;
-
-// to hold onto exit code for atexit routine
-extern int exitstatus;
-
-UINT8 gBuf[GBUF_SIZE];
-#define LOG_RESP_LEN 252
-#define LOG_RESP_LONG_LEN 8192
-#define LOG_RESP_TAPE_ALERT_LEN 0x144
-
-/* Log pages supported */
-static int gSmartLPage = 0;     /* Informational Exceptions log page */
-static int gTempLPage = 0;
-static int gSelfTestLPage = 0;
-static int gStartStopLPage = 0;
-static int gReadECounterLPage = 0;
-static int gWriteECounterLPage = 0;
-static int gVerifyECounterLPage = 0;
-static int gNonMediumELPage = 0;
-static int gLastNErrorLPage = 0;
-static int gTapeAlertsLPage = 0;
-static int gSeagateCacheLPage = 0;
-static int gSeagateFactoryLPage = 0;
-
-/* Mode pages supported */
-static int gIecMPage = 1;     /* N.B. assume it until we know otherwise */
-
-/* Remember last successful mode sense/select command */
-static int modese_len = 0;
-
-// Compares failure type to policy in effect, and either exits or
-// simply returns to the calling routine.
-extern void failuretest(int type, int returnvalue);
-
-static void scsiGetSupportedLogPages(int device)
-{
-    int i, err;
-
-    if ((err = scsiLogSense(device, SUPPORTED_LPAGES, gBuf, 
-                            LOG_RESP_LEN, 0))) {
-        if (con->reportscsiioctl > 0)
-            pout("Log Sense for supported pages failed [%s]\n", 
-                 scsiErrString(err)); 
-        return;
-    } 
-
-    for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
-        switch (gBuf[i])
-        {
-            case READ_ERROR_COUNTER_LPAGE:
-                gReadECounterLPage = 1;
-                break;
-            case WRITE_ERROR_COUNTER_LPAGE:
-                gWriteECounterLPage = 1;
-                break;
-            case VERIFY_ERROR_COUNTER_LPAGE:
-                gVerifyECounterLPage = 1;
-                break;
-            case LAST_N_ERROR_LPAGE:
-                gLastNErrorLPage = 1;
-                break;
-            case NON_MEDIUM_ERROR_LPAGE:
-                gNonMediumELPage = 1;
-                break;
-            case TEMPERATURE_LPAGE:
-                gTempLPage = 1;
-                break;
-            case STARTSTOP_CYCLE_COUNTER_LPAGE:
-                gStartStopLPage = 1;
-                break;
-            case SELFTEST_RESULTS_LPAGE:
-                gSelfTestLPage = 1;
-                break;
-            case IE_LPAGE:
-                gSmartLPage = 1;
-                break;
-            case TAPE_ALERTS_LPAGE:
-                gTapeAlertsLPage = 1;
-                break;
-            case SEAGATE_CACHE_LPAGE:
-                gSeagateCacheLPage = 1;
-                break;
-            case SEAGATE_FACTORY_LPAGE:
-                gSeagateFactoryLPage = 1;
-                break;
-            default:
-                break;
-        }
-    }
-}
-
-/* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
-   (or at least something to report). */
-static int scsiGetSmartData(int device, int attribs)
-{
-    UINT8 asc;
-    UINT8 ascq;
-    UINT8 currenttemp = 0;
-    UINT8 triptemp = 0;
-    const char * cp;
-    int err = 0;
-
-    PRINT_ON(con);
-    if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
-                    &currenttemp, &triptemp)) {
-        /* error message already announced */
-        PRINT_OFF(con);
-        return -1;
-    }
-    PRINT_OFF(con);
-    cp = scsiGetIEString(asc, ascq);
-    if (cp) {
-        err = -2;
-        PRINT_ON(con);
-        pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq); 
-        PRINT_OFF(con);
-    } else if (gIecMPage)
-        pout("SMART Health Status: OK\n");
-
-    if (attribs && !gTempLPage) {
-        if (currenttemp || triptemp)
-            pout("\n");
-        if (currenttemp) {
-            if (255 != currenttemp)
-                pout("Current Drive Temperature:     %d C\n", currenttemp);
-            else
-                pout("Current Drive Temperature:     <not available>\n");
-        }
-        if (triptemp)
-            pout("Drive Trip Temperature:        %d C\n", triptemp);
-    }
-    return err;
-}
-
-
-// Returns number of logged errors or zero if none or -1 if fetching
-// TapeAlerts fails
-static char *severities = "CWI";
-
-static int scsiGetTapeAlertsData(int device, int peripheral_type)
-{
-    unsigned short pagelength;
-    unsigned short parametercode;
-    int i, err;
-    char *s;
-    const char *ts;
-    int failures = 0;
-
-    PRINT_ON(con);
-    if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, gBuf, 
-                        LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) {
-        pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err));
-        PRINT_OFF(con);
-        return -1;
-    }
-    if (gBuf[0] != 0x2e) {
-        pout("TapeAlerts Log Sense Failed\n");
-        PRINT_OFF(con);
-        return -1;
-    }
-    pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3];
-
-    for (s=severities; *s; s++) {
-        for (i = 4; i < pagelength; i += 5) {
-            parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1];
-
-            if (gBuf[i + 4]) {
-                ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ?
-                    scsiTapeAlertsChangerDevice(parametercode) :
-                    scsiTapeAlertsTapeDevice(parametercode);
-                if (*ts == *s) {
-                    if (!failures)
-                        pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n");
-                    pout("[0x%02x] %s\n", parametercode, ts);
-                    failures += 1; 
-                }
-            }
-        }
-    }
-    PRINT_OFF(con);
-
-    if (! failures)
-        pout("TapeAlert: OK\n");
-
-    return failures;
-}
-
-static void scsiGetStartStopData(int device)
-{
-    UINT32 currentStartStop;
-    UINT32 recommendedStartStop; 
-    int err, len, k;
-    char str[6];
-
-    if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, gBuf,
-                            LOG_RESP_LEN, 0))) {
-        PRINT_ON(con);
-        pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err));
-        PRINT_OFF(con);
-        return;
-    }
-    if (gBuf[0] != STARTSTOP_CYCLE_COUNTER_LPAGE) {
-        PRINT_ON(con);
-        pout("StartStop Log Sense Failed, page mismatch\n");
-        PRINT_OFF(con);
-        return;
-    }
-    len = ((gBuf[2] << 8) | gBuf[3]) + 4;
-    if (len > 13) {
-        for (k = 0; k < 2; ++k)
-            str[k] = gBuf[12 + k];
-        str[k] = '\0';
-        pout("Manufactured in week %s of year ", str);
-        for (k = 0; k < 4; ++k)
-            str[k] = gBuf[8 + k];
-        str[k] = '\0';
-        pout("%s\n", str);
-    }
-    if (len > 39) {
-        recommendedStartStop = (gBuf[28] << 24) | (gBuf[29] << 16) |
-                               (gBuf[30] << 8) | gBuf[31];
-        currentStartStop = (gBuf[36] << 24) | (gBuf[37] << 16) |
-                           (gBuf[38] << 8) | gBuf[39];
-        pout("Current start stop count:      %u times\n", currentStartStop);
-       if (0xffffffff != recommendedStartStop)
-            pout("Recommended maximum start stop count:  %u times\n", 
-                 recommendedStartStop);
-    }
-} 
-
-static void scsiPrintGrownDefectListLen(int device)
-{
-    int err, dl_format, dl_len, div;
-
-    memset(gBuf, 0, 4);
-    if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */,
-                                4 /* bytes from index */, gBuf, 4))) {
-        if (con->reportscsiioctl > 0) {
-            PRINT_ON(con);
-            pout("Read defect list (10) Failed: %s\n", scsiErrString(err));
-            PRINT_OFF(con);
-        }
-        return;
-    }
-    if (0x8 != (gBuf[1] & 0x18)) {
-        PRINT_ON(con);
-        pout("Read defect list: asked for grown list but didn't get it\n");
-        PRINT_OFF(con);
-        return;
-    }
-    div = 0;
-    dl_format = (gBuf[1] & 0x7);
-    switch (dl_format) {
-        case 0:     /* short block */
-            div = 4;
-            break;
-        case 3:     /* long block */
-        case 4:     /* bytes from index */
-        case 5:     /* physical sector */
-            div = 8;
-            break;
-        default:
-            PRINT_ON(con);
-            pout("defect list format %d unknown\n", dl_format);
-            PRINT_OFF(con);
-            break;
-    }
-    dl_len = (gBuf[2] << 8) + gBuf[3];
-    if (0 == dl_len)
-        pout("Elements in grown defect list: 0\n");
-    else {
-        if (0 == div)
-            pout("Grown defect list length=%d bytes [unknown "
-                 "number of elements]\n", dl_len);
-        else
-            pout("Elements in grown defect list: %d\n", dl_len / div);
-    }
-}
-
-static void scsiPrintSeagateCacheLPage(int device)
-{
-    int k, j, num, pl, pc, err, len;
-    unsigned char * ucp;
-    unsigned char * xp;
-    uint64_t ull;
-
-    if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, gBuf,
-                            LOG_RESP_LEN, 0))) {
-        PRINT_ON(con);
-        pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err));
-        PRINT_OFF(con);
-        return;
-    }
-    if (gBuf[0] != SEAGATE_CACHE_LPAGE) {
-        PRINT_ON(con);
-        pout("Seagate Cache Log Sense Failed, page mismatch\n");
-        PRINT_OFF(con);
-        return;
-    }
-    len = ((gBuf[2] << 8) | gBuf[3]) + 4;
-    num = len - 4;
-    ucp = &gBuf[0] + 4;
-    while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
-        pl = ucp[3] + 4;
-        switch (pc) {
-        case 0: case 1: case 2: case 3: case 4:
-            break;
-        default: 
-            if (con->reportscsiioctl > 0) {
-                PRINT_ON(con);
-                pout("Vendor (Seagate) cache lpage has unexpected parameter"
-                     ", skip\n");
-                PRINT_OFF(con);
-            }
-            return;
-        }
-        num -= pl;
-        ucp += pl;
-    }
-    pout("Vendor (Seagate) cache information\n");
-    num = len - 4;
-    ucp = &gBuf[0] + 4;
-    while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
-        pl = ucp[3] + 4;
-        switch (pc) {
-        case 0: pout("  Blocks sent to initiator"); break;
-        case 1: pout("  Blocks received from initiator"); break;
-        case 2: pout("  Blocks read from cache and sent to initiator"); break;
-        case 3: pout("  Number of read and write commands whose size "
-                       "<= segment size"); break;
-        case 4: pout("  Number of read and write commands whose size "
-                       "> segment size"); break;
-        default: pout("  Unknown Seagate parameter code [0x%x]", pc); break;
-        }
-        k = pl - 4;
-        xp = ucp + 4;
-        if (k > (int)sizeof(ull)) {
-            xp += (k - (int)sizeof(ull));
-            k = (int)sizeof(ull);
-        }
-        ull = 0;
-        for (j = 0; j < k; ++j) {
-            if (j > 0)
-                ull <<= 8;
-            ull |= xp[j];
-        }
-        pout(" = %"PRIu64"\n", ull);
-        num -= pl;
-        ucp += pl;
-    }
-}
-
-static void scsiPrintSeagateFactoryLPage(int device)
-{
-    int k, j, num, pl, pc, len, err, good, bad;
-    unsigned char * ucp;
-    unsigned char * xp;
-    uint64_t ull;
-
-    if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, gBuf,
-                            LOG_RESP_LEN, 0))) {
-        PRINT_ON(con);
-        pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err));
-        PRINT_OFF(con);
-        return;
-    }
-    if (gBuf[0] != SEAGATE_FACTORY_LPAGE) {
-        PRINT_ON(con);
-        pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n");
-        PRINT_OFF(con);
-        return;
-    }
-    len = ((gBuf[2] << 8) | gBuf[3]) + 4;
-    num = len - 4;
-    ucp = &gBuf[0] + 4;
-    good = 0;
-    bad = 0;
-    while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
-        pl = ucp[3] + 4;
-        switch (pc) {
-        case 0: case 8:
-            ++good;
-            break;
-        default: 
-            ++bad;
-            break;
-        }
-        num -= pl;
-        ucp += pl;
-    }
-    if ((good < 2) || (bad > 4)) {  /* heuristic */
-        if (con->reportscsiioctl > 0) {
-            PRINT_ON(con);
-            pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
-                 "unexpected parameters, skip\n");
-            PRINT_OFF(con);
-        }
-        return;
-    }
-    pout("Vendor (Seagate/Hitachi) factory information\n");
-    num = len - 4;
-    ucp = &gBuf[0] + 4;
-    while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
-        pl = ucp[3] + 4;
-        good = 0;
-        switch (pc) {
-        case 0: pout("  number of hours powered up");
-            good = 1;
-            break;
-        case 8: pout("  number of minutes until next internal SMART test");
-            good = 1;
-            break;
-        default:
-            if (con->reportscsiioctl > 0) {
-                PRINT_ON(con);
-                pout("Vendor (Seagate/Hitachi) factory lpage: "
-                     "unknown parameter code [0x%x]\n", pc);
-                PRINT_OFF(con);
-            }
-            break;
-        }
-        if (good) {
-            k = pl - 4;
-            xp = ucp + 4;
-            if (k > (int)sizeof(ull)) {
-                xp += (k - (int)sizeof(ull));
-                k = (int)sizeof(ull);
-            }
-            ull = 0;
-            for (j = 0; j < k; ++j) {
-                if (j > 0)
-                    ull <<= 8;
-                ull |= xp[j];
-            }
-            if (0 == pc)
-                pout(" = %.2f\n", uint64_to_double(ull) / 60.0 );
-            else
-                pout(" = %"PRIu64"\n", ull);
-        }
-        num -= pl;
-        ucp += pl;
-    }
-}
-
-static void scsiPrintErrorCounterLog(int device)
-{
-    struct scsiErrorCounter errCounterArr[3];
-    struct scsiErrorCounter * ecp;
-    struct scsiNonMediumError nme;
-    int found[3] = {0, 0, 0};
-    const char * pageNames[3] = {"read:   ", "write:  ", "verify: "};
-    int k;
-    double processed_gb;
-
-    if (gReadECounterLPage && (0 == scsiLogSense(device,
-                READ_ERROR_COUNTER_LPAGE, gBuf, LOG_RESP_LEN, 0))) {
-        scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]);
-        found[0] = 1;
-    }
-    if (gWriteECounterLPage && (0 == scsiLogSense(device,
-                WRITE_ERROR_COUNTER_LPAGE, gBuf, LOG_RESP_LEN, 0))) {
-        scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]);
-        found[1] = 1;
-    }
-    if (gVerifyECounterLPage && (0 == scsiLogSense(device,
-                VERIFY_ERROR_COUNTER_LPAGE, gBuf, LOG_RESP_LEN, 0))) {
-        scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]);
-        ecp = &errCounterArr[2];
-        for (k = 0; k < 7; ++k) {
-            if (ecp->gotPC[k] && ecp->counter[k]) {
-                found[2] = 1;
-                break;
-            }
-        }
-    }
-    if (found[0] || found[1] || found[2]) {
-        pout("\nError counter log:\n");
-        pout("           Errors Corrected by           Total   "
-             "Correction     Gigabytes    Total\n");
-        pout("               ECC          rereads/    errors   "
-             "algorithm      processed    uncorrected\n");
-        pout("           fast | delayed   rewrites  corrected  "
-             "invocations   [10^9 bytes]  errors\n");
-        for (k = 0; k < 3; ++k) {
-            if (! found[k])
-                continue;
-            ecp = &errCounterArr[k];
-            pout("%s%8"PRIu64" %8"PRIu64"  %8"PRIu64"  %8"PRIu64"   %8"PRIu64, 
-                 pageNames[k], ecp->counter[0], ecp->counter[1], 
-                 ecp->counter[2], ecp->counter[3], ecp->counter[4]);
-            processed_gb = uint64_to_double(ecp->counter[5]) / 1000000000.0;
-            pout("   %12.3f    %8"PRIu64"\n", processed_gb, ecp->counter[6]);
-        }
-    }
-    else 
-        pout("\nError Counter logging not supported\n");
-    if (gNonMediumELPage && (0 == scsiLogSense(device,
-                NON_MEDIUM_ERROR_LPAGE, gBuf, LOG_RESP_LEN, 0))) {
-        scsiDecodeNonMediumErrPage(gBuf, &nme);
-        if (nme.gotPC0)
-            pout("\nNon-medium error count: %8"PRIu64"\n", nme.counterPC0);
-        if (nme.gotTFE_H)
-            pout("Track following error count [Hitachi]: %8"PRIu64"\n",
-                 nme.counterTFE_H);
-        if (nme.gotPE_H)
-            pout("Positioning error count [Hitachi]: %8"PRIu64"\n",
-                 nme.counterPE_H);
-    }
-    if (gLastNErrorLPage && (0 == scsiLogSense(device,
-                LAST_N_ERROR_LPAGE, gBuf, LOG_RESP_LONG_LEN, 0))) {
-        unsigned char * ucp;
-        int num, k, pc, pl;
-
-        num = (gBuf[2] << 8) + gBuf[3] + 4;
-        num = (num < LOG_RESP_LONG_LEN) ? num : LOG_RESP_LONG_LEN;
-        ucp = gBuf + 4;
-        num -= 4;
-        if (num < 4)
-            pout("\nNo error events logged\n");
-        else {
-            pout("\nLast n error events log page\n");
-            for (k = num; k > 0; k -= pl, ucp += pl) {
-                if (k < 3) {
-                    pout("  <<short Last n error events log page>>\n");
-                    break;
-                }
-                pl = ucp[3] + 4;
-                pc = (ucp[0] << 8) + ucp[1];
-                if (pl > 4) {
-                    if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) {
-                        pout("  Error event %d:\n", pc);
-                        pout("    [binary]:\n");
-                        dStrHex((const char *)ucp + 4, pl - 4, 1);
-                    } else if (ucp[2] & 0x1) {
-                        pout("  Error event %d:\n", pc);
-                        pout("    %.*s\n", pl - 4, (const char *)(ucp + 4));
-                    } else {
-                        if (con->reportscsiioctl > 0) {
-                            pout("  Error event %d:\n", pc);
-                            pout("    [data counter??]:\n");
-                            dStrHex((const char *)ucp + 4, pl - 4, 1);
-                       }
-                    }
-                }
-            }
-        }
-    }
-}
-
-static const char * self_test_code[] = {
-        "Default         ", 
-        "Background short", 
-        "Background long ", 
-        "Reserved(3)     ",
-        "Abort background", 
-        "Foreground short", 
-        "Foreground long ",
-        "Reserved(7)     "
-};
-
-static const char * self_test_result[] = {
-        "Completed                ",
-        "Interrupted ('-X' switch)",
-        "Interrupted (bus reset ?)",
-        "Unknown error, incomplete",
-        "Completed, segment failed",
-        "Failed in first segment  ",
-        "Failed in second segment ",
-        "Failed in segment -->    ",
-        "Reserved(8)              ", 
-        "Reserved(9)              ", 
-        "Reserved(10)             ", 
-        "Reserved(11)             ", 
-        "Reserved(12)             ", 
-        "Reserved(13)             ", 
-        "Reserved(14)             ",
-        "Self test in progress ..."
-};
-
-// See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 .
-// Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
-// 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
-// FAILSMART is returned.
-static int scsiPrintSelfTest(int device)
-{
-    int num, k, n, res, err, durationSec;
-    int noheader = 1;
-    int retval = 0;
-    UINT8 * ucp;
-    uint64_t ull=0;
-
-    if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, gBuf, 
-                            LOG_RESP_SELF_TEST_LEN, 0))) {
-        PRINT_ON(con);
-        pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err));
-        PRINT_OFF(con);
-        return FAILSMART;
-    }
-    if (gBuf[0] != SELFTEST_RESULTS_LPAGE) {
-        PRINT_ON(con);
-        pout("Self-test Log Sense Failed, page mismatch\n");
-        PRINT_OFF(con);
-        return FAILSMART;
-    }
-    // compute page length
-    num = (gBuf[2] << 8) + gBuf[3];
-    // Log sense page length 0x190 bytes
-    if (num != 0x190) {
-        PRINT_ON(con);
-        pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num);
-        PRINT_OFF(con);
-        return FAILSMART;
-    }
-    // loop through the twenty possible entries
-    for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) {
-        int i;
-
-        // timestamp in power-on hours (or zero if test in progress)
-        n = (ucp[6] << 8) | ucp[7];
-
-        // The spec says "all 20 bytes will be zero if no test" but
-        // DG has found otherwise.  So this is a heuristic.
-        if ((0 == n) && (0 == ucp[4]))
-            break;
-
-        // only print header if needed
-        if (noheader) {
-            pout("\nSMART Self-test log\n");
-            pout("Num  Test              Status                 segment  "
-                   "LifeTime  LBA_first_err [SK ASC ASQ]\n");
-            pout("     Description                              number   "
-                   "(hours)\n");
-            noheader=0;
-        }
-
-        // print parameter code (test number) & self-test code text
-        pout("#%2d  %s", (ucp[0] << 8) | ucp[1], 
-            self_test_code[(ucp[4] >> 5) & 0x7]);
-
-        // check the self-test result nibble, using the self-test results
-        // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10:
-        switch ((res = ucp[4] & 0xf)) {
-        case 0x3:
-            // an unknown error occurred while the device server
-            // was processing the self-test and the device server
-            // was unable to complete the self-test
-            retval|=FAILSMART;
-            break;
-        case 0x4:
-            // the self-test completed with a failure in a test
-            // segment, and the test segment that failed is not
-            // known
-            retval|=FAILLOG;
-            break;
-        case 0x5:
-            // the first segment of the self-test failed
-            retval|=FAILLOG;
-            break;
-        case 0x6:
-            // the second segment of the self-test failed
-            retval|=FAILLOG;
-            break;
-        case 0x7:
-            // another segment of the self-test failed and which
-            // test is indicated by the contents of the SELF-TEST
-            // NUMBER field
-            retval|=FAILLOG;
-            break;
-        default:
-            break;
-        }
-        pout("  %s", self_test_result[res]);
-
-        // self-test number identifies test that failed and consists
-        // of either the number of the segment that failed during
-        // the test, or the number of the test that failed and the
-        // number of the segment in which the test was run, using a
-        // vendor-specific method of putting both numbers into a
-        // single byte.
-        if (ucp[5])
-            pout(" %3d",  (int)ucp[5]);
-        else
-            pout("   -");
-
-        // print time that the self-test was completed
-        if (n==0 && res==0xf)
-        // self-test in progress
-            pout("     NOW");
-        else   
-            pout("   %5d", n);
-          
-        // construct 8-byte integer address of first failure
-        for (i = 0; i < 8; i++) {
-            ull <<= 8;
-            ull |= ucp[i+8];
-        }
-        // print Address of First Failure, if sensible
-        if ((~(uint64_t)0 != ull) && (res > 0) && (res < 0xf)) {
-            char buff[32];
-
-            // was hex but change to decimal to conform with ATA
-            snprintf(buff, sizeof(buff), "%"PRIu64, ull);
-            // snprintf(buff, sizeof(buff), "0x%"PRIx64, ull);
-            pout("%18s", buff);
-        } else
-            pout("                 -");
-
-        // if sense key nonzero, then print it, along with
-        // additional sense code and additional sense code qualifier
-        if (ucp[16] & 0xf)
-            pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]);
-        else
-            pout(" [-   -    -]\n");
-    }
-
-    // if header never printed, then there was no output
-    if (noheader)
-        pout("No self-tests have been logged\n");
-    else
-        pout("\n");
-    if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, 
-                        modese_len)) && (durationSec > 0)) {
-        pout("Long (extended) Self Test duration: %d seconds "
-             "[%.1f minutes]\n", durationSec, durationSec / 60.0);
-    }
-    return retval;
-}
-
-static const char * peripheral_dt_arr[] = {
-        "disk",
-        "tape",
-        "printer",
-        "processor",
-        "optical disk(4)",
-        "CD/DVD",
-        "scanner",
-        "optical disk(7)",
-        "medium changer",
-        "communications",
-        "graphics(10)",
-        "graphics(11)",
-        "storage array",
-        "enclosure",
-        "simplified disk",
-        "optical card reader"
-};
-
-static const char * transport_proto_arr[] = {
-        "Fibre channel (FCP-2)",
-        "Parallel SCSI (SPI-4)",
-        "SSA",
-        "IEEE 1394 (SBP-2)",
-        "RDMA (SRP)",
-        "iSCSI",
-        "SAS",
-        "ADT",
-        "0x8",
-        "0x9",
-        "0xa",
-        "0xb",
-        "0xc",
-        "0xd",
-        "0xe",
-        "0xf"
-};
-
-/* Returns 0 on success, 1 on general error and 2 for early, clean exit */
-static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
-{
-    char manufacturer[9];
-    char product[17];
-    char revision[5];
-    char timedatetz[DATEANDEPOCHLEN];
-    struct scsi_iec_mode_page iec;
-    int err, iec_err, len, req_len, avail_len, val;
-    int is_tape = 0;
-    int peri_dt = 0;
-    int returnval=0;
-        
-    memset(gBuf, 0, 96);
-    req_len = 36;
-    if ((err = scsiStdInquiry(device, gBuf, req_len))) {
-        PRINT_ON(con);
-        pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
-        pout("Retrying with a 64 byte Standard Inquiry\n");
-        PRINT_OFF(con);
-        /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
-        req_len = 64;
-        if ((err = scsiStdInquiry(device, gBuf, req_len))) {
-            PRINT_ON(con);
-            pout("Standard Inquiry (64 bytes) failed [%s]\n",
-                 scsiErrString(err));
-            PRINT_OFF(con);
-            return 1;
-        }
-    }
-    avail_len = gBuf[4] + 5;
-    len = (avail_len < req_len) ? avail_len : req_len;
-    peri_dt = gBuf[0] & 0x1f;
-    if (peripheral_type)
-        *peripheral_type = peri_dt;
-    if (! all)
-        return 0;
-
-    if (len < 36) {
-        PRINT_ON(con);
-        pout("Short INQUIRY response, skip product id\n");
-        PRINT_OFF(con);
-        return 1;
-    }
-    memset(manufacturer, 0, sizeof(manufacturer));
-    strncpy(manufacturer, (char *)&gBuf[8], 8);
-    memset(product, 0, sizeof(product));
-    strncpy(product, (char *)&gBuf[16], 16);
-        
-    memset(revision, 0, sizeof(revision));
-    strncpy(revision, (char *)&gBuf[32], 4);
-    pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
-
-    if (0 == strncmp(manufacturer, "3ware", 5) || 0 == strncmp(manufacturer, "AMCC", 4) ) {
-        pout("please try adding '-d 3ware,N'\n");
-        pout("you may also need to change device to /dev/twaN or /dev/tweN\n");
-        return 2;
-    } else if ((len >= 42) &&
-              (0 == strncmp((const char *)(gBuf + 36), "MVSATA", 6))) {
-        pout("please try '-d marvell'\n");
-        return 2;
-    } else if ((avail_len >= 96) && (0 == strncmp(manufacturer, "ATA", 3))) {
-        /* <<<< This is Linux specific code to detect SATA disks using a
-                SCSI-ATA command translation layer. This may be generalized
-                later when the t10.org SAT project matures. >>>> */
-        req_len = 96;
-        memset(gBuf, 0, req_len);
-        if ((err = scsiInquiryVpd(device, 0x83, gBuf, req_len))) {
-            PRINT_ON(con);
-            pout("Inquiry for VPD page 0x83 [device id] failed [%s]\n",
-                  scsiErrString(err));
-            PRINT_OFF(con);
-            return 1;
-        }
-        avail_len = ((gBuf[2] << 8) + gBuf[3]) + 4;
-        len = (avail_len < req_len) ? avail_len : req_len;
-        if (isLinuxLibAta(gBuf, len)) {
-            pout("\nIn Linux, SATA disks accessed via libata are "
-                 "only supported by smartmontools\n"
-                 "for kernel versions 2.6.15 and above. Try "
-                 "an additional '-d ata' argument.\n");
-            return 2;
-        }
-    }
-
-    /* Do this here to try and detect badly conforming devices (some USB
-       keys) that will lock up on a InquiryVpd or log sense or ... */
-    if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) {
-        if (SIMPLE_ERR_BAD_RESP == iec_err) {
-            pout(">> Terminate command early due to bad response to IEC "
-                 "mode page\n");
-            PRINT_OFF(con);
-            gIecMPage = 0;
-            return 1;
-        }
-    } else
-        modese_len = iec.modese_len;
-
-    if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) {
-        /* should use VPD page 0x83 and fall back to this page (0x80)
-         * if 0x83 not supported. NAA requires a lot of decoding code */
-        len = gBuf[3];
-        gBuf[4 + len] = '\0';
-        pout("Serial number: %s\n", &gBuf[4]);
-    }
-    else if (con->reportscsiioctl > 0) {
-        PRINT_ON(con);
-        if (SIMPLE_ERR_BAD_RESP == err)
-            pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
-        else
-            pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
-        PRINT_OFF(con);
-    }
-
-    // print SCSI peripheral device type
-    if (peri_dt < (int)(sizeof(peripheral_dt_arr) / 
-                        sizeof(peripheral_dt_arr[0])))
-        pout("Device type: %s\n", peripheral_dt_arr[peri_dt]);
-    else
-        pout("Device type: <%d>\n", peri_dt);
-
-    // See if transport protocol is known
-    val = scsiFetchTransportProtocol(device, modese_len);
-    if ((val >= 0) && (val <= 0xf))
-        pout("Transport protocol: %s\n", transport_proto_arr[val]);
-
-    // print current time and date and timezone
-    dateandtimezone(timedatetz);
-    pout("Local Time is: %s\n", timedatetz);
-
-    if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) ||
-        (SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
-        is_tape = 1;
-    // See if unit accepts SCSI commmands from us
-    if ((err = scsiTestUnitReady(device))) {
-        if (SIMPLE_ERR_NOT_READY == err) {
-            PRINT_ON(con);
-            if (!is_tape)
-                pout("device is NOT READY (e.g. spun down, busy)\n");
-            else
-                pout("device is NOT READY (e.g. no tape)\n");
-            PRINT_OFF(con);
-         } else if (SIMPLE_ERR_NO_MEDIUM == err) {
-            PRINT_ON(con);
-            pout("NO MEDIUM present on device\n");
-            PRINT_OFF(con);
-         } else if (SIMPLE_ERR_BECOMING_READY == err) {
-            PRINT_ON(con);
-            pout("device becoming ready (wait)\n");
-            PRINT_OFF(con);
-        } else {
-            PRINT_ON(con);
-            pout("device Test Unit Ready  [%s]\n", scsiErrString(err));
-            PRINT_OFF(con);
-        }
-        failuretest(MANDATORY_CMD, returnval|=FAILID);
-    }
-   
-    if (iec_err) {
-        if (!is_tape) {
-            PRINT_ON(con);
-            pout("Device does not support SMART");
-            if (con->reportscsiioctl > 0)
-                pout(" [%s]\n", scsiErrString(iec_err));
-            else
-                pout("\n");
-            PRINT_OFF(con);
-        }
-        gIecMPage = 0;
-        return 0;
-    }
-
-    if (!is_tape)
-        pout("Device supports SMART and is %s\n",
-             (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled");
-    pout("%s\n", (scsi_IsWarningEnabled(&iec)) ? 
-                  "Temperature Warning Enabled" :
-                  "Temperature Warning Disabled or Not Supported");
-    return 0;
-}
-
-static int scsiSmartEnable(int device)
-{
-    struct scsi_iec_mode_page iec;
-    int err;
-
-    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
-        PRINT_ON(con);
-        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
-             scsiErrString(err));
-        PRINT_OFF(con);
-        return 1;
-    } else
-        modese_len = iec.modese_len;
-
-    if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) {
-        PRINT_ON(con);
-        pout("unable to enable Exception control and warning [%s]\n",
-             scsiErrString(err));
-        PRINT_OFF(con);
-        return 1;
-    }
-    /* Need to refetch 'iec' since could be modified by previous call */
-    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
-        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
-             scsiErrString(err));
-        return 1;
-    } else
-        modese_len = iec.modese_len;
-
-    pout("Informational Exceptions (SMART) %s\n",
-         scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
-    pout("Temperature warning %s\n",
-         scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
-    return 0;
-}
-        
-static int scsiSmartDisable(int device)
-{
-    struct scsi_iec_mode_page iec;
-    int err;
-
-    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
-        PRINT_ON(con);
-        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
-             scsiErrString(err));
-        PRINT_OFF(con);
-        return 1;
-    } else
-        modese_len = iec.modese_len;
-
-    if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) {
-        PRINT_ON(con);
-        pout("unable to disable Exception control and warning [%s]\n",
-             scsiErrString(err));
-        PRINT_OFF(con);
-        return 1;
-    }
-    /* Need to refetch 'iec' since could be modified by previous call */
-    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
-        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
-             scsiErrString(err));
-        return 1;
-    } else
-        modese_len = iec.modese_len;
-
-    pout("Informational Exceptions (SMART) %s\n",
-         scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
-    pout("Temperature warning %s\n",
-         scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
-    return 0;
-}
-
-static void scsiPrintTemp(int device)
-{
-    UINT8 temp = 0;
-    UINT8 trip = 0;
-
-    if (scsiGetTemp(device, &temp, &trip))
-        return;
-  
-    if (temp) {
-        if (255 != temp)
-            pout("Current Drive Temperature:     %d C\n", temp);
-        else
-            pout("Current Drive Temperature:     <not available>\n");
-    }
-    if (trip)
-        pout("Drive Trip Temperature:        %d C\n", trip);
-}
-
-/* Main entry point used by smartctl command. Return 0 for success */
-int scsiPrintMain(int fd)
-{
-    int checkedSupportedLogPages = 0;
-    UINT8 peripheral_type = 0;
-    int returnval = 0;
-    int res, durationSec;
-
-    res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo);
-    if (res) {
-        if (2 == res)
-            return 0;
-        else
-            failuretest(MANDATORY_CMD, returnval |= FAILID);
-    }
-
-    if (con->smartenable) {
-        if (scsiSmartEnable(fd))
-            failuretest(MANDATORY_CMD, returnval |= FAILSMART);
-    }
-
-    if (con->smartdisable) {
-        if (scsiSmartDisable(fd))
-            failuretest(MANDATORY_CMD,returnval |= FAILSMART);
-    }
-    
-    if (con->smartautosaveenable) {
-      if (scsiSetControlGLTSD(fd, 0, modese_len)) {
-        pout("Enable autosave (clear GLTSD bit) failed\n");
-        failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
-      }
-    }
-    
-    if (con->smartautosavedisable) {
-      if (scsiSetControlGLTSD(fd, 1, modese_len)) {
-        pout("Disable autosave (set GLTSD bit) failed\n");
-        failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
-      }
-    }
-    
-    if (con->checksmart) {
-        scsiGetSupportedLogPages(fd);
-        checkedSupportedLogPages = 1;
-        if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
-            (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
-            if (gTapeAlertsLPage) {
-                if (con->driveinfo)
-                    pout("TapeAlert Supported\n");
-                if (-1 == scsiGetTapeAlertsData(fd, peripheral_type))
-                    failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
-            }
-            else
-                pout("TapeAlert Not Supported\n");
-        } else { /* disk, cd/dvd, enclosure, etc */
-            if ((res = scsiGetSmartData(fd, con->smartvendorattrib))) {
-                if (-2 == res)
-                    returnval |= FAILSTATUS;
-                else
-                    returnval |= FAILSMART;
-            }
-        }
-    }   
-    if (con->smartvendorattrib) {
-        if (! checkedSupportedLogPages)
-            scsiGetSupportedLogPages(fd);
-        if (gTempLPage) {
-            if (con->checksmart)
-                pout("\n");
-            scsiPrintTemp(fd);         
-        }
-        if (gStartStopLPage)
-            scsiGetStartStopData(fd);
-        if (SCSI_PT_DIRECT_ACCESS == peripheral_type) {
-            scsiPrintGrownDefectListLen(fd);
-            if (gSeagateCacheLPage)
-                scsiPrintSeagateCacheLPage(fd);
-            if (gSeagateFactoryLPage)
-                scsiPrintSeagateFactoryLPage(fd);
-        }
-    }
-    if (con->smarterrorlog) {
-        if (! checkedSupportedLogPages)
-            scsiGetSupportedLogPages(fd);
-        scsiPrintErrorCounterLog(fd);
-        if (1 == scsiFetchControlGLTSD(fd, modese_len, 1))
-            pout("\n[GLTSD (Global Logging Target Save Disable) set. "
-                 "Enable Save with '-S on']\n");
-    }
-    if (con->smartselftestlog) {
-        if (! checkedSupportedLogPages)
-            scsiGetSupportedLogPages(fd);
-        res = 0;
-        if (gSelfTestLPage)
-            res = scsiPrintSelfTest(fd);
-        else {
-            pout("Device does not support Self Test logging\n");
-            failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
-        }
-        if (0 != res)
-            failuretest(OPTIONAL_CMD, returnval|=res);
-    }
-    if (con->smartexeoffimmediate) {
-        if (scsiSmartDefaultSelfTest(fd))
-            return returnval | FAILSMART;
-        pout("Default Self Test Successful\n");
-    }
-    if (con->smartshortcapselftest) {
-        if (scsiSmartShortCapSelfTest(fd))
-            return returnval | FAILSMART;
-        pout("Short Foreground Self Test Successful\n");
-    }
-    if (con->smartshortselftest ) { 
-        if (scsiSmartShortSelfTest(fd))
-            return returnval | FAILSMART;
-        pout("Short Background Self Test has begun\n");
-        pout("Use smartctl -X to abort test\n");
-    }
-    if (con->smartextendselftest) {
-        if (scsiSmartExtendSelfTest(fd))
-            return returnval | FAILSMART;
-        pout("Extended Background Self Test has begun\n");
-        if ((0 == scsiFetchExtendedSelfTestTime(fd, &durationSec, 
-                        modese_len)) && (durationSec > 0)) {
-            time_t t = time(NULL);
-
-            t += durationSec;
-            pout("Please wait %d minutes for test to complete.\n", 
-                 durationSec / 60);
-            pout("Estimated completion time: %s\n", ctime(&t));
-        }
-        pout("Use smartctl -X to abort test\n");        
-    }
-    if (con->smartextendcapselftest) {
-        if (scsiSmartExtendCapSelfTest(fd))
-            return returnval | FAILSMART;
-        pout("Extended Foreground Self Test Successful\n");
-    }
-    if (con->smartselftestabort) {
-        if (scsiSmartSelfTestAbort(fd))
-            return returnval | FAILSMART;
-        pout("Self Test returned without error\n");
-    }           
-    return returnval;
-}
diff --git a/scsiprint.cpp b/scsiprint.cpp
new file mode 100644 (file)
index 0000000..f426b27
--- /dev/null
@@ -0,0 +1,1419 @@
+/*
+ * scsiprint.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * Additional SCSI work:
+ * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "config.h"
+#include "int64.h"
+#include "extern.h"
+#include "scsicmds.h"
+#include "scsiprint.h"
+#include "smartctl.h"
+#include "utility.h"
+#include "scsiata.h"
+
+#define GBUF_SIZE 65535
+
+const char* scsiprint_c_cvsid="$Id: scsiprint.cpp,v 1.118 2006/09/27 21:42:03 chrfranke Exp $"
+CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
+
+// control block which points to external global control variables
+extern smartmonctrl *con;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+UINT8 gBuf[GBUF_SIZE];
+#define LOG_RESP_LEN 252
+#define LOG_RESP_LONG_LEN 16384
+#define LOG_RESP_TAPE_ALERT_LEN 0x144
+
+/* Log pages supported */
+static int gSmartLPage = 0;     /* Informational Exceptions log page */
+static int gTempLPage = 0;
+static int gSelfTestLPage = 0;
+static int gStartStopLPage = 0;
+static int gReadECounterLPage = 0;
+static int gWriteECounterLPage = 0;
+static int gVerifyECounterLPage = 0;
+static int gNonMediumELPage = 0;
+static int gLastNErrorLPage = 0;
+static int gBackgroundResultsLPage = 0;
+static int gTapeAlertsLPage = 0;
+static int gSeagateCacheLPage = 0;
+static int gSeagateFactoryLPage = 0;
+
+/* Mode pages supported */
+static int gIecMPage = 1;     /* N.B. assume it until we know otherwise */
+
+/* Remember last successful mode sense/select command */
+static int modese_len = 0;
+
+// Compares failure type to policy in effect, and either exits or
+// simply returns to the calling routine.
+extern void failuretest(int type, int returnvalue);
+
+static void scsiGetSupportedLogPages(int device)
+{
+    int i, err;
+
+    if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
+                            LOG_RESP_LEN, 0))) {
+        if (con->reportscsiioctl > 0)
+            pout("Log Sense for supported pages failed [%s]\n", 
+                 scsiErrString(err)); 
+        return;
+    } 
+
+    for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
+        switch (gBuf[i])
+        {
+            case READ_ERROR_COUNTER_LPAGE:
+                gReadECounterLPage = 1;
+                break;
+            case WRITE_ERROR_COUNTER_LPAGE:
+                gWriteECounterLPage = 1;
+                break;
+            case VERIFY_ERROR_COUNTER_LPAGE:
+                gVerifyECounterLPage = 1;
+                break;
+            case LAST_N_ERROR_LPAGE:
+                gLastNErrorLPage = 1;
+                break;
+            case NON_MEDIUM_ERROR_LPAGE:
+                gNonMediumELPage = 1;
+                break;
+            case TEMPERATURE_LPAGE:
+                gTempLPage = 1;
+                break;
+            case STARTSTOP_CYCLE_COUNTER_LPAGE:
+                gStartStopLPage = 1;
+                break;
+            case SELFTEST_RESULTS_LPAGE:
+                gSelfTestLPage = 1;
+                break;
+            case IE_LPAGE:
+                gSmartLPage = 1;
+                break;
+            case BACKGROUND_RESULTS_LPAGE:
+                gBackgroundResultsLPage = 1;
+                break;
+            case TAPE_ALERTS_LPAGE:
+                gTapeAlertsLPage = 1;
+                break;
+            case SEAGATE_CACHE_LPAGE:
+                gSeagateCacheLPage = 1;
+                break;
+            case SEAGATE_FACTORY_LPAGE:
+                gSeagateFactoryLPage = 1;
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+/* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
+   (or at least something to report). */
+static int scsiGetSmartData(int device, int attribs)
+{
+    UINT8 asc;
+    UINT8 ascq;
+    UINT8 currenttemp = 0;
+    UINT8 triptemp = 0;
+    const char * cp;
+    int err = 0;
+
+    PRINT_ON(con);
+    if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
+                    &currenttemp, &triptemp)) {
+        /* error message already announced */
+        PRINT_OFF(con);
+        return -1;
+    }
+    PRINT_OFF(con);
+    cp = scsiGetIEString(asc, ascq);
+    if (cp) {
+        err = -2;
+        PRINT_ON(con);
+        pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq); 
+        PRINT_OFF(con);
+    } else if (gIecMPage)
+        pout("SMART Health Status: OK\n");
+
+    if (attribs && !gTempLPage) {
+        if (currenttemp || triptemp)
+            pout("\n");
+        if (currenttemp) {
+            if (255 != currenttemp)
+                pout("Current Drive Temperature:     %d C\n", currenttemp);
+            else
+                pout("Current Drive Temperature:     <not available>\n");
+        }
+        if (triptemp)
+            pout("Drive Trip Temperature:        %d C\n", triptemp);
+    }
+    return err;
+}
+
+
+// Returns number of logged errors or zero if none or -1 if fetching
+// TapeAlerts fails
+static char *severities = "CWI";
+
+static int scsiGetTapeAlertsData(int device, int peripheral_type)
+{
+    unsigned short pagelength;
+    unsigned short parametercode;
+    int i, err;
+    char *s;
+    const char *ts;
+    int failures = 0;
+
+    PRINT_ON(con);
+    if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, 0, gBuf,
+                        LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) {
+        pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return -1;
+    }
+    if (gBuf[0] != 0x2e) {
+        pout("TapeAlerts Log Sense Failed\n");
+        PRINT_OFF(con);
+        return -1;
+    }
+    pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3];
+
+    for (s=severities; *s; s++) {
+        for (i = 4; i < pagelength; i += 5) {
+            parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1];
+
+            if (gBuf[i + 4]) {
+                ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ?
+                    scsiTapeAlertsChangerDevice(parametercode) :
+                    scsiTapeAlertsTapeDevice(parametercode);
+                if (*ts == *s) {
+                    if (!failures)
+                        pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n");
+                    pout("[0x%02x] %s\n", parametercode, ts);
+                    failures += 1; 
+                }
+            }
+        }
+    }
+    PRINT_OFF(con);
+
+    if (! failures)
+        pout("TapeAlert: OK\n");
+
+    return failures;
+}
+
+static void scsiGetStartStopData(int device)
+{
+    UINT32 u;
+    int err, len, k, extra, pc;
+    unsigned char * ucp;
+
+    if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, 0, gBuf,
+                            LOG_RESP_LEN, 0))) {
+        PRINT_ON(con);
+        pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return;
+    }
+    if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) {
+        PRINT_ON(con);
+        pout("StartStop Log Sense Failed, page mismatch\n");
+        PRINT_OFF(con);
+        return;
+    }
+    len = ((gBuf[2] << 8) | gBuf[3]);
+    ucp = gBuf + 4;
+    for (k = len; k > 0; k -= extra, ucp += extra) {
+        if (k < 3) {
+            PRINT_ON(con);
+            pout("StartStop Log Sense Failed: short\n");
+            PRINT_OFF(con);
+            return;
+        }
+        extra = ucp[3] + 4;
+        pc = (ucp[0] << 8) + ucp[1];
+        switch (pc) {
+        case 1:
+            if (10 == extra)
+                pout("Manufactured in week %.2s of year %.4s\n", ucp + 8,
+                     ucp + 4);
+            break;
+        case 2:
+            /* ignore Accounting date */
+            break;
+        case 3:
+            if (extra > 7) {
+                u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
+                if (0xffffffff != u)
+                    pout("Recommended maximum start stop count:  %u times\n",
+                         u);
+            }
+            break;
+        case 4:
+            if (extra > 7) {
+                u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
+                if (0xffffffff != u)
+                    pout("Current start stop count:      %u times\n", u);
+            }
+            break;
+        default:
+            /* ignore */
+            break;
+        }
+    }
+} 
+
+static void scsiPrintGrownDefectListLen(int device)
+{
+    int err, dl_format, dl_len, div;
+
+    memset(gBuf, 0, 4);
+    if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */,
+                                4 /* bytes from index */, gBuf, 4))) {
+        if (con->reportscsiioctl > 0) {
+            PRINT_ON(con);
+            pout("Read defect list (10) Failed: %s\n", scsiErrString(err));
+            PRINT_OFF(con);
+        }
+        return;
+    }
+    if (0x8 != (gBuf[1] & 0x18)) {
+        PRINT_ON(con);
+        pout("Read defect list: asked for grown list but didn't get it\n");
+        PRINT_OFF(con);
+        return;
+    }
+    div = 0;
+    dl_format = (gBuf[1] & 0x7);
+    switch (dl_format) {
+        case 0:     /* short block */
+            div = 4;
+            break;
+        case 3:     /* long block */
+        case 4:     /* bytes from index */
+        case 5:     /* physical sector */
+            div = 8;
+            break;
+        default:
+            PRINT_ON(con);
+            pout("defect list format %d unknown\n", dl_format);
+            PRINT_OFF(con);
+            break;
+    }
+    dl_len = (gBuf[2] << 8) + gBuf[3];
+    if (0 == dl_len)
+        pout("Elements in grown defect list: 0\n");
+    else {
+        if (0 == div)
+            pout("Grown defect list length=%d bytes [unknown "
+                 "number of elements]\n", dl_len);
+        else
+            pout("Elements in grown defect list: %d\n", dl_len / div);
+    }
+}
+
+static void scsiPrintSeagateCacheLPage(int device)
+{
+    int k, j, num, pl, pc, err, len;
+    unsigned char * ucp;
+    unsigned char * xp;
+    uint64_t ull;
+
+    if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf,
+                            LOG_RESP_LEN, 0))) {
+        PRINT_ON(con);
+        pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return;
+    }
+    if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) {
+        PRINT_ON(con);
+        pout("Seagate Cache Log Sense Failed, page mismatch\n");
+        PRINT_OFF(con);
+        return;
+    }
+    len = ((gBuf[2] << 8) | gBuf[3]) + 4;
+    num = len - 4;
+    ucp = &gBuf[0] + 4;
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        switch (pc) {
+        case 0: case 1: case 2: case 3: case 4:
+            break;
+        default: 
+            if (con->reportscsiioctl > 0) {
+                PRINT_ON(con);
+                pout("Vendor (Seagate) cache lpage has unexpected parameter"
+                     ", skip\n");
+                PRINT_OFF(con);
+            }
+            return;
+        }
+        num -= pl;
+        ucp += pl;
+    }
+    pout("Vendor (Seagate) cache information\n");
+    num = len - 4;
+    ucp = &gBuf[0] + 4;
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        switch (pc) {
+        case 0: pout("  Blocks sent to initiator"); break;
+        case 1: pout("  Blocks received from initiator"); break;
+        case 2: pout("  Blocks read from cache and sent to initiator"); break;
+        case 3: pout("  Number of read and write commands whose size "
+                       "<= segment size"); break;
+        case 4: pout("  Number of read and write commands whose size "
+                       "> segment size"); break;
+        default: pout("  Unknown Seagate parameter code [0x%x]", pc); break;
+        }
+        k = pl - 4;
+        xp = ucp + 4;
+        if (k > (int)sizeof(ull)) {
+            xp += (k - (int)sizeof(ull));
+            k = (int)sizeof(ull);
+        }
+        ull = 0;
+        for (j = 0; j < k; ++j) {
+            if (j > 0)
+                ull <<= 8;
+            ull |= xp[j];
+        }
+        pout(" = %"PRIu64"\n", ull);
+        num -= pl;
+        ucp += pl;
+    }
+}
+
+static void scsiPrintSeagateFactoryLPage(int device)
+{
+    int k, j, num, pl, pc, len, err, good, bad;
+    unsigned char * ucp;
+    unsigned char * xp;
+    uint64_t ull;
+
+    if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf,
+                            LOG_RESP_LEN, 0))) {
+        PRINT_ON(con);
+        pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return;
+    }
+    if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) {
+        PRINT_ON(con);
+        pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n");
+        PRINT_OFF(con);
+        return;
+    }
+    len = ((gBuf[2] << 8) | gBuf[3]) + 4;
+    num = len - 4;
+    ucp = &gBuf[0] + 4;
+    good = 0;
+    bad = 0;
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        switch (pc) {
+        case 0: case 8:
+            ++good;
+            break;
+        default: 
+            ++bad;
+            break;
+        }
+        num -= pl;
+        ucp += pl;
+    }
+    if ((good < 2) || (bad > 4)) {  /* heuristic */
+        if (con->reportscsiioctl > 0) {
+            PRINT_ON(con);
+            pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
+                 "unexpected parameters, skip\n");
+            PRINT_OFF(con);
+        }
+        return;
+    }
+    pout("Vendor (Seagate/Hitachi) factory information\n");
+    num = len - 4;
+    ucp = &gBuf[0] + 4;
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        good = 0;
+        switch (pc) {
+        case 0: pout("  number of hours powered up");
+            good = 1;
+            break;
+        case 8: pout("  number of minutes until next internal SMART test");
+            good = 1;
+            break;
+        default:
+            if (con->reportscsiioctl > 0) {
+                PRINT_ON(con);
+                pout("Vendor (Seagate/Hitachi) factory lpage: "
+                     "unknown parameter code [0x%x]\n", pc);
+                PRINT_OFF(con);
+            }
+            break;
+        }
+        if (good) {
+            k = pl - 4;
+            xp = ucp + 4;
+            if (k > (int)sizeof(ull)) {
+                xp += (k - (int)sizeof(ull));
+                k = (int)sizeof(ull);
+            }
+            ull = 0;
+            for (j = 0; j < k; ++j) {
+                if (j > 0)
+                    ull <<= 8;
+                ull |= xp[j];
+            }
+            if (0 == pc)
+                pout(" = %.2f\n", uint64_to_double(ull) / 60.0 );
+            else
+                pout(" = %"PRIu64"\n", ull);
+        }
+        num -= pl;
+        ucp += pl;
+    }
+}
+
+static void scsiPrintErrorCounterLog(int device)
+{
+    struct scsiErrorCounter errCounterArr[3];
+    struct scsiErrorCounter * ecp;
+    struct scsiNonMediumError nme;
+    int found[3] = {0, 0, 0};
+    const char * pageNames[3] = {"read:   ", "write:  ", "verify: "};
+    int k;
+    double processed_gb;
+
+    if (gReadECounterLPage && (0 == scsiLogSense(device,
+                READ_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
+        scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]);
+        found[0] = 1;
+    }
+    if (gWriteECounterLPage && (0 == scsiLogSense(device,
+                WRITE_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
+        scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]);
+        found[1] = 1;
+    }
+    if (gVerifyECounterLPage && (0 == scsiLogSense(device,
+                VERIFY_ERROR_COUNTER_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
+        scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]);
+        ecp = &errCounterArr[2];
+        for (k = 0; k < 7; ++k) {
+            if (ecp->gotPC[k] && ecp->counter[k]) {
+                found[2] = 1;
+                break;
+            }
+        }
+    }
+    if (found[0] || found[1] || found[2]) {
+        pout("\nError counter log:\n");
+        pout("           Errors Corrected by           Total   "
+             "Correction     Gigabytes    Total\n");
+        pout("               ECC          rereads/    errors   "
+             "algorithm      processed    uncorrected\n");
+        pout("           fast | delayed   rewrites  corrected  "
+             "invocations   [10^9 bytes]  errors\n");
+        for (k = 0; k < 3; ++k) {
+            if (! found[k])
+                continue;
+            ecp = &errCounterArr[k];
+            pout("%s%8"PRIu64" %8"PRIu64"  %8"PRIu64"  %8"PRIu64"   %8"PRIu64, 
+                 pageNames[k], ecp->counter[0], ecp->counter[1], 
+                 ecp->counter[2], ecp->counter[3], ecp->counter[4]);
+            processed_gb = uint64_to_double(ecp->counter[5]) / 1000000000.0;
+            pout("   %12.3f    %8"PRIu64"\n", processed_gb, ecp->counter[6]);
+        }
+    }
+    else 
+        pout("\nError Counter logging not supported\n");
+    if (gNonMediumELPage && (0 == scsiLogSense(device,
+                NON_MEDIUM_ERROR_LPAGE, 0, gBuf, LOG_RESP_LEN, 0))) {
+        scsiDecodeNonMediumErrPage(gBuf, &nme);
+        if (nme.gotPC0)
+            pout("\nNon-medium error count: %8"PRIu64"\n", nme.counterPC0);
+        if (nme.gotTFE_H)
+            pout("Track following error count [Hitachi]: %8"PRIu64"\n",
+                 nme.counterTFE_H);
+        if (nme.gotPE_H)
+            pout("Positioning error count [Hitachi]: %8"PRIu64"\n",
+                 nme.counterPE_H);
+    }
+    if (gLastNErrorLPage && (0 == scsiLogSense(device,
+                LAST_N_ERROR_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) {
+        unsigned char * ucp;
+        int num, k, pc, pl, truncated;
+
+        num = (gBuf[2] << 8) + gBuf[3] + 4;
+        truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
+        if (truncated)
+            num = LOG_RESP_LONG_LEN;
+        ucp = gBuf + 4;
+        num -= 4;
+        if (num < 4)
+            pout("\nNo error events logged\n");
+        else {
+            pout("\nLast n error events log page\n");
+            for (k = num; k > 0; k -= pl, ucp += pl) {
+                if (k < 3) {
+                    pout("  <<short Last n error events log page>>\n");
+                    break;
+                }
+                pl = ucp[3] + 4;
+                pc = (ucp[0] << 8) + ucp[1];
+                if (pl > 4) {
+                    if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) {
+                        pout("  Error event %d:\n", pc);
+                        pout("    [binary]:\n");
+                        dStrHex((const char *)ucp + 4, pl - 4, 1);
+                    } else if (ucp[2] & 0x1) {
+                        pout("  Error event %d:\n", pc);
+                        pout("    %.*s\n", pl - 4, (const char *)(ucp + 4));
+                    } else {
+                        if (con->reportscsiioctl > 0) {
+                            pout("  Error event %d:\n", pc);
+                            pout("    [data counter??]:\n");
+                            dStrHex((const char *)ucp + 4, pl - 4, 1);
+                        }
+                    }
+                }
+            }
+            if (truncated)
+                pout(" >>>> log truncated, fetched %d of %d available "
+                     "bytes\n", LOG_RESP_LONG_LEN, truncated);
+        }
+    }
+}
+
+static const char * self_test_code[] = {
+        "Default         ", 
+        "Background short", 
+        "Background long ", 
+        "Reserved(3)     ",
+        "Abort background", 
+        "Foreground short", 
+        "Foreground long ",
+        "Reserved(7)     "
+};
+
+static const char * self_test_result[] = {
+        "Completed                ",
+        "Interrupted ('-X' switch)",
+        "Interrupted (bus reset ?)",
+        "Unknown error, incomplete",
+        "Completed, segment failed",
+        "Failed in first segment  ",
+        "Failed in second segment ",
+        "Failed in segment -->    ",
+        "Reserved(8)              ", 
+        "Reserved(9)              ", 
+        "Reserved(10)             ", 
+        "Reserved(11)             ", 
+        "Reserved(12)             ", 
+        "Reserved(13)             ", 
+        "Reserved(14)             ",
+        "Self test in progress ..."
+};
+
+// See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 .
+// Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
+// 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
+// FAILSMART is returned.
+static int scsiPrintSelfTest(int device)
+{
+    int num, k, n, res, err, durationSec;
+    int noheader = 1;
+    int retval = 0;
+    UINT8 * ucp;
+    uint64_t ull=0;
+
+    if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, 0, gBuf,
+                            LOG_RESP_SELF_TEST_LEN, 0))) {
+        PRINT_ON(con);
+        pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
+        PRINT_ON(con);
+        pout("Self-test Log Sense Failed, page mismatch\n");
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    // compute page length
+    num = (gBuf[2] << 8) + gBuf[3];
+    // Log sense page length 0x190 bytes
+    if (num != 0x190) {
+        PRINT_ON(con);
+        pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num);
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    // loop through the twenty possible entries
+    for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) {
+        int i;
+
+        // timestamp in power-on hours (or zero if test in progress)
+        n = (ucp[6] << 8) | ucp[7];
+
+        // The spec says "all 20 bytes will be zero if no test" but
+        // DG has found otherwise.  So this is a heuristic.
+        if ((0 == n) && (0 == ucp[4]))
+            break;
+
+        // only print header if needed
+        if (noheader) {
+            pout("\nSMART Self-test log\n");
+            pout("Num  Test              Status                 segment  "
+                   "LifeTime  LBA_first_err [SK ASC ASQ]\n");
+            pout("     Description                              number   "
+                   "(hours)\n");
+            noheader=0;
+        }
+
+        // print parameter code (test number) & self-test code text
+        pout("#%2d  %s", (ucp[0] << 8) | ucp[1], 
+            self_test_code[(ucp[4] >> 5) & 0x7]);
+
+        // check the self-test result nibble, using the self-test results
+        // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10:
+        switch ((res = ucp[4] & 0xf)) {
+        case 0x3:
+            // an unknown error occurred while the device server
+            // was processing the self-test and the device server
+            // was unable to complete the self-test
+            retval|=FAILSMART;
+            break;
+        case 0x4:
+            // the self-test completed with a failure in a test
+            // segment, and the test segment that failed is not
+            // known
+            retval|=FAILLOG;
+            break;
+        case 0x5:
+            // the first segment of the self-test failed
+            retval|=FAILLOG;
+            break;
+        case 0x6:
+            // the second segment of the self-test failed
+            retval|=FAILLOG;
+            break;
+        case 0x7:
+            // another segment of the self-test failed and which
+            // test is indicated by the contents of the SELF-TEST
+            // NUMBER field
+            retval|=FAILLOG;
+            break;
+        default:
+            break;
+        }
+        pout("  %s", self_test_result[res]);
+
+        // self-test number identifies test that failed and consists
+        // of either the number of the segment that failed during
+        // the test, or the number of the test that failed and the
+        // number of the segment in which the test was run, using a
+        // vendor-specific method of putting both numbers into a
+        // single byte.
+        if (ucp[5])
+            pout(" %3d",  (int)ucp[5]);
+        else
+            pout("   -");
+
+        // print time that the self-test was completed
+        if (n==0 && res==0xf)
+        // self-test in progress
+            pout("     NOW");
+        else   
+            pout("   %5d", n);
+          
+        // construct 8-byte integer address of first failure
+        for (i = 0; i < 8; i++) {
+            ull <<= 8;
+            ull |= ucp[i+8];
+        }
+        // print Address of First Failure, if sensible
+        if ((~(uint64_t)0 != ull) && (res > 0) && (res < 0xf)) {
+            char buff[32];
+
+            // was hex but change to decimal to conform with ATA
+            snprintf(buff, sizeof(buff), "%"PRIu64, ull);
+            // snprintf(buff, sizeof(buff), "0x%"PRIx64, ull);
+            pout("%18s", buff);
+        } else
+            pout("                 -");
+
+        // if sense key nonzero, then print it, along with
+        // additional sense code and additional sense code qualifier
+        if (ucp[16] & 0xf)
+            pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]);
+        else
+            pout(" [-   -    -]\n");
+    }
+
+    // if header never printed, then there was no output
+    if (noheader)
+        pout("No self-tests have been logged\n");
+    else
+        pout("\n");
+    if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, 
+                        modese_len)) && (durationSec > 0)) {
+        pout("Long (extended) Self Test duration: %d seconds "
+             "[%.1f minutes]\n", durationSec, durationSec / 60.0);
+    }
+    return retval;
+}
+
+static const char * bms_status[] = {
+    "no scans active",
+    "scan is active",
+    "pre-scan is active",
+    "halted due to fatal error",
+    "halted due to a vendor specific pattern of error",
+    "halted due to medium formatted without P-List",
+    "halted - vendor specific cause",
+    "halted due to temperature out of range",
+    "halted until BM interval timer expires", /* 8 */
+};
+
+static const char * reassign_status[] = {
+    "No reassignment needed",
+    "Require Reassign or Write command",
+    "Successfully reassigned",
+    "Reserved [0x3]",
+    "Failed",
+    "Recovered via rewrite in-place",
+    "Reassigned by app, has valid data",
+    "Reassigned by app, has no valid data",
+    "Unsuccessfully reassigned by app", /* 8 */
+};
+
+// See SCSI Block Commands - 3 (SBC-3) rev 6 (draft) section 6.2.2 .
+// Returns 0 if ok else FAIL* bitmask. Note can have a status entry
+// and up to 2048 events (although would hope to have less). May set
+// FAILLOG if serious errors detected (in the future).
+static int scsiPrintBackgroundResults(int device)
+{
+    int num, j, m, err, pc, pl, truncated;
+    int noheader = 1;
+    int firstresult = 1;
+    int retval = 0;
+    UINT8 * ucp;
+
+    if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf,
+                            LOG_RESP_LONG_LEN, 0))) {
+        PRINT_ON(con);
+        pout("scsiPrintBackgroundResults Failed [%s]\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) {
+        PRINT_ON(con);
+        pout("Background scan results Log Sense Failed, page mismatch\n");
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    // compute page length
+    num = (gBuf[2] << 8) + gBuf[3] + 4;
+    if (num < 20) {
+        PRINT_ON(con);
+        pout("Background scan results Log Sense length is %d, no scan "
+             "status\n", num);
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
+    if (truncated)
+        num = LOG_RESP_LONG_LEN;
+    ucp = gBuf + 4;
+    num -= 4;
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        // pcb = ucp[2];
+        pl = ucp[3] + 4;
+        switch (pc) {
+        case 0:
+            if (noheader) {
+                noheader = 0;
+                pout("\nBackground scan results log\n");
+            }
+            pout("  Status: ");
+            if ((pl < 16) || (num < 16)) {
+                pout("\n");
+                break;
+            }
+            j = ucp[9];
+            if (j < (int)(sizeof(bms_status) / sizeof(bms_status[0])))
+                pout("%s\n", bms_status[j]);
+            else
+                pout("unknown [0x%x] background scan status value\n", j);
+            j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
+            pout("    Accumulated power on time, hours:minutes %d:%02d "
+                 "[%d minutes]\n", (j / 60), (j % 60), j);
+            pout("    Number of background scans performed: %d,  ",
+                 (ucp[10] << 8) + ucp[11]);
+            pout("scan progress: %.2f%%\n",
+                 (double)((ucp[12] << 8) + ucp[13]) * 100.0 / 65536.0);
+            break;
+        default:
+            if (noheader) {
+                noheader = 0;
+                pout("\nBackground scan results log\n");
+            }
+            if (firstresult) {
+                firstresult = 0;
+                pout("\n   #  when        lba(hex)    [sk,asc,ascq]    "
+                     "reassign_status\n");
+            }
+            pout(" %3d ", pc);
+            if ((pl < 24) || (num < 24)) {
+                if (pl < 24)
+                    pout("parameter length >= 24 expected, got %d\n", pl);
+                break;
+            }
+            j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
+            pout("%4d:%02d  ", (j / 60), (j % 60));
+            for (m = 0; m < 8; ++m)
+                pout("%02x", ucp[16 + m]);
+            pout("  [%x,%x,%x]   ", ucp[8] & 0xf, ucp[9], ucp[10]);
+            j = (ucp[8] >> 4) & 0xf;
+            if (j <
+                (int)(sizeof(reassign_status) / sizeof(reassign_status[0])))
+                pout("%s\n", reassign_status[j]);
+            else
+                pout("Reassign status: reserved [0x%x]\n", j);
+            break;
+        }
+        num -= pl;
+        ucp += pl;
+    }
+    if (truncated)
+        pout(" >>>> log truncated, fetched %d of %d available "
+             "bytes\n", LOG_RESP_LONG_LEN, truncated);
+    return retval;
+}
+
+static const char * peripheral_dt_arr[] = {
+        "disk",
+        "tape",
+        "printer",
+        "processor",
+        "optical disk(4)",
+        "CD/DVD",
+        "scanner",
+        "optical disk(7)",
+        "medium changer",
+        "communications",
+        "graphics(10)",
+        "graphics(11)",
+        "storage array",
+        "enclosure",
+        "simplified disk",
+        "optical card reader"
+};
+
+static const char * transport_proto_arr[] = {
+        "Fibre channel (FCP-2)",
+        "Parallel SCSI (SPI-4)",
+        "SSA",
+        "IEEE 1394 (SBP-2)",
+        "RDMA (SRP)",
+        "iSCSI",
+        "SAS",
+        "ADT",
+        "0x8",
+        "0x9",
+        "0xa",
+        "0xb",
+        "0xc",
+        "0xd",
+        "0xe",
+        "0xf"
+};
+
+/* Returns 0 on success, 1 on general error and 2 for early, clean exit */
+static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
+{
+    char manufacturer[9];
+    char product[17];
+    char revision[5];
+    char timedatetz[DATEANDEPOCHLEN];
+    struct scsi_iec_mode_page iec;
+    int err, iec_err, len, req_len, avail_len, val;
+    int is_tape = 0;
+    int peri_dt = 0;
+    int returnval=0;
+        
+    memset(gBuf, 0, 96);
+    req_len = 36;
+    if ((err = scsiStdInquiry(device, gBuf, req_len))) {
+        PRINT_ON(con);
+        pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
+        pout("Retrying with a 64 byte Standard Inquiry\n");
+        PRINT_OFF(con);
+        /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
+        req_len = 64;
+        if ((err = scsiStdInquiry(device, gBuf, req_len))) {
+            PRINT_ON(con);
+            pout("Standard Inquiry (64 bytes) failed [%s]\n",
+                 scsiErrString(err));
+            PRINT_OFF(con);
+            return 1;
+        }
+    }
+    avail_len = gBuf[4] + 5;
+    len = (avail_len < req_len) ? avail_len : req_len;
+    peri_dt = gBuf[0] & 0x1f;
+    if (peripheral_type)
+        *peripheral_type = peri_dt;
+
+    if (len < 36) {
+        PRINT_ON(con);
+        pout("Short INQUIRY response, skip product id\n");
+        PRINT_OFF(con);
+        return 1;
+    }
+    memset(manufacturer, 0, sizeof(manufacturer));
+    strncpy(manufacturer, (char *)&gBuf[8], 8);
+    memset(product, 0, sizeof(product));
+    strncpy(product, (char *)&gBuf[16], 16);
+        
+    memset(revision, 0, sizeof(revision));
+    strncpy(revision, (char *)&gBuf[32], 4);
+    if (all && (0 != strncmp(manufacturer, "ATA", 3)))
+        pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
+
+    if (0 == strncmp(manufacturer, "3ware", 5) || 0 == strncmp(manufacturer, "AMCC", 4) ) {
+#if defined(_WIN32) || defined(__CYGWIN__)
+        pout("please try changing device to /dev/hdX,N\n");
+#else
+        pout("please try adding '-d 3ware,N'\n");
+        pout("you may also need to change device to /dev/twaN or /dev/tweN\n");
+#endif
+        return 2;
+    } else if ((len >= 42) &&
+              (0 == strncmp((const char *)(gBuf + 36), "MVSATA", 6))) {
+        pout("please try '-d marvell'\n");
+        return 2;
+    } else if ((0 == con->controller_explicit) &&
+              (0 == strncmp(manufacturer, "ATA     ", 8)) &&
+               has_sat_pass_through(device, 0)) {
+        con->controller_type = CONTROLLER_SAT;
+        if (con->reportscsiioctl > 0) {
+            PRINT_ON(con);
+            pout("Detected SAT interface, switch to device type 'sat'\n");
+            PRINT_OFF(con);
+        }
+       return 2;
+    } else if ((avail_len >= 96) && (0 == strncmp(manufacturer, "ATA", 3))) {
+        /* <<<< This is Linux specific code to detect SATA disks using a
+                SCSI-ATA command translation layer. This may be generalized
+                later when the t10.org SAT project matures. >>>> */
+        pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
+        req_len = 252;
+        memset(gBuf, 0, req_len);
+        if ((err = scsiInquiryVpd(device, 0x83, gBuf, req_len))) {
+            PRINT_ON(con);
+            pout("Inquiry for VPD page 0x83 [device id] failed [%s]\n",
+                  scsiErrString(err));
+            PRINT_OFF(con);
+            return 1;
+        }
+        avail_len = ((gBuf[2] << 8) + gBuf[3]) + 4;
+        len = (avail_len < req_len) ? avail_len : req_len;
+        if (isLinuxLibAta(gBuf, len)) {
+            pout("\nIn Linux, SATA disks accessed via libata are "
+                 "only supported by smartmontools\n"
+                 "for kernel versions 2.6.15 and above.\n  Try "
+                 "an additional '-d ata' or '-d sat' argument.\n");
+            return 2;
+        }
+    }
+    if (! all)
+        return 0;
+
+    /* Do this here to try and detect badly conforming devices (some USB
+       keys) that will lock up on a InquiryVpd or log sense or ... */
+    if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        if (SIMPLE_ERR_BAD_RESP == iec_err) {
+            pout(">> Terminate command early due to bad response to IEC "
+                 "mode page\n");
+            PRINT_OFF(con);
+            gIecMPage = 0;
+            return 1;
+        }
+    } else
+        modese_len = iec.modese_len;
+
+    if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) {
+        /* should use VPD page 0x83 and fall back to this page (0x80)
+         * if 0x83 not supported. NAA requires a lot of decoding code */
+        len = gBuf[3];
+        gBuf[4 + len] = '\0';
+        pout("Serial number: %s\n", &gBuf[4]);
+    }
+    else if (con->reportscsiioctl > 0) {
+        PRINT_ON(con);
+        if (SIMPLE_ERR_BAD_RESP == err)
+            pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
+        else
+            pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
+        PRINT_OFF(con);
+    }
+
+    // print SCSI peripheral device type
+    if (peri_dt < (int)(sizeof(peripheral_dt_arr) / 
+                        sizeof(peripheral_dt_arr[0])))
+        pout("Device type: %s\n", peripheral_dt_arr[peri_dt]);
+    else
+        pout("Device type: <%d>\n", peri_dt);
+
+    // See if transport protocol is known
+    val = scsiFetchTransportProtocol(device, modese_len);
+    if ((val >= 0) && (val <= 0xf))
+        pout("Transport protocol: %s\n", transport_proto_arr[val]);
+
+    // print current time and date and timezone
+    dateandtimezone(timedatetz);
+    pout("Local Time is: %s\n", timedatetz);
+
+    if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) ||
+        (SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
+        is_tape = 1;
+    // See if unit accepts SCSI commmands from us
+    if ((err = scsiTestUnitReady(device))) {
+        if (SIMPLE_ERR_NOT_READY == err) {
+            PRINT_ON(con);
+            if (!is_tape)
+                pout("device is NOT READY (e.g. spun down, busy)\n");
+            else
+                pout("device is NOT READY (e.g. no tape)\n");
+            PRINT_OFF(con);
+         } else if (SIMPLE_ERR_NO_MEDIUM == err) {
+            PRINT_ON(con);
+            pout("NO MEDIUM present on device\n");
+            PRINT_OFF(con);
+         } else if (SIMPLE_ERR_BECOMING_READY == err) {
+            PRINT_ON(con);
+            pout("device becoming ready (wait)\n");
+            PRINT_OFF(con);
+        } else {
+            PRINT_ON(con);
+            pout("device Test Unit Ready  [%s]\n", scsiErrString(err));
+            PRINT_OFF(con);
+        }
+        failuretest(MANDATORY_CMD, returnval|=FAILID);
+    }
+   
+    if (iec_err) {
+        if (!is_tape) {
+            PRINT_ON(con);
+            pout("Device does not support SMART");
+            if (con->reportscsiioctl > 0)
+                pout(" [%s]\n", scsiErrString(iec_err));
+            else
+                pout("\n");
+            PRINT_OFF(con);
+        }
+        gIecMPage = 0;
+        return 0;
+    }
+
+    if (!is_tape)
+        pout("Device supports SMART and is %s\n",
+             (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled");
+    pout("%s\n", (scsi_IsWarningEnabled(&iec)) ? 
+                  "Temperature Warning Enabled" :
+                  "Temperature Warning Disabled or Not Supported");
+    return 0;
+}
+
+static int scsiSmartEnable(int device)
+{
+    struct scsi_iec_mode_page iec;
+    int err;
+
+    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        PRINT_ON(con);
+        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
+             scsiErrString(err));
+        PRINT_OFF(con);
+        return 1;
+    } else
+        modese_len = iec.modese_len;
+
+    if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) {
+        PRINT_ON(con);
+        pout("unable to enable Exception control and warning [%s]\n",
+             scsiErrString(err));
+        PRINT_OFF(con);
+        return 1;
+    }
+    /* Need to refetch 'iec' since could be modified by previous call */
+    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
+             scsiErrString(err));
+        return 1;
+    } else
+        modese_len = iec.modese_len;
+
+    pout("Informational Exceptions (SMART) %s\n",
+         scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
+    pout("Temperature warning %s\n",
+         scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
+    return 0;
+}
+        
+static int scsiSmartDisable(int device)
+{
+    struct scsi_iec_mode_page iec;
+    int err;
+
+    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        PRINT_ON(con);
+        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
+             scsiErrString(err));
+        PRINT_OFF(con);
+        return 1;
+    } else
+        modese_len = iec.modese_len;
+
+    if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) {
+        PRINT_ON(con);
+        pout("unable to disable Exception control and warning [%s]\n",
+             scsiErrString(err));
+        PRINT_OFF(con);
+        return 1;
+    }
+    /* Need to refetch 'iec' since could be modified by previous call */
+    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
+             scsiErrString(err));
+        return 1;
+    } else
+        modese_len = iec.modese_len;
+
+    pout("Informational Exceptions (SMART) %s\n",
+         scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
+    pout("Temperature warning %s\n",
+         scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
+    return 0;
+}
+
+static void scsiPrintTemp(int device)
+{
+    UINT8 temp = 0;
+    UINT8 trip = 0;
+
+    if (scsiGetTemp(device, &temp, &trip))
+        return;
+  
+    if (temp) {
+        if (255 != temp)
+            pout("Current Drive Temperature:     %d C\n", temp);
+        else
+            pout("Current Drive Temperature:     <not available>\n");
+    }
+    if (trip)
+        pout("Drive Trip Temperature:        %d C\n", trip);
+}
+
+/* Main entry point used by smartctl command. Return 0 for success */
+int scsiPrintMain(int fd)
+{
+    int checkedSupportedLogPages = 0;
+    UINT8 peripheral_type = 0;
+    int returnval = 0;
+    int res, durationSec;
+
+    res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo);
+    if (res) {
+        if (2 == res)
+            return 0;
+        else
+            failuretest(MANDATORY_CMD, returnval |= FAILID);
+    }
+
+    if (con->smartenable) {
+        if (scsiSmartEnable(fd))
+            failuretest(MANDATORY_CMD, returnval |= FAILSMART);
+    }
+
+    if (con->smartdisable) {
+        if (scsiSmartDisable(fd))
+            failuretest(MANDATORY_CMD,returnval |= FAILSMART);
+    }
+    
+    if (con->smartautosaveenable) {
+      if (scsiSetControlGLTSD(fd, 0, modese_len)) {
+        pout("Enable autosave (clear GLTSD bit) failed\n");
+        failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
+      }
+    }
+    
+    if (con->smartautosavedisable) {
+      if (scsiSetControlGLTSD(fd, 1, modese_len)) {
+        pout("Disable autosave (set GLTSD bit) failed\n");
+        failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
+      }
+    }
+    
+    if (con->checksmart) {
+        scsiGetSupportedLogPages(fd);
+        checkedSupportedLogPages = 1;
+        if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
+            (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
+            if (gTapeAlertsLPage) {
+                if (con->driveinfo)
+                    pout("TapeAlert Supported\n");
+                if (-1 == scsiGetTapeAlertsData(fd, peripheral_type))
+                    failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
+            }
+            else
+                pout("TapeAlert Not Supported\n");
+        } else { /* disk, cd/dvd, enclosure, etc */
+            if ((res = scsiGetSmartData(fd, con->smartvendorattrib))) {
+                if (-2 == res)
+                    returnval |= FAILSTATUS;
+                else
+                    returnval |= FAILSMART;
+            }
+        }
+    }   
+    if (con->smartvendorattrib) {
+        if (! checkedSupportedLogPages)
+            scsiGetSupportedLogPages(fd);
+        if (gTempLPage) {
+            if (con->checksmart)
+                pout("\n");
+            scsiPrintTemp(fd);         
+        }
+        if (gStartStopLPage)
+            scsiGetStartStopData(fd);
+        if (SCSI_PT_DIRECT_ACCESS == peripheral_type) {
+            scsiPrintGrownDefectListLen(fd);
+            if (gSeagateCacheLPage)
+                scsiPrintSeagateCacheLPage(fd);
+            if (gSeagateFactoryLPage)
+                scsiPrintSeagateFactoryLPage(fd);
+        }
+    }
+    if (con->smarterrorlog) {
+        if (! checkedSupportedLogPages)
+            scsiGetSupportedLogPages(fd);
+        scsiPrintErrorCounterLog(fd);
+        if (1 == scsiFetchControlGLTSD(fd, modese_len, 1))
+            pout("\n[GLTSD (Global Logging Target Save Disable) set. "
+                 "Enable Save with '-S on']\n");
+    }
+    if (con->smartselftestlog) {
+        if (! checkedSupportedLogPages)
+            scsiGetSupportedLogPages(fd);
+        res = 0;
+        if (gSelfTestLPage)
+            res = scsiPrintSelfTest(fd);
+        else {
+            pout("Device does not support Self Test logging\n");
+            failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+        }
+        if (0 != res)
+            failuretest(OPTIONAL_CMD, returnval|=res);
+    }
+    if (con->smartbackgroundlog) {
+        if (! checkedSupportedLogPages)
+            scsiGetSupportedLogPages(fd);
+        res = 0;
+        if (gBackgroundResultsLPage)
+            res = scsiPrintBackgroundResults(fd);
+        else {
+            pout("Device does not support Background scan results logging\n");
+            failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+        }
+        if (0 != res)
+            failuretest(OPTIONAL_CMD, returnval|=res);
+    }
+    if (con->smartexeoffimmediate) {
+        if (scsiSmartDefaultSelfTest(fd))
+            return returnval | FAILSMART;
+        pout("Default Self Test Successful\n");
+    }
+    if (con->smartshortcapselftest) {
+        if (scsiSmartShortCapSelfTest(fd))
+            return returnval | FAILSMART;
+        pout("Short Foreground Self Test Successful\n");
+    }
+    if (con->smartshortselftest ) { 
+        if (scsiSmartShortSelfTest(fd))
+            return returnval | FAILSMART;
+        pout("Short Background Self Test has begun\n");
+        pout("Use smartctl -X to abort test\n");
+    }
+    if (con->smartextendselftest) {
+        if (scsiSmartExtendSelfTest(fd))
+            return returnval | FAILSMART;
+        pout("Extended Background Self Test has begun\n");
+        if ((0 == scsiFetchExtendedSelfTestTime(fd, &durationSec, 
+                        modese_len)) && (durationSec > 0)) {
+            time_t t = time(NULL);
+
+            t += durationSec;
+            pout("Please wait %d minutes for test to complete.\n", 
+                 durationSec / 60);
+            pout("Estimated completion time: %s\n", ctime(&t));
+        }
+        pout("Use smartctl -X to abort test\n");        
+    }
+    if (con->smartextendcapselftest) {
+        if (scsiSmartExtendCapSelfTest(fd))
+            return returnval | FAILSMART;
+        pout("Extended Foreground Self Test Successful\n");
+    }
+    if (con->smartselftestabort) {
+        if (scsiSmartSelfTestAbort(fd))
+            return returnval | FAILSMART;
+        pout("Self Test returned without error\n");
+    }           
+    return returnval;
+}
index d1eabab3f6e85489c617805abb2b9e27a5505e38..1c11629c47589238b563c194e00c3882514f5a48 100644 (file)
@@ -1,7 +1,7 @@
 .ig
  Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
- $Id: smartctl.8.in,v 1.78 2006/04/12 15:45:38 ballen4705 Exp $
+ $Id: smartctl.8.in,v 1.86 2006/09/27 21:42:03 chrfranke Exp $
  
  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
@@ -63,7 +63,8 @@ SCSI Tape Drives and Changers with TapeAlert support use the devices
 For SATA disks accessed with libata, use \fB"/dev/sd[a\-z]"\fP
 and append \fB"\-d ata"\fP. For disks behind 3ware controllers
 you may need \fB"/dev/sd[a\-z]"\fP or \fB"/dev/twe[0\-9]"\fP
-or \fB"/dev/twa[0\-9]"\fP: see details below.
+or \fB"/dev/twa[0\-9]"\fP: see details below. For disks behind
+HighPoint RocketRAID controllers you may need \fB"/dev/sd[a\-z]"\fP.
 More general paths (such as devfs ones) may also be specified.
 .IP \fBDARWIN\fP: 9
 Use the forms \fB/dev/disk[0\-9]\fP or equivalently \fBdisk[0\-9]\fP or equivalently
@@ -83,9 +84,15 @@ Use the forms \fB"/dev/rdsk/c?t?d?s?"\fP for IDE/ATA and SCSI disk
 devices, and \fB"/dev/rmt/*"\fP for SCSI tape devices.
 .IP \fBWINDOWS\fP: 9
 Use the forms \fB"/dev/hd[a\-j]"\fP for IDE/ATA devices
-"\\\\.\\PhysicalDrive[0\-9]" on WinNT4/2000/XP,
-\fB"/dev/hd[a\-d]"\fP for standard IDE/ATA devices on Win95/98/98SE/ME,
+"\\\\.\\PhysicalDrive[0\-9]" on WinNT4/2000/XP/2003,
 and \fB"/dev/scsi[0\-9][0\-f]"\fP for SCSI devices on ASPI adapter 0\-9, ID 0\-15.
+For IDE/ATA devices on Win95/98/98SE/ME, use \fB"/dev/hd[a\-d]"\fP for standard devices
+accessed via SMARTVSD.VXD, and \fB"/dev/hd[e\-h]"\fP for additional devices
+accessed via a patched SMARTVSE.VXD (see INSTALL file for details).
+For disks behind 3ware controllers use \fB"/dev/hd[a\-j],N"\fP where
+N specifies the disk number (3ware \'port\') behind the controller
+providing the logical drive (\'unit\') specified by \fB"/dev/hd[a\-j]"\fP.
+The option \'-d 3ware,N\' is not necessary on Windows.
 The prefix \fB"/dev/"\fP is optional.
 .IP \fBCYGWIN\fP: 9
 See "WINDOWS" above.
@@ -98,7 +105,7 @@ this guess
 
 Note that the printed output of \fBsmartctl\fP displays most numerical
 values in base 10 (decimal), but some values are displayed in base 16
-(hexidecimal).  To distinguish them, the base 16 values are always
+(hexadecimal).  To distinguish them, the base 16 values are always
 displayed with a leading \fB"0x"\fP, for example: "0xff". This man
 page follows the same convention.
 
@@ -110,9 +117,9 @@ will execute the corresponding commands in the order: INFORMATION,
 ENABLE/DISABLE, DISPLAY DATA, RUN/ABORT TESTS.
 
 SCSI devices only accept the options \fB\-h, \-V, \-i, \-a, \-A, \-d,
-\-s, \-S,\-H, \-t, \-C, \-l selftest, \-l error, \-r,\fP and
-\fB\-X\fP.  TapeAlert devices only accept the options \fB\-h, \-V,
-\-i, \-a, \-A, \-d, \-s, \-S, \-t, \-l selftest, \-l error, \-r,\fP
+\-s, \-S,\-H, \-t, \-C, \-l background, \-l error, \-l selftest, \-r,\fP
+and \fB\-X\fP.  TapeAlert devices only accept the options \fB\-h, \-V,
+\-i, \-a, \-A, \-d, \-s, \-S, \-t, \-l error, \-l selftest, \-r,\fP
 and \fB\-H\fP.
 
 Long options  are  not  supported  on  all  systems.   Use
@@ -139,7 +146,8 @@ mode) print current user drive capacity in bytes. (If drive is has a
 user protected area reserved, or is "clipped", this may be smaller
 than the potential maximum drive capacity.)  Indicates if the drive is
 in the smartmontools database (see \'\-v\' options below).  If so, the
-drive model family may also be printed.
+drive model family may also be printed. If \'\-n\' (see below) is
+specified, the power mode of the drive is printed.
 .TP
 .B \-a, \-\-all
 Prints all SMART information about the disk, or TapeAlert information
@@ -177,9 +185,16 @@ use the exit status of \fBsmartctl\fP (see RETURN VALUES below).
 .TP
 .B \-d TYPE, \-\-device=TYPE
 Specifies the type of the device.  The valid arguments to this option
-are \fIata\fP, \fIscsi\fP, \fImarvell\fP, and \fI3ware,N\fP. If this option is not
-used then \fBsmartctl\fP will attempt to guess the device type from
-the device name.
+are \fIata\fP, \fIscsi\fP, \fIsat\fP, \fImarvell\fP, \fI3ware,N\fP, and \fIhpt,L/M\fP
+or \fIhpt,L/M/N\fP.  If this option is not used then \fBsmartctl\fP will attempt to
+guess the device type from the device name.
+
+The \'sat\' device type is for ATA disks that have a SCSI to ATA
+Translation (SAT) Layer (SATL) between the disk and the operating system.
+SAT defines two ATA PASS THROUGH SCSI commands, one 12 bytes long and
+the other 16 bytes long that \fBsmartctl\fP will utilize when this device
+type is selected. The default is the 16 byte variant which can be
+overridden with either \'\-d sat,12\' or \'\-d sat,16\'.
 
 Under Linux, to look at SATA disks behind Marvell SATA controllers
 (using Marvell's \'linuxIAL\' driver rather than libata driver) use \'\-d marvell\'. Such
@@ -189,8 +204,8 @@ either 0x5040, 0x5041, 0x5080, 0x5081, 0x6041 or 0x6081. The \'linuxIAL\' driver
 seems not (yet?) available in the Linux kernel source tree, but should be available
 from system vendors (ftp://ftp.aslab.com/ is known to provide a patch with the driver).
 
-To look at ATA disks behind 3ware SCSI RAID controllers, use syntax
-such as:
+Under Linux and FreeBSD, to look at ATA disks behind 3ware SCSI RAID controllers,
+use syntax such as:
 .nf
 \fBsmartctl \-a \-d 3ware,2 /dev/sda\fP
 .fi
@@ -255,7 +270,26 @@ using the character device interface /dev/twa0\-15 and /dev/twe0\-15.
 The necessary WRITE LOG commands can not be passed through the SCSI
 interface.
 
-.B 3ware controllers are currently ONLY supported under Linux and FreeBSD.
+.B 3ware controllers are supported under Linux, FreeBSD and Windows.
+
+To look at (S)ATA disks behind HighPoint RocketRAID controllers, use syntax
+such as:
+.nf
+\fBsmartctl \-a \-d hpt,1/3 /dev/sda\fP
+.fi
+or
+.nf
+\fBsmartctl \-a \-d hpt,1/2/3 /dev/sda\fP
+.fi
+where in the argument \fIhpt,L/M\fP or \fIhpt,L/M/N\fP, the integer L is the
+controller id, the integer M is the channel number, and the integer N is the
+PMPort number if it is available. The allowed values of L are from 1 to 4
+inclusive, M are from 1 to 8 inclusive and N from 1 to 4 if PMPort available.
+Note that the /dev/sda-z form should be the device node which stands for
+the disks derived from the HighPoint RocketRAID controllers.  And also
+these values are limited by the model of the HighPoint RocketRAID controller.
+
+.B HighPoint RocketRAID controllers are currently ONLY supported under Linux.
 
 .TP
 .B \-T TYPE, \-\-tolerance=TYPE
@@ -347,6 +381,30 @@ the integer with no spaces.  For example,
 The default
 level is 1, so \'\-r ataioctl,1\' and \'\-r ataioctl\' are equivalent.
 
+.TP
+.B \-n POWERMODE, \-\-nocheck=POWERMODE
+Specifieds if \fBsmartctl\fP should exit before performing any checks
+when the device is in a low\-power mode. It may be used to prevent a disk
+from being spun\-up by \fBsmartctl\fP. The power mode is ignored by
+default. The allowed values of POWERMODE are:
+
+.I never
+\- check the device always, but print the power mode if \'\-i\' is
+specified.
+
+.I sleep
+\- check the device unless it is in SLEEP mode.
+
+.I standby
+\- check the device unless it is in SLEEP or STANDBY mode.  In
+these modes most disks are not spinning, so if you want to prevent
+a disk from spinning up, this is probably what you want.
+
+.I idle
+\- check the device unless it is in SLEEP, STANDBY or IDLE mode.
+In the IDLE state, most disks are still spinning, so this is probably
+not what you want.
+
 .TP
 .B SMART FEATURE ENABLE/DISABLE COMMANDS:
 .IP
@@ -582,7 +640,8 @@ relatively free format (compared with ATA disk attributes).
 .TP
 .B \-l TYPE, \-\-log=TYPE
 Prints either the SMART Error Log, the SMART Self\-Test Log, the SMART
-Selective Self\-Test Log [ATA only], or the Log Directory [ATA only].
+Selective Self\-Test Log [ATA only], the Log Directory [ATA only], or
+the Background Scan Results Log [SCSI only].
 The valid arguments to this option are:
 
 .I error
@@ -732,6 +791,18 @@ extended and comprehensive SMART self\-test and error logs.  If your
 disk supports these, and you would like to assist, please contact the
 \fBsmartmontools\fP developers.]
 
+.I background [SCSI]
+\- the background scan results log outputs information derived from
+Background Media Scans (BMS) done after power up and/or periodocally (e.g.
+every 24 hours) on recent SCSI disks. If supported, the BMS status
+is output first, indicating whether a background scan is currently
+underway (and if so a progress percentage), the amount of time the disk
+has been powered up and the number of scans already completed. Then there
+is a header and a line for each background scan "event". These will
+typically be either recovered or unrecoverable errors. That latter group
+may need some attention. There is a description of the background scan
+mechansim in section 4.18 of SBC\-3 revision 6 (see www.t10.org ).
+
 .TP
 .B \-v N,OPTION, \-\-vendorattribute=N,OPTION
 Sets a vendor\-specific display OPTION for Attribute N.  This option
@@ -1149,6 +1220,20 @@ RAID 9000 controller card.
 .fi
 Start a short self\-test on the fourth ATA disk connected to the 3ware RAID
 controller card which is the second SCSI device /dev/sdb.
+.PP
+.nf
+.B smartctl \-a \-d hpt,1/3 /dev/sda
+.fi
+Examine all SMART data for the (S)ATA disk directly connected to the third channel of the
+first HighPoint RocketRAID controller card.
+.nf
+.PP
+.nf
+.B smartctl \-t short \-d hpt,1/1/2 /dev/sda
+.fi
+Start a short self\-test on the (S)ATA disk connected to second pmport on the
+first channel of the first HighPoint RocketRAID controller card.
+.PP
 .nf
 .B smartctl \-t select,10\-100 \-t select,30\-300 \-t afterselect,on \-t pending,45 /dev/hda
 .fi
@@ -1284,7 +1369,7 @@ these documents may be found in the References section of the
 
 .SH
 CVS ID OF THIS PAGE:
-$Id: smartctl.8.in,v 1.78 2006/04/12 15:45:38 ballen4705 Exp $
+$Id: smartctl.8.in,v 1.86 2006/09/27 21:42:03 chrfranke Exp $
 .\" Local Variables:            
 .\" mode: nroff         
 .\" End:
diff --git a/smartctl.8.in.orig b/smartctl.8.in.orig
new file mode 100644 (file)
index 0000000..3898fd5
--- /dev/null
@@ -0,0 +1,1358 @@
+.ig
+ Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+
+ $Id: smartctl.8.in,v 1.83 2006/09/15 08:03:52 sxzzsf Exp $
+ 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, or (at your option) any later
+ version.
+ You should have received a copy of the GNU General Public License (for
+ example COPYING); if not, write to the Free Software Foundation, Inc., 675
+ Mass Ave, Cambridge, MA 02139, USA.
+
+ This code was originally developed as a Senior Thesis by Michael Cornwell
+ at the Concurrent Systems Laboratory (now part of the Storage Systems
+ Research Center), Jack Baskin School of Engineering, University of
+ California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+
+..
+.TH SMARTCTL 8 CURRENT_CVS_DATE CURRENT_CVS_VERSION CURRENT_CVS_DATE
+.SH NAME
+\fBsmartctl\fP \- Control and Monitor Utility for SMART Disks
+
+.SH SYNOPSIS
+.B smartctl [options] device
+
+.SH FULL PATH
+.B /usr/local/sbin/smartctl
+
+.SH PACKAGE VERSION
+CURRENT_CVS_VERSION released CURRENT_CVS_DATE at CURRENT_CVS_TIME
+
+.SH DESCRIPTION
+\fBsmartctl\fP controls the Self\-Monitoring, Analysis and Reporting
+Technology (SMART) system built into many ATA\-3 and later ATA, IDE and
+SCSI\-3 hard drives. The purpose of SMART is to monitor the reliability
+of the hard drive and predict drive failures, and to carry out
+different types of drive self\-tests.  This version of \fBsmartctl\fP
+is compatible with ATA/ATAPI\-7 and earlier standards (see REFERENCES
+below)
+
+\fBsmartctl\fP is a command line utility designed to perform SMART
+tasks such as printing the SMART self\-test and error logs, enabling
+and disabling SMART automatic testing, and initiating device
+self\-tests. Note: if the user issues a SMART command that is
+(apparently) not implemented by the device, \fBsmartctl\fP will print
+a warning message but issue the command anyway (see the \fB\-T,
+\-\-tolerance\fP option below).  This should not cause problems: on
+most devices, unimplemented SMART commands issued to a drive are
+ignored and/or return an error.
+
+\fBsmartctl\fP also provides support for polling TapeAlert messages
+from SCSI tape drives and changers.
+
+The user must specify the device to be controlled or interrogated as
+the final argument to \fBsmartctl\fP.  Device paths are as follows:
+.IP \fBLINUX\fP: 9
+Use the forms \fB"/dev/hd[a\-t]"\fP for IDE/ATA
+devices, and \fB"/dev/sd[a\-z]"\fP for SCSI devices. For
+SCSI Tape Drives and Changers with TapeAlert support use the devices
+\fB"/dev/nst*"\fP and \fB"/dev/sg*"\fP. 
+For SATA disks accessed with libata, use \fB"/dev/sd[a\-z]"\fP
+and append \fB"\-d ata"\fP. For disks behind 3ware controllers
+you may need \fB"/dev/sd[a\-z]"\fP or \fB"/dev/twe[0\-9]"\fP
+or \fB"/dev/twa[0\-9]"\fP: see details below. For disks behind
+HighPoint RocketRAID controllers you may need \fB"/dev/sd[a\-z]"\fP.
+More general paths (such as devfs ones) may also be specified.
+.IP \fBDARWIN\fP: 9
+Use the forms \fB/dev/disk[0\-9]\fP or equivalently \fBdisk[0\-9]\fP or equivalently
+\fB/dev/rdisk[0\-9]\fP.  Long forms are also available: please use \'\-h\' to see some
+examples. Note that there is currently no Darwin SCSI support.
+.IP \fBFREEBSD\fP: 9
+Use the forms \fB"/dev/ad[0\-9]+"\fP for IDE/ATA
+devices and \fB"/dev/da[0\-9]+"\fP for SCSI devices.
+.IP \fBNETBSD/OPENBSD\fP: 9
+Use the form \fB"/dev/wd[0\-9]+c"\fP for IDE/ATA
+devices.  For SCSI disk and tape devices, use the device names
+\fB"/dev/sd[0\-9]+c"\fP and \fB"/dev/st[0\-9]+c"\fP respectively.  
+Be sure to specify the correct "whole disk" partition letter for 
+your architecture.
+.IP \fBSOLARIS\fP: 9
+Use the forms \fB"/dev/rdsk/c?t?d?s?"\fP for IDE/ATA and SCSI disk
+devices, and \fB"/dev/rmt/*"\fP for SCSI tape devices.
+.IP \fBWINDOWS\fP: 9
+Use the forms \fB"/dev/hd[a\-j]"\fP for IDE/ATA devices
+"\\\\.\\PhysicalDrive[0\-9]" on WinNT4/2000/XP/2003,
+and \fB"/dev/scsi[0\-9][0\-f]"\fP for SCSI devices on ASPI adapter 0\-9, ID 0\-15.
+For IDE/ATA devices on Win95/98/98SE/ME, use \fB"/dev/hd[a\-d]"\fP for standard devices
+accessed via SMARTVSD.VXD, and \fB"/dev/hd[e\-h]"\fP for additional devices
+accessed via a patched SMARTVSE.VXD (see INSTALL file for details).
+The prefix \fB"/dev/"\fP is optional.
+.IP \fBCYGWIN\fP: 9
+See "WINDOWS" above.
+.IP \fBOS/2,eComStation\fP: 9
+Use the form \fB"/dev/hd[a\-z]"\fP for IDE/ATA devices.
+.PP
+Based on the device path, \fBsmartctl\fP will guess the device type
+(ATA or SCSI).  If necessary, the \'\-d\' option can be used to over\-ride
+this guess
+
+Note that the printed output of \fBsmartctl\fP displays most numerical
+values in base 10 (decimal), but some values are displayed in base 16
+(hexidecimal).  To distinguish them, the base 16 values are always
+displayed with a leading \fB"0x"\fP, for example: "0xff". This man
+page follows the same convention.
+
+.PP
+.SH OPTIONS
+.PP
+The options are grouped below into several categories.  \fBsmartctl\fP
+will execute the corresponding commands in the order: INFORMATION,
+ENABLE/DISABLE, DISPLAY DATA, RUN/ABORT TESTS.
+
+SCSI devices only accept the options \fB\-h, \-V, \-i, \-a, \-A, \-d,
+\-s, \-S,\-H, \-t, \-C, \-l selftest, \-l error, \-r,\fP and
+\fB\-X\fP.  TapeAlert devices only accept the options \fB\-h, \-V,
+\-i, \-a, \-A, \-d, \-s, \-S, \-t, \-l selftest, \-l error, \-r,\fP
+and \fB\-H\fP.
+
+Long options  are  not  supported  on  all  systems.   Use
+.B \'smartctl \-h\'
+to see the available options.
+
+.TP
+.B SHOW INFORMATION OPTIONS:
+.TP
+.B \-h, \-\-help, \-\-usage
+Prints a usage message to STDOUT and exits.
+.TP
+.B \-V, \-\-version, \-\-copyright, \-\-license
+Prints version, copyright, license, home page and CVS\-id information
+for your copy of \fBsmartctl\fP to STDOUT and then exits.  Please
+include this information if you are reporting bugs or problems.
+.TP
+.B \-i, \-\-info
+Prints the device model number, serial number, firmware version, and
+ATA Standard version/revision information.  Says if the device
+supports SMART, and if so, whether SMART support is currently enabled
+or disabled.  If the device supports Logical Block Address mode (LBA
+mode) print current user drive capacity in bytes. (If drive is has a
+user protected area reserved, or is "clipped", this may be smaller
+than the potential maximum drive capacity.)  Indicates if the drive is
+in the smartmontools database (see \'\-v\' options below).  If so, the
+drive model family may also be printed. If \'\-n\' (see below) is
+specified, the power mode of the drive is printed.
+.TP
+.B \-a, \-\-all
+Prints all SMART information about the disk, or TapeAlert information
+about the tape drive or changer.  For ATA devices this is equivalent
+to
+.nf
+\'\-H \-i \-c \-A \-l error \-l selftest -l selective\'
+.fi
+and for SCSI, this is equivalent to
+.nf
+\'\-H \-i \-A \-l error \-l selftest\'.
+.fi
+Note that for ATA disks this does \fBnot\fP enable the \'\-l
+directory\' option.
+
+.TP
+.B RUN\-TIME BEHAVIOR OPTIONS:
+.TP
+.B \-q TYPE, \-\-quietmode=TYPE
+Specifies that \fBsmartctl\fP should run in one of the two quiet modes
+described here.  The valid arguments to this option are:
+
+.I errorsonly
+\- only print: For the \'\-l error\' option, if nonzero, the number
+of errors recorded in the SMART error log and the power\-on time when
+they occurred; For the \'\-l selftest\' option, errors recorded in the device
+self\-test log; For the \'\-H\' option, SMART "disk failing" status or device
+Attributes (pre\-failure or usage) which failed either now or in the
+past; For the \'\-A\' option, device Attributes (pre\-failure or usage)
+which failed either now or in the past.
+
+.I silent
+\- print no output.  The only way to learn about what was found is to
+use the exit status of \fBsmartctl\fP (see RETURN VALUES below).
+.TP
+.B \-d TYPE, \-\-device=TYPE
+Specifies the type of the device.  The valid arguments to this option
+are \fIata\fP, \fIscsi\fP, \fIsat\fP, \fImarvell\fP, \fI3ware,N\fP, and \fIhpt,L/M\fP
+or \fIhpt,L/M/N\fP.  If this option is not used then \fBsmartctl\fP will attempt to
+guess the device type from the device name.
+
+The \'sat\' device type is for ATA disks that have a SCSI to ATA
+Translation (SAT) Layer (SATL) between the disk and the operating system.
+SAT defines two ATA PASS THROUGH SCSI commands, one 12 bytes long and
+the other 16 bytes long that \fBsmartctl\fP will utilize when this device
+type is selected. The default is the 16 byte variant which can be
+overridden with either \'\-d sat,12\' or \'\-d sat,16\'.
+
+Under Linux, to look at SATA disks behind Marvell SATA controllers
+(using Marvell's \'linuxIAL\' driver rather than libata driver) use \'\-d marvell\'. Such
+controllers show up as Marvell Technology Group Ltd. SATA I or II controllers
+using lspci, or using lspci -n show a vendor ID 0x11ab and a device ID of
+either 0x5040, 0x5041, 0x5080, 0x5081, 0x6041 or 0x6081. The \'linuxIAL\' driver
+seems not (yet?) available in the Linux kernel source tree, but should be available
+from system vendors (ftp://ftp.aslab.com/ is known to provide a patch with the driver).
+
+To look at ATA disks behind 3ware SCSI RAID controllers, use syntax
+such as:
+.nf
+\fBsmartctl \-a \-d 3ware,2 /dev/sda\fP
+.fi
+.nf
+\fBsmartctl \-a \-d 3ware,0 /dev/twe0\fP
+.fi
+.nf
+\fBsmartctl \-a \-d 3ware,1 /dev/twa0\fP
+.fi
+where in the argument \fI3ware,N\fP, the integer N is the disk number
+(3ware \'port\') within the 3ware ATA RAID controller.  The allowed
+values of N are from 0 to 15 inclusive.  The first two forms, which
+refer to devices /dev/sda-z and /dev/twe0-15, may be used with 3ware
+series 6000, 7000, and 8000 series controllers that use the 3x-xxxx
+driver.  \fBNote that the /dev/sda-z form is deprecated\fP starting
+with the Linux 2.6 kernel series and may not be supported by the Linux
+kernel in the near future. The final form, which refers to devices
+/dev/twa0-15, must be used with 3ware 9000 series controllers, which
+use the 3w-9xxx driver.
+
+Note that if the special character device nodes /dev/twa? and
+/dev/twe? do not exist, or exist with the incorrect major or minor
+numbers, smartctl will recreate them on the fly.  Typically /dev/twa0
+refers to the first 9000-series controller, /dev/twa1 refers to the
+second 9000 series controller, and so on. Likewise /dev/twe0 refers to
+the first 6/7/8000-series controller, /dev/twa1 refers to the second
+6/7/8000 series controller, and so on.
+
+Note that for the 6/7/8000 controllers, \fBany\fP of the physical
+disks can be queried or examined using \fBany\fP of the 3ware's SCSI
+logical device /dev/sd?  entries.  Thus, if logical device /dev/sda is
+made up of two physical disks (3ware ports zero and one) and logical
+device /dev/sdb is made up of two other physical disks (3ware ports
+two and three) then you can examine the SMART data on \fBany\fP of the
+four physical disks using \fBeither\fP SCSI device /dev/sda \fBor\fP
+/dev/sdb.  If you need to know which logical SCSI device a particular
+physical disk (3ware port) is associated with, use the dmesg or SYSLOG
+output to show which SCSI ID corresponds to a particular 3ware unit,
+and then use the 3ware CLI or 3dm tool to determine which ports
+(physical disks) correspond to particular 3ware units.
+
+If the value of N corresponds to a port that does \fBnot\fP exist on
+the 3ware controller, or to a port that does not physically have a
+disk attached to it, the behavior of \fBsmartctl\fP depends upon the
+specific controller model, firmware, Linux kernel and platform.  In
+some cases you will get a warning message that the device does not
+exist. In other cases you will be presented with \'void\' data for a
+non\-existent device.
+
+Note that if the /dev/sd? addressing form is used, then older 3w\-xxxx
+drivers do not pass the "Enable Autosave"
+(\'\fB\-S on\fP\') and "Enable Automatic Offline" (\'\fB\-o on\fP\')
+commands to the disk, and produce these types of harmless syslog error
+messages instead: "\fB3w\-xxxx: tw_ioctl(): Passthru size (123392) too
+big\fP". This can be fixed by upgrading to version 1.02.00.037 or
+later of the 3w\-xxxx driver, or by applying a patch to older
+versions. See \fBhttp://smartmontools.sourceforge.net/\fP for
+instructions.  Alternatively, use the character device /dev/twe0-15 interface.
+
+The selective self\-test functions (\'\-t select,A\-B\') are only supported
+using the character device interface /dev/twa0\-15 and /dev/twe0\-15.
+The necessary WRITE LOG commands can not be passed through the SCSI
+interface.
+
+.B 3ware controllers are currently ONLY supported under Linux and FreeBSD.
+
+To look at (S)ATA disks behind HighPoint RocketRAID controllers, use syntax
+such as:
+.nf
+\fBsmartctl \-a \-d hpt,1/3 /dev/sda\fP
+.fi
+or
+.nf
+\fBsmartctl \-a \-d hpt,1/2/3 /dev/sda\fP
+.fi
+where in the argument \fIhpt,L/M\fP or \fIhpt,L/M/N\fP, the integer L is the
+controller id, the integer M is the channel number, and the integer N is the
+PMPort number if it is available. The allowed values of L are from 1 to 4
+inclusive, M are from 1 to 8 inclusive and N from 1 to 4 if PMPort available.
+Note that the /dev/sda-z form should be the device node which stands for
+the disks derived from the HighPoint RocketRAID controllers.  And also
+these values are limited by the model of the HighPoint RocketRAID controller.
+
+.B HighPoint RocketRAID controllers are currently ONLY supported under Linux.
+
+.TP
+.B \-T TYPE, \-\-tolerance=TYPE
+Specifies how tolerant \fBsmartctl\fP should be of ATA and SMART command
+failures. 
+
+The behavior of \fBsmartctl\fP depends upon whether the command is
+"\fBoptional\fP" or "\fBmandatory\fP". Here "\fBmandatory\fP" means
+"required by the ATA/ATAPI\-5 Specification if the device implements
+the SMART command set" and "\fBoptional\fP" means "not required by the
+ATA/ATAPI\-5 Specification even if the device implements the SMART
+command set."  The "\fBmandatory\fP" ATA and SMART commands are: (1)
+ATA IDENTIFY DEVICE, (2) SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE, (3)
+SMART ENABLE/DISABLE, and (4) SMART RETURN STATUS.
+
+The valid arguments to this option are:
+
+.I normal
+\- exit on failure of any \fBmandatory\fP SMART command, and ignore
+all failures of \fBoptional\fP SMART commands.  This is the default.
+Note that on some devices, issuing unimplemented optional SMART
+commands doesn\'t cause an error.  This can result in misleading
+\fBsmartctl\fP messages such as "Feature X not implemented", followed
+shortly by "Feature X: enabled".  In most such cases, contrary to the
+final message, Feature X is \fBnot\fP enabled.
+
+.I conservative
+\- exit on failure of any \fBoptional\fP SMART command.
+
+.I permissive
+\- ignore failure(s) of \fBmandatory\fP SMART commands.  This option
+may be given more than once.  Each additional use of this option will
+cause one more additional failure to be ignored.  Note that the use of
+this option can lead to messages like "Feature X not implemented",
+followed shortly by "Error: unable to enable Feature X".  In a few
+such cases, contrary to the final message, Feature X \fBis\fP enabled.
+
+.I verypermissive
+\- equivalent to giving a large number of \'\-T permissive\' options:
+ignore failures of \fBany number\fP of \fBmandatory\fP SMART commands.
+Please see the note above.
+
+.TP
+.B \-b TYPE, \-\-badsum=TYPE
+Specifies the action \fBsmartctl\fP should take if a checksum error is
+detected in the: (1) Device Identity Structure, (2) SMART Self\-Test
+Log Structure, (3) SMART Attribute Value Structure, (4) SMART
+Attribute Threshold Structure, or (5) ATA Error Log Structure.
+
+The valid arguments to this option are:
+
+.I warn
+\- report the incorrect checksum but carry on in spite of it.  This is the
+default.
+
+.I exit
+\- exit \fBsmartctl\fP.
+
+.I ignore
+\- continue silently without issuing a warning.
+
+.TP
+.B \-r TYPE, \-\-report=TYPE
+Intended primarily to help \fBsmartmontools\fP developers understand
+the behavior of \fBsmartmontools\fP on non\-conforming or poorly
+conforming hardware.  This option reports details of \fBsmartctl\fP
+transactions with the device.  The option can be used multiple times.
+When used just once, it shows a record of the ioctl() transactions
+with the device.  When used more than once, the detail of these
+ioctl() transactions are reported in greater detail.  The valid
+arguments to this option are:
+
+.I ioctl
+\- report all ioctl() transactions.
+
+.I ataioctl
+\- report only ioctl() transactions with ATA devices.
+
+.I scsiioctl
+\- report only ioctl() transactions with SCSI devices. Invoking this once
+shows the SCSI commands in hex and the corresponding status. Invoking
+it a second time adds a hex listing of the first 64 bytes of data send to, 
+or received from the device.
+
+Any argument may include a positive integer to specify the level of detail
+that should be reported.  The argument should be followed by a comma then
+the integer with no spaces.  For example, 
+.I ataioctl,2
+The default
+level is 1, so \'\-r ataioctl,1\' and \'\-r ataioctl\' are equivalent.
+
+.TP
+.B \-n POWERMODE, \-\-nocheck=POWERMODE
+Specifieds if \fBsmartctl\fP should exit before performing any checks
+when the device is in a low\-power mode. It may be used to prevent a disk
+from being spun\-up by \fBsmartctl\fP. The power mode is ignored by
+default. The allowed values of POWERMODE are:
+
+.I never
+\- check the device always, but print the power mode if \'\-i\' is
+specified.
+
+.I sleep
+\- check the device unless it is in SLEEP mode.
+
+.I standby
+\- check the device unless it is in SLEEP or STANDBY mode.  In
+these modes most disks are not spinning, so if you want to prevent
+a disk from spinning up, this is probably what you want.
+
+.I idle
+\- check the device unless it is in SLEEP, STANDBY or IDLE mode.
+In the IDLE state, most disks are still spinning, so this is probably
+not what you want.
+
+.TP
+.B SMART FEATURE ENABLE/DISABLE COMMANDS:
+.IP
+.B Note: 
+if multiple options are used to both enable and disable a
+feature, then 
+.B both
+the enable and disable commands will be issued.  The enable command
+will always be issued
+.B before
+the corresponding disable command.
+.TP
+.B \-s VALUE, \-\-smart=VALUE 
+Enables or disables SMART on device.  The valid arguments to
+this option are \fIon\fP and \fIoff\fP.  Note that the command \'\-s on\'
+(perhaps used with with the \'\-o on\' and \'\-S on\' options) should be placed
+in a start\-up script for your machine, for example in rc.local or rc.sysinit.
+In principle the SMART feature settings are preserved over
+power\-cycling, but it doesn\'t hurt to be sure. It is not necessary (or
+useful) to enable SMART to see the TapeAlert messages.
+.TP
+.B \-o VALUE, \-\-offlineauto=VALUE
+Enables or disables SMART automatic offline test, which scans the drive
+every four hours for disk defects. This command can be given during normal
+system operation.  The valid arguments to this option are \fIon\fP
+and \fIoff\fP.
+
+Note that the SMART automatic offline test command is listed as
+"Obsolete" in every version of the ATA and ATA/ATAPI Specifications.
+It was originally part of the SFF\-8035i Revision 2.0 specification,
+but was never part of any ATA specification.  However it is
+implemented and used by many vendors. [Good documentation can be found
+in IBM\'s Official Published Disk Specifications.  For example the IBM
+Travelstar 40GNX Hard Disk Drive Specifications (Revision 1.1, 22
+April 2002, Publication # 1541, Document S07N\-7715\-02) page 164. You
+can also read the SFF\-8035i Specification \-\- see REFERENCES below.]
+You can tell if automatic offline testing is supported by seeing if
+this command enables and disables it, as indicated by the \'Auto
+Offline Data Collection\' part of the SMART capabilities report
+(displayed with \'\-c\').
+
+SMART provides \fBthree\fP basic categories of testing.  The
+\fBfirst\fP category, called "online" testing, has no effect on the
+performance of the device.  It is turned on by the \'\-s on\' option.
+
+The \fBsecond\fP category of testing is called "offline" testing. This
+type of test can, in principle, degrade the device performance.  The
+\'\-o on\' option causes this offline testing to be carried out,
+automatically, on a regular scheduled basis.  Normally, the disk will
+suspend offline testing while disk accesses are taking place, and then
+automatically resume it when the disk would otherwise be idle, so in
+practice it has little effect.  Note that a one\-time offline test can
+also be carried out immediately upon receipt of a user command.  See
+the \'\-t offline\' option below, which causes a one\-time offline test
+to be carried out immediately.
+
+The choice (made by the SFF\-8035i and ATA specification authors) of
+the word \fItesting\fP for these first two categories is unfortunate,
+and often leads to confusion.  In fact these first two categories of
+online and offline testing could have been more accurately described
+as online and offline \fBdata collection\fP.
+
+The results of this automatic or immediate offline testing (data
+collection) are reflected in the values of the SMART Attributes.
+Thus, if problems or errors are detected, the values of these
+Attributes will go below their failure thresholds; some types of
+errors may also appear in the SMART error log. These are visible with
+the \'\-A\' and \'\-l error\' options respectively.
+
+Some SMART attribute values are updated only during off\-line data
+collection activities; the rest are updated during normal operation of
+the device or during both normal operation and off\-line testing.  The
+Attribute value table produced by the \'\-A\' option indicates this in
+the UPDATED column.  Attributes of the first type are labeled
+"Offline" and Attributes of the second type are labeled "Always".
+
+The \fBthird\fP category of testing (and the \fIonly\fP category for
+which the word \'testing\' is really an appropriate choice) is "self"
+testing.  This third type of test is only performed (immediately) when
+a command to run it is issued.  The \'\-t\' and \'\-X\' options can be
+used to carry out and abort such self\-tests; please see below for
+further details.
+
+Any errors detected in the self testing will be shown in the
+SMART self\-test log, which can be examined using the \'\-l selftest\'
+option.
+
+\fBNote:\fP in this manual page, the word \fB"Test"\fP is used in
+connection with the second category just described, e.g. for the
+"offline" testing.  The words \fB"Self\-test"\fP are used in
+connection with the third category.
+.TP
+.B \-S VALUE, \-\-saveauto=VALUE
+Enables or disables SMART autosave of device vendor\-specific
+Attributes. The valid arguments to this option are \fIon\fP
+and \fIoff\fP.  Note that this feature is preserved across disk power
+cycles, so you should only need to issue it once.
+
+For SCSI devices this toggles the value of the Global Logging Target
+Save Disabled (GLTSD) bit in the Control Mode Page. Some disk
+manufacturers set this bit by default. This prevents error counters,
+power\-up hours and other useful data from being placed in non\-volatile
+storage, so these values may be reset to zero the next time the device
+is power\-cycled.  If the GLTSD bit is set then \'smartctl \-a\' will
+issue a warning. Use \fIon\fP to clear the GLTSD bit and thus enable
+saving counters to non\-volatile storage. For extreme streaming\-video
+type applications you might consider using \fIoff\fP to set the GLTSD
+bit.
+
+.TP
+.B SMART READ AND DISPLAY DATA OPTIONS:
+.TP
+.B \-H, \-\-health
+Check: Ask the device to report its SMART health status or pending
+TapeAlert messages.  SMART status is based on
+information that it has gathered from online and offline
+tests, which were used to determine/update its
+SMART vendor\-specific Attribute values. TapeAlert status is obtained
+by reading the TapeAlert log page.
+
+If the device reports failing health status, this means
+.B either
+that the device has already failed, 
+.B or 
+that it is predicting its own failure within the next 24 hours.  If
+this happens, use the \'\-a\' option to get more information, and
+.B get your data off the disk and someplace safe as soon as you can.
+.TP
+.B \-c, \-\-capabilities
+Prints only the generic SMART capabilities.  These show
+what SMART features are implemented and how the device will
+respond to some of the different SMART commands.  For example it
+shows if the device logs errors, if it supports offline surface
+scanning, and so on.  If the device can carry out self\-tests, this
+option also shows the estimated time required to run those tests.
+
+Note that the time required to run the Self\-tests (listed in minutes)
+are fixed.  However the time required to run the Immediate Offline
+Test (listed in seconds) is variable.  This means that if you issue a
+command to perform an Immediate Offline test with the \'\-t offline\' option,
+then the time may jump to a larger value and then count down as the
+Immediate Offline Test is carried out.  Please see REFERENCES below
+for further information about the the flags and capabilities described
+by this option.
+.TP
+.B \-A, \-\-attributes
+Prints only the vendor specific SMART Attributes.  The Attributes are
+numbered from 1 to 253 and have specific names and ID numbers. For
+example Attribute 12 is "power cycle count": how many times has the
+disk been powered up.
+
+Each Attribute has a "Raw" value, printed under the heading
+"RAW_VALUE", and a "Normalized" value printed under the heading
+"VALUE".  [Note: \fBsmartctl\fP prints these values in base\-10.]  In
+the example just given, the "Raw Value" for Attribute 12 would be the
+actual number of times that the disk has been power\-cycled, for
+example 365 if the disk has been turned on once per day for exactly
+one year.  Each vendor uses their own algorithm to convert this "Raw"
+value to a "Normalized" value in the range from 1 to 254.  Please keep
+in mind that \fBsmartctl\fP only reports the different Attribute
+types, values, and thresholds as read from the device.  It does
+\fBnot\fP carry out the conversion between "Raw" and "Normalized"
+values: this is done by the disk\'s firmware.
+
+The conversion from Raw value to a quantity with physical units is
+not specified by the SMART standard. In most cases, the values printed
+by \fBsmartctl\fP are sensible.  For example the temperature Attribute
+generally has its raw value equal to the temperature in Celsius.
+However in some cases vendors use unusual conventions.  For example
+the Hitachi disk on my laptop reports its power\-on hours in minutes,
+not hours. Some IBM disks track three temperatures rather than one, in
+their raw values.  And so on.
+
+Each Attribute also has a Threshold value (whose range is 0 to 255)
+which is printed under the heading "THRESH".  If the Normalized value
+is \fBless than or equal to\fP the Threshold value, then the Attribute
+is said to have failed.  If the Attribute is a pre\-failure Attribute,
+then disk failure is imminent.
+
+Each Attribute also has a "Worst" value shown under the heading
+"WORST".  This is the smallest (closest to failure) value that the
+disk has recorded at any time during its lifetime when SMART was
+enabled.  [Note however that some vendors firmware may actually
+\fBincrease\fP the "Worst" value for some "rate\-type" Attributes.]
+
+The Attribute table printed out by \fBsmartctl\fP also shows the
+"TYPE" of the Attribute. Attributes are one of two possible types:
+Pre\-failure or Old age.  Pre\-failure Attributes are ones which, if
+less than or equal to their threshold values, indicate pending disk
+failure.  Old age, or usage Attributes, are ones which indicate
+end\-of\-product life from old\-age or normal aging and wearout, if
+the Attribute value is less than or equal to the threshold.  \fBPlease
+note\fP: the fact that an Attribute is of type 'Pre\-fail' does
+\fBnot\fP mean that your disk is about to fail!  It only has this
+meaning if the Attribute\'s current Normalized value is less than or
+equal to the threshold value.
+
+If the Attribute\'s current Normalized value is less than or equal to
+the threshold value, then the "WHEN_FAILED" column will display
+"FAILING_NOW". If not, but the worst recorded value is less than or
+equal to the threshold value, then this column will display
+"In_the_past".  If the "WHEN_FAILED" column has no entry (indicated by
+a dash: \'\-\') then this Attribute is OK now (not failing) and has
+also never failed in the past.
+
+The table column labeled "UPDATED" shows if the SMART Attribute values
+are updated during both normal operation and off\-line testing, or
+only during offline testing.  The former are labeled "Always" and the
+latter are labeled "Offline".
+
+So to summarize: the Raw Attribute values are the ones that might have
+a real physical interpretation, such as "Temperature Celsius",
+"Hours", or "Start\-Stop Cycles".  Each manufacturer converts these,
+using their detailed knowledge of the disk\'s operations and failure
+modes, to Normalized Attribute values in the range 1\-254.  The
+current and worst (lowest measured) of these Normalized Attribute
+values are stored on the disk, along with a Threshold value that the
+manufacturer has determined will indicate that the disk is going to
+fail, or that it has exceeded its design age or aging limit.
+\fBsmartctl\fP does \fBnot\fP calculate any of the Attribute values,
+thresholds, or types, it merely reports them from the SMART data on
+the device.
+
+Note that starting with ATA/ATAPI\-4, revision 4, the meaning of these
+Attribute fields has been made entirely vendor\-specific.  However most
+ATA/ATAPI\-5 disks seem to respect their meaning, so we have retained
+the option of printing the Attribute values.
+
+For SCSI devices the "attributes" are obtained from the temperature
+and start-stop cycle counter log pages. Certain vendor specific
+attributes are listed if recognised. The attributes are output in a
+relatively free format (compared with ATA disk attributes).
+.TP
+.B \-l TYPE, \-\-log=TYPE
+Prints either the SMART Error Log, the SMART Self\-Test Log, the SMART
+Selective Self\-Test Log [ATA only], or the Log Directory [ATA only].
+The valid arguments to this option are:
+
+.I error
+\- prints only the SMART error log.  SMART disks maintain a log of the
+most recent five non\-trivial errors. For each of these errors, the
+disk power\-on lifetime at which the error occurred is recorded, as is
+the device status (idle, standby, etc) at the time of the error.  For
+some common types of errors, the Error Register (ER) and Status
+Register (SR) values are decoded and printed as text. The meanings of these
+are:
+.nf
+   \fBABRT\fP:  Command \fBAB\fPo\fBRT\fPed
+   \fBAMNF\fP:  \fBA\fPddress \fBM\fPark \fBN\fPot \fBF\fPound
+   \fBCCTO\fP:  \fBC\fPommand \fBC\fPompletion \fBT\fPimed \fBO\fPut
+   \fBEOM\fP:   \fBE\fPnd \fBO\fPf \fBM\fPedia
+   \fBICRC\fP:  \fBI\fPnterface \fBC\fPyclic \fBR\fPedundancy \fBC\fPode (CRC) error
+   \fBIDNF\fP:  \fBID\fPentity \fBN\fPot \fBF\fPound
+   \fBILI\fP:   (packet command\-set specific)
+   \fBMC\fP:    \fBM\fPedia \fBC\fPhanged
+   \fBMCR\fP:   \fBM\fPedia \fBC\fPhange \fBR\fPequest
+   \fBNM\fP:    \fBN\fPo \fBM\fPedia
+   \fBobs\fP:   \fBobs\fPolete
+   \fBTK0NF\fP: \fBT\fPrac\fBK 0 N\fPot \fBF\fPound
+   \fBUNC\fP:   \fBUNC\fPorrectable Error in Data
+   \fBWP\fP:    Media is \fBW\fPrite \fBP\fProtected
+.fi
+In addition, up to the last five commands that preceded the error are
+listed, along with a timestamp measured from the start of the
+corresponding power cycle. This is displayed in the form
+Dd+HH:MM:SS.msec where D is the number of days, HH is hours, MM is
+minutes, SS is seconds and msec is milliseconds.  [Note: this time
+stamp wraps after 2^32 milliseconds, or 49 days 17 hours 2 minutes and
+47.296 seconds.]  The key ATA disk registers are also recorded in the
+log.  The final column of the error log is a text\-string description
+of the ATA command defined by the Command Register (CR) and Feature
+Register (FR) values.  Commands that are obsolete in the most current
+(ATA\-7) spec are listed like this: \fBREAD LONG (w/ retry) [OBS\-4]\fP,
+indicating that the command became obsolete with or in the ATA\-4
+specification.  Similarly, the notation \fB[RET\-\fP\fIN\fP\fB]\fP is
+used to indicate that a command was retired in the ATA\-\fIN\fP
+specification.  Some commands are not defined in any version of the
+ATA specification but are in common use nonetheless; these are marked
+\fB[NS]\fP, meaning non\-standard.
+
+The ATA Specification (ATA\-5 Revision 1c, Section 8.41.6.8.2) says:
+\fB"Error log structures shall include UNC errors, IDNF errors for
+which the address requested was valid, servo errors, write fault
+errors, etc.  Error log data structures shall not include errors
+attributed to the receipt of faulty commands such as command codes not
+implemented by the device or requests with invalid parameters or
+invalid addresses."\fP The definitions of these terms are:
+.br
+\fBUNC\fP (\fBUNC\fPorrectable): data is uncorrectable.  This refers
+to data which has been read from the disk, but for which the Error
+Checking and Correction (ECC) codes are inconsistent.  In effect, this
+means that the data can not be read.
+.br
+\fBIDNF\fP (\fBID N\fPot \fBF\fPound): user\-accessible address could
+not be found. For READ LOG type commands, \fBIDNF\fP can also indicate
+that a device data log structure checksum was incorrect.
+
+If the command that caused the error was a READ or WRITE command, then
+the Logical Block Address (LBA) at which the error occurred will be
+printed in base 10 and base 16.  The LBA is a linear address, which
+counts 512\-byte sectors on the disk, starting from zero.  (Because of
+the limitations of the SMART error log, if the LBA is greater than
+0xfffffff, then either no error log entry will be made, or the error
+log entry will have an incorrect LBA. This may happen for drives with
+a capacity greater than 128 GiB or 137 GB.) On Linux systems the
+smartmontools web page has instructions about how to convert the LBA
+address to the name of the disk file containing the erroneous disk
+sector.
+
+Please note that some manufacturers \fBignore\fP the ATA
+specifications, and make entries in the error log if the device
+receives a command which is not implemented or is not valid.
+
+.I error [SCSI]
+\- prints the error counter log pages for reads, write and verifies.
+The verify row is only output if it has an element other than zero.
+
+.I selftest
+\- prints the SMART self\-test log.  The disk maintains a self\-test log
+showing the results of the self tests, which can be run using the
+\'\-t\' option described below.  For each of the most recent
+twenty\-one self\-tests, the log shows the type of test (short or
+extended, off\-line or captive) and the final status of the test.  If
+the test did not complete successfully, then the percentage of the
+test remaining is shown.  The time at which the test took place,
+measured in hours of disk lifetime, is also printed.  If any errors
+were detected, the Logical Block Address (LBA) of the first error is
+printed in decimal notation. On Linux systems the smartmontools
+web page has instructions about how to convert this LBA address to the
+name of the disk file containing the erroneous block.
+
+.I selftest [SCSI]
+\- the self\-test log for a SCSI device has a slightly different format
+than for an ATA device.  For each of the most recent twenty
+self\-tests, it shows the type of test and the status (final or in
+progress) of the test. SCSI standards use the terms "foreground" and
+"background" (rather than ATA\'s corresponding "captive" and
+"off\-line") and "short" and "long" (rather than ATA\'s corresponding
+"short" and "extended") to describe the type of the test.  The printed
+segment number is only relevant when a test fails in the third or
+later test segment.  It identifies the test that failed and consists
+of either the number of the segment that failed during the test, or
+the number of the test that failed and the number of the segment in
+which the test was run, using a vendor\-specific method of putting both
+numbers into a single byte.  The Logical Block Address (LBA) of the
+first error is printed in hexadecimal notation.  On Linux systems the
+smartmontools web page has instructions about how to convert this LBA
+address to the name of the disk file containing the erroneous block.
+If provided, the SCSI Sense Key (SK), Additional Sense Code (ASC) and
+Additional Sense Code Qualifier (ASQ) are also printed. The self tests
+can be run using the \'\-t\' option described below (using the ATA
+test terminology).
+
+.I selective [ATA]
+\- Some ATA\-7 disks (example: Maxtor) also maintain a selective
+self\-test log.  Please see the \'\-t select\' option below for a
+description of selective self\-tests.  The selective self\-test log
+shows the start/end Logical Block Addresses (LBA) of each of the five
+test spans, and their current test status.  If the span is being
+tested or the remainder of the disk is being read\-scanned, the
+current 65536\-sector block of LBAs being tested is also displayed.
+The selective self\-test log also shows if a read\-scan of the
+remainder of the disk will be carried out after the selective
+self\-test has completed (see \'\-t afterselect\' option) and the time
+delay before restarting this read\-scan if it is interrupted (see
+\'\-t pending\' option). This is a new smartmontools feature; please
+report unusual or incorrect behavior to the smartmontools\-support
+mailing list.
+
+.I directory
+\- if the device supports the General Purpose Logging feature set
+(ATA\-6 and ATA\-7 only) then this prints the Log Directory (the log at
+address 0).  The Log Directory shows what logs are available and their
+length in sectors (512 bytes).  The contents of the logs at address 1
+[Summary SMART error log] and at address 6 [SMART self\-test log] may
+be printed using the previously\-described
+.I error
+and
+.I selftest
+arguments to this option. [Please note: this is a new, experimental
+feature.  We would like to add support for printing the contents of
+extended and comprehensive SMART self\-test and error logs.  If your
+disk supports these, and you would like to assist, please contact the
+\fBsmartmontools\fP developers.]
+
+.TP
+.B \-v N,OPTION, \-\-vendorattribute=N,OPTION
+Sets a vendor\-specific display OPTION for Attribute N.  This option
+may be used multiple times. Valid arguments to this option are:
+
+.I help
+\- Prints (to STDOUT) a list of all valid arguments to this option,
+then exits.
+
+.I 9,minutes
+\- Raw Attribute number 9 is power\-on time in minutes.  Its raw value
+will be displayed in the form "Xh+Ym".  Here X is hours, and Y is
+minutes in the range 0\-59 inclusive.  Y is always printed with two
+digits, for example "06" or "31" or "00".
+
+.I 9,seconds
+\- Raw Attribute number 9 is power\-on time in seconds.  Its raw value
+will be displayed in the form "Xh+Ym+Zs".  Here X is hours, Y is
+minutes in the range 0\-59 inclusive, and Z is seconds in the range
+0\-59 inclusive.  Y and Z are always printed with two digits, for
+example "06" or "31" or "00".
+
+.I 9,halfminutes
+\- Raw Attribute number 9 is power\-on time, measured in units of 30
+seconds.  This format is used by some Samsung disks.  Its raw value
+will be displayed in the form "Xh+Ym".  Here X is hours, and Y is
+minutes in the range 0\-59 inclusive.  Y is always printed with two
+digits, for example "06" or "31" or "00".
+
+.I 9,temp
+\- Raw Attribute number 9 is the disk temperature in Celsius.
+
+.I 192,emergencyretractcyclect
+\- Raw Attribute number 192 is the Emergency Retract Cycle Count.
+
+.I 193,loadunload
+\- Raw Attribute number 193 contains two values. The first is the
+number of load cycles.  The second is the number of unload cycles.
+The difference between these two values is the number of times that
+the drive was unexpectedly powered off (also called an emergency
+unload). As a rule of thumb, the mechanical stress created by one
+emergency unload is equivalent to that created by one hundred normal
+unloads.
+
+.I 194,10xCelsius
+\- Raw Attribute number 194 is ten times the disk temperature in
+Celsius.  This is used by some Samsung disks (example: model SV1204H
+with RK100\-13 firmware).
+
+.I 194,unknown
+\- Raw Attribute number 194 is NOT the disk temperature, and its
+interpretation is unknown. This is primarily useful for the \-P
+(presets) option.
+
+.I 198,offlinescanuncsectorct
+\- Raw Attribute number 198 is the Offline Scan UNC Sector Count.
+
+.I 200,writeerrorcount
+\- Raw Attribute number 200 is the Write Error Count.
+
+.I 201,detectedtacount
+\- Raw Attribute number 201 is the Detected TA Count.
+
+.I 220,temp
+\- Raw Attribute number 220 is the disk temperature in Celsius.
+
+Note: a table of hard drive models, listing which Attribute
+corresponds to temperature, can be found at:
+\fBhttp://www.guzu.net/linux/hddtemp.db\fP
+
+.I N,raw8
+\- Print the Raw value of Attribute N as six 8\-bit unsigned base\-10
+integers.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw8\' prints Raw values for ALL Attributes in this
+form.  The form (for example) \'123,raw8\' only prints the Raw value for
+Attribute 123 in this form.
+
+.I N,raw16
+\- Print the Raw value of Attribute N as three 16\-bit unsigned base\-10
+integers.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw16\' prints Raw values for ALL Attributes in this
+form.  The form (for example) \'123,raw16\' only prints the Raw value for
+Attribute 123 in this form.
+
+.I N,raw48
+\- Print the Raw value of Attribute N as a 48\-bit unsigned base\-10
+integer.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw48\' prints Raw values for ALL Attributes in
+this form.  The form (for example) \'123,raw48\' only prints the Raw
+value for Attribute 123 in this form.
+
+.TP
+.B \-F TYPE, \-\-firmwarebug=TYPE
+Modifies the behavior of \fBsmartctl\fP to compensate for some known
+and understood device firmware bug.  The arguments to this option are
+exclusive, so that only the final option given is used.  The valid
+values are:
+
+.I none
+\- Assume that the device firmware obeys the ATA specifications.  This
+is the default, unless the device has presets for \'\-F\' in the
+device database (see note below).
+
+.I samsung
+\- In some Samsung disks (example: model SV4012H Firmware Version:
+RM100\-08) some of the two\- and four\-byte quantities in the SMART data
+structures are byte\-swapped (relative to the ATA specification).
+Enabling this option tells \fBsmartctl\fP to evaluate these quantities
+in byte\-reversed order.  Some signs that your disk needs this option
+are (1) no self\-test log printed, even though you have run self\-tests;
+(2) very large numbers of ATA errors reported in the ATA error log;
+(3) strange and impossible values for the ATA error log timestamps.
+
+.I samsung2
+\- In more recent Samsung disks (firmware revisions ending in "\-23")
+the number of ATA errors reported is byte swapped.  Enabling this
+option tells \fBsmartctl\fP to evaluate this quantity in
+byte\-reversed order. An indication that your Samsung disk needs this
+option is that the self-test log is printed correctly, but there are a
+very large number of errors in the SMART error log.  This is because
+the error count is byte swapped.  Thus a disk with five errors
+(0x0005) will appear to have 20480 errors (0x5000).
+
+Note that an explicit \'\-F\' option on the command line will
+over\-ride any preset values for \'\-F\' (see the \'\-P\' option
+below).
+
+.TP
+.B \-P TYPE, \-\-presets=TYPE
+Specifies whether \fBsmartctl\fP should use any preset options that
+are available for this drive. By default, if the drive is recognized
+in the \fBsmartmontools\fP database, then the presets are used.
+
+\fBsmartctl\fP can automatically set appropriate options for known
+drives.  For example, the Maxtor 4D080H4 uses Attribute 9 to stores
+power\-on time in minutes whereas most drives use that Attribute to
+store the power\-on time in hours.  The command\-line option \'\-v
+9,minutes\' ensures that \fBsmartctl\fP correctly interprets Attribute
+9 in this case, but that option is preset for the Maxtor 4D080H4 and
+so need not be specified by the user on the \fBsmartctl\fP command
+line.
+
+The argument
+.I show
+will show any preset options for your drive and the argument
+.I showall
+will show all known drives in the \fBsmartmontools\fP database, along
+with their preset options.  If there are no presets for your drive and
+you think there should be (for example, a \-v or \-F option is needed
+to get \fBsmartctl\fP to display correct values) then please contact
+the \fBsmartmontools\fP developers so that this information can be
+added to the \fBsmartmontools\fP database.  Contact information is at the
+end of this man page.
+
+The valid arguments to this option are:
+
+.I use
+\- if a drive is recognized, then use the stored presets for it.  This
+is the default. Note that presets will NOT over\-ride additional
+Attribute interpretation (\'\-v N,something\') command\-line options or
+explicit \'\-F\' command\-line options..
+
+.I ignore
+\- do not use presets.
+
+.I show
+\- show if the drive is recognized in the database, and if so, its
+presets, then exit.
+
+.I showall
+\- list all recognized drives, and the presets that are set for them,
+then exit.
+
+The \'\-P showall\' option takes up to two optional arguments to
+match a specific drive type and firmware version. The command:
+.nf
+  smartctl \-P showall
+.fi
+lists all entries, the command:
+.nf
+  smartctl \-P showall \'MODEL\'
+.fi
+lists all entries matching MODEL, and the command:
+.nf
+  smartctl \-P showall \'MODEL\' \'FIRMWARE\'
+.fi
+lists all entries for this MODEL and a specific FIRMWARE version.
+
+.TP
+.B SMART RUN/ABORT OFFLINE TEST AND SELF\-TEST OPTIONS:
+.TP
+.B \-t TEST, \-\-test=TEST
+Executes TEST immediately.  The \'\-C\' option can be used in
+conjunction with this option to run the short or long (and also for
+ATA devices, selective or conveyance) self\-tests in captive mode
+(known as "foreground mode" for SCSI devices).  Note that only one
+test type can be run at a time, so only one test type should be
+specified per command line.  Note also that if a computer is shutdown
+or power cycled during a self\-test, no harm should result.  The
+self\-test will either be aborted or will resume automatically.
+
+The valid arguments to this option are:  
+
+.I offline
+\- runs SMART Immediate Offline Test.  This immediately
+starts the test described above.  This command can be given during
+normal system operation.  The effects of this test are visible only in
+that it updates the SMART Attribute values, and if errors are
+found they will appear in the SMART error log, visible with the \'\-l error\'
+option. [In the case of SCSI devices runs the default self test in
+foreground. No entry is placed in the self test log.]
+
+If the \'\-c\' option to \fBsmartctl\fP shows that the device has the
+"Suspend Offline collection upon new command" capability then you can
+track the progress of the Immediate Offline test using the \'\-c\'
+option to \fBsmartctl\fP.  If the \'\-c\' option show that the device
+has the "Abort Offline collection upon new command" capability then
+most commands will abort the Immediate Offline Test, so you should not
+try to track the progress of the test with \'\-c\', as it will abort
+the test.
+
+.I short
+\- runs SMART Short Self Test (usually under ten minutes).
+[Note: in the case of SCSI devices,
+this command option runs the "Background short" self\-test.]
+This command can be given during normal system operation (unless run in
+captive mode \- see the \'\-C\' option below).  This is a
+test in a different category than the immediate or automatic offline
+tests.  The "Self" tests check the electrical and mechanical
+performance as well as the read performance of the disk.  Their
+results are reported in the Self Test Error Log, readable with
+the \'\-l selftest\' option.  Note that on some disks the progress of the
+self\-test can be monitored by watching this log during the self\-test; with other disks
+use the \'\-c\' option to monitor progress.
+
+.I long
+\- runs SMART Extended Self Test (tens of minutes).
+[Note: in the case of SCSI devices,
+this command option runs the "Background long" self\-test.]
+This is a
+longer and more thorough version of the Short Self Test described
+above.  Note that this command can be given during normal
+system operation (unless run in captive mode \- see the \'\-C\' option below).
+
+.I conveyance
+\- [ATA ONLY] runs a SMART Conveyance Self Test (minutes).  This
+self\-test routine is intended to identify damage incurred during
+transporting of the device. This self\-test routine should take on the
+order of minutes to complete.  Note that this command can be given
+during normal system operation (unless run in captive mode \- see the
+\'\-C\' option below).
+
+.I select,N\-M
+\- [ATA ONLY] [NEW EXPERIMENTAL SMARTCTL FEATURE] runs a SMART
+Selective Self Test, to test a \fBrange\fP of disk Logical Block
+Addresses (LBAs), rather than the entire disk.  Each range of LBAs
+that is checked is called a "span" and is specified by a starting LBA
+(N) and an ending LBA (M) with N less than or equal to M.  For example
+the command:
+.nf
+  smartctl \-t select,10\-20 /dev/hda
+.fi
+runs a self test on one span consisting of LBAs ten to twenty
+(inclusive). The \'\-t\' option can be given up to five times, to test
+up to five spans.  For example the command:
+.nf
+  smartctl \-t select,0\-100 \-t select,1000\-2000 /dev/hda
+.fi
+runs a self test on two spans.  The first span consists of 101 LBAs
+and the second span consists of 1001 LBAs.  Note that the spans can
+overlap partially or completely, for example:
+.nf
+  smartctl \-t select,0\-10 \-t select,5\-15 \-t select,10\-20 /dev/hda
+.fi
+The results of the selective self\-test can be obtained (both during
+and after the test) by printing the SMART self\-test log, using the
+\'\-l selftest\' option to smartctl.
+
+Selective self tests are particularly useful as disk capacities
+increase: an extended self test (smartctl \-t long) can take several
+hours.  Selective self\-tests are helpful if (based on SYSLOG error
+messages, previous failed self\-tests, or SMART error log entries) you
+suspect that a disk is having problems at a particular range of
+Logical Block Addresses (LBAs).
+
+Selective self\-tests can be run during normal system operation (unless
+done in captive mode \- see the \'\-C\' option below).
+
+[Note: this new experimental smartmontools feature is currently only
+available under Linux.  The Linux kernel must be compiled with the
+configuration option CONFIG_IDE_TASKFILE_IO enabled.  Please report
+unusual or incorrect behavior to the smartmontools\-support mailing
+list.]
+
+.I afterselect,on
+\- [ATA ONLY] perform an offline read scan after a Selective Self\-test
+has completed. This option must be used together with one or more of
+the \fIselect,N\-M\fP options above. If the LBAs that have been
+specified in the Selective self\-test pass the test with no errors
+found, then read scan the \fBremainder\fP of the disk.  If the device
+is powered\-cycled while this read scan is in progress, the read scan
+will be automatically resumed after a time specified by the pending
+timer (see below).  The value of this option is preserved between
+selective self\-tests.
+
+.I afterselect,off
+\- [ATA ONLY] do not read scan the remainder of the disk after a
+Selective self\-test has completed.  This option must be use together
+with one or more of the \fIselect,N\-M\fP options above.  The value of this
+option is preserved between selective self\-tests.
+
+.I pending,N 
+\- [ATA ONLY] set the pending offline read scan timer to N minutes.
+Here N is an integer in the range from 0 to 65535 inclusive.  If the
+device is powered off during a read scan after a Selective self\-test,
+then resume the test automatically N minutes after power\-up.  This
+option must be use together with one or more of the \fIselect,N\-M\fP
+options above. The value of this option is preserved between selective
+self\-tests.
+
+.TP
+.B \-C, \-\-captive
+Runs self\-tests in captive mode.  This has no effect with \'\-t
+offline\' or if the \'\-t\' option is not used. [Note: in the case of
+SCSI devices, this command option runs the self\-test in "Foreground"
+mode.]
+
+\fBWARNING: Tests run in captive mode may busy out the drive for the
+length of the test.  Only run captive tests on drives without any
+mounted partitions!\fP
+
+.TP
+.B \-X, \-\-abort
+Aborts non\-captive SMART Self Tests.  Note that this
+command will abort the Offline Immediate Test routine only if your
+disk has the "Abort Offline collection upon new command" capability.
+.PP
+.SH EXAMPLES
+.nf
+.B smartctl \-a /dev/hda
+.fi
+Print all SMART information for drive /dev/hda (Primary Master).
+.PP
+.nf
+.B smartctl \-s off /dev/hdd
+.fi
+Disable SMART on drive /dev/hdd (Secondary Slave).
+.PP
+.nf
+.B smartctl \-\-smart=on \-\-offlineauto=on \-\-saveauto=on /dev/hda
+.fi
+Enable SMART on drive /dev/hda, enable automatic offline
+testing every four hours, and enable autosaving of
+SMART Attributes.  This is a good start\-up line for your system\'s
+init files.  You can issue this command on a running system.
+.PP
+.nf
+.B smartctl \-t long /dev/hdc
+.fi
+Begin an extended self\-test of drive /dev/hdc.  You can issue this
+command on a running system.  The results can be seen in the self\-test
+log visible with the \'\-l selftest\' option after it has completed.
+.PP
+.nf
+.B smartctl \-s on \-t offline /dev/hda
+.fi
+Enable SMART on the disk, and begin an immediate offline test of
+drive /dev/hda.  You can issue this command on a running system.  The
+results are only used to update the SMART Attributes, visible
+with the \'\-A\' option.  If any device errors occur, they are logged to
+the SMART error log, which can be seen with the \'\-l error\' option.
+.PP
+.nf
+.B smartctl \-A \-v 9,minutes /dev/hda
+.fi
+Shows the vendor Attributes, when the disk stores its power\-on time
+internally in minutes rather than hours.
+.PP
+.nf
+.B smartctl \-q errorsonly \-H \-l selftest /dev/hda
+.fi
+Produces output only if the device returns failing SMART status,
+or if some of the logged self\-tests ended with errors.
+.PP
+.nf
+.B smartctl \-q silent \-a /dev/hda
+.fi
+Examine all SMART data for device /dev/hda, but produce no
+printed output.  You must use the exit status (the
+.B $?
+shell variable) to learn if any Attributes are out of bound, if the
+SMART status is failing, if there are errors recorded in the
+self\-test log, or if there are errors recorded in the disk error log.
+.PP
+.nf
+.B smartctl \-a \-d 3ware,0 /dev/sda
+.fi
+Examine all SMART data for the first ATA disk connected to a 3ware
+RAID controller card.
+.PP
+.nf
+.B smartctl \-a \-d 3ware,0 /dev/twe0
+.fi
+Examine all SMART data for the first ATA disk connected to a 3ware
+RAID 6000/7000/8000 controller card.
+.PP
+.nf
+.B smartctl \-a \-d 3ware,0 /dev/twa0
+.fi
+Examine all SMART data for the first ATA disk connected to a 3ware
+RAID 9000 controller card.
+.PP
+.nf
+.B smartctl \-t short \-d 3ware,3 /dev/sdb
+.fi
+Start a short self\-test on the fourth ATA disk connected to the 3ware RAID
+controller card which is the second SCSI device /dev/sdb.
+.PP
+.nf
+.B smartctl \-a \-d hpt,1/3 /dev/sda
+.fi
+Examine all SMART data for the (S)ATA disk directly connected to the third channel of the
+first HighPoint RocketRAID controller card.
+.nf
+.PP
+.nf
+.B smartctl \-t short \-d hpt,1/1/2 /dev/sda
+.fi
+Start a short self\-test on the (S)ATA disk connected to second pmport on the
+first channel of the first HighPoint RocketRAID controller card.
+.PP
+.nf
+.B smartctl \-t select,10\-100 \-t select,30\-300 \-t afterselect,on \-t pending,45 /dev/hda
+.fi
+Run a selective self\-test on LBAs 10 to 100 and 30 to 300.  After the
+these LBAs have been tested, read\-scan the remainder of the disk.  If the disk is
+power\-cycled during the read\-scan, resume the scan 45 minutes after power to the
+device is restored.
+.PP
+.SH RETURN VALUES
+The return values of \fBsmartctl\fP are defined by a bitmask.  If all
+is well with the disk, the return value (exit status) of
+\fBsmartctl\fP is 0 (all bits turned off).  If a problem occurs, or an
+error, potential error, or fault is detected, then a non\-zero status
+is returned.  In this case, the eight different bits in the return
+value have the following meanings for ATA disks; some of these values
+may also be returned for SCSI disks.
+.TP
+.B Bit 0:
+Command line did not parse.
+.TP
+.B Bit 1:
+Device open failed, or device did not return an IDENTIFY DEVICE structure. 
+.TP
+.B Bit 2:
+Some SMART command to the disk failed, or there was a checksum error
+in a SMART data structure (see \'\-b\' option above).
+.TP
+.B Bit 3:
+SMART status check returned "DISK FAILING".
+.TP
+.B Bit 4:
+SMART status check returned "DISK OK" but we found prefail Attributes <= threshold.
+.TP
+.B Bit 5:
+SMART status check returned "DISK OK" but we found that some (usage
+or prefail) Attributes have been <= threshold at some time in the
+past. 
+.TP
+.B Bit 6:
+The device error log contains records of errors.
+.TP
+.B Bit 7:
+The device self\-test log contains records of errors.
+
+To test within the shell for whether or not the different bits are
+turned on or off, you can use the following type of construction (this
+is bash syntax):
+.nf
+.B smartstat=$(($? & 8))
+.fi
+This looks at only at bit 3 of the exit status
+.B $?
+(since 8=2^3).  The shell variable
+$smartstat will be nonzero if SMART status check returned "disk
+failing" and zero otherwise.
+
+.PP
+.SH NOTES
+The TapeAlert log page flags are cleared for the initiator when the
+page is read. This means that each alert condition is reported only
+once by \fBsmartctl\fP for each initiator for each activation of the
+condition.
+
+.PP
+.SH AUTHOR
+\fBBruce Allen\fP smartmontools\-support@lists.sourceforge.net
+.fi
+University of Wisconsin \- Milwaukee Physics Department
+.PP
+.SH CONTRIBUTORS
+The following have made large contributions to smartmontools:
+.nf
+\fBCasper Dik\fP (Solaris SCSI interface)
+\fBChristian Franke\fP (Windows interface and Cygwin package)
+\fBDouglas Gilbert\fP (SCSI subsystem)
+\fBGuido Guenther\fP (Autoconf/Automake packaging)
+\fBGeoffrey Keating\fP (Darwin ATA interface)
+\fBEduard Martinescu\fP (FreeBSD interface)
+\fBFr\*'ed\*'eric L. W. Meunier\fP (Web site and Mailing list)
+\fBKeiji Sawada\fP (Solaris ATA interface)
+\fBSergey Svishchev\fP (NetBSD interface)
+\fBDavid Snyder and Sergey Svishchev\fP (OpenBSD interface)
+\fBPhil Williams\fP (User interface and drive database)
+\fBYuri Dario\fP (OS/2, eComStation interface)
+.fi
+Many other individuals have made smaller contributions and corrections.
+
+.PP
+.SH CREDITS
+.fi
+This code was derived from the smartsuite package, written by Michael
+Cornwell, and from the previous UCSC smartsuite package.  It extends
+these to cover ATA\-5 disks.  This code was originally developed as a
+Senior Thesis by Michael Cornwell at the Concurrent Systems Laboratory
+(now part of the Storage Systems Research Center), Jack Baskin School
+of Engineering, University of California, Santa
+Cruz. \fBhttp://ssrc.soe.ucsc.edu/\fP .
+.SH
+HOME PAGE FOR SMARTMONTOOLS: 
+.fi
+Please see the following web site for updates, further documentation, bug
+reports and patches: \fBhttp://smartmontools.sourceforge.net/\fP
+
+.SH
+SEE ALSO:
+\fBsmartd\fP(8), \fBbadblocks\fP(8), \fBide\-smart\fP(8).
+.SH
+REFERENCES FOR SMART
+.fi
+An introductory article about smartmontools is \fIMonitoring Hard
+Disks with SMART\fP, by Bruce Allen, Linux Journal, January 2004,
+pages 74-77. This is \fBhttp://www.linuxjournal.com/article.php?sid=6983\fP
+online.
+
+If you would like to understand better how SMART works, and what it
+does, a good place to start is with Sections 4.8 and 6.54 of the first
+volume of the \'AT Attachment with Packet Interface-7\' (ATA/ATAPI-7)
+specification.  This documents the SMART functionality which the
+\fBsmartmontools\fP utilities provide access to.  You can find
+Revision 4b of this document at
+\fBhttp://www.t13.org/docs2004/d1532v1r4b-ATA-ATAPI-7.pdf\fP .
+Earlier and later versions of this Specification are available from
+the T13 web site \fBhttp://www.t13.org/\fP .
+
+.fi
+The functioning of SMART was originally defined by the SFF\-8035i
+revision 2 and the SFF\-8055i revision 1.4 specifications.  These are
+publications of the Small Form Factors (SFF) Committee.  Links to
+these documents may be found in the References section of the
+\fBsmartmontools\fP home page at
+\fBhttp://smartmontools.sourceforge.net/\fP .
+
+.SH
+CVS ID OF THIS PAGE:
+$Id: smartctl.8.in,v 1.83 2006/09/15 08:03:52 sxzzsf Exp $
+.\" Local Variables:            
+.\" mode: nroff         
+.\" End:
diff --git a/smartctl.c b/smartctl.c
deleted file mode 100644 (file)
index 6c0886f..0000000
+++ /dev/null
@@ -1,896 +0,0 @@
-/*
- * smartctl.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <string.h>
-#include <stdarg.h>
-
-#include "config.h"
-#ifdef HAVE_GETOPT_LONG
-#include <getopt.h>
-#endif
-#if defined(__FreeBSD_version) && (__FreeBSD_version < 500000)
-#include <unistd.h>
-#endif
-
-#include "int64.h"
-#include "atacmds.h"
-#include "ataprint.h"
-#include "extern.h"
-#include "knowndrives.h"
-#include "scsicmds.h"
-#include "scsiprint.h"
-#include "smartctl.h"
-#include "utility.h"
-
-#ifdef NEED_SOLARIS_ATA_CODE
-extern const char *os_solaris_ata_s_cvsid;
-#endif
-extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid;
-const char* smartctl_c_cvsid="$Id: smartctl.c,v 1.143 2006/04/12 14:54:28 ballen4705 Exp $"
-ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
-
-// This is a block containing all the "control variables".  We declare
-// this globally in this file, and externally in other files.
-smartmonctrl *con=NULL;
-
-// to hold onto exit code for atexit routine
-extern int exitstatus;
-
-// Track memory use
-extern int64_t bytes;
-
-void printslogan(){
-#ifdef HAVE_GET_OS_VERSION_STR
-  const char * ver = get_os_version_str();
-#else
-  const char * ver = SMARTMONTOOLS_BUILD_HOST;
-#endif
-  pout("smartctl version %s [%s] Copyright (C) 2002-6 Bruce Allen\n", PACKAGE_VERSION, ver);
-  pout("Home page is " PACKAGE_HOMEPAGE "\n\n");
-  return;
-}
-
-void PrintOneCVS(const char *a_cvs_id){
-  char out[CVSMAXLEN];
-  printone(out,a_cvs_id);
-  pout("%s",out);
-  return;
-}
-
-void printcopy(){
-  char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]";
-
-  pout("smartctl comes with ABSOLUTELY NO WARRANTY. This\n");
-  pout("is free software, and you are welcome to redistribute it\n");
-  pout("under the terms of the GNU General Public License Version 2.\n");
-  pout("See http://www.gnu.org for further details.\n\n");
-  pout("CVS version IDs of files used to build this code are:\n");
-  PrintOneCVS(atacmdnames_c_cvsid);
-  PrintOneCVS(atacmds_c_cvsid);
-  PrintOneCVS(ataprint_c_cvsid);
-  PrintOneCVS(knowndrives_c_cvsid);
-  PrintOneCVS(os_XXXX_c_cvsid);
-#ifdef NEED_SOLARIS_ATA_CODE
-  PrintOneCVS(os_solaris_ata_s_cvsid);
-#endif
-  PrintOneCVS(scsicmds_c_cvsid);
-  PrintOneCVS(scsiprint_c_cvsid);
-  PrintOneCVS(smartctl_c_cvsid);
-  PrintOneCVS(utility_c_cvsid);
-  pout("\nsmartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n");
-  pout("smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n");
-  pout("smartmontools build configured: " SMARTMONTOOLS_CONFIGURE_DATE "\n");
-  pout("smartctl compile dated " __DATE__ " at "__TIME__ "\n");
-  pout("smartmontools configure arguments: %s\n", configargs);
-  return;
-}
-
-void UsageSummary(){
-  pout("\nUse smartctl -h to get a usage summary\n\n");
-  return;
-}
-
-/*  void prints help information for command syntax */
-void Usage (void){
-  printf("Usage: smartctl [options] device\n\n");
-  printf("============================================ SHOW INFORMATION OPTIONS =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
-"  -h, --help, --usage\n"
-"         Display this help and exit\n\n"
-"  -V, --version, --copyright, --license\n"
-"         Print license, copyright, and version information and exit\n\n"
-"  -i, --info                                                       \n"
-"         Show identity information for device\n\n"
-"  -a, --all                                                        \n"
-"         Show all SMART information for device\n\n"
-  );
-#else
-  printf(
-"  -h        Display this help and exit\n"
-"  -V        Print license, copyright, and version information\n"
-"  -i        Show identity information for device\n"
-"  -a        Show all SMART information for device\n\n"
-  );
-#endif
-  printf("================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
-"  -q TYPE, --quietmode=TYPE                                           (ATA)\n"
-"         Set smartctl quiet mode to one of: errorsonly, silent\n\n"
-"  -d TYPE, --device=TYPE\n"
-"         Specify device type to one of: ata, scsi, marvell, 3ware,N\n\n"
-"  -T TYPE, --tolerance=TYPE                                           (ATA)\n"
-"         Tolerance: normal, conservative, permissive, verypermissive\n\n"
-"  -b TYPE, --badsum=TYPE                                              (ATA)\n"
-"         Set action on bad checksum to one of: warn, exit, ignore\n\n"
-"  -r TYPE, --report=TYPE\n"
-"         Report transactions (see man page)\n\n"
-  );
-#else
-  printf(
-"  -q TYPE   Set smartctl quiet mode to one of: errorsonly, silent     (ATA)\n"
-"  -d TYPE   Specify device type to one of: ata, scsi, 3ware,N\n"
-"  -T TYPE   Tolerance: normal, conservative,permissive,verypermissive (ATA\n"
-"  -b TYPE   Set action on bad checksum to one of: warn, exit, ignore  (ATA)\n"
-"  -r TYPE   Report transactions (see man page)\n\n"
-  );
-#endif
-  printf("============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
-"  -s VALUE, --smart=VALUE\n"
-"        Enable/disable SMART on device (on/off)\n\n"
-"  -o VALUE, --offlineauto=VALUE                                       (ATA)\n"
-"        Enable/disable automatic offline testing on device (on/off)\n\n"
-"  -S VALUE, --saveauto=VALUE                                          (ATA)\n"
-"        Enable/disable Attribute autosave on device (on/off)\n\n"
-  );
-#else
-  printf(
-"  -s VALUE  Enable/disable SMART on device (on/off)\n"
-"  -o VALUE  Enable/disable device automatic offline testing (on/off)  (ATA)\n"
-"  -S VALUE  Enable/disable device Attribute autosave (on/off)         (ATA)\n\n"
-  );
-#endif
-  printf("======================================= READ AND DISPLAY DATA OPTIONS =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
-"  -H, --health\n"
-"        Show device SMART health status\n\n"
-"  -c, --capabilities                                                  (ATA)\n"
-"        Show device SMART capabilities\n\n"
-"  -A, --attributes                                                         \n"
-"        Show device SMART vendor-specific Attributes and values\n\n"
-"  -l TYPE, --log=TYPE\n"
-"        Show device log. TYPE: error, selftest, selective, directory\n\n"
-"  -v N,OPTION , --vendorattribute=N,OPTION                            (ATA)\n"
-"        Set display OPTION for vendor Attribute N (see man page)\n\n"
-"  -F TYPE, --firmwarebug=TYPE                                         (ATA)\n"
-"        Use firmware bug workaround: none, samsung, samsung2\n\n"
-"  -P TYPE, --presets=TYPE                                             (ATA)\n"
-"        Drive-specific presets: use, ignore, show, showall\n\n"
-  );
-#else
-  printf(
-"  -H        Show device SMART health status\n"
-"  -c        Show device SMART capabilities                             (ATA)\n"
-"  -A        Show device SMART vendor-specific Attributes and values    (ATA)\n"
-"  -l TYPE   Show device log. TYPE: error,selftest,selective,directory\n"
-"  -v N,OPT  Set display OPTion for vendor Attribute N (see man page)   (ATA)\n"
-"  -F TYPE   Use firmware bug workaround: none, samsung, samsung2       (ATA)\n"
-"  -P TYPE   Drive-specific presets: use, ignore, show, showall         (ATA)\n\n"
-  );
-#endif
-  printf("============================================ DEVICE SELF-TEST OPTIONS =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
-"  -t TEST, --test=TEST\n"
-"        Run test.  TEST is: offline short long conveyance select,M-N pending,N afterselect,on afterselect,off\n\n"
-"  -C, --captive\n"
-"        Do test in captive mode (along with -t)\n\n"
-"  -X, --abort\n"
-"        Abort any non-captive test on device\n\n"
-);
-#else
-  printf(
-"  -t TEST   Run test.  TEST is: offline short long conveyance select,M-N pending,N afterselect,on afterselect,off\n"
-"  -C        Do test in captive mode (along with -t)\n"
-"  -X        Abort any non-captive test\n\n"
-  );
-#endif
-  print_smartctl_examples();
-  return;
-}
-
-/* Returns a pointer to a static string containing a formatted list of the valid
-   arguments to the option opt or NULL on failure. Note 'v' case different */
-const char *getvalidarglist(char opt) {
-  switch (opt) {
-  case 'q':
-    return "errorsonly, silent";
-  case 'd':
-    return "ata, scsi, marvell, 3ware,N";
-  case 'T':
-    return "normal, conservative, permissive, verypermissive";
-  case 'b':
-    return "warn, exit, ignore";
-  case 'r':
-    return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
-  case 's':
-  case 'o':
-  case 'S':
-    return "on, off";
-  case 'l':
-    return "error, selftest, selective, directory";
-  case 'P':
-    return "use, ignore, show, showall";
-  case 't':
-    return "offline, short, long, conveyance, select,M-N, pending,N, afterselect,on, afterselect,off";
-  case 'F':
-    return "none, samsung, samsung2";
-  case 'v':
-  default:
-    return NULL;
-  }
-}
-
-/* Prints the message "=======> VALID ARGUMENTS ARE: <LIST> \n", where
-   <LIST> is the list of valid arguments for option opt. */
-void printvalidarglistmessage(char opt) {
-  char *s;
-  
-  if (opt=='v')
-    s=create_vendor_attribute_arg_list();
-  else
-    s=(char *)getvalidarglist(opt);
-  
-  if (!s) {
-    pout("Error whilst constructing argument list for option %c", opt);
-    return;
-  }
-  if (opt=='v'){
-    pout("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n", s);
-    free(s);
-  }
-  else {
-  // getvalidarglist() might produce a multiline or single line string.  We
-  // need to figure out which to get the formatting right.
-    char separator = strchr(s, '\n') ? '\n' : ' ';
-    pout("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, (char *)s, separator);
-  }
-
-  return;
-}
-
-/*      Takes command options and sets features to be run */    
-void ParseOpts (int argc, char** argv){
-  int optchar;
-  int badarg;
-  int captive;
-  unsigned char *charp;
-  extern char *optarg;
-  extern int optopt, optind, opterr;
-  char extraerror[256];
-  // Please update getvalidarglist() if you edit shortopts
-  const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iav:P:t:CXF:";
-#ifdef HAVE_GETOPT_LONG
-  char *arg;
-  // Please update getvalidarglist() if you edit longopts
-  struct option longopts[] = {
-    { "help",            no_argument,       0, 'h' },
-    { "usage",           no_argument,       0, 'h' },
-    { "version",         no_argument,       0, 'V' },
-    { "copyright",       no_argument,       0, 'V' },
-    { "license",         no_argument,       0, 'V' },
-    { "quietmode",       required_argument, 0, 'q' },
-    { "device",          required_argument, 0, 'd' },
-    { "tolerance",       required_argument, 0, 'T' },
-    { "badsum",          required_argument, 0, 'b' },
-    { "report",          required_argument, 0, 'r' },
-    { "smart",           required_argument, 0, 's' },
-    { "offlineauto",     required_argument, 0, 'o' },
-    { "saveauto",        required_argument, 0, 'S' },
-    { "health",          no_argument,       0, 'H' },
-    { "capabilities",    no_argument,       0, 'c' },
-    { "attributes",      no_argument,       0, 'A' },
-    { "log",             required_argument, 0, 'l' },
-    { "info",            no_argument,       0, 'i' },
-    { "all",             no_argument,       0, 'a' },
-    { "vendorattribute", required_argument, 0, 'v' },
-    { "presets",         required_argument, 0, 'P' },
-    { "test",            required_argument, 0, 't' },
-    { "captive",         no_argument,       0, 'C' },
-    { "abort",           no_argument,       0, 'X' },
-    { "firmwarebug",     required_argument, 0, 'F' },
-    { 0,                 0,                 0, 0   }
-  };
-#endif
-  
-  memset(extraerror, 0, sizeof(extraerror));
-  memset(con,0,sizeof(*con));
-  con->testcase=-1;
-  opterr=optopt=0;
-  badarg = captive = FALSE;
-  
-  // This miserable construction is needed to get emacs to do proper indenting. Sorry!
-  while (-1 != (optchar = 
-#ifdef HAVE_GETOPT_LONG
-                getopt_long(argc, argv, shortopts, longopts, NULL)
-#else
-                getopt(argc, argv, shortopts)
-#endif
-                )){
-    switch (optchar){
-    case 'V':
-      con->dont_print=FALSE;
-      printslogan();
-      printcopy();
-      exit(0);
-      break;
-    case 'q':
-      if (!strcmp(optarg,"errorsonly")) {
-        con->printing_switchable     = TRUE;
-        con->dont_print = FALSE;
-      } else if (!strcmp(optarg,"silent")) {
-        con->printing_switchable     = FALSE;
-        con->dont_print = TRUE;
-      } else {
-        badarg = TRUE;
-      }
-      break;
-    case 'd':
-      if (!strcmp(optarg,"ata")) {
-       con->controller_type = CONTROLLER_ATA;
-        con->controller_port = 0;
-      } else if (!strcmp(optarg,"scsi")) {
-       con->controller_type = CONTROLLER_SCSI;
-        con->controller_port = 0;
-      } else if (!strcmp(optarg,"marvell")) {
-       con->controller_type = CONTROLLER_MARVELL_SATA;
-        con->controller_port = 0;
-      } else {
-        // look for RAID-type device
-        int i;
-        char *s;
-        
-        // make a copy of the string to mess with
-        if (!(s = strdup(optarg))) {
-          con->dont_print = FALSE;
-          pout("No memory for argument of -d. Exiting...\n");
-          exit(FAILCMD);
-        } else if (strncmp(s,"3ware,",6)) {
-          badarg = TRUE;
-        } else if (split_report_arg2(s, &i)) {
-          sprintf(extraerror, "Option -d 3ware,N requires N to be a non-negative integer\n");
-          badarg = TRUE;
-        } else if (i<0 || i>15) {
-          sprintf(extraerror, "Option -d 3ware,N (N=%d) must have 0 <= N <= 15\n", i);
-          badarg = TRUE;
-        } else {
-         // NOTE: controller_port == disk number + 1
-         con->controller_type = CONTROLLER_3WARE;
-          con->controller_port = i+1;
-        }
-        free(s);
-      }         
-      break;
-    case 'T':
-      if (!strcmp(optarg,"normal")) {
-        con->conservative = FALSE;
-        con->permissive   = 0;
-      } else if (!strcmp(optarg,"conservative")) {
-        con->conservative = TRUE;
-      } else if (!strcmp(optarg,"permissive")) {
-        if (con->permissive<0xff)
-          con->permissive++;
-      } else if (!strcmp(optarg,"verypermissive")) {
-        con->permissive=0xff;
-      } else {
-        badarg = TRUE;
-      }
-      break;
-    case 'b':
-      if (!strcmp(optarg,"warn")) {
-        con->checksumfail   = FALSE;
-        con->checksumignore = FALSE;
-      } else if (!strcmp(optarg,"exit")) {
-        con->checksumfail   = TRUE;
-        con->checksumignore = FALSE;
-      } else if (!strcmp(optarg,"ignore")) {
-        con->checksumignore = TRUE;
-        con->checksumfail   = FALSE;
-      } else {
-        badarg = TRUE;
-      }
-      break;
-    case 'r':
-      {
-        int i;
-        char *s;
-
-        // split_report_arg() may modify its first argument string, so use a
-        // copy of optarg in case we want optarg for an error message.
-        if (!(s = strdup(optarg))) {
-          con->dont_print = FALSE;
-          pout("Can't allocate memory to copy argument to -r option"
-               " - exiting\n");
-          EXIT(FAILCMD);
-        }
-        if (split_report_arg(s, &i)) {
-          badarg = TRUE;
-        } else if (!strcmp(s,"ioctl")) {
-          con->reportataioctl  = con->reportscsiioctl = i;
-        } else if (!strcmp(s,"ataioctl")) {
-          con->reportataioctl = i;
-        } else if (!strcmp(s,"scsiioctl")) {
-          con->reportscsiioctl = i;
-        } else {
-          badarg = TRUE;
-        }
-        free(s);
-      }
-      break;
-    case 's':
-      if (!strcmp(optarg,"on")) {
-        con->smartenable  = TRUE;
-        con->smartdisable = FALSE;
-      } else if (!strcmp(optarg,"off")) {
-        con->smartdisable = TRUE;
-        con->smartenable  = FALSE;
-      } else {
-        badarg = TRUE;
-      }
-      break;
-    case 'o':
-      if (!strcmp(optarg,"on")) {
-        con->smartautoofflineenable  = TRUE;
-        con->smartautoofflinedisable = FALSE;
-      } else if (!strcmp(optarg,"off")) {
-        con->smartautoofflinedisable = TRUE;
-        con->smartautoofflineenable  = FALSE;
-      } else {
-        badarg = TRUE;
-      }
-      break;
-    case 'S':
-      if (!strcmp(optarg,"on")) {
-        con->smartautosaveenable  = TRUE;
-        con->smartautosavedisable = FALSE;
-      } else if (!strcmp(optarg,"off")) {
-        con->smartautosavedisable = TRUE;
-        con->smartautosaveenable  = FALSE;
-      } else {
-        badarg = TRUE;
-      }
-      break;
-    case 'H':
-      con->checksmart = TRUE;           
-      break;
-    case 'F':
-      if (!strcmp(optarg,"none")) {
-        con->fixfirmwarebug = FIX_NONE;
-      } else if (!strcmp(optarg,"samsung")) {
-        con->fixfirmwarebug = FIX_SAMSUNG;
-      } else if (!strcmp(optarg,"samsung2")) {
-        con->fixfirmwarebug = FIX_SAMSUNG2;
-      } else {
-        badarg = TRUE;
-      }
-      break;
-    case 'c':
-      con->generalsmartvalues = TRUE;
-      break;
-    case 'A':
-      con->smartvendorattrib = TRUE;
-      break;
-    case 'l':
-      if (!strcmp(optarg,"error")) {
-        con->smarterrorlog = TRUE;
-      } else if (!strcmp(optarg,"selftest")) {
-        con->smartselftestlog = TRUE;
-      } else if (!strcmp(optarg, "selective")) {
-       con->selectivetestlog = TRUE;
-      } else if (!strcmp(optarg,"directory")) {
-        con->smartlogdirectory = TRUE;
-      } else {
-        badarg = TRUE;
-      }
-      break;
-    case 'i':
-      con->driveinfo = TRUE;
-      break;            
-    case 'a':
-      con->driveinfo          = TRUE;
-      con->checksmart         = TRUE;
-      con->generalsmartvalues = TRUE;
-      con->smartvendorattrib  = TRUE;
-      con->smarterrorlog      = TRUE;
-      con->smartselftestlog   = TRUE;
-      con->selectivetestlog   = TRUE;
-      break;
-    case 'v':
-      // parse vendor-specific definitions of attributes
-      if (!strcmp(optarg,"help")) {
-        char *s;
-        con->dont_print=FALSE;
-        printslogan();
-        if (!(s = create_vendor_attribute_arg_list())) {
-          pout("Insufficient memory to construct argument list\n");
-          EXIT(FAILCMD);
-        }
-        pout("The valid arguments to -v are:\n\thelp\n%s\n", s);
-        free(s);
-        EXIT(0);
-      }
-      charp=con->attributedefs;
-      if (!charp){
-        pout("Fatal internal error in ParseOpts()\n");
-        EXIT(FAILCMD);
-      }
-      if (parse_attribute_def(optarg, &charp))
-        badarg = TRUE;
-      break;    
-    case 'P':
-      if (!strcmp(optarg, "use")) {
-        con->ignorepresets = FALSE;
-      } else if (!strcmp(optarg, "ignore")) {
-        con->ignorepresets = TRUE;
-      } else if (!strcmp(optarg, "show")) {
-        con->showpresets = TRUE;
-      } else if (!strcmp(optarg, "showall")) {
-        if (optind < argc) { // -P showall MODEL [FIRMWARE]
-          int cnt = showmatchingpresets(argv[optind], (optind+1<argc ? argv[optind+1] : NULL));
-          EXIT(cnt); // report #matches
-        }
-        if (showallpresets())
-          EXIT(FAILCMD); // report regexp syntax error
-        EXIT(0);
-      } else {
-        badarg = TRUE;
-      }
-      break;
-    case 't':
-      if (!strcmp(optarg,"offline")) {
-        con->smartexeoffimmediate = TRUE;
-        con->testcase             = OFFLINE_FULL_SCAN;
-      } else if (!strcmp(optarg,"short")) {
-        con->smartshortselftest = TRUE;
-        con->testcase           = SHORT_SELF_TEST;
-      } else if (!strcmp(optarg,"long")) {
-        con->smartextendselftest = TRUE;
-        con->testcase            = EXTEND_SELF_TEST;
-      } else if (!strcmp(optarg,"conveyance")) {
-        con->smartconveyanceselftest = TRUE;
-        con->testcase            = CONVEYANCE_SELF_TEST;
-      } else if (!strcmp(optarg,"afterselect,on")) {
-       // scan remainder of disk after doing selected segments
-       con->scanafterselect=2;
-      } else if (!strcmp(optarg,"afterselect,off")) {
-       // don't scan remainder of disk after doing selected segments
-       con->scanafterselect=1;
-      } else if (!strncmp(optarg,"pending,",strlen("pending,"))) {
-       // parse number of minutes that test should be pending
-       int i;
-       char *tailptr=NULL;
-       errno=0;
-       i=(int)strtol(optarg+strlen("pending,"), &tailptr, 10);
-       if (errno || *tailptr != '\0') {
-         sprintf(extraerror, "Option -t pending,N requires N to be a non-negative integer\n");
-         badarg = TRUE;
-       } else if (i<0 || i>65535) {
-         sprintf(extraerror, "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i);
-         badarg = TRUE;
-       } else {
-         con->pendingtime=i+1;
-       }
-      } else if (!strncmp(optarg,"select",strlen("select"))) {
-       // parse range of LBAs to test
-       uint64_t start, stop;
-
-        if (split_selective_arg(optarg, &start, &stop)) {
-         sprintf(extraerror, "Option -t select,M-N must have non-negative integer M and N\n");
-          badarg = TRUE;
-        } else {
-          if (con->smartselectivenumspans >= 5 || start > stop) {
-            if (start > stop) {
-              sprintf(extraerror, "ERROR: Start LBA (%"PRIu64") > ending LBA (%"PRId64") in argument \"%s\"\n",
-                start, stop, optarg);
-            } else {
-              sprintf(extraerror,"ERROR: No more than five selective self-test spans may be"
-                " defined\n");
-            }
-           badarg = TRUE;
-          }
-          con->smartselectivespan[con->smartselectivenumspans][0] = start;
-          con->smartselectivespan[con->smartselectivenumspans][1] = stop;
-          con->smartselectivenumspans++;
-          con->testcase            = SELECTIVE_SELF_TEST;
-        }
-      } else {
-        badarg = TRUE;
-      }
-      break;
-    case 'C':
-      captive = TRUE;
-      break;
-    case 'X':
-      con->smartselftestabort = TRUE;
-      con->testcase           = ABORT_SELF_TEST;
-      break;
-    case 'h':
-      con->dont_print=FALSE;
-      printslogan();
-      Usage();
-      EXIT(0);  
-      break;
-    case '?':
-    default:
-      con->dont_print=FALSE;
-      printslogan();
-#ifdef HAVE_GETOPT_LONG
-      // Point arg to the argument in which this option was found.
-      arg = argv[optind-1];
-      // Check whether the option is a long option that doesn't map to -h.
-      if (arg[1] == '-' && optchar != 'h') {
-        // Iff optopt holds a valid option then argument must be missing.
-        if (optopt && (strchr(shortopts, optopt) != NULL)) {
-          pout("=======> ARGUMENT REQUIRED FOR OPTION: %s\n", arg+2);
-          printvalidarglistmessage(optopt);
-        } else
-          pout("=======> UNRECOGNIZED OPTION: %s\n",arg+2);
-       if (extraerror[0])
-         pout("=======> %s", extraerror);
-        UsageSummary();
-        EXIT(FAILCMD);
-      }
-#endif
-      if (optopt) {
-        // Iff optopt holds a valid option then argument must be
-        // missing.  Note (BA) this logic seems to fail using Solaris
-        // getopt!
-        if (strchr(shortopts, optopt) != NULL) {
-          pout("=======> ARGUMENT REQUIRED FOR OPTION: %c\n", optopt);
-          printvalidarglistmessage(optopt);
-        } else
-          pout("=======> UNRECOGNIZED OPTION: %c\n",optopt);
-       if (extraerror[0])
-         pout("=======> %s", extraerror);
-        UsageSummary();
-        EXIT(FAILCMD);
-      }
-      Usage();
-      EXIT(0);  
-    } // closes switch statement to process command-line options
-    
-    // Check to see if option had an unrecognized or incorrect argument.
-    if (badarg) {
-      printslogan();
-      // It would be nice to print the actual option name given by the user
-      // here, but we just print the short form.  Please fix this if you know
-      // a clean way to do it.
-      pout("=======> INVALID ARGUMENT TO -%c: %s\n", optchar, optarg);
-      printvalidarglistmessage(optchar);
-      if (extraerror[0])
-       pout("=======> %s", extraerror);
-      UsageSummary();
-      EXIT(FAILCMD);
-    }
-  }
-  // At this point we have processed all command-line options.  If the
-  // print output is switchable, then start with the print output
-  // turned off
-  if (con->printing_switchable)
-    con->dont_print=TRUE;
-
-  // error message if user has asked for more than one test
-  if (1<(con->smartexeoffimmediate+con->smartshortselftest+con->smartextendselftest+
-         con->smartshortcapselftest+con->smartextendcapselftest+con->smartselftestabort + (con->smartselectivenumspans>0?1:0))){
-    con->dont_print=FALSE;
-    printslogan();
-    pout("\nERROR: smartctl can only run a single test type (or abort) at a time.\n");
-    UsageSummary();
-    EXIT(FAILCMD);
-  }
-
-  // error message if user has set selective self-test options without
-  // asking for a selective self-test
-  if ((con->pendingtime || con->scanafterselect) && !con->smartselectivenumspans){
-    con->dont_print=FALSE;
-    printslogan();
-    if (con->pendingtime)
-      pout("\nERROR: smartctl -t pending,N must be used with -t select,N-M.\n");
-    else
-      pout("\nERROR: smartctl -t afterselect,(on|off) must be used with -t select,N-M.\n");
-    UsageSummary();
-    EXIT(FAILCMD);
-  }
-
-  // If captive option was used, change test type if appropriate.
-  if (captive && con->smartshortselftest) {
-    con->smartshortselftest    = FALSE;
-    con->smartshortcapselftest = TRUE;
-    con->testcase              = SHORT_CAPTIVE_SELF_TEST;
-  } else if (captive && con->smartextendselftest) {
-    con->smartextendselftest    = FALSE;
-    con->smartextendcapselftest = TRUE;
-    con->testcase               = EXTEND_CAPTIVE_SELF_TEST;
-  }
-  else if (captive && con->smartconveyanceselftest) {
-    con->smartconveyanceselftest    = FALSE;
-    con->smartconveyancecapselftest = TRUE;
-    con->testcase                   = CONVEYANCE_CAPTIVE_SELF_TEST;
-  }
-  else if (captive && con->smartselectiveselftest) {
-    con->smartselectiveselftest    = FALSE;
-    con->smartselectivecapselftest = TRUE;
-    con->testcase                  = SELECTIVE_CAPTIVE_SELF_TEST;
-  }
-  // From here on, normal operations...
-  printslogan();
-  
-  // Warn if the user has provided no device name
-  if (argc-optind<1){
-    pout("ERROR: smartctl requires a device name as the final command-line argument.\n\n");
-    UsageSummary();
-    EXIT(FAILCMD);
-  }
-  
-  // Warn if the user has provided more than one device name
-  if (argc-optind>1){
-    int i;
-    pout("ERROR: smartctl takes ONE device name as the final command-line argument.\n");
-    pout("You have provided %d device names:\n",argc-optind);
-    for (i=0; i<argc-optind; i++)
-      pout("%s\n",argv[optind+i]);
-    UsageSummary();
-    EXIT(FAILCMD);
-  }  
-}
-
-// Printing function (controlled by global con->dont_print) 
-// [From GLIBC Manual: Since the prototype doesn't specify types for
-// optional arguments, in a call to a variadic function the default
-// argument promotions are performed on the optional argument
-// values. This means the objects of type char or short int (whether
-// signed or not) are promoted to either int or unsigned int, as
-// appropriate.]
-void pout(char *fmt, ...){
-  va_list ap;
-  
-  // initialize variable argument list 
-  va_start(ap,fmt);
-  if (con->dont_print){
-    va_end(ap);
-    return;
-  }
-
-  // print out
-  vprintf(fmt,ap);
-  va_end(ap);
-  fflush(stdout);
-  return;
-}
-
-// This function is used by utility.c to report LOG_CRIT errors.
-// The smartctl version prints to stdout instead of syslog().
-void PrintOut(int priority, char *fmt, ...) {
-  va_list ap;
-
-  // avoid warning message about unused variable from gcc -W: just
-  // change value of local copy.
-  priority=0;
-
-  va_start(ap,fmt);
-  vprintf(fmt,ap);
-  va_end(ap);
-  return;
-}
-
-
-/* Main Program */
-int main (int argc, char **argv){
-  int fd,retval=0;
-  char *device;
-  smartmonctrl control;
-  char *mode=NULL;
-
-  // define control block for external functions
-  con=&control;
-
-  // Part input arguments
-  ParseOpts(argc,argv);
-
-  device = argv[argc-1];
-
-  // If use has specified 3ware controller, determine which interface 
-  if (con->controller_type == CONTROLLER_3WARE) {
-    con->controller_type=guess_device_type(device);
-    if (con->controller_type!=CONTROLLER_3WARE_9000_CHAR && con->controller_type!=CONTROLLER_3WARE_678K_CHAR)
-      con->controller_type = CONTROLLER_3WARE_678K;
-  }
-
-  if (con->controller_type == CONTROLLER_UNKNOWN)
-    con->controller_type=guess_device_type(device);
-  
-  if (con->controller_type == CONTROLLER_UNKNOWN) {
-    pout("Smartctl: please specify device type with the -d option.\n");
-    UsageSummary();
-    return FAILCMD;
-  }
-  
-  // set up mode for open() call.  SCSI case is:
-  switch (con->controller_type) {
-  case CONTROLLER_SCSI:
-    mode="SCSI";
-    break;
-  case CONTROLLER_3WARE_9000_CHAR:
-    mode="ATA_3WARE_9000";
-    break;
-  case CONTROLLER_3WARE_678K_CHAR:
-    mode="ATA_3WARE_678K";
-    break;
-  default:
-    mode="ATA";
-    break;
-  }
-  
-  // open device - SCSI devices are opened (O_RDWR | O_NONBLOCK) so the
-  // scsi generic device can be used (needs write permission for MODE 
-  // SELECT command) plus O_NONBLOCK to stop open hanging if media not
-  // present (e.g. with st).  Opening is retried O_RDONLY if read-only
-  // media prevents opening O_RDWR (it cannot happen for scsi generic
-  // devices, but it can for the others).
-  fd = deviceopen(device, mode);
-  if (fd<0) {
-    char errmsg[256];
-    snprintf(errmsg,256,"Smartctl open device: %s failed",argv[argc-1]);
-    errmsg[255]='\0';
-    syserror(errmsg);
-    return FAILDEV;
-  }
-
-  // now call appropriate ATA or SCSI routine
-  switch (con->controller_type) {
-  case CONTROLLER_UNKNOWN:
-    // we should never fall into this branch!
-    pout("Smartctl: please specify device type with the -d option.\n");
-    UsageSummary();
-    retval = FAILCMD;
-    break;
-  case CONTROLLER_SCSI:
-    retval = scsiPrintMain(fd);
-    break;
-  default:
-    retval = ataPrintMain(fd);
-    break;
-  }
-  
-  return retval;
-}
diff --git a/smartctl.cpp b/smartctl.cpp
new file mode 100644 (file)
index 0000000..4459198
--- /dev/null
@@ -0,0 +1,990 @@
+/*
+ * smartctl.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "config.h"
+#ifdef HAVE_GETOPT_LONG
+#include <getopt.h>
+#endif
+#if defined(__FreeBSD_version) && (__FreeBSD_version < 500000)
+#include <unistd.h>
+#endif
+
+#include "int64.h"
+#include "atacmds.h"
+#include "ataprint.h"
+#include "extern.h"
+#include "knowndrives.h"
+#include "scsicmds.h"
+#include "scsiprint.h"
+#include "smartctl.h"
+#include "utility.h"
+
+#ifdef NEED_SOLARIS_ATA_CODE
+extern const char *os_solaris_ata_s_cvsid;
+#endif
+extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid;
+const char* smartctl_c_cvsid="$Id: smartctl.cpp,v 1.155 2006/09/20 16:17:31 shattered Exp $"
+ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
+
+// This is a block containing all the "control variables".  We declare
+// this globally in this file, and externally in other files.
+smartmonctrl *con=NULL;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// Track memory use
+extern int64_t bytes;
+
+void printslogan(){
+#ifdef HAVE_GET_OS_VERSION_STR
+  const char * ver = get_os_version_str();
+#else
+  const char * ver = SMARTMONTOOLS_BUILD_HOST;
+#endif
+  pout("smartctl version %s [%s] Copyright (C) 2002-6 Bruce Allen\n", PACKAGE_VERSION, ver);
+  pout("Home page is " PACKAGE_HOMEPAGE "\n\n");
+  return;
+}
+
+void PrintOneCVS(const char *a_cvs_id){
+  char out[CVSMAXLEN];
+  printone(out,a_cvs_id);
+  pout("%s",out);
+  return;
+}
+
+void printcopy(){
+  char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]";
+
+  pout("smartctl comes with ABSOLUTELY NO WARRANTY. This\n");
+  pout("is free software, and you are welcome to redistribute it\n");
+  pout("under the terms of the GNU General Public License Version 2.\n");
+  pout("See http://www.gnu.org for further details.\n\n");
+  pout("CVS version IDs of files used to build this code are:\n");
+  PrintOneCVS(atacmdnames_c_cvsid);
+  PrintOneCVS(atacmds_c_cvsid);
+  PrintOneCVS(ataprint_c_cvsid);
+  PrintOneCVS(knowndrives_c_cvsid);
+  PrintOneCVS(os_XXXX_c_cvsid);
+#ifdef NEED_SOLARIS_ATA_CODE
+  PrintOneCVS(os_solaris_ata_s_cvsid);
+#endif
+  PrintOneCVS(scsicmds_c_cvsid);
+  PrintOneCVS(scsiprint_c_cvsid);
+  PrintOneCVS(smartctl_c_cvsid);
+  PrintOneCVS(utility_c_cvsid);
+  pout("\nsmartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n");
+  pout("smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n");
+  pout("smartmontools build configured: " SMARTMONTOOLS_CONFIGURE_DATE "\n");
+  pout("smartctl compile dated " __DATE__ " at "__TIME__ "\n");
+  pout("smartmontools configure arguments: %s\n", configargs);
+  return;
+}
+
+void UsageSummary(){
+  pout("\nUse smartctl -h to get a usage summary\n\n");
+  return;
+}
+
+/*  void prints help information for command syntax */
+void Usage (void){
+  printf("Usage: smartctl [options] device\n\n");
+  printf("============================================ SHOW INFORMATION OPTIONS =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+"  -h, --help, --usage\n"
+"         Display this help and exit\n\n"
+"  -V, --version, --copyright, --license\n"
+"         Print license, copyright, and version information and exit\n\n"
+"  -i, --info                                                       \n"
+"         Show identity information for device\n\n"
+"  -a, --all                                                        \n"
+"         Show all SMART information for device\n\n"
+  );
+#else
+  printf(
+"  -h        Display this help and exit\n"
+"  -V        Print license, copyright, and version information\n"
+"  -i        Show identity information for device\n"
+"  -a        Show all SMART information for device\n\n"
+  );
+#endif
+  printf("================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+"  -q TYPE, --quietmode=TYPE                                           (ATA)\n"
+"         Set smartctl quiet mode to one of: errorsonly, silent\n\n"
+"  -d TYPE, --device=TYPE\n"
+"         Specify device type to one of: ata, scsi, marvell, sat, 3ware,N\n\n"
+"  -T TYPE, --tolerance=TYPE                                           (ATA)\n"
+"         Tolerance: normal, conservative, permissive, verypermissive\n\n"
+"  -b TYPE, --badsum=TYPE                                              (ATA)\n"
+"         Set action on bad checksum to one of: warn, exit, ignore\n\n"
+"  -r TYPE, --report=TYPE\n"
+"         Report transactions (see man page)\n\n"
+"  -n MODE, --nocheck=MODE                                             (ATA)\n"
+"         No check if: never, sleep, standby, idle (see man page)\n\n"
+  );
+#else
+  printf(
+"  -q TYPE   Set smartctl quiet mode to one of: errorsonly, silent     (ATA)\n"
+"  -d TYPE   Specify device type to one of: ata, scsi, 3ware,N\n"
+"  -T TYPE   Tolerance: normal, conservative,permissive,verypermissive (ATA\n"
+"  -b TYPE   Set action on bad checksum to one of: warn, exit, ignore  (ATA)\n"
+"  -r TYPE   Report transactions (see man page)\n"
+"  -n MODE   No check if: never, sleep, standby, idle (see man page)   (ATA)\n\n"
+  );
+#endif
+  printf("============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+"  -s VALUE, --smart=VALUE\n"
+"        Enable/disable SMART on device (on/off)\n\n"
+"  -o VALUE, --offlineauto=VALUE                                       (ATA)\n"
+"        Enable/disable automatic offline testing on device (on/off)\n\n"
+"  -S VALUE, --saveauto=VALUE                                          (ATA)\n"
+"        Enable/disable Attribute autosave on device (on/off)\n\n"
+  );
+#else
+  printf(
+"  -s VALUE  Enable/disable SMART on device (on/off)\n"
+"  -o VALUE  Enable/disable device automatic offline testing (on/off)  (ATA)\n"
+"  -S VALUE  Enable/disable device Attribute autosave (on/off)         (ATA)\n\n"
+  );
+#endif
+  printf("======================================= READ AND DISPLAY DATA OPTIONS =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+"  -H, --health\n"
+"        Show device SMART health status\n\n"
+"  -c, --capabilities                                                  (ATA)\n"
+"        Show device SMART capabilities\n\n"
+"  -A, --attributes                                                         \n"
+"        Show device SMART vendor-specific Attributes and values\n\n"
+"  -l TYPE, --log=TYPE\n"
+"        Show device log. TYPE: error, selftest, selective, directory,\n"
+"                               background\n\n"
+"  -v N,OPTION , --vendorattribute=N,OPTION                            (ATA)\n"
+"        Set display OPTION for vendor Attribute N (see man page)\n\n"
+"  -F TYPE, --firmwarebug=TYPE                                         (ATA)\n"
+"        Use firmware bug workaround: none, samsung, samsung2\n\n"
+"  -P TYPE, --presets=TYPE                                             (ATA)\n"
+"        Drive-specific presets: use, ignore, show, showall\n\n"
+  );
+#else
+  printf(
+"  -H        Show device SMART health status\n"
+"  -c        Show device SMART capabilities                             (ATA)\n"
+"  -A        Show device SMART vendor-specific Attributes and values    (ATA)\n"
+"  -l TYPE   Show device log. TYPE: error, selftest, selective, directory,\n"
+"                                   background\n"
+"  -v N,OPT  Set display OPTion for vendor Attribute N (see man page)   (ATA)\n"
+"  -F TYPE   Use firmware bug workaround: none, samsung, samsung2       (ATA)\n"
+"  -P TYPE   Drive-specific presets: use, ignore, show, showall         (ATA)\n\n"
+  );
+#endif
+  printf("============================================ DEVICE SELF-TEST OPTIONS =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+"  -t TEST, --test=TEST\n"
+"        Run test.  TEST is: offline short long conveyance select,M-N pending,N afterselect,on afterselect,off\n\n"
+"  -C, --captive\n"
+"        Do test in captive mode (along with -t)\n\n"
+"  -X, --abort\n"
+"        Abort any non-captive test on device\n\n"
+);
+#else
+  printf(
+"  -t TEST   Run test.  TEST is: offline short long conveyance select,M-N pending,N afterselect,on afterselect,off\n"
+"  -C        Do test in captive mode (along with -t)\n"
+"  -X        Abort any non-captive test\n\n"
+  );
+#endif
+  print_smartctl_examples();
+  return;
+}
+
+/* Returns a pointer to a static string containing a formatted list of the valid
+   arguments to the option opt or NULL on failure. Note 'v' case different */
+const char *getvalidarglist(char opt) {
+  switch (opt) {
+  case 'q':
+    return "errorsonly, silent";
+  case 'd':
+    return "ata, scsi, marvell, sat, 3ware,N, hpt,L/M/N";
+  case 'T':
+    return "normal, conservative, permissive, verypermissive";
+  case 'b':
+    return "warn, exit, ignore";
+  case 'r':
+    return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
+  case 's':
+  case 'o':
+  case 'S':
+    return "on, off";
+  case 'l':
+    return "error, selftest, selective, directory, background";
+  case 'P':
+    return "use, ignore, show, showall";
+  case 't':
+    return "offline, short, long, conveyance, select,M-N, pending,N, afterselect,on, afterselect,off";
+  case 'F':
+    return "none, samsung, samsung2";
+  case 'n':
+    return "never, sleep, standby, idle";
+  case 'v':
+  default:
+    return NULL;
+  }
+}
+
+/* Prints the message "=======> VALID ARGUMENTS ARE: <LIST> \n", where
+   <LIST> is the list of valid arguments for option opt. */
+void printvalidarglistmessage(char opt) {
+  char *s;
+  
+  if (opt=='v')
+    s=create_vendor_attribute_arg_list();
+  else
+    s=(char *)getvalidarglist(opt);
+  
+  if (!s) {
+    pout("Error whilst constructing argument list for option %c", opt);
+    return;
+  }
+  if (opt=='v'){
+    pout("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n", s);
+    free(s);
+  }
+  else {
+  // getvalidarglist() might produce a multiline or single line string.  We
+  // need to figure out which to get the formatting right.
+    char separator = strchr(s, '\n') ? '\n' : ' ';
+    pout("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, (char *)s, separator);
+  }
+
+  return;
+}
+
+/*      Takes command options and sets features to be run */    
+void ParseOpts (int argc, char** argv){
+  int optchar;
+  int badarg;
+  int captive;
+  unsigned char *charp;
+  extern char *optarg;
+  extern int optopt, optind, opterr;
+  char extraerror[256];
+  // Please update getvalidarglist() if you edit shortopts
+  const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iav:P:t:CXF:n:";
+#ifdef HAVE_GETOPT_LONG
+  char *arg;
+  // Please update getvalidarglist() if you edit longopts
+  struct option longopts[] = {
+    { "help",            no_argument,       0, 'h' },
+    { "usage",           no_argument,       0, 'h' },
+    { "version",         no_argument,       0, 'V' },
+    { "copyright",       no_argument,       0, 'V' },
+    { "license",         no_argument,       0, 'V' },
+    { "quietmode",       required_argument, 0, 'q' },
+    { "device",          required_argument, 0, 'd' },
+    { "tolerance",       required_argument, 0, 'T' },
+    { "badsum",          required_argument, 0, 'b' },
+    { "report",          required_argument, 0, 'r' },
+    { "smart",           required_argument, 0, 's' },
+    { "offlineauto",     required_argument, 0, 'o' },
+    { "saveauto",        required_argument, 0, 'S' },
+    { "health",          no_argument,       0, 'H' },
+    { "capabilities",    no_argument,       0, 'c' },
+    { "attributes",      no_argument,       0, 'A' },
+    { "log",             required_argument, 0, 'l' },
+    { "info",            no_argument,       0, 'i' },
+    { "all",             no_argument,       0, 'a' },
+    { "vendorattribute", required_argument, 0, 'v' },
+    { "presets",         required_argument, 0, 'P' },
+    { "test",            required_argument, 0, 't' },
+    { "captive",         no_argument,       0, 'C' },
+    { "abort",           no_argument,       0, 'X' },
+    { "firmwarebug",     required_argument, 0, 'F' },
+    { "nocheck",         required_argument, 0, 'n' },
+    { 0,                 0,                 0, 0   }
+  };
+#endif
+  
+  memset(extraerror, 0, sizeof(extraerror));
+  memset(con,0,sizeof(*con));
+  con->testcase=-1;
+  opterr=optopt=0;
+  badarg = captive = FALSE;
+  
+  // This miserable construction is needed to get emacs to do proper indenting. Sorry!
+  while (-1 != (optchar = 
+#ifdef HAVE_GETOPT_LONG
+                getopt_long(argc, argv, shortopts, longopts, NULL)
+#else
+                getopt(argc, argv, shortopts)
+#endif
+                )){
+    switch (optchar){
+    case 'V':
+      con->dont_print=FALSE;
+      printslogan();
+      printcopy();
+      exit(0);
+      break;
+    case 'q':
+      if (!strcmp(optarg,"errorsonly")) {
+        con->printing_switchable     = TRUE;
+        con->dont_print = FALSE;
+      } else if (!strcmp(optarg,"silent")) {
+        con->printing_switchable     = FALSE;
+        con->dont_print = TRUE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'd':
+      con->controller_explicit = 1;
+      if (!strcmp(optarg,"ata")) {
+        con->controller_type = CONTROLLER_ATA;
+        con->controller_port = 0;
+      } else if (!strcmp(optarg,"scsi")) {
+        con->controller_type = CONTROLLER_SCSI;
+        con->controller_port = 0;
+      } else if (!strcmp(optarg,"marvell")) {
+        con->controller_type = CONTROLLER_MARVELL_SATA;
+        con->controller_port = 0;
+      } else if (!strncmp(optarg, "sat", 3)) {
+        con->controller_type = CONTROLLER_SAT;
+        con->controller_port = 0;
+        con->satpassthrulen = 0;
+        if (strlen(optarg) > 3) {
+          int k;
+          char * cp;
+
+          cp = strchr(optarg, ',');
+          if (cp && (1 == sscanf(cp + 1, "%d", &k)) &&
+              ((0 == k) || (12 == k) || (16 == k)))
+            con->satpassthrulen = k;
+          else {
+            sprintf(extraerror, "Option '-d sat,<n>' requires <n> to be "
+                    "0, 12 or 16\n");
+            badarg = TRUE;
+          }
+        }
+      } else if (!strncmp(optarg, "hpt", 3)){
+        unsigned char i, slash = 0;
+        con->hpt_data[0] = 0;
+        con->hpt_data[1] = 0;
+        con->hpt_data[2] = 0;
+        con->controller_type = CONTROLLER_HPT;
+        for (i=4; i < strlen(optarg); i++) {
+          if(optarg[i] == '/') {
+            slash++;
+            if(slash == 3) {
+              sprintf(extraerror, "Option '-d hpt,L/M/N' supports 2-3 items\n");
+              badarg = TRUE;
+              break;
+            }
+          }
+          else if ((optarg[i])>='0' && (optarg[i])<='9') {
+            if (con->hpt_data[slash]>1) { /* hpt_data[x] max 19 */
+              badarg = TRUE;
+              break;
+            }
+            con->hpt_data[slash] = con->hpt_data[slash]*10 + optarg[i] - '0';
+          }
+          else {
+            badarg = TRUE;
+            break;
+          }
+        }
+        if (slash == 0) {
+          sprintf(extraerror, "Option '-d hpt,L/M/N' requires 2-3 items\n");
+          badarg = TRUE;
+        } else if (badarg != TRUE) {
+          if (con->hpt_data[0]==0 || con->hpt_data[0]>8){
+            sprintf(extraerror, "Option '-d hpt,L/M/N' no/invalid controller id L supplied\n");
+            badarg = TRUE;
+          }
+          if (con->hpt_data[1]==0 || con->hpt_data[1]>8){
+            sprintf(extraerror, "Option '-d hpt,L/M/N' no/invalid channel number M supplied\n");
+            badarg = TRUE;
+          }
+          if (slash==2) {
+            if ( con->hpt_data[2]==0 || con->hpt_data[2]>15) {
+              sprintf(extraerror, "Option '-d hpt,L/M/N' no/invalid pmport number N supplied\n");
+              badarg = TRUE;
+            }
+          } else {
+            con->hpt_data[2]=1;
+          }
+        }
+      } else {
+        // look for RAID-type device
+        int i;
+        char *s;
+        
+        // make a copy of the string to mess with
+        if (!(s = strdup(optarg))) {
+          con->dont_print = FALSE;
+          pout("No memory for argument of -d. Exiting...\n");
+          exit(FAILCMD);
+        } else if (strncmp(s,"3ware,",6)) {
+          badarg = TRUE;
+        } else if (split_report_arg2(s, &i)) {
+          sprintf(extraerror, "Option -d 3ware,N requires N to be a non-negative integer\n");
+          badarg = TRUE;
+        } else if (i<0 || i>15) {
+          sprintf(extraerror, "Option -d 3ware,N (N=%d) must have 0 <= N <= 15\n", i);
+          badarg = TRUE;
+        } else {
+         // NOTE: controller_port == disk number + 1
+          con->controller_type = CONTROLLER_3WARE;
+          con->controller_port = i+1;
+        }
+        free(s);
+      }         
+      break;
+    case 'T':
+      if (!strcmp(optarg,"normal")) {
+        con->conservative = FALSE;
+        con->permissive   = 0;
+      } else if (!strcmp(optarg,"conservative")) {
+        con->conservative = TRUE;
+      } else if (!strcmp(optarg,"permissive")) {
+        if (con->permissive<0xff)
+          con->permissive++;
+      } else if (!strcmp(optarg,"verypermissive")) {
+        con->permissive=0xff;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'b':
+      if (!strcmp(optarg,"warn")) {
+        con->checksumfail   = FALSE;
+        con->checksumignore = FALSE;
+      } else if (!strcmp(optarg,"exit")) {
+        con->checksumfail   = TRUE;
+        con->checksumignore = FALSE;
+      } else if (!strcmp(optarg,"ignore")) {
+        con->checksumignore = TRUE;
+        con->checksumfail   = FALSE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'r':
+      {
+        int i;
+        char *s;
+
+        // split_report_arg() may modify its first argument string, so use a
+        // copy of optarg in case we want optarg for an error message.
+        if (!(s = strdup(optarg))) {
+          con->dont_print = FALSE;
+          pout("Can't allocate memory to copy argument to -r option"
+               " - exiting\n");
+          EXIT(FAILCMD);
+        }
+        if (split_report_arg(s, &i)) {
+          badarg = TRUE;
+        } else if (!strcmp(s,"ioctl")) {
+          con->reportataioctl  = con->reportscsiioctl = i;
+        } else if (!strcmp(s,"ataioctl")) {
+          con->reportataioctl = i;
+        } else if (!strcmp(s,"scsiioctl")) {
+          con->reportscsiioctl = i;
+        } else {
+          badarg = TRUE;
+        }
+        free(s);
+      }
+      break;
+    case 's':
+      if (!strcmp(optarg,"on")) {
+        con->smartenable  = TRUE;
+        con->smartdisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartdisable = TRUE;
+        con->smartenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'o':
+      if (!strcmp(optarg,"on")) {
+        con->smartautoofflineenable  = TRUE;
+        con->smartautoofflinedisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartautoofflinedisable = TRUE;
+        con->smartautoofflineenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'S':
+      if (!strcmp(optarg,"on")) {
+        con->smartautosaveenable  = TRUE;
+        con->smartautosavedisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartautosavedisable = TRUE;
+        con->smartautosaveenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'H':
+      con->checksmart = TRUE;           
+      break;
+    case 'F':
+      if (!strcmp(optarg,"none")) {
+        con->fixfirmwarebug = FIX_NONE;
+      } else if (!strcmp(optarg,"samsung")) {
+        con->fixfirmwarebug = FIX_SAMSUNG;
+      } else if (!strcmp(optarg,"samsung2")) {
+        con->fixfirmwarebug = FIX_SAMSUNG2;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'c':
+      con->generalsmartvalues = TRUE;
+      break;
+    case 'A':
+      con->smartvendorattrib = TRUE;
+      break;
+    case 'l':
+      if (!strcmp(optarg,"error")) {
+        con->smarterrorlog = TRUE;
+      } else if (!strcmp(optarg,"selftest")) {
+        con->smartselftestlog = TRUE;
+      } else if (!strcmp(optarg, "selective")) {
+       con->selectivetestlog = TRUE;
+      } else if (!strcmp(optarg,"directory")) {
+        con->smartlogdirectory = TRUE;
+      } else if (!strcmp(optarg,"background")) {
+        con->smartbackgroundlog = TRUE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'i':
+      con->driveinfo = TRUE;
+      break;            
+    case 'a':
+      con->driveinfo          = TRUE;
+      con->checksmart         = TRUE;
+      con->generalsmartvalues = TRUE;
+      con->smartvendorattrib  = TRUE;
+      con->smarterrorlog      = TRUE;
+      con->smartselftestlog   = TRUE;
+      con->selectivetestlog   = TRUE;
+      /* con->smartbackgroundlog = TRUE; */
+      break;
+    case 'v':
+      // parse vendor-specific definitions of attributes
+      if (!strcmp(optarg,"help")) {
+        char *s;
+        con->dont_print=FALSE;
+        printslogan();
+        if (!(s = create_vendor_attribute_arg_list())) {
+          pout("Insufficient memory to construct argument list\n");
+          EXIT(FAILCMD);
+        }
+        pout("The valid arguments to -v are:\n\thelp\n%s\n", s);
+        free(s);
+        EXIT(0);
+      }
+      charp=con->attributedefs;
+      if (!charp){
+        pout("Fatal internal error in ParseOpts()\n");
+        EXIT(FAILCMD);
+      }
+      if (parse_attribute_def(optarg, &charp))
+        badarg = TRUE;
+      break;    
+    case 'P':
+      if (!strcmp(optarg, "use")) {
+        con->ignorepresets = FALSE;
+      } else if (!strcmp(optarg, "ignore")) {
+        con->ignorepresets = TRUE;
+      } else if (!strcmp(optarg, "show")) {
+        con->showpresets = TRUE;
+      } else if (!strcmp(optarg, "showall")) {
+        if (optind < argc) { // -P showall MODEL [FIRMWARE]
+          int cnt = showmatchingpresets(argv[optind], (optind+1<argc ? argv[optind+1] : NULL));
+          EXIT(cnt); // report #matches
+        }
+        if (showallpresets())
+          EXIT(FAILCMD); // report regexp syntax error
+        EXIT(0);
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 't':
+      if (!strcmp(optarg,"offline")) {
+        con->smartexeoffimmediate = TRUE;
+        con->testcase             = OFFLINE_FULL_SCAN;
+      } else if (!strcmp(optarg,"short")) {
+        con->smartshortselftest = TRUE;
+        con->testcase           = SHORT_SELF_TEST;
+      } else if (!strcmp(optarg,"long")) {
+        con->smartextendselftest = TRUE;
+        con->testcase            = EXTEND_SELF_TEST;
+      } else if (!strcmp(optarg,"conveyance")) {
+        con->smartconveyanceselftest = TRUE;
+        con->testcase            = CONVEYANCE_SELF_TEST;
+      } else if (!strcmp(optarg,"afterselect,on")) {
+       // scan remainder of disk after doing selected segments
+       con->scanafterselect=2;
+      } else if (!strcmp(optarg,"afterselect,off")) {
+       // don't scan remainder of disk after doing selected segments
+       con->scanafterselect=1;
+      } else if (!strncmp(optarg,"pending,",strlen("pending,"))) {
+       // parse number of minutes that test should be pending
+       int i;
+       char *tailptr=NULL;
+       errno=0;
+       i=(int)strtol(optarg+strlen("pending,"), &tailptr, 10);
+       if (errno || *tailptr != '\0') {
+         sprintf(extraerror, "Option -t pending,N requires N to be a non-negative integer\n");
+         badarg = TRUE;
+       } else if (i<0 || i>65535) {
+         sprintf(extraerror, "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i);
+         badarg = TRUE;
+       } else {
+         con->pendingtime=i+1;
+       }
+      } else if (!strncmp(optarg,"select",strlen("select"))) {
+       // parse range of LBAs to test
+       uint64_t start, stop;
+
+        if (split_selective_arg(optarg, &start, &stop)) {
+         sprintf(extraerror, "Option -t select,M-N must have non-negative integer M and N\n");
+          badarg = TRUE;
+        } else {
+          if (con->smartselectivenumspans >= 5 || start > stop) {
+            if (start > stop) {
+              sprintf(extraerror, "ERROR: Start LBA (%"PRIu64") > ending LBA (%"PRId64") in argument \"%s\"\n",
+                start, stop, optarg);
+            } else {
+              sprintf(extraerror,"ERROR: No more than five selective self-test spans may be"
+                " defined\n");
+            }
+           badarg = TRUE;
+          }
+          con->smartselectivespan[con->smartselectivenumspans][0] = start;
+          con->smartselectivespan[con->smartselectivenumspans][1] = stop;
+          con->smartselectivenumspans++;
+          con->testcase            = SELECTIVE_SELF_TEST;
+        }
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'C':
+      captive = TRUE;
+      break;
+    case 'X':
+      con->smartselftestabort = TRUE;
+      con->testcase           = ABORT_SELF_TEST;
+      break;
+    case 'n':
+      // skip disk check if in low-power mode
+      if (!strcmp(optarg, "never"))
+        con->powermode = 1; // do not skip, but print mode
+      else if (!strcmp(optarg, "sleep"))
+        con->powermode = 2;
+      else if (!strcmp(optarg, "standby"))
+        con->powermode = 3;
+      else if (!strcmp(optarg, "idle"))
+        con->powermode = 4;
+      else
+        badarg = TRUE;
+      break;
+    case 'h':
+      con->dont_print=FALSE;
+      printslogan();
+      Usage();
+      EXIT(0);  
+      break;
+    case '?':
+    default:
+      con->dont_print=FALSE;
+      printslogan();
+#ifdef HAVE_GETOPT_LONG
+      // Point arg to the argument in which this option was found.
+      arg = argv[optind-1];
+      // Check whether the option is a long option that doesn't map to -h.
+      if (arg[1] == '-' && optchar != 'h') {
+        // Iff optopt holds a valid option then argument must be missing.
+        if (optopt && (strchr(shortopts, optopt) != NULL)) {
+          pout("=======> ARGUMENT REQUIRED FOR OPTION: %s\n", arg+2);
+          printvalidarglistmessage(optopt);
+        } else
+          pout("=======> UNRECOGNIZED OPTION: %s\n",arg+2);
+       if (extraerror[0])
+         pout("=======> %s", extraerror);
+        UsageSummary();
+        EXIT(FAILCMD);
+      }
+#endif
+      if (optopt) {
+        // Iff optopt holds a valid option then argument must be
+        // missing.  Note (BA) this logic seems to fail using Solaris
+        // getopt!
+        if (strchr(shortopts, optopt) != NULL) {
+          pout("=======> ARGUMENT REQUIRED FOR OPTION: %c\n", optopt);
+          printvalidarglistmessage(optopt);
+        } else
+          pout("=======> UNRECOGNIZED OPTION: %c\n",optopt);
+       if (extraerror[0])
+         pout("=======> %s", extraerror);
+        UsageSummary();
+        EXIT(FAILCMD);
+      }
+      Usage();
+      EXIT(0);  
+    } // closes switch statement to process command-line options
+    
+    // Check to see if option had an unrecognized or incorrect argument.
+    if (badarg) {
+      printslogan();
+      // It would be nice to print the actual option name given by the user
+      // here, but we just print the short form.  Please fix this if you know
+      // a clean way to do it.
+      pout("=======> INVALID ARGUMENT TO -%c: %s\n", optchar, optarg);
+      printvalidarglistmessage(optchar);
+      if (extraerror[0])
+       pout("=======> %s", extraerror);
+      UsageSummary();
+      EXIT(FAILCMD);
+    }
+  }
+  // At this point we have processed all command-line options.  If the
+  // print output is switchable, then start with the print output
+  // turned off
+  if (con->printing_switchable)
+    con->dont_print=TRUE;
+
+  // error message if user has asked for more than one test
+  if (1<(con->smartexeoffimmediate+con->smartshortselftest+con->smartextendselftest+
+         con->smartshortcapselftest+con->smartextendcapselftest+con->smartselftestabort + (con->smartselectivenumspans>0?1:0))){
+    con->dont_print=FALSE;
+    printslogan();
+    pout("\nERROR: smartctl can only run a single test type (or abort) at a time.\n");
+    UsageSummary();
+    EXIT(FAILCMD);
+  }
+
+  // error message if user has set selective self-test options without
+  // asking for a selective self-test
+  if ((con->pendingtime || con->scanafterselect) && !con->smartselectivenumspans){
+    con->dont_print=FALSE;
+    printslogan();
+    if (con->pendingtime)
+      pout("\nERROR: smartctl -t pending,N must be used with -t select,N-M.\n");
+    else
+      pout("\nERROR: smartctl -t afterselect,(on|off) must be used with -t select,N-M.\n");
+    UsageSummary();
+    EXIT(FAILCMD);
+  }
+
+  // If captive option was used, change test type if appropriate.
+  if (captive && con->smartshortselftest) {
+    con->smartshortselftest    = FALSE;
+    con->smartshortcapselftest = TRUE;
+    con->testcase              = SHORT_CAPTIVE_SELF_TEST;
+  } else if (captive && con->smartextendselftest) {
+    con->smartextendselftest    = FALSE;
+    con->smartextendcapselftest = TRUE;
+    con->testcase               = EXTEND_CAPTIVE_SELF_TEST;
+  }
+  else if (captive && con->smartconveyanceselftest) {
+    con->smartconveyanceselftest    = FALSE;
+    con->smartconveyancecapselftest = TRUE;
+    con->testcase                   = CONVEYANCE_CAPTIVE_SELF_TEST;
+  }
+  else if (captive && con->smartselectiveselftest) {
+    con->smartselectiveselftest    = FALSE;
+    con->smartselectivecapselftest = TRUE;
+    con->testcase                  = SELECTIVE_CAPTIVE_SELF_TEST;
+  }
+  // From here on, normal operations...
+  printslogan();
+  
+  // Warn if the user has provided no device name
+  if (argc-optind<1){
+    pout("ERROR: smartctl requires a device name as the final command-line argument.\n\n");
+    UsageSummary();
+    EXIT(FAILCMD);
+  }
+  
+  // Warn if the user has provided more than one device name
+  if (argc-optind>1){
+    int i;
+    pout("ERROR: smartctl takes ONE device name as the final command-line argument.\n");
+    pout("You have provided %d device names:\n",argc-optind);
+    for (i=0; i<argc-optind; i++)
+      pout("%s\n",argv[optind+i]);
+    UsageSummary();
+    EXIT(FAILCMD);
+  }  
+}
+
+// Printing function (controlled by global con->dont_print) 
+// [From GLIBC Manual: Since the prototype doesn't specify types for
+// optional arguments, in a call to a variadic function the default
+// argument promotions are performed on the optional argument
+// values. This means the objects of type char or short int (whether
+// signed or not) are promoted to either int or unsigned int, as
+// appropriate.]
+void pout(const char *fmt, ...){
+  va_list ap;
+  
+  // initialize variable argument list 
+  va_start(ap,fmt);
+  if (con->dont_print){
+    va_end(ap);
+    return;
+  }
+
+  // print out
+  vprintf(fmt,ap);
+  va_end(ap);
+  fflush(stdout);
+  return;
+}
+
+// This function is used by utility.cpp to report LOG_CRIT errors.
+// The smartctl version prints to stdout instead of syslog().
+void PrintOut(int priority, const char *fmt, ...) {
+  va_list ap;
+
+  // avoid warning message about unused variable from gcc -W: just
+  // change value of local copy.
+  priority=0;
+
+  va_start(ap,fmt);
+  vprintf(fmt,ap);
+  va_end(ap);
+  return;
+}
+
+
+/* Main Program */
+int main (int argc, char **argv){
+  int fd,retval=0;
+  char *device;
+  smartmonctrl control;
+  char *mode=NULL;
+
+  // define control block for external functions
+  con=&control;
+
+  // Part input arguments
+  ParseOpts(argc,argv);
+
+  device = argv[argc-1];
+
+  // If use has specified 3ware controller, determine which interface 
+  if (con->controller_type == CONTROLLER_3WARE) {
+    con->controller_type=guess_device_type(device);
+    if (con->controller_type!=CONTROLLER_3WARE_9000_CHAR && con->controller_type!=CONTROLLER_3WARE_678K_CHAR)
+      con->controller_type = CONTROLLER_3WARE_678K;
+  }
+
+  if (con->controller_type == CONTROLLER_UNKNOWN)
+    con->controller_type=guess_device_type(device);
+  
+  if (con->controller_type == CONTROLLER_UNKNOWN) {
+    pout("Smartctl: please specify device type with the -d option.\n");
+    UsageSummary();
+    return FAILCMD;
+  }
+  
+  // set up mode for open() call.  SCSI case is:
+  switch (con->controller_type) {
+  case CONTROLLER_SCSI:
+  case CONTROLLER_SAT:
+    mode="SCSI";
+    break;
+  case CONTROLLER_3WARE_9000_CHAR:
+    mode="ATA_3WARE_9000";
+    break;
+  case CONTROLLER_3WARE_678K_CHAR:
+    mode="ATA_3WARE_678K";
+    break;
+  default:
+    mode="ATA";
+    break;
+  }
+  
+  // open device - SCSI devices are opened (O_RDWR | O_NONBLOCK) so the
+  // scsi generic device can be used (needs write permission for MODE 
+  // SELECT command) plus O_NONBLOCK to stop open hanging if media not
+  // present (e.g. with st).  Opening is retried O_RDONLY if read-only
+  // media prevents opening O_RDWR (it cannot happen for scsi generic
+  // devices, but it can for the others).
+  fd = deviceopen(device, mode);
+  if (fd<0) {
+    char errmsg[256];
+    snprintf(errmsg,256,"Smartctl open device: %s failed",argv[argc-1]);
+    errmsg[255]='\0';
+    syserror(errmsg);
+    return FAILDEV;
+  }
+
+  // now call appropriate ATA or SCSI routine
+  switch (con->controller_type) {
+  case CONTROLLER_UNKNOWN:
+    // we should never fall into this branch!
+    pout("Smartctl: please specify device type with the -d option.\n");
+    UsageSummary();
+    retval = FAILCMD;
+    break;
+  case CONTROLLER_SCSI:
+    retval = scsiPrintMain(fd);
+    if ((0 == retval) && (CONTROLLER_SAT == con->controller_type))
+        retval = ataPrintMain(fd);
+    break;
+  default:
+    retval = ataPrintMain(fd);
+    break;
+  }
+  
+  return retval;
+}
index cba0616564d140db2ae076d0d826a02e4f13a729..fca1df20fff8ecd2c4ad1957e955667a91a18458 100644 (file)
@@ -25,7 +25,7 @@
 #ifndef SMARTCTL_H_
 #define SMARTCTL_H_
 
-#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.23 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.24 2006/07/20 20:59:45 chrfranke Exp $\n"
 
 /* Boolean Values */
 #define TRUE 0x01
@@ -39,6 +39,9 @@
 // device open failed
 #define FAILDEV   (0x01<<1)
 
+// device is in low power mode and -n option requests to exit
+#define FAILPOWER (0x01<<1)
+
 // read device identity (ATA only) failed
 #define FAILID    (0x01<<1)
 
index 16b426ad97fbf5e12cf7cd1ce9a71d6ce20723a7..907ccd5c01e695c14d86f493bb149c09415a235b 100644 (file)
@@ -1,7 +1,7 @@
 .ig
 Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  
-$Id: smartd.8.in,v 1.100 2006/04/12 13:55:44 ballen4705 Exp $
+$Id: smartd.8.in,v 1.106 2006/09/27 21:42:03 chrfranke Exp $
 
 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
@@ -107,6 +107,10 @@ for IDE/ATA devices on WinNT4/2000/XP, \fB"/dev/hd[a-d]"\fP
 (bitmask from "\\\\.\\SMARTVSD") for IDE/ATA devices on Win95/98/98SE/ME,
 and \fB"/dev/scsi[0-9][0-7]"\fP (ASPI adapter 0-9, ID 0-7) for SCSI
 devices on all versions of Windows.
+If a 3ware 9000 controller is installed, examine all entries
+\fB"/dev/hdX,N"\fP for the first logical drive (\'unit\'
+\fB"/dev/hdX"\fP) and all physical disks (\'ports\' \fB",N"\fP)
+detected behind this controller. Same for a second controller if present.
 .IP \fBCYGWIN\fP: 9
 See "WINDOWS" above.
 .IP \fBOS/2,eComStation\fP: 9
@@ -340,7 +344,7 @@ equivalent.
 .B \-\-service
 Cygwin and Windows only: Enables \fBsmartd\fP to run as a Windows service.
 
-On Cygwin, this option simply prevents fork'ing into background mode to
+On Cygwin, this option simply prevents forking into background mode to
 allow running \fBsmartd\fP as service via cygrunsrv, see NOTES below.
 
 On Windows, this option enables the buildin service support.
@@ -479,7 +483,11 @@ Section below!
 .B # This is an example smartd startup config file
 .B # /usr/local/etc/smartd.conf for monitoring three
 .B # ATA disks, three SCSI disks, six ATA disks
-.B # behind two 3ware controllers and one SATA disk
+.B # behind two 3ware controllers, three SATA disks
+.B # directly connected to the highpoint rocket-
+.B # raid controller, two SATA disks connected to
+.B # the highpoint controller via a pmport device
+.B # and one SATA disk.
 .B #
 .nf
 .B # First ATA disk on two different interfaces. On
@@ -528,6 +536,21 @@ Section below!
 .B \ \ /dev/twa0 -d 3ware,1 -a -s L/../../7/02
 .B #
 .nf
+.B # Three SATA disks on a highpoint rocketraid controller.
+.B # Start short self-tests daily between 1-2, 2-3, and
+.B # 3-4 am.
+.B \ \ /dev/sde -d hpt,1/1 -a -s S/../.././01
+.B \ \ /dev/sde -d hpt,1/2 -a -s S/../.././02
+.B \ \ /dev/sde -d hpt,1/3 -a -s S/../.././03
+.B #
+.nf
+.B # Two SATA disks connected to a highpoint rocketraid 
+.B # via a pmport device.  Start long self-tests Sundays
+.B # between midnight and 1am and 2-3 am
+.B \ \ /dev/sde -d hpt,1/4/1 -a -s L/../../7/00
+.B \ \ /dev/sde -d hpt,1/4/2 -a -s L/../../7/02
+.B #
+.nf
 .B # The following line enables monitoring of the 
 .B # ATA Error Log and the Self-Test Error Log.  
 .B # It also tracks changes in both Prefailure
@@ -595,7 +618,7 @@ directives can be used for these disks (but see note below).
 .TP
 .B \-d TYPE
 Specifies the type of the device.  This Directive may be used multiple
-times for one device, but the arguments \fIata\fP, \fIscsi\fP,
+times for one device, but the arguments \fIata\fP, \fIscsi\fP, \fIsat\fP,
 \fImarvell\fP, and \fI3ware,N\fP are mutually-exclusive. If more than
 one is given then \fBsmartd\fP will use the last one which appears.
 
@@ -620,6 +643,18 @@ from issuing SCSI commands to an ATA device.
 \fBsmartd\fP
 from issuing ATA commands to a SCSI device.
 
+.I sat
+\- the device type is SCSI to ATA Translation (SAT).
+\fBsmartd\fP
+will generate ATA (smart) commands and then package them in
+the SAT defined ATA PASS THROUGH SCSI commands. The commands
+are then routed through the SCSI pass through interface to the
+operating system. There are two types of ATA PASS THROUGH
+SCSI commands: a 12 byte and 16 byte variant.
+\fBsmartd\fP
+can use either and defaults to the 16 byte variant. This can
+be overridden with this syntax: \'\-d sat,12\' or \'\-d sat,16\'.
+
 .I marvell
 \- Under Linux, interact with SATA disks behind Marvell chip-set
 controllers (using the Marvell rather than libata driver).
@@ -667,6 +702,19 @@ controllers).
 
 .B 3ware controllers are currently ONLY supported under Linux.
 
+.I hpt,L/M/N
+\- the device consists of one or more ATA disks connected to a HighPoint
+RocketRAID controller. The integer L is the controller id, the integer M
+is the channel number, and the integer N is the PMPort number if it is
+available. The allowed values of L are from 1 to 4 inclusive, M are from
+1 to 8 inclusive and N from 1 to 4 if PMPort available.  And also these
+values are limited by the model of the HighPoint RocketRAID controller.
+In log files and email messages this disk will be identified as
+hpt_X/X/X and X/X/X is the same as L/M/N, note if no N indicated, N set
+to the default value 1.
+
+.B HighPoint RocketRAID controllers are currently ONLY supported under Linux.
+
 .I removable
 \- the device or its media is removable.  This indicates to
 \fBsmartd\fP
@@ -721,6 +769,9 @@ this is probably what you want.
 In the IDLE state, most disks are still spinning, so this is probably
 not what you want.
 
+When a self test is scheduled (see \'\-s\' Directive below), the
+\'\fB\-n\fP\' Directive is ignored, and all tests are carried out.
+
 When a periodic test is skipped, \fBsmartd\fP normally writes an
 informal log message. The message can be suppressed by appending
 the option \',q\' to POWERMODE (like \'\-n standby,q\').
@@ -1030,13 +1081,15 @@ is set to the argument of \-M exec, if present or else to \'mail\'
 .IP \fBSMARTD_DEVICE\fP 4
 is set to the device path (examples: /dev/hda, /dev/sdb).
 .IP \fBSMARTD_DEVICETYPE\fP 4
-is set to the device type (possible values: ata, scsi, 3ware,N). Here
-N=0,...,15 denotes the ATA disk behind a 3ware RAID controller.
+is set to the device type (possible values: ata, scsi, 3ware,N, hpt,L/M/N).
+Here N=0,...,15 denotes the ATA disk behind a 3ware RAID controller and
+L/M/N denotes the SATA disk behind a HighPoint RocketRAID controller.
 .IP \fBSMARTD_DEVICESTRING\fP 4
 is set to the device description.  For SMARTD_DEVICETYPE of ata or
 scsi, this is the same as SMARTD_DEVICE.  For 3ware RAID controllers,
-the form used is \'/dev/sdc [3ware_disk_01]\'. In this case the device
-string contains a space and is NOT quoted.  So to use
+the form used is \'/dev/sdc [3ware_disk_01]\'.  For HighPoint RocketRAID
+controller, the form is \'/dev/sdd [hpt_1/1/1]\'.  In these cases the
+device string contains a space and is NOT quoted.  So to use
 $SMARTD_DEVICESTRING in a bash script you should probably enclose it
 in double quotes.
 .IP \fBSMARTD_FAILTYPE\fP 4
@@ -1276,6 +1329,41 @@ to know, because if you have data stored in this disk sector, and you
 need to read it, the read will fail.  Please see the previous \'\-C\'
 option for more details.
 
+.TP
+.B \-W DIFF[,INFO[,CRIT]]
+Report if the current temperature had changed by at least \fBDIFF\fP
+degrees since last report. Report or Warn if the temperature is greater
+or equal than one of \fBINFO\fP or \fBCRIT\fP degrees Celsius. If the
+limit \fBCRIT\fP is reached, a message with loglevel
+\fB\'LOG_CRITICAL\'\fP will be logged to syslog and a warning email
+will be send if '-m' is specified. If only the limit \fBINFO\fP is
+reached, a message with loglevel \fB\'LOG_INFO\'\fP will be logged.
+
+To disable any of the 3 reports, set the corresponding limit to 0.
+Trailing zero arguments may be omitted. By default, all temperature
+reports are disabled (\'-W 0\').
+
+To track temperature changes of at least 2 degrees, use:
+.nf
+\fB \-W 2
+.fi
+To log informal messages on temperatures of at least 40 degrees, use:
+.nf
+\fB \-W 0,40
+.fi
+For warning messages/mails on temperatures of at least 45 degrees, use:
+.nf
+\fB \-W 0,0,45
+.fi
+To combine all of the above reports, use:
+.nf
+\fB \-W 2,40,45
+.fi
+
+For ATA devices, smartd interprets Attribute 194 as Temperature Celsius
+by default. This can be changed to Attribute 9 or 220 by the drive
+database or by the \'-v\' directive, see below.
+
 .TP
 .B \-F TYPE
 [ATA only] Modifies the behavior of \fBsmartd\fP to compensate for
@@ -1841,4 +1929,4 @@ smartmontools home page at \fBhttp://smartmontools.sourceforge.net/#references\f
 
 .SH
 CVS ID OF THIS PAGE:
-$Id: smartd.8.in,v 1.100 2006/04/12 13:55:44 ballen4705 Exp $
+$Id: smartd.8.in,v 1.106 2006/09/27 21:42:03 chrfranke Exp $
diff --git a/smartd.c b/smartd.c
deleted file mode 100644 (file)
index 63242fe..0000000
--- a/smartd.c
+++ /dev/null
@@ -1,4127 +0,0 @@
-/*
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
- */
-
-// unconditionally included files
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>   // umask
-#ifndef _WIN32
-#include <sys/wait.h>
-#include <unistd.h>
-#endif
-#include <signal.h>
-#include <fcntl.h>
-#include <string.h>
-#include <syslog.h>
-#include <stdarg.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <time.h>
-#include <limits.h>
-
-#if SCSITIMEOUT
-#include <setjmp.h>
-#endif
-
-// see which system files to conditionally include
-#include "config.h"
-
-// conditionally included files
-#ifdef HAVE_GETOPT_LONG
-#include <getopt.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-
-#ifdef _WIN32
-#ifdef _MSC_VER
-#pragma warning(disable:4761) // "conversion supplied"
-typedef unsigned short mode_t;
-typedef int pid_t;
-#endif
-#include <io.h> // umask()
-#include <process.h> // getpid()
-#endif // _WIN32
-
-#ifdef __CYGWIN__
-// From <windows.h>:
-// BOOL WINAPI FreeConsole(void);
-int __stdcall FreeConsole(void);
-#include <io.h> // setmode()
-#endif // __CYGWIN__
-
-// locally included files
-#include "int64.h"
-#include "atacmds.h"
-#include "ataprint.h"
-#include "extern.h"
-#include "knowndrives.h"
-#include "scsicmds.h"
-#include "smartd.h"
-#include "utility.h"
-
-#ifdef _WIN32
-#include "hostname_win32.h" // gethost/domainname()
-#define HAVE_GETHOSTNAME   1
-#define HAVE_GETDOMAINNAME 1
-// fork()/signal()/initd simulation for native Windows
-#include "daemon_win32.h" // daemon_main/detach/signal()
-#undef SIGNALFN
-#define SIGNALFN  daemon_signal
-#define strsignal daemon_strsignal
-#define sleep     daemon_sleep
-#undef EXIT // see utility.h
-#define EXIT(x)  { exitstatus = daemon_winsvc_exitcode = (x); exit((x)); }
-// SIGQUIT does not exits, CONTROL-Break signals SIGBREAK.
-#define SIGQUIT SIGBREAK
-#define SIGQUIT_KEYNAME "CONTROL-Break"
-#else  // _WIN32
-#ifdef __CYGWIN__
-// 2x CONTROL-C simulates missing SIGQUIT via keyboard
-#define SIGQUIT_KEYNAME "2x CONTROL-C"
-#else // __CYGWIN__
-#define SIGQUIT_KEYNAME "CONTROL-\\"
-#endif // __CYGWIN__
-#endif // _WIN32
-
-#if defined (__SVR4) && defined (__sun)
-int getdomainname(char *, int); /* no declaration in header files! */
-#endif
-
-#define ARGUSED(x) ((void)(x))
-
-// These are CVS identification information for *.c and *.h files
-extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *escalade_c_cvsid, 
-                  *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid;
-
-static const char *filenameandversion="$Id: smartd.c,v 1.362 2006/04/12 16:18:57 ballen4705 Exp $";
-#ifdef NEED_SOLARIS_ATA_CODE
-extern const char *os_solaris_ata_s_cvsid;
-#endif
-#ifdef _WIN32
-extern const char *daemon_win32_c_cvsid, *hostname_win32_c_cvsid, *syslog_win32_c_cvsid;
-#endif
-const char *smartd_c_cvsid="$Id: smartd.c,v 1.362 2006/04/12 16:18:57 ballen4705 Exp $" 
-ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID
-#ifdef DAEMON_WIN32_H_CVSID
-DAEMON_WIN32_H_CVSID
-#endif
-EXTERN_H_CVSID INT64_H_CVSID
-#ifdef HOSTNAME_WIN32_H_CVSID
-HOSTNAME_WIN32_H_CVSID
-#endif
-KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID
-#ifdef SYSLOG_H_CVSID
-SYSLOG_H_CVSID
-#endif
-UTILITY_H_CVSID;
-
-extern const char *reportbug;
-
-// GNU copyleft statement.  Needed for GPL purposes.
-const char *copyleftstring="smartd comes with ABSOLUTELY NO WARRANTY. This is\n"
-                           "free software, and you are welcome to redistribute it\n"
-                           "under the terms of the GNU General Public License\n"
-                           "Version 2. See http://www.gnu.org for further details.\n\n";
-
-extern unsigned char debugmode;
-
-// command-line: how long to sleep between checks
-static int checktime=CHECKTIME;
-
-// command-line: name of PID file (NULL for no pid file)
-static char* pid_file=NULL;
-
-// configuration file name
-#ifndef _WIN32
-static char* configfile = SMARTMONTOOLS_SYSCONFDIR "/" CONFIGFILENAME ;
-#else
-static char* configfile = "./" CONFIGFILENAME ;
-#endif
-// configuration file "name" if read from stdin
-static /*const*/ char * const configfile_stdin = "<stdin>";
-// allocated memory for alternate configuration file name
-static char* configfile_alt = NULL;
-
-// command-line: when should we exit?
-static int quit=0;
-
-// command-line; this is the default syslog(3) log facility to use.
-static int facility=LOG_DAEMON;
-
-#ifdef __CYGWIN__
-// command-line: running as service, so don't fork()
-static int is_service=0;
-#endif
-
-// used for control of printing, passing arguments to atacmds.c
-smartmonctrl *con=NULL;
-
-// pointers to (real or simulated) entries in configuration file, and
-// maximum space currently allocated for these entries.
-cfgfile **cfgentries=NULL;
-int cfgentries_max=0;
-
-// pointers to ATA and SCSI devices being monitored, maximum and
-// actual numbers
-cfgfile **atadevlist=NULL, **scsidevlist=NULL;
-int atadevlist_max=0, scsidevlist_max=0;
-int numdevata=0, numdevscsi=0;
-
-// track memory usage
-extern int64_t bytes;
-
-// exit status
-extern int exitstatus;
-
-// set to one if we catch a USR1 (check devices now)
-volatile int caughtsigUSR1=0;
-
-#ifdef _WIN32
-// set to one if we catch a USR2 (toggle debug mode)
-volatile int caughtsigUSR2=0;
-#endif
-
-// set to one if we catch a HUP (reload config file). In debug mode,
-// set to two, if we catch INT (also reload config file).
-volatile int caughtsigHUP=0;
-
-// set to signal value if we catch INT, QUIT, or TERM
-volatile int caughtsigEXIT=0;
-
-#if SCSITIMEOUT
-// stack environment if we time out during SCSI access (USB devices)
-jmp_buf registerscsienv;
-#endif
-
-// tranlate cfg->pending into the correct Attribute numbers
-void TranslatePending(unsigned short pending, unsigned char *current, unsigned char *offline) {
-
-  unsigned char curr = CURR_PEND(pending);
-  unsigned char off =  OFF_PEND(pending);
-
-  // look for special value of CUR_UNC_DEFAULT that means DONT
-  // monitor. 0 means DO test.
-  if (curr==CUR_UNC_DEFAULT)
-    curr=0;
-  else if (curr==0)
-    curr=CUR_UNC_DEFAULT;
-       
-  // look for special value of OFF_UNC_DEFAULT that means DONT
-  // monitor.  0 means DO TEST.
-  if (off==OFF_UNC_DEFAULT)
-    off=0;
-  else if (off==0)
-    off=OFF_UNC_DEFAULT;
-
-  *current=curr;
-  *offline=off;
-
-  return;
-}
-
-
-// free all memory associated with selftest part of configfile entry.  Return NULL
-testinfo* FreeTestData(testinfo *data){
-  
-  // make sure we have something to do.
-  if (!data)
-    return NULL;
-  
-  // free space for text pattern
-  data->regex=FreeNonZero(data->regex, -1, __LINE__, filenameandversion);
-  
-  // free compiled expression
-  regfree(&(data->cregex));
-
-  // make sure that no sign of the compiled expression is left behind
-  // (just in case, to help detect bugs if we ever try and refer to
-  // that again).
-  memset(&(data->cregex), '0', sizeof(regex_t));
-
-  // free remaining memory space
-  data=FreeNonZero(data, sizeof(testinfo), __LINE__, filenameandversion);
-
-  return NULL;
-}
-
-cfgfile **AllocateMoreSpace(cfgfile **oldarray, int *oldsize, char *listname){
-  // for now keep BLOCKSIZE small to help detect coding problems.
-  // Perhaps increase in the future.
-  const int BLOCKSIZE=8;
-  int i;
-  int old = *oldsize;
-  int new = old + BLOCKSIZE;
-  cfgfile **newptr=realloc(oldarray, new*sizeof(cfgfile *));
-  
-  // did we get more space?
-  if (newptr) {
-
-    // clear remaining entries ala calloc()
-    for (i=old; i<new; i++)
-      newptr[i]=NULL;
-    
-    bytes += BLOCKSIZE*sizeof(cfgfile *);
-    
-    *oldsize=new;
-    
-#if 0
-    PrintOut(LOG_INFO, "allocating %d slots for %s\n", BLOCKSIZE, listname);
-#endif
-
-    return newptr;
-  }
-  
-  PrintOut(LOG_CRIT, "out of memory for allocating %s list\n", listname);
-  EXIT(EXIT_NOMEM);
-}
-
-void PrintOneCVS(const char *a_cvs_id){
-  char out[CVSMAXLEN];
-  printone(out,a_cvs_id);
-  PrintOut(LOG_INFO,"%s",out);
-  return;
-}
-
-// prints CVS identity information for the executable
-void PrintCVS(void){
-  char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]";
-
-  PrintOut(LOG_INFO,(char *)copyleftstring);
-  PrintOut(LOG_INFO,"CVS version IDs of files used to build this code are:\n");
-  PrintOneCVS(atacmdnames_c_cvsid);
-  PrintOneCVS(atacmds_c_cvsid);
-  PrintOneCVS(ataprint_c_cvsid);
-#ifdef _WIN32
-  PrintOneCVS(daemon_win32_c_cvsid);
-#endif
-#ifdef _WIN32
-  PrintOneCVS(hostname_win32_c_cvsid);
-#endif
-  PrintOneCVS(knowndrives_c_cvsid);
-  PrintOneCVS(os_XXXX_c_cvsid);
-#ifdef NEED_SOLARIS_ATA_CODE
-  PrintOneCVS( os_solaris_ata_s_cvsid);
-#endif
-  PrintOneCVS(scsicmds_c_cvsid);
-  PrintOneCVS(smartd_c_cvsid);
-#ifdef _WIN32
-  PrintOneCVS(syslog_win32_c_cvsid);
-#endif
-  PrintOneCVS(utility_c_cvsid);
-  PrintOut(LOG_INFO, "\nsmartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n");
-  PrintOut(LOG_INFO, "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n");
-  PrintOut(LOG_INFO, "smartmontools build configured: " SMARTMONTOOLS_CONFIGURE_DATE "\n");
-  PrintOut(LOG_INFO, "smartd compile dated " __DATE__ " at "__TIME__ "\n");
-  PrintOut(LOG_INFO, "smartmontools configure arguments: %s\n", configargs);
-  return;
-}
-
-// Removes config file entry, freeing all memory
-void RmConfigEntry(cfgfile **anentry, int whatline){
-  
-  cfgfile *cfg;
-
-  // pointer should never be null!
-  if (!anentry){
-    PrintOut(LOG_CRIT,"Internal error in RmConfigEntry() at line %d of file %s\n%s",
-             whatline, filenameandversion, reportbug);    
-    EXIT(EXIT_BADCODE);
-  }
-  
-  // only remove entries that exist!
-  if (!(cfg=*anentry))
-    return;
-
-  // entry exists -- free all of its memory  
-  cfg->name            = FreeNonZero(cfg->name,           -1,__LINE__,filenameandversion);
-  cfg->smartthres      = FreeNonZero(cfg->smartthres,      sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion);
-  cfg->smartval        = FreeNonZero(cfg->smartval,        sizeof(struct ata_smart_values),__LINE__,filenameandversion);
-  cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion);
-  cfg->attributedefs   = FreeNonZero(cfg->attributedefs,   MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion);
-  if (cfg->mailwarn){
-    cfg->mailwarn->address         = FreeNonZero(cfg->mailwarn->address,        -1,__LINE__,filenameandversion);
-    cfg->mailwarn->emailcmdline    = FreeNonZero(cfg->mailwarn->emailcmdline,   -1,__LINE__,filenameandversion);
-    cfg->mailwarn                  = FreeNonZero(cfg->mailwarn,   sizeof(maildata),__LINE__,filenameandversion);
-  }
-  cfg->testdata        = FreeTestData(cfg->testdata);
-  *anentry             = FreeNonZero(cfg,                  sizeof(cfgfile),__LINE__,filenameandversion);
-
-  return;
-}
-
-// deallocates all memory associated with cfgentries list
-void RmAllConfigEntries(){
-  int i;
-
-  for (i=0; i<cfgentries_max; i++)
-    RmConfigEntry(cfgentries+i, __LINE__);
-
-  cfgentries=FreeNonZero(cfgentries, sizeof(cfgfile *)*cfgentries_max, __LINE__, filenameandversion);
-  cfgentries_max=0;
-
-  return;
-}
-
-// deallocates all memory associated with ATA/SCSI device lists
-void RmAllDevEntries(){
-  int i;
-  
-  for (i=0; i<atadevlist_max; i++)
-    RmConfigEntry(atadevlist+i, __LINE__);
-
-  atadevlist=FreeNonZero(atadevlist, sizeof(cfgfile *)*atadevlist_max, __LINE__, filenameandversion);
-  atadevlist_max=0;
-
-  for (i=0; i<scsidevlist_max; i++)
-    RmConfigEntry(scsidevlist+i, __LINE__);
-  
-  scsidevlist=FreeNonZero(scsidevlist, sizeof(cfgfile *)*scsidevlist_max, __LINE__, filenameandversion);
-  scsidevlist_max=0;
-
-  return;
-}
-
-// remove the PID file
-void RemovePidFile(){
-  if (pid_file) {
-    if ( -1==unlink(pid_file) )
-      PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n", 
-               pid_file, strerror(errno));
-    pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion);
-  }
-  return;
-}
-
-
-//  Note if we catch a SIGUSR1
-void USR1handler(int sig){
-  if (SIGUSR1==sig)
-    caughtsigUSR1=1;
-  return;
-}
-
-#ifdef _WIN32
-//  Note if we catch a SIGUSR2
-void USR2handler(int sig){
-  if (SIGUSR2==sig)
-    caughtsigUSR2=1;
-  return;
-}
-#endif
-
-// Note if we catch a HUP (or INT in debug mode)
-void HUPhandler(int sig){
-  if (sig==SIGHUP)
-    caughtsigHUP=1;
-  else
-    caughtsigHUP=2;
-  return;
-}
-
-// signal handler for TERM, QUIT, and INT (if not in debug mode)
-void sighandler(int sig){
-  if (!caughtsigEXIT)
-    caughtsigEXIT=sig;
-  return;
-}
-
-
-// signal handler that prints Goodbye message and removes pidfile
-void Goodbye(void){
-  
-  // clean up memory -- useful for debugging
-  RmAllConfigEntries();
-  RmAllDevEntries();
-
-  // delete PID file, if one was created
-  RemovePidFile();
-
-  // remove alternate configfile name
-  configfile_alt=FreeNonZero(configfile_alt, -1,__LINE__,filenameandversion);
-
-  // useful for debugging -- have we managed memory correctly?
-  if (debugmode || (bytes && exitstatus!=EXIT_NOMEM))
-    PrintOut(LOG_INFO, "Memory still allocated for devices at exit is %" PRId64 " bytes.\n", bytes);
-
-  // if we are exiting because of a code bug, tell user
-  if (exitstatus==EXIT_BADCODE || (bytes && exitstatus!=EXIT_NOMEM))
-        PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n");
-
-  if (exitstatus==0 && bytes)
-    exitstatus=EXIT_BADCODE;
-
-  // and this should be the final output from smartd before it exits
-  PrintOut(exitstatus?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", exitstatus);
-
-  return;
-}
-
-#define ENVLENGTH 1024
-
-// a replacement for setenv() which is not available on all platforms.
-// Note that the string passed to putenv must not be freed or made
-// invalid, since a pointer to it is kept by putenv(). This means that
-// it must either be a static buffer or allocated off the heap. The
-// string can be freed if the environment variable is redefined or
-// deleted via another call to putenv(). So we keep these on the stack
-// as long as the popen() call is underway.
-int exportenv(char* stackspace, const char *name, const char *value){
-  snprintf(stackspace,ENVLENGTH, "%s=%s", name, value);
-  return putenv(stackspace);
-}
-
-char* dnsdomain(const char* hostname) {
-  char *p = NULL;
-#ifdef HAVE_GETHOSTBYNAME
-  struct hostent *hp;
-  
-  if ((hp = gethostbyname(hostname))) {
-    // Does this work if gethostbyname() returns an IPv6 name in
-    // colon/dot notation?  [BA]
-    if ((p = strchr(hp->h_name, '.')))
-      p++; // skip "."
-  }
-#else
-  ARGUSED(hostname);
-#endif
-  return p;
-}
-
-#define EBUFLEN 1024
-
-// If either address or executable path is non-null then send and log
-// a warning email, or execute executable
-void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
-  char command[2048], message[256], hostname[256], domainname[256], additional[256],fullmessage[1024];
-  char original[256], further[256], nisdomain[256], subject[256],dates[DATEANDEPOCHLEN];
-  char environ_strings[11][ENVLENGTH];
-  time_t epoch;
-  va_list ap;
-  const int day=24*3600;
-  int days=0;
-  char *whichfail[]={
-    "EmailTest",                  // 0
-    "Health",                     // 1
-    "Usage",                      // 2
-    "SelfTest",                   // 3
-    "ErrorCount",                 // 4
-    "FailedHealthCheck",          // 5
-    "FailedReadSmartData",        // 6
-    "FailedReadSmartErrorLog",    // 7
-    "FailedReadSmartSelfTestLog", // 8
-    "FailedOpenDevice",           // 9
-    "CurrentPendingSector",       // 10
-    "OfflineUncorrectableSector" //  11
-  };
-  
-  char *address, *executable;
-  mailinfo *mail;
-  maildata* data=cfg->mailwarn;
-#ifndef _WIN32
-  FILE *pfp=NULL;
-#else
-  char stdinbuf[1024]; int boxmsgoffs, boxtype;
-#endif
-  char *newadd=NULL, *newwarn=NULL;
-  const char *unknown="[Unknown]";
-
-  // See if user wants us to send mail
-  if(!data)
-    return;
-  
-  address=data->address;
-  executable=data->emailcmdline;
-  
-  if (!address && !executable)
-    return;
-  
-  // which type of mail are we sending?
-  mail=(data->maillog)+which;
-  
-  // checks for sanity
-  if (data->emailfreq<1 || data->emailfreq>3) {
-    PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg->mailwarn->emailfreq=%d\n",data->emailfreq);
-    return;
-  }
-  if (which<0 || which>=SMARTD_NMAIL || sizeof(whichfail)!=SMARTD_NMAIL*sizeof(char *)) {
-    PrintOut(LOG_CRIT,"Contact " PACKAGE_BUGREPORT "; internal error in MailWarning(): which=%d, size=%d\n",
-             which, (int)sizeof(whichfail));
-    return;
-  }
-  
-  // Return if a single warning mail has been sent.
-  if ((data->emailfreq==1) && mail->logged)
-    return;
-
-  // Return if this is an email test and one has already been sent.
-  if (which == 0 && mail->logged)
-    return;
-  
-  // To decide if to send mail, we need to know what time it is.
-  epoch=time(NULL);
-
-  // Return if less than one day has gone by
-  if (data->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day))
-    return;
-
-  // Return if less than 2^(logged-1) days have gone by
-  if (data->emailfreq==3 && mail->logged){
-    days=0x01<<(mail->logged-1);
-    days*=day;
-    if  (epoch<(mail->lastsent+days))
-      return;
-  }
-
-  // record the time of this mail message, and the first mail message
-  if (!mail->logged)
-    mail->firstsent=epoch;
-  mail->lastsent=epoch;
-  
-  // get system host & domain names (not null terminated if length=MAX) 
-#ifdef HAVE_GETHOSTNAME
-  if (gethostname(hostname, 256))
-    strcpy(hostname, unknown);
-  else {
-    char *p=NULL;
-    hostname[255]='\0';
-    p = dnsdomain(hostname);
-    if (p && *p) {
-      strncpy(domainname, p, 255);
-      domainname[255]='\0';
-    } else
-      strcpy(domainname, unknown);
-  }
-#else
-  strcpy(hostname, unknown);
-  strcpy(domainname, unknown);
-#endif
-  
-#ifdef HAVE_GETDOMAINNAME
-  if (getdomainname(nisdomain, 256))
-    strcpy(nisdomain, unknown);
-  else
-    nisdomain[255]='\0';
-#else
-  strcpy(nisdomain, unknown);
-#endif
-  
-  // print warning string into message
-  va_start(ap, fmt);
-  vsnprintf(message, 256, fmt, ap);
-  va_end(ap);
-
-  // appropriate message about further information
-  additional[0]=original[0]=further[0]='\0';
-  if (which) {
-    sprintf(further,"You can also use the smartctl utility for further investigation.\n");
-
-    switch (data->emailfreq){
-    case 1:
-      sprintf(additional,"No additional email messages about this problem will be sent.\n");
-      break;
-    case 2:
-      sprintf(additional,"Another email message will be sent in 24 hours if the problem persists.\n");
-      break;
-    case 3:
-      sprintf(additional,"Another email message will be sent in %d days if the problem persists\n",
-              (0x01)<<mail->logged);
-      break;
-    }
-    if (data->emailfreq>1 && mail->logged){
-      dateandtimezoneepoch(dates, mail->firstsent);
-      sprintf(original,"The original email about this issue was sent at %s\n", dates);
-    }
-  }
-  
-  snprintf(subject, 256,"SMART error (%s) detected on host: %s", whichfail[which], hostname);
-
-  // If the user has set cfg->emailcmdline, use that as mailer, else "mail" or "mailx".
-  if (!executable)
-#ifdef DEFAULT_MAILER
-    executable = DEFAULT_MAILER ;
-#else
-#ifndef _WIN32
-    executable = "mail";
-#else
-    executable = "blat"; // http://blat.sourceforge.net/
-#endif
-#endif
-
-  // make a private copy of address with commas replaced by spaces
-  // to separate recipients
-  if (address) {
-    address=CustomStrDup(data->address, 1, __LINE__, filenameandversion);
-#ifndef _WIN32 // blat mailer needs comma
-    {
-      char *comma=address;
-      while ((comma=strchr(comma, ',')))
-        *comma=' ';
-    }
-#endif
-  }
-
-  // Export information in environment variables that will be useful
-  // for user scripts
-  exportenv(environ_strings[0], "SMARTD_MAILER", executable);
-  exportenv(environ_strings[1], "SMARTD_MESSAGE", message);
-  exportenv(environ_strings[2], "SMARTD_SUBJECT", subject);
-  dateandtimezoneepoch(dates, mail->firstsent);
-  exportenv(environ_strings[3], "SMARTD_TFIRST", dates);
-  snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent);
-  exportenv(environ_strings[4], "SMARTD_TFIRSTEPOCH", dates);
-  exportenv(environ_strings[5], "SMARTD_FAILTYPE", whichfail[which]);
-  if (address)
-    exportenv(environ_strings[6], "SMARTD_ADDRESS", address);
-  exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg->name);
-
-  switch (cfg->controller_type) {
-  case CONTROLLER_3WARE_678K:
-  case CONTROLLER_3WARE_9000_CHAR:
-  case CONTROLLER_3WARE_678K_CHAR:
-    {
-      char *s,devicetype[16];
-      sprintf(devicetype, "3ware,%d", cfg->controller_port-1);
-      exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype);
-      if ((s=strchr(cfg->name, ' ')))
-       *s='\0';
-      exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-      if (s)
-       *s=' ';
-    }
-    break;
-  case CONTROLLER_ATA:
-    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "ata");
-    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-    break;
-  case CONTROLLER_MARVELL_SATA:
-    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "marvell");
-    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-    break;
-  case CONTROLLER_SCSI:
-    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "scsi");
-    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-  }
-
-  snprintf(fullmessage, 1024,
-             "This email was generated by the smartd daemon running on:\n\n"
-             "   host name: %s\n"
-             "  DNS domain: %s\n"
-             "  NIS domain: %s\n\n"
-             "The following warning/error was logged by the smartd daemon:\n\n"
-             "%s\n\n"
-             "For details see host's SYSLOG (default: /var/log/messages).\n\n"
-             "%s%s%s",
-            hostname, domainname, nisdomain, message, further, original, additional);
-  exportenv(environ_strings[10], "SMARTD_FULLMESSAGE", fullmessage);
-
-  // now construct a command to send this as EMAIL
-#ifndef _WIN32
-  if (address)
-    snprintf(command, 2048, 
-             "$SMARTD_MAILER -s '%s' %s 2>&1 << \"ENDMAIL\"\n"
-            "%sENDMAIL\n", subject, address, fullmessage);
-  else
-    snprintf(command, 2048, "%s 2>&1", executable);
-  
-  // tell SYSLOG what we are about to do...
-  newadd=address?address:"<nomailer>";
-  newwarn=which?"Warning via":"Test of";
-
-  PrintOut(LOG_INFO,"%s %s to %s ...\n",
-           which?"Sending warning via":"Executing test of", executable, newadd);
-  
-  // issue the command to send mail or to run the user's executable
-  errno=0;
-  if (!(pfp=popen(command, "r")))
-    // failed to popen() mail process
-    PrintOut(LOG_CRIT,"%s %s to %s: failed (fork or pipe failed, or no memory) %s\n", 
-            newwarn,  executable, newadd, errno?strerror(errno):"");
-  else {
-    // pipe suceeded!
-    int len, status;
-    char buffer[EBUFLEN];
-
-    // if unexpected output on stdout/stderr, null terminate, print, and flush
-    if ((len=fread(buffer, 1, EBUFLEN, pfp))) {
-      int count=0;
-      int newlen = len<EBUFLEN ? len : EBUFLEN-1;
-      buffer[newlen]='\0';
-      PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%s%d bytes) to STDOUT/STDERR: \n%s\n", 
-              newwarn, executable, newadd, len!=newlen?"here truncated to ":"", newlen, buffer);
-      
-      // flush pipe if needed
-      while (fread(buffer, 1, EBUFLEN, pfp) && count<EBUFLEN)
-       count++;
-
-      // tell user that pipe was flushed, or that something is really wrong
-      if (count && count<EBUFLEN)
-       PrintOut(LOG_CRIT,"%s %s to %s: flushed remaining STDOUT/STDERR\n", 
-                newwarn, executable, newadd);
-      else if (count)
-       PrintOut(LOG_CRIT,"%s %s to %s: more than 1 MB STDOUT/STDERR flushed, breaking pipe\n", 
-                newwarn, executable, newadd);
-    }
-    
-    // if something went wrong with mail process, print warning
-    errno=0;
-    if (-1==(status=pclose(pfp)))
-      PrintOut(LOG_CRIT,"%s %s to %s: pclose(3) failed %s\n", newwarn, executable, newadd,
-              errno?strerror(errno):"");
-    else {
-      // mail process apparently succeeded. Check and report exit status
-      int status8;
-
-      if (WIFEXITED(status)) {
-       // exited 'normally' (but perhaps with nonzero status)
-       status8=WEXITSTATUS(status);
-       
-       if (status8>128)  
-         PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d) perhaps caught signal %d [%s]\n", 
-                  newwarn, executable, newadd, status, status8, status8-128, strsignal(status8-128));
-       else if (status8)  
-         PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d)\n", 
-                  newwarn, executable, newadd, status, status8);
-       else
-         PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
-      }
-      
-      if (WIFSIGNALED(status))
-       PrintOut(LOG_INFO,"%s %s to %s: exited because of uncaught signal %d [%s]\n", 
-                newwarn, executable, newadd, WTERMSIG(status), strsignal(WTERMSIG(status)));
-      
-      // this branch is probably not possible. If subprocess is
-      // stopped then pclose() should not return.
-      if (WIFSTOPPED(status)) 
-       PrintOut(LOG_CRIT,"%s %s to %s: process STOPPED because it caught signal %d [%s]\n",
-                newwarn, executable, newadd, WSTOPSIG(status), strsignal(WSTOPSIG(status)));
-      
-    }
-  }
-  
-#else // _WIN32
-
-  // No "here-documents" on Windows, so must use separate commandline and stdin
-  command[0] = stdinbuf[0] = 0;
-  boxtype = -1; boxmsgoffs = 0;
-  newadd = "<nomailer>";
-  if (address) {
-    // address "[sys]msgbox ..." => show warning (also) as [system modal ]messagebox
-    int addroffs = (!strncmp(address, "sys", 3) ? 3 : 0);
-    if (!strncmp(address+addroffs, "msgbox", 6) && (!address[addroffs+6] || address[addroffs+6] == ',')) {
-      boxtype = (addroffs > 0 ? 1 : 0);
-      addroffs += 6;
-      if (address[addroffs])
-        addroffs++;
-    }
-    else
-      addroffs = 0;
-
-    if (address[addroffs]) {
-      // Use "blat" parameter syntax (TODO: configure via -M for other mailers)
-      snprintf(command, sizeof(command),
-               "%s - -q -subject \"%s\" -to \"%s\"",
-               executable, subject, address+addroffs);
-      newadd = address+addroffs;
-    }
-    // Message for mail [0...] and messagebox [boxmsgoffs...]
-    snprintf(stdinbuf, sizeof(stdinbuf),
-             "This email was generated by the smartd daemon running on:\n\n"
-             "   host name: %s\n"
-             "  DNS domain: %s\n"
-//           "  NIS domain: %s\n"
-             "\n%n"
-             "The following warning/error was logged by the smartd daemon:\n\n"
-             "%s\n\n"
-             "For details see the event log or log file of smartd.\n\n"
-             "%s%s%s"
-             "\n",
-             hostname, /*domainname, */ nisdomain, &boxmsgoffs, message, further, original, additional);
-  }
-  else
-    snprintf(command, sizeof(command), "%s", executable);
-
-  newwarn=which?"Warning via":"Test of";
-  if (boxtype >= 0) {
-    // show message box
-    daemon_messagebox(boxtype, subject, stdinbuf+boxmsgoffs);
-    PrintOut(LOG_INFO,"%s message box\n", newwarn);
-  }
-  if (command[0]) {
-    char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog()
-    int rc;
-    // run command
-    PrintOut(LOG_INFO,"%s %s to %s ...\n",
-             (which?"Sending warning via":"Executing test of"), executable, newadd);
-    rc = daemon_spawn(command, stdinbuf, strlen(stdinbuf), stdoutbuf, sizeof(stdoutbuf));
-    if (rc >= 0 && stdoutbuf[0])
-      PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%d bytes) to STDOUT/STDERR:\n%s\n",
-        newwarn, executable, newadd, strlen(stdoutbuf), stdoutbuf);
-    if (rc != 0)
-      PrintOut(LOG_CRIT,"%s %s to %s: failed, exit status %d\n",
-        newwarn, executable, newadd, rc);
-    else
-      PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
-  }
-
-#endif // _WIN32
-
-  // increment mail sent counter
-  mail->logged++;
-  
-  // free copy of address (without commas)
-  address=FreeNonZero(address, -1, __LINE__, filenameandversion);
-
-  return;
-}
-
-// Printing function for watching ataprint commands, or losing them
-// [From GLIBC Manual: Since the prototype doesn't specify types for
-// optional arguments, in a call to a variadic function the default
-// argument promotions are performed on the optional argument
-// values. This means the objects of type char or short int (whether
-// signed or not) are promoted to either int or unsigned int, as
-// appropriate.]
-void pout(char *fmt, ...){
-  va_list ap;
-
-  // get the correct time in syslog()
-  FixGlibcTimeZoneBug();
-  // initialize variable argument list 
-  va_start(ap,fmt);
-  // in debug==1 mode we will print the output from the ataprint.o functions!
-  if (debugmode && debugmode!=2)
-#ifdef _WIN32
-   if (facility == LOG_LOCAL1) // logging to stdout
-    vfprintf(stderr,fmt,ap);
-   else   
-#endif
-    vprintf(fmt,ap);
-  // in debug==2 mode we print output from knowndrives.o functions
-  else if (debugmode==2 || con->reportataioctl || con->reportscsiioctl || con->controller_port) {
-    openlog("smartd", LOG_PID, facility);
-    vsyslog(LOG_INFO, fmt, ap);
-    closelog();
-  }
-  va_end(ap);
-  fflush(NULL);
-  return;
-}
-
-// This function prints either to stdout or to the syslog as needed.
-// This function is also used by utility.c to report LOG_CRIT errors.
-void PrintOut(int priority,char *fmt, ...){
-  va_list ap;
-  
-  // get the correct time in syslog()
-  FixGlibcTimeZoneBug();
-  // initialize variable argument list 
-  va_start(ap,fmt);
-  if (debugmode) 
-#ifdef _WIN32
-   if (facility == LOG_LOCAL1) // logging to stdout
-    vfprintf(stderr,fmt,ap);
-   else   
-#endif
-    vprintf(fmt,ap);
-  else {
-    openlog("smartd", LOG_PID, facility);
-    vsyslog(priority,fmt,ap);
-    closelog();
-  }
-  va_end(ap);
-  return;
-}
-
-// Forks new process, closes ALL file descriptors, redirects stdin,
-// stdout, and stderr.  Not quite daemon().  See
-// http://www.iar.unlp.edu.ar/~fede/revistas/lj/Magazines/LJ47/2335.html
-// for a good description of why we do things this way.
-void DaemonInit(){
-#ifndef _WIN32
-  pid_t pid;
-  int i;  
-
-  // flush all buffered streams.  Else we might get two copies of open
-  // streams since both parent and child get copies of the buffers.
-  fflush(NULL);
-  
-  if ((pid=fork()) < 0) {
-    // unable to fork!
-    PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
-    EXIT(EXIT_STARTUP);
-  }
-  else if (pid)
-    // we are the parent process -- exit cleanly
-    EXIT(0);
-  
-  // from here on, we are the child process.
-  setsid();
-
-  // Fork one more time to avoid any possibility of having terminals
-  if ((pid=fork()) < 0) {
-    // unable to fork!
-    PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
-    EXIT(EXIT_STARTUP);
-  }
-  else if (pid)
-    // we are the parent process -- exit cleanly
-    EXIT(0);
-
-  // Now we are the child's child...
-
-  // close any open file descriptors
-  for (i=getdtablesize();i>=0;--i)
-    close(i);
-  
-#ifdef __CYGWIN__
-  // Cygwin's setsid() does not detach the process from Windows console
-  FreeConsole();
-#endif // __CYGWIN__
-
-  // redirect any IO attempts to /dev/null for stdin
-  i=open("/dev/null",O_RDWR);
-  // stdout
-  dup(i);
-  // stderr
-  dup(i);
-  umask(0);
-  chdir("/");
-  
-  PrintOut(LOG_INFO, "smartd has fork()ed into background mode. New PID=%d.\n", (int)getpid());
-
-#else // _WIN32
-
-  // No fork() on native Win32
-  // Detach this process from console
-  fflush(NULL);
-  if (daemon_detach("smartd")) {
-    PrintOut(LOG_CRIT,"smartd unable to detach from console!\n");
-    EXIT(EXIT_STARTUP);
-  }
-  // stdin/out/err now closed if not redirected
-
-#endif // _WIN32
-  return;
-}
-
-// create a PID file containing the current process id
-void WritePidFile() {
-  if (pid_file) {
-    int error = 0;
-    pid_t pid = getpid();
-    mode_t old_umask;
-    FILE* fp; 
-
-#ifndef __CYGWIN__
-    old_umask = umask(0077); // rwx------
-#else
-    // Cygwin: smartd service runs on system account, ensure PID file can be read by admins
-    old_umask = umask(0033); // rwxr--r--
-#endif
-    fp = fopen(pid_file, "w");
-    umask(old_umask);
-    if (fp == NULL) {
-      error = 1;
-    } else if (fprintf(fp, "%d\n", (int)pid) <= 0) {
-      error = 1;
-    } else if (fclose(fp) != 0) {
-      error = 1;
-    }
-    if (error) {
-      PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file);
-      EXIT(EXIT_PID);
-    }
-    PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file, (int)pid);
-  }
-  return;
-}
-
-// Prints header identifying version of code and home
-void PrintHead(){
-#ifdef HAVE_GET_OS_VERSION_STR
-  const char * ver = get_os_version_str();
-#else
-  const char * ver = SMARTMONTOOLS_BUILD_HOST;
-#endif
-  PrintOut(LOG_INFO,"smartd version %s [%s] Copyright (C) 2002-6 Bruce Allen\n", PACKAGE_VERSION, ver);
-  PrintOut(LOG_INFO,"Home page is " PACKAGE_HOMEPAGE "\n\n");
-  return;
-}
-
-// prints help info for configuration file Directives
-void Directives() {
-  PrintOut(LOG_INFO,
-           "Configuration file (%s) Directives (after device name):\n"
-           "  -d TYPE Set the device type: ata, scsi, marvell, removable, 3ware,N\n"
-           "  -T TYPE Set the tolerance to one of: normal, permissive\n"
-           "  -o VAL  Enable/disable automatic offline tests (on/off)\n"
-           "  -S VAL  Enable/disable attribute autosave (on/off)\n"
-           "  -n MODE No check if: never[,q], sleep[,q], standby[,q], idle[,q]\n"
-           "  -H      Monitor SMART Health Status, report if failed\n"
-           "  -s REG  Do Self-Test at time(s) given by regular expression REG\n"
-           "  -l TYPE Monitor SMART log.  Type is one of: error, selftest\n"
-           "  -f      Monitor 'Usage' Attributes, report failures\n"
-           "  -m ADD  Send email warning to address ADD\n"
-           "  -M TYPE Modify email warning behavior (see man page)\n"
-           "  -p      Report changes in 'Prefailure' Attributes\n"
-           "  -u      Report changes in 'Usage' Attributes\n"
-           "  -t      Equivalent to -p and -u Directives\n"
-           "  -r ID   Also report Raw values of Attribute ID with -p, -u or -t\n"
-           "  -R ID   Track changes in Attribute ID Raw value with -p, -u or -t\n"
-           "  -i ID   Ignore Attribute ID for -f Directive\n"
-           "  -I ID   Ignore Attribute ID for -p, -u or -t Directive\n"
-          "  -C ID   Monitor Current Pending Sectors in Attribute ID\n"
-          "  -U ID   Monitor Offline Uncorrectable Sectors in Attribute ID\n"
-           "  -v N,ST Modifies labeling of Attribute N (see man page)  \n"
-           "  -P TYPE Drive-specific presets: use, ignore, show, showall\n"
-           "  -a      Default: -H -f -t -l error -l selftest -C 197 -U 198\n"
-           "  -F TYPE Firmware bug workaround: none, samsung, samsung2\n"
-           "   #      Comment: text after a hash sign is ignored\n"
-           "   \\      Line continuation character\n"
-           "Attribute ID is a decimal integer 1 <= ID <= 255\n"
-          "Use ID = 0 to turn off -C and/or -U Directives\n"
-           "Example: /dev/hda -a\n", 
-           configfile);
-  return;
-}
-
-/* Returns a pointer to a static string containing a formatted list of the valid
-   arguments to the option opt or NULL on failure. */
-const char *GetValidArgList(char opt) {
-  switch (opt) {
-  case 'c':
-    return "<FILE_NAME>, -";
-  case 's':
-    return "valid_regular_expression";
-  case 'l':
-    return "daemon, local0, local1, local2, local3, local4, local5, local6, local7";
-  case 'q':
-    return "nodev, errors, nodevstartup, never, onecheck, showtests";
-  case 'r':
-    return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
-  case 'p':
-    return "<FILE_NAME>";
-  case 'i':
-    return "<INTEGER_SECONDS>";
-  default:
-    return NULL;
-  }
-}
-
-/* prints help information for command syntax */
-void Usage (void){
-  PrintOut(LOG_INFO,"Usage: smartd [options]\n\n");
-#ifdef HAVE_GETOPT_LONG
-  PrintOut(LOG_INFO,"  -c NAME|-, --configfile=NAME|-\n");
-  PrintOut(LOG_INFO,"        Read configuration file NAME or stdin [default is %s]\n\n", configfile);
-  PrintOut(LOG_INFO,"  -d, --debug\n");
-  PrintOut(LOG_INFO,"        Start smartd in debug mode\n\n");
-  PrintOut(LOG_INFO,"  -D, --showdirectives\n");
-  PrintOut(LOG_INFO,"        Print the configuration file Directives and exit\n\n");
-  PrintOut(LOG_INFO,"  -h, --help, --usage\n");
-  PrintOut(LOG_INFO,"        Display this help and exit\n\n");
-  PrintOut(LOG_INFO,"  -i N, --interval=N\n");
-  PrintOut(LOG_INFO,"        Set interval between disk checks to N seconds, where N >= 10\n\n");
-  PrintOut(LOG_INFO,"  -l local[0-7], --logfacility=local[0-7]\n");
-#ifndef _WIN32
-  PrintOut(LOG_INFO,"        Use syslog facility local0 - local7 or daemon [default]\n\n");
-#else
-  PrintOut(LOG_INFO,"        Log to \"./smartd.log\", stdout, stderr [default is event log]\n\n");
-#endif
-  PrintOut(LOG_INFO,"  -p NAME, --pidfile=NAME\n");
-  PrintOut(LOG_INFO,"        Write PID file NAME\n\n");
-  PrintOut(LOG_INFO,"  -q WHEN, --quit=WHEN\n");
-  PrintOut(LOG_INFO,"        Quit on one of: %s\n\n", GetValidArgList('q'));
-  PrintOut(LOG_INFO,"  -r, --report=TYPE\n");
-  PrintOut(LOG_INFO,"        Report transactions for one of: %s\n\n", GetValidArgList('r'));
-#if defined(_WIN32) || defined(__CYGWIN__)
-  PrintOut(LOG_INFO,"  --service\n");
-  PrintOut(LOG_INFO,"        Running as windows service (see man page), install with:\n");
-#ifdef _WIN32
-  PrintOut(LOG_INFO,"          smartd install [options]\n");
-  PrintOut(LOG_INFO,"        Remove service with:\n");
-  PrintOut(LOG_INFO,"          smartd remove\n\n");
-#else
-  PrintOut(LOG_INFO,"          /etc/rc.d/init.d/smartd install [options]\n");
-  PrintOut(LOG_INFO,"        Remove service with:\n");
-  PrintOut(LOG_INFO,"          /etc/rc.d/init.d/smartd remove\n\n");
-#endif
-#endif // _WIN32 || __CYGWIN__
-  PrintOut(LOG_INFO,"  -V, --version, --license, --copyright\n");
-  PrintOut(LOG_INFO,"        Print License, Copyright, and version information\n");
-#else
-  PrintOut(LOG_INFO,"  -c NAME|-  Read configuration file NAME or stdin [default is %s]\n", configfile);
-  PrintOut(LOG_INFO,"  -d         Start smartd in debug mode\n");
-  PrintOut(LOG_INFO,"  -D         Print the configuration file Directives and exit\n");
-  PrintOut(LOG_INFO,"  -h         Display this help and exit\n");
-  PrintOut(LOG_INFO,"  -i N       Set interval between disk checks to N seconds, where N >= 10\n");
-  PrintOut(LOG_INFO,"  -l local?  Use syslog facility local0 - local7, or daemon\n");
-  PrintOut(LOG_INFO,"  -p NAME    Write PID file NAME\n");
-  PrintOut(LOG_INFO,"  -q WHEN    Quit on one of: %s\n", GetValidArgList('q'));
-  PrintOut(LOG_INFO,"  -r TYPE    Report transactions for one of: %s\n", GetValidArgList('r'));
-  PrintOut(LOG_INFO,"  -V         Print License, Copyright, and version information\n");
-#endif
-}
-
-// returns negative if problem, else fd>=0
-static int OpenDevice(char *device, char *mode, int scanning) {
-  int fd;
-  char *s=device;
-  
-  // If there is an ASCII "space" character in the device name,
-  // terminate string there.  This is for 3ware devices only.
-  if ((s=strchr(device,' ')))
-    *s='\0';
-
-  // open the device
-  fd = deviceopen(device, mode);
-
-  // if we removed a space, put it back in please
-  if (s)
-    *s=' ';
-
-  // if we failed to open the device, complain!
-  if (fd < 0) {
-
-    // For linux+devfs, a nonexistent device gives a strange error
-    // message.  This makes the error message a bit more sensible.
-    // If no debug and scanning - don't print errors
-    if (debugmode || !scanning) {
-      if (errno==ENOENT || errno==ENOTDIR)
-       errno=ENODEV;
-      
-      PrintOut(LOG_INFO,"Device: %s, %s, open() failed\n",
-              device, strerror(errno));
-    }
-    return -1;
-  }
-  // device opened sucessfully
-  return fd;
-}
-
-int CloseDevice(int fd, char *name){
-  if (deviceclose(fd)){
-    PrintOut(LOG_INFO,"Device: %s, %s, close(%d) failed\n", name, strerror(errno), fd);
-    return 1;
-  }
-  // device sucessfully closed
-  return 0;
-}
-
-// returns <0 on failure
-int ATAErrorCount(int fd, char *name){
-  struct ata_smart_errorlog log;
-  
-  if (-1==ataReadErrorLog(fd,&log)){
-    PrintOut(LOG_INFO,"Device: %s, Read SMART Error Log Failed\n",name);
-    return -1;
-  }
-  
-  // return current number of ATA errors
-  return log.error_log_pointer?log.ata_error_count:0;
-}
-
-// returns <0 if problem.  Otherwise, bottom 8 bits are the self test
-// error count, and top bits are the power-on hours of the last error.
-int SelfTestErrorCount(int fd, char *name){
-  struct ata_smart_selftestlog log;
-
-  if (-1==ataReadSelfTestLog(fd,&log)){
-    PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name);
-    return -1;
-  }
-  
-  // return current number of self-test errors
-  return ataPrintSmartSelfTestlog(&log,0);
-}
-
-// scan to see what ata devices there are, and if they support SMART
-int ATADeviceScan(cfgfile *cfg, int scanning){
-  int fd, supported=0;
-  struct ata_identify_device drive;
-  char *name=cfg->name;
-  int retainsmartdata=0;
-  int retid;
-  char *mode;
-  
-  // should we try to register this as an ATA device?
-  switch (cfg->controller_type) {
-  case CONTROLLER_ATA:
-  case CONTROLLER_3WARE_678K:
-  case CONTROLLER_MARVELL_SATA:
-  case CONTROLLER_UNKNOWN:
-    mode="ATA";
-    break;
-  case CONTROLLER_3WARE_678K_CHAR:
-    mode="ATA_3WARE_678K";
-    break;
-  case CONTROLLER_3WARE_9000_CHAR:
-    mode="ATA_3WARE_9000";
-    break;
-  default:
-    // not a recognized ATA or SATA device.  We should never enter
-    // this branch.
-    return 1;
-  }
-  
-  // open the device
-  if ((fd=OpenDevice(name, mode, scanning))<0)
-    // device open failed
-    return 1;
-  PrintOut(LOG_INFO,"Device: %s, opened\n", name);
-  
-  // pass user settings on to low-level ATA commands
-  con->controller_port=cfg->controller_port;
-  con->controller_type=cfg->controller_type;
-  con->fixfirmwarebug = cfg->fixfirmwarebug;
-  
-  // Get drive identity structure
-  if ((retid=ataReadHDIdentity (fd,&drive))){
-    if (retid<0)
-      // Unable to read Identity structure
-      PrintOut(LOG_INFO,"Device: %s, not ATA, no IDENTIFY DEVICE Structure\n",name);
-    else
-      PrintOut(LOG_INFO,"Device: %s, packet devices [this device %s] not SMART capable\n",
-               name, packetdevicetype(retid-1));
-    CloseDevice(fd, name);
-    return 2; 
-  }
-
-  // Show if device in database, and use preset vendor attribute
-  // options unless user has requested otherwise.
-  if (cfg->ignorepresets)
-    PrintOut(LOG_INFO, "Device: %s, smartd database not searched (Directive: -P ignore).\n", name);
-  else {
-    // do whatever applypresets decides to do. Will allocate memory if
-    // cfg->attributedefs is needed.
-    if (applypresets(&drive, &cfg->attributedefs, con)<0)
-      PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name);
-    else
-      PrintOut(LOG_INFO, "Device: %s, found in smartd database.\n", name);
-    
-    // then save the correct state of the flag (applypresets may have changed it)
-    cfg->fixfirmwarebug = con->fixfirmwarebug;
-  }
-  
-  // If requested, show which presets would be used for this drive
-  if (cfg->showpresets) {
-    int savedebugmode=debugmode;
-    PrintOut(LOG_INFO, "Device %s: presets are:\n", name);
-    if (!debugmode)
-      debugmode=2;
-    showpresets(&drive);
-    debugmode=savedebugmode;
-  }
-
-  // see if drive supports SMART
-  supported=ataSmartSupport(&drive);
-  if (supported!=1) {
-    if (supported==0)
-      // drive does NOT support SMART
-      PrintOut(LOG_INFO,"Device: %s, lacks SMART capability\n",name);
-    else
-      // can't tell if drive supports SMART
-      PrintOut(LOG_INFO,"Device: %s, ATA IDENTIFY DEVICE words 82-83 don't specify if SMART capable.\n",name);
-  
-    // should we proceed anyway?
-    if (cfg->permissive){
-      PrintOut(LOG_INFO,"Device: %s, proceeding since '-T permissive' Directive given.\n",name);
-    }
-    else {
-      PrintOut(LOG_INFO,"Device: %s, to proceed anyway, use '-T permissive' Directive.\n",name);
-      CloseDevice(fd, name);
-      return 2;
-    }
-  }
-  
-  if (ataEnableSmart(fd)){
-    // Enable SMART command has failed
-    PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name);
-    CloseDevice(fd, name);
-    return 2; 
-  }
-  
-  // disable device attribute autosave...
-  if (cfg->autosave==1){
-    if (ataDisableAutoSave(fd))
-      PrintOut(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",name);
-    else
-      PrintOut(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",name);
-  }
-
-  // or enable device attribute autosave
-  if (cfg->autosave==2){
-    if (ataEnableAutoSave(fd))
-      PrintOut(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",name);
-    else
-      PrintOut(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",name);
-  }
-
-  // capability check: SMART status
-  if (cfg->smartcheck && ataSmartStatus2(fd)==-1){
-    PrintOut(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",name);
-    cfg->smartcheck=0;
-  }
-  
-  // capability check: Read smart values and thresholds.  Note that
-  // smart values are ALSO needed even if we ONLY want to know if the
-  // device is self-test log or error-log capable!  After ATA-5, this
-  // information was ALSO reproduced in the IDENTIFY DEVICE response,
-  // but sadly not for ATA-5.  Sigh.
-
-  // do we need to retain SMART data after returning from this routine?
-  retainsmartdata=cfg->usagefailed || cfg->prefail || cfg->usage;
-  
-  // do we need to get SMART data?
-  if (retainsmartdata || cfg->autoofflinetest || cfg->selftest || cfg->errorlog || cfg->pending!=DONT_MONITOR_UNC) {
-
-    unsigned char currentpending, offlinepending;
-
-    cfg->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values));
-    cfg->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt));
-    
-    if (!cfg->smartval || !cfg->smartthres){
-      PrintOut(LOG_CRIT,"Not enough memory to obtain SMART data\n");
-      EXIT(EXIT_NOMEM);
-    }
-    
-    if (ataReadSmartValues(fd,cfg->smartval) ||
-        ataReadSmartThresholds (fd,cfg->smartthres)){
-      PrintOut(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",name);
-      retainsmartdata=cfg->usagefailed=cfg->prefail=cfg->usage=0;
-      cfg->pending=DONT_MONITOR_UNC;
-    }
-    
-    // see if the necessary Attribute is there to monitor offline or
-    // current pending sectors
-    TranslatePending(cfg->pending, &currentpending, &offlinepending);
-    
-    if (currentpending && ATAReturnAttributeRawValue(currentpending, cfg->smartval)<0) {
-      PrintOut(LOG_INFO,"Device: %s, can't monitor Current Pending Sector count - no Attribute %d\n",
-              name, (int)currentpending);
-      cfg->pending &= 0xff00;
-      cfg->pending |= CUR_UNC_DEFAULT;
-    }
-    
-    if (offlinepending && ATAReturnAttributeRawValue(offlinepending, cfg->smartval)<0) {
-      PrintOut(LOG_INFO,"Device: %s, can't monitor Offline Uncorrectable Sector count  - no Attribute %d\n",
-              name, (int)offlinepending);
-      cfg->pending &= 0x00ff;
-      cfg->pending |= OFF_UNC_DEFAULT<<8;
-    }
-  }
-  
-  // enable/disable automatic on-line testing
-  if (cfg->autoofflinetest){
-    // is this an enable or disable request?
-    char *what=(cfg->autoofflinetest==1)?"disable":"enable";
-    if (!cfg->smartval)
-      PrintOut(LOG_INFO,"Device: %s, could not %s SMART Automatic Offline Testing.\n",name, what);
-    else {
-      // if command appears unsupported, issue a warning...
-      if (!isSupportAutomaticTimer(cfg->smartval))
-        PrintOut(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",name);
-      // ... but then try anyway
-      if ((cfg->autoofflinetest==1)?ataDisableAutoOffline(fd):ataEnableAutoOffline(fd))
-        PrintOut(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", name, what);
-      else
-        PrintOut(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", name, what);
-    }
-  }
-  
-  // capability check: self-test-log
-  if (cfg->selftest){
-    int retval;
-    
-    // start with service disabled, and re-enable it if all works OK
-    cfg->selftest=0;
-    cfg->selflogcount=0;
-    cfg->selfloghour=0;
-
-    if (!cfg->smartval)
-      PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log (SMART READ DATA failed); disabling -l selftest\n", name);
-    else if (!cfg->permissive && !isSmartTestLogCapable(cfg->smartval, &drive))
-      PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Self-Test log; disabling -l selftest (override with -T permissive Directive)\n", name);
-    else if ((retval=SelfTestErrorCount(fd, name))<0)
-      PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log; remove -l selftest Directive from smartd.conf\n", name);
-    else {
-      cfg->selftest=1;
-      cfg->selflogcount=SELFTEST_ERRORCOUNT(retval);
-      cfg->selfloghour =SELFTEST_ERRORHOURS(retval);
-    }
-  }
-  
-  // capability check: ATA error log
-  if (cfg->errorlog){
-    int val;
-
-    // start with service disabled, and re-enable it if all works OK
-    cfg->errorlog=0;
-    cfg->ataerrorcount=0;
-
-    if (!cfg->smartval)
-      PrintOut(LOG_INFO, "Device: %s, no SMART Error log (SMART READ DATA failed); disabling -l error\n", name);
-    else if (!cfg->permissive && !isSmartErrorLogCapable(cfg->smartval, &drive))
-      PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Error log; disabling -l error (override with -T permissive Directive)\n", name);
-    else if ((val=ATAErrorCount(fd, name))<0)
-      PrintOut(LOG_INFO, "Device: %s, no SMART Error log; remove -l error Directive from smartd.conf\n", name);
-    else {
-        cfg->errorlog=1;
-        cfg->ataerrorcount=val;
-    }
-  }
-  
-  // If we don't need to save SMART data, get rid of it now
-  if (!retainsmartdata) {
-    if (cfg->smartval) {
-      cfg->smartval=CheckFree(cfg->smartval, __LINE__,filenameandversion);
-      bytes-=sizeof(struct ata_smart_values);
-    }
-    if (cfg->smartthres) {
-      cfg->smartthres=CheckFree(cfg->smartthres, __LINE__,filenameandversion);
-      bytes-=sizeof(struct ata_smart_thresholds_pvt);
-    }
-  }
-
-  // capabilities check -- does it support powermode?
-  if (cfg->powermode) {
-    int powermode=ataCheckPowerMode(fd);
-    
-    if (-1 == powermode) {
-      PrintOut(LOG_CRIT, "Device: %s, no ATA CHECK POWER STATUS support, ignoring -n Directive\n", name);
-      cfg->powermode=0;
-    } 
-    else if (powermode!=0 && powermode!=0x80 && powermode!=0xff) {
-      PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
-              name, powermode);
-      cfg->powermode=0;
-    }
-  }
-
-  // If no tests available or selected, return
-  if (!(cfg->errorlog || cfg->selftest || cfg->smartcheck || 
-        cfg->usagefailed || cfg->prefail || cfg->usage)) {
-    CloseDevice(fd, name);
-    return 3;
-  }
-  
-  // Do we still have entries available?
-  while (numdevata>=atadevlist_max)
-    atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device");
-  
-  // register device
-  PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",name);
-  
-    // record number of device, type of device, increment device count
-  if (cfg->controller_type == CONTROLLER_UNKNOWN)
-    cfg->controller_type=CONTROLLER_ATA;
-  
-  // close file descriptor
-  CloseDevice(fd, name);
-  return 0;
-}
-
-// Returns 1 if device recognised as one we do not want to treat as a general
-// SCSI device. Also returns 1 if INQUIRY fails (all "SCSI" devices should
-// respond to INQUIRY). Otherwise returns 0 (i.e. normal SCSI device).
-static int SCSIFilterKnown(int fd, char * device)
-{
-  char req_buff[256];
-  char di_buff[256];
-  int req_len, avail_len, len;
-
-  memset(req_buff, 0, 96);
-  req_len = 36;
-  if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) {
-    /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
-    /* watch this spot ... other devices could lock up here */
-    req_len = 64;
-    if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) {
-      PrintOut(LOG_INFO, "Device: %s, failed on INQUIRY; skip device\n", device);
-      // device doesn't like INQUIRY commands
-      return 1;
-    }
-  }
-  avail_len = req_buff[4] + 5;
-  len = (avail_len < req_len) ? avail_len : req_len;
-  if (len >= 36) {
-    if (0 == strncmp(req_buff + 8, "3ware", 5) || 0 == strncmp(req_buff + 8, "AMCC", 4) ) {
-      PrintOut(LOG_INFO, "Device %s, please try adding '-d 3ware,N'\n", device);
-      PrintOut(LOG_INFO, "Device %s, you may need to replace %s with /dev/twaN or /dev/tweN\n", device, device);
-      return 1;
-    } else if ((len >= 42) && (0 == strncmp(req_buff + 36, "MVSATA", 6))) {
-      PrintOut(LOG_INFO, "Device %s, please try '-d marvell'\n", device);
-      return 1;
-    } else if ((avail_len >= 96) && (0 == strncmp(req_buff + 8, "ATA", 3))) {
-      /* <<<< This is Linux specific code to detect SATA disks using a
-              SCSI-ATA command translation layer. This may be generalized
-              later when the t10.org SAT project matures. >>>> */
-      req_len = 96;
-      memset(di_buff, 0, req_len);
-      if (scsiInquiryVpd(fd, 0x83, (unsigned char *)di_buff, req_len)) {
-        return 0;    // guess it is normal device
-      }
-      avail_len = ((di_buff[2] << 8) + di_buff[3]) + 4;
-      len = (avail_len < req_len) ? avail_len : req_len;
-      if (isLinuxLibAta((unsigned char *)di_buff, len)) {
-        PrintOut(LOG_INFO, "Device %s: SATA disks accessed via libata are "
-                 "supported by Linux kernel versions 2.6.15-rc1 and above.\n"
-                 "Try adding '-d ata' to the smartd.conf config file line.\n", device);
-        return 1;
-      }
-    }
-  }
-  return 0;
-}
-
-// on success, return 0. On failure, return >0.  Never return <0,
-// please.
-static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
-  int k, fd, err; 
-  char *device = cfg->name;
-  struct scsi_iec_mode_page iec;
-  UINT8  tBuf[64];
-  
-  // should we try to register this as a SCSI device?
-  switch (cfg->controller_type) {
-  case CONTROLLER_SCSI:
-  case CONTROLLER_UNKNOWN:
-    break;
-  default:
-    return 1;
-  }
-  
-  // open the device
-  if ((fd = OpenDevice(device, "SCSI", scanning)) < 0)
-    return 1;
-  PrintOut(LOG_INFO,"Device: %s, opened\n", device);
-
-  // early skip if device known and needs to be handled by some other
-  // device type (e.g. '-d 3ware,<n>')
-  if (SCSIFilterKnown(fd, device)) {
-    CloseDevice(fd, device);
-    return 2; 
-  }
-    
-  // check that device is ready for commands. IE stores its stuff on
-  // the media.
-  if ((err = scsiTestUnitReady(fd))) {
-    if (SIMPLE_ERR_NOT_READY == err)
-      PrintOut(LOG_INFO, "Device: %s, NOT READY (e.g. spun down); skip device\n", device);
-    else if (SIMPLE_ERR_NO_MEDIUM == err)
-      PrintOut(LOG_INFO, "Device: %s, NO MEDIUM present; skip device\n", device);
-    else if (SIMPLE_ERR_BECOMING_READY == err)
-      PrintOut(LOG_INFO, "Device: %s, BECOMING (but not yet) READY; skip device\n", device);
-    else
-      PrintOut(LOG_CRIT, "Device: %s, failed Test Unit Ready [err=%d]\n", device, err);
-    CloseDevice(fd, device);
-    return 2; 
-  }
-  
-  // Badly-conforming USB storage devices may fail this check.
-  // The response to the following IE mode page fetch (current and
-  // changeable values) is carefully examined. It has been found
-  // that various USB devices that malform the response will lock up
-  // if asked for a log page (e.g. temperature) so it is best to
-  // bail out now.
-  if (!(err = scsiFetchIECmpage(fd, &iec, cfg->modese_len)))
-    cfg->modese_len = iec.modese_len;
-  else if (SIMPLE_ERR_BAD_FIELD == err)
-    ;  /* continue since it is reasonable not to support IE mpage */
-  else { /* any other error (including malformed response) unreasonable */
-    PrintOut(LOG_INFO, 
-             "Device: %s, Bad IEC (SMART) mode page, err=%d, skip device\n", 
-             device, err);
-    CloseDevice(fd, device);
-    return 3;
-  }
-  
-  // N.B. The following is passive (i.e. it doesn't attempt to turn on
-  // smart if it is off). This may change to be the same as the ATA side.
-  if (!scsi_IsExceptionControlEnabled(&iec)) {
-    PrintOut(LOG_INFO, "Device: %s, IE (SMART) not enabled, skip device\n"
-                      "Try 'smartctl -s on %s' to turn on SMART features\n", 
-                        device, device);
-    CloseDevice(fd, device);
-    return 3;
-  }
-  
-  // Device exists, and does SMART.  Add to list (allocating more space if needed)
-  while (numdevscsi >= scsidevlist_max)
-    scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device");
-  
-  // Flag that certain log pages are supported (information may be
-  // available from other sources).
-  if (0 == scsiLogSense(fd, SUPPORTED_LPAGES, tBuf, sizeof(tBuf), 0)) {
-    for (k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) {
-      switch (tBuf[k]) { 
-      case TEMPERATURE_LPAGE:
-        cfg->TempPageSupported = 1;
-        break;
-      case IE_LPAGE:
-        cfg->SmartPageSupported = 1;
-        break;
-      default:
-        break;
-      }
-    }   
-  }
-  
-  // record type of device
-  cfg->controller_type = CONTROLLER_SCSI;
-  
-  // get rid of allocated memory only needed for ATA devices.  These
-  // might have been allocated if the user specified Ignore options or
-  // other ATA-only Attribute-specific options on the DEVICESCAN line.
-  cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion);
-  cfg->attributedefs   = FreeNonZero(cfg->attributedefs,   MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion);
-  cfg->smartval        = FreeNonZero(cfg->smartval,        sizeof(struct ata_smart_values),__LINE__,filenameandversion);
-  cfg->smartthres      = FreeNonZero(cfg->smartthres,      sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion);
-  
-  // Check if scsiCheckIE() is going to work
-  {
-    UINT8 asc = 0;
-    UINT8 ascq = 0;
-    UINT8 currenttemp = 0;
-    UINT8 triptemp = 0;
-    
-    if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
-                    &asc, &ascq, &currenttemp, &triptemp)) {
-      PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device);
-      cfg->SuppressReport = 1;
-    }
-  }
-  
-  // capability check: self-test-log
-  if (cfg->selftest){
-    int retval=scsiCountFailedSelfTests(fd, 0);
-    if (retval<0) {
-      // no self-test log, turn off monitoring
-      PrintOut(LOG_INFO, "Device: %s, does not support SMART Self-Test Log.\n", device);
-      cfg->selftest=0;
-      cfg->selflogcount=0;
-      cfg->selfloghour=0;
-    }
-    else {
-      // register starting values to watch for changes
-      cfg->selflogcount=SELFTEST_ERRORCOUNT(retval);
-      cfg->selfloghour =SELFTEST_ERRORHOURS(retval);
-    }
-  }
-  
-  // disable autosave (set GLTSD bit)
-  if (cfg->autosave==1){
-    if (scsiSetControlGLTSD(fd, 1, cfg->modese_len))
-      PrintOut(LOG_INFO,"Device: %s, could not disable autosave (set GLTSD bit).\n",device);
-    else
-      PrintOut(LOG_INFO,"Device: %s, disabled autosave (set GLTSD bit).\n",device);
-  }
-
-  // or enable autosave (clear GLTSD bit)
-  if (cfg->autosave==2){
-    if (scsiSetControlGLTSD(fd, 0, cfg->modese_len))
-      PrintOut(LOG_INFO,"Device: %s, could not enable autosave (clear GLTSD bit).\n",device);
-    else
-      PrintOut(LOG_INFO,"Device: %s, enabled autosave (cleared GLTSD bit).\n",device);
-  }
-  
-  // tell user we are registering device
-  PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device);
-  
-  // close file descriptor
-  CloseDevice(fd, device);
-  return 0;
-}
-
-// We compare old and new values of the n'th attribute.  Note that n
-// is NOT the attribute ID number.. If (Normalized & Raw) equal,
-// then return 0, else nonzero.
-int  ATACompareValues(changedattribute_t *delta,
-                            struct ata_smart_values *new,
-                            struct ata_smart_values *old,
-                            struct ata_smart_thresholds_pvt *thresholds,
-                            int n, char *name){
-  struct ata_smart_attribute *now,*was;
-  struct ata_smart_threshold_entry *thre;
-  unsigned char oldval,newval;
-  int sameraw;
-
-  // check that attribute number in range, and no null pointers
-  if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !new || !old || !thresholds)
-    return 0;
-  
-  // pointers to disk's values and vendor's thresholds
-  now=new->vendor_attributes+n;
-  was=old->vendor_attributes+n;
-  thre=thresholds->thres_entries+n;
-
-  // consider only valid attributes
-  if (!now->id || !was->id || !thre->id)
-    return 0;
-  
-  
-  // issue warning if they don't have the same ID in all structures:
-  if ( (now->id != was->id) || (now->id != thre->id) ){
-    PrintOut(LOG_INFO,"Device: %s, same Attribute has different ID numbers: %d = %d = %d\n",
-             name, (int)now->id, (int)was->id, (int)thre->id);
-    return 0;
-  }
-
-  // new and old values of Normalized Attributes
-  newval=now->current;
-  oldval=was->current;
-
-  // See if the RAW values are unchanged (ie, the same)
-  if (memcmp(now->raw, was->raw, 6))
-    sameraw=0;
-  else
-    sameraw=1;
-  
-  // if any values out of the allowed range, or if the values haven't
-  // changed, return 0
-  if (!newval || !oldval || newval>0xfe || oldval>0xfe || (oldval==newval && sameraw))
-    return 0;
-  
-  // values have changed.  Construct output and return
-  delta->newval=newval;
-  delta->oldval=oldval;
-  delta->id=now->id;
-  delta->prefail=ATTRIBUTE_FLAGS_PREFAILURE(now->flags);
-  delta->sameraw=sameraw;
-
-  return 1;
-}
-
-// This looks to see if the corresponding bit of the 32 bytes is set.
-// This wastes a few bytes of storage but eliminates all searching and
-// sorting functions! Entry is ZERO <==> the attribute ON. Calling
-// with set=0 tells you if the attribute is being tracked or not.
-// Calling with set=1 turns the attribute OFF.
-int IsAttributeOff(unsigned char attr, unsigned char **datap, int set, int which, int whatline){
-  unsigned char *data;
-  int loc=attr>>3;
-  int bit=attr & 0x07;
-  unsigned char mask=0x01<<bit;
-
-  if (which>=NMONITOR || which < 0){
-    PrintOut(LOG_CRIT, "Internal error in IsAttributeOff() at line %d of file %s (which=%d)\n%s",
-             whatline, filenameandversion, which, reportbug);
-    EXIT(EXIT_BADCODE);
-  }
-
-  if (*datap == NULL){
-    // NULL data implies Attributes are ON...
-    if (!set)
-      return 0;
-    
-    // we are writing
-    if (!(*datap=(unsigned char *)Calloc(NMONITOR*32, 1))){
-      PrintOut(LOG_CRIT,"No memory to create monattflags\n");
-      EXIT(EXIT_NOMEM);
-    }
-  }
-  
-  // pointer to the 256 bits that we need
-  data=*datap+which*32;
-
-  // attribute zero is always OFF
-  if (!attr)
-    return 1;
-
-  if (!set)
-    return (data[loc] & mask);
-  
-  data[loc]|=mask;
-
-  // return value when setting has no sense
-  return 0;
-}
-
-// If the self-test log has got more self-test errors (or more recent
-// self-test errors) recorded, then notify user.
-void CheckSelfTestLogs(cfgfile *cfg, int new){
-  char *name=cfg->name;
-
-  if (new<0)
-    // command failed
-    MailWarning(cfg, 8, "Device: %s, Read SMART Self-Test Log Failed", name);
-  else {      
-    // old and new error counts
-    int oldc=cfg->selflogcount;
-    int newc=SELFTEST_ERRORCOUNT(new);
-    
-    // old and new error timestamps in hours
-    int oldh=cfg->selfloghour;
-    int newh=SELFTEST_ERRORHOURS(new);
-    
-    if (oldc<newc) {
-      // increase in error count
-      PrintOut(LOG_CRIT, "Device: %s, Self-Test Log error count increased from %d to %d\n",
-               name, oldc, newc);
-      MailWarning(cfg, 3, "Device: %s, Self-Test Log error count increased from %d to %d",
-                   name, oldc, newc);
-    } else if (oldh!=newh) {
-      // more recent error
-      // a 'more recent' error might actually be a smaller hour number,
-      // if the hour number has wrapped.
-      // There's still a bug here.  You might just happen to run a new test
-      // exactly 32768 hours after the previous failure, and have run exactly
-      // 20 tests between the two, in which case smartd will miss the
-      // new failure.
-      PrintOut(LOG_CRIT, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
-               name, newh);
-      MailWarning(cfg, 3, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
-                   name, newh);
-    }
-    
-    // Needed since self-test error count may DECREASE.  Hour might
-    // also have changed.
-    cfg->selflogcount= newc;
-    cfg->selfloghour = newh;
-  }
-  return;
-}
-
-// returns 1 if time to do test of type testtype, 0 if not time to do
-// test, < 0 if error
-int DoTestNow(cfgfile *cfg, char testtype, time_t testtime) {
-  // start by finding out the time:
-  struct tm *timenow;
-  time_t epochnow;
-  char matchpattern[16];
-  regmatch_t substring;
-  int weekday, length;
-  unsigned short hours;
-  testinfo *dat=cfg->testdata;
-
-  // check that self-testing has been requested
-  if (!dat)
-    return 0;
-  
-  // since we are about to call localtime(), be sure glibc is informed
-  // of any timezone changes we make.
-  if (!testtime)
-    FixGlibcTimeZoneBug();
-  
-  // construct pattern containing the month, day of month, day of
-  // week, and hour
-  epochnow = (!testtime ? time(NULL) : testtime);
-  timenow=localtime(&epochnow);
-  
-  // tm_wday is 0 (Sunday) to 6 (Saturday).  We use 1 (Monday) to 7
-  // (Sunday).
-  weekday=timenow->tm_wday?timenow->tm_wday:7;
-  sprintf(matchpattern, "%c/%02d/%02d/%1d/%02d", testtype, timenow->tm_mon+1, 
-          timenow->tm_mday, weekday, timenow->tm_hour);
-  
-  // if no match, we are done
-  if (regexec(&(dat->cregex), matchpattern, 1, &substring, 0))
-    return 0;
-  
-  // must match the ENTIRE type/date/time string
-  length=strlen(matchpattern);
-  if (substring.rm_so!=0 || substring.rm_eo!=length)
-    return 0;
-  
-  // never do a second test in the same hour as another test (the % 7 ensures
-  // that the RHS will never be greater than 65535 and so will always fit into
-  // an unsigned short)
-  hours=1+timenow->tm_hour+24*(timenow->tm_yday+366*(timenow->tm_year % 7));
-  if (hours==dat->hour) {
-    if (!testtime && testtype!=dat->testtype)
-      PrintOut(LOG_INFO, "Device: %s, did test of type %c in current hour, skipping test of type %c\n",
-              cfg->name, dat->testtype, testtype);
-    return 0;
-  }
-  
-  // save time and type of the current test; we are ready to do a test
-  dat->hour=hours;
-  dat->testtype=testtype;
-  return 1;
-}
-
-// Print a list of future tests.
-void PrintTestSchedule(cfgfile **atadevices, cfgfile **scsidevices){
-  int i, t;
-  cfgfile * cfg;
-  char datenow[DATEANDEPOCHLEN], date[DATEANDEPOCHLEN];
-  time_t now; long seconds;
-  int numdev = numdevata+numdevscsi;
-  typedef int cnt_t[4];
-  cnt_t * testcnts; // testcnts[numdev][4]
-  if (numdev <= 0)
-    return;
-  testcnts = calloc(numdev, sizeof(testcnts[0]));
-  if (!testcnts)
-    return;
-
-  PrintOut(LOG_INFO, "\nNext scheduled self tests (at most 5 of each type per device):\n");
-
-  // FixGlibcTimeZoneBug(); // done in PrintOut()
-  now=time(NULL);
-  dateandtimezoneepoch(datenow, now);
-  for (seconds=0; seconds<3600L*24*90; seconds+=checktime) {
-    // Check for each device whether a test will be run
-    time_t testtime = now + seconds;
-    for (i=0; i<numdev; i++) {
-      cfg = (i<numdevata? atadevices[i] : scsidevices[i-numdevata]);
-      for (t=0; t<(i<numdevata?4:2); t++) {
-        char testtype = "LSCO"[t];
-        if (DoTestNow(cfg, testtype, testtime)) {
-          // Report at most 5 tests of each type
-          if (++testcnts[i][t] <= 5) {
-            dateandtimezoneepoch(date, testtime);
-            PrintOut(LOG_INFO, "Device: %s, will do test %d of type %c at %s\n", cfg->name,
-              testcnts[i][t], testtype, date);
-          }
-        }
-      }
-    }
-  }
-
-  // Report totals
-  dateandtimezoneepoch(date, now+seconds);
-  PrintOut(LOG_INFO, "\nTotals [%s - %s]:\n", datenow, date);
-  for (i=0; i<numdev; i++) {
-    cfg = (i<numdevata? atadevices[i] : scsidevices[i-numdevata]);
-    for (t=0; t<(i<numdevata?4:2); t++) {
-      PrintOut(LOG_INFO, "Device: %s, will do %3d test%s of type %c\n", cfg->name, testcnts[i][t],
-        (testcnts[i][t]==1?"":"s"), "LSCO"[t]);
-    }
-  }
-
-  free(testcnts);
-}
-
-// Return zero on success, nonzero on failure. Perform offline (background)
-// short or long (extended) self test on given scsi device.
-int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) {
-  int retval = 0;
-  char *testname = NULL;
-  char *name = cfg->name;
-  int inProgress;
-
-  if (scsiSelfTestInProgress(fd, &inProgress)) {
-    PrintOut(LOG_CRIT, "Device: %s, does not support Self-Tests\n", name);
-    cfg->testdata->not_cap_short=cfg->testdata->not_cap_long=1;
-    return 1;
-  }
-
-  if (1 == inProgress) {
-    PrintOut(LOG_INFO, "Device: %s, skip since Self-Test already in "
-             "progress.\n", name);
-    return 1;
-  }
-
-  switch (testtype) {
-  case 'S':
-    testname = "Short Self";
-    retval = scsiSmartShortSelfTest(fd);
-    break;
-  case 'L':
-    testname = "Long Self";
-    retval = scsiSmartExtendSelfTest(fd);
-    break;
-  }
-  // If we can't do the test, exit
-  if (NULL == testname) {
-    PrintOut(LOG_CRIT, "Device: %s, not capable of %c Self-Test\n", name, 
-             testtype);
-    return 1;
-  }
-  if (retval) {
-    if ((SIMPLE_ERR_BAD_OPCODE == retval) || 
-        (SIMPLE_ERR_BAD_FIELD == retval)) {
-      PrintOut(LOG_CRIT, "Device: %s, not capable of %s-Test\n", name, 
-               testname);
-      if ('L'==testtype)
-        cfg->testdata->not_cap_long=1;
-      else
-        cfg->testdata->not_cap_short=1;
-     
-      return 1;
-    }
-    PrintOut(LOG_CRIT, "Device: %s, execute %s-Test failed (err: %d)\n", name, 
-             testname, retval);
-    return 1;
-  }
-  
-  PrintOut(LOG_INFO, "Device: %s, starting scheduled %s-Test.\n", name, testname);
-  
-  return 0;
-}
-
-// Do an offline immediate or self-test.  Return zero on success,
-// nonzero on failure.
-int DoATASelfTest(int fd, cfgfile *cfg, char testtype) {
-  
-  struct ata_smart_values data;
-  char *testname=NULL;
-  int retval, dotest=-1;
-  char *name=cfg->name;
-  
-  // Read current smart data and check status/capability
-  if (ataReadSmartValues(fd, &data) || !(data.offline_data_collection_capability)) {
-    PrintOut(LOG_CRIT, "Device: %s, not capable of Offline or Self-Testing.\n", name);
-    return 1;
-  }
-  
-  // Check for capability to do the test
-  switch (testtype) {
-  case 'O':
-    testname="Offline Immediate ";
-    if (isSupportExecuteOfflineImmediate(&data))
-      dotest=OFFLINE_FULL_SCAN;
-    else
-      cfg->testdata->not_cap_offline=1;
-    break;
-  case 'C':
-    testname="Conveyance Self-";
-    if (isSupportConveyanceSelfTest(&data))
-      dotest=CONVEYANCE_SELF_TEST;
-    else
-      cfg->testdata->not_cap_conveyance=1;
-    break;
-  case 'S':
-    testname="Short Self-";
-    if (isSupportSelfTest(&data))
-      dotest=SHORT_SELF_TEST;
-    else
-      cfg->testdata->not_cap_short=1;
-    break;
-  case 'L':
-    testname="Long Self-";
-    if (isSupportSelfTest(&data))
-      dotest=EXTEND_SELF_TEST;
-    else
-      cfg->testdata->not_cap_long=1;
-    break;
-  }
-  
-  // If we can't do the test, exit
-  if (dotest<0) {
-    PrintOut(LOG_CRIT, "Device: %s, not capable of %sTest\n", name, testname);
-    return 1;
-  }
-  
-  // If currently running a self-test, do not interrupt it to start another.
-  if (15==(data.self_test_exec_status >> 4)) {
-    PrintOut(LOG_INFO, "Device: %s, skip scheduled %sTest; %1d0%% remaining of current Self-Test.\n",
-             name, testname, (int)(data.self_test_exec_status & 0x0f));
-    return 1;
-  }
-
-  // else execute the test, and return status
-  if ((retval=smartcommandhandler(fd, IMMEDIATE_OFFLINE, dotest, NULL)))
-    PrintOut(LOG_CRIT, "Device: %s, execute %sTest failed.\n", name, testname);
-  else
-    PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname);
-  
-  return retval;
-}
-
-
-int ATACheckDevice(cfgfile *cfg){
-  int fd,i;
-  char *name=cfg->name;
-  char *mode="ATA";
-  
-  // fix firmware bug if requested
-  con->fixfirmwarebug=cfg->fixfirmwarebug;
-  con->controller_port=cfg->controller_port;
-  con->controller_type=cfg->controller_type;
-
-  // If user has asked, test the email warning system
-  if (cfg->mailwarn && cfg->mailwarn->emailtest)
-    MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name);
-
-  if (cfg->controller_type == CONTROLLER_3WARE_9000_CHAR)
-    mode="ATA_3WARE_9000";
-  
-  if (cfg->controller_type == CONTROLLER_3WARE_678K_CHAR)
-    mode="ATA_3WARE_678K";
-
-  // if we can't open device, fail gracefully rather than hard --
-  // perhaps the next time around we'll be able to open it.  ATAPI
-  // cd/dvd devices will hang awaiting media if O_NONBLOCK is not
-  // given (see linux cdrom driver).
-  if ((fd=OpenDevice(name, mode, 0))<0){
-    MailWarning(cfg, 9, "Device: %s, unable to open device", name);
-    return 1;
-  }
-
-  // user may have requested (with the -n Directive) to leave the disk
-  // alone if it is in idle or sleeping mode.  In this case check the
-  // power mode and exit without check if needed
-  if (cfg->powermode){
-    int dontcheck=0, powermode=ataCheckPowerMode(fd);
-    char *mode=NULL;
-    if (powermode >= 0) {
-      int powermode2 = ataCheckPowerMode(fd);
-      if (powermode2 > powermode)
-        PrintOut(LOG_INFO, "Device: %s, CHECK POWER STATUS spins up disk (0x%02x -> 0x%02x)\n", name, powermode, powermode2);
-      powermode = powermode2;
-    }
-        
-    switch (powermode){
-    case -1:
-      // SLEEP
-      mode="SLEEP";
-      if (cfg->powermode>=1)
-       dontcheck=1;
-      break;
-    case 0:
-      // STANDBY
-      mode="STANDBY";
-      if (cfg->powermode>=2)
-       dontcheck=1;
-      break;
-    case 0x80:
-      // IDLE
-      mode="IDLE";
-      if (cfg->powermode>=3)
-       dontcheck=1;
-      break;
-    case 0xff:
-      // ACTIVE/IDLE
-      break;
-    default:
-      // UNKNOWN
-      PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
-              name, powermode);
-      cfg->powermode=0;
-      break;
-    }
-
-    // if we are going to skip a check, return now
-    if (dontcheck){
-      CloseDevice(fd, name);
-      if (!cfg->powerquiet) // to avoid waking up system disk
-        PrintOut(LOG_INFO, "Device: %s, is in %s mode, skipping checks\n", name, mode);
-      return 0;
-    }    
-  }
-
-  // check smart status
-  if (cfg->smartcheck){
-    int status=ataSmartStatus2(fd);
-    if (status==-1){
-      PrintOut(LOG_INFO,"Device: %s, not capable of SMART self-check\n",name);
-      MailWarning(cfg, 5, "Device: %s, not capable of SMART self-check", name);
-    }
-    else if (status==1){
-      PrintOut(LOG_CRIT, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!\n", name);
-      MailWarning(cfg, 1, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!", name);
-    }
-  }
-  
-  // Check everything that depends upon SMART Data (eg, Attribute values)
-  if (cfg->usagefailed || cfg->prefail || cfg->usage || cfg->pending!=DONT_MONITOR_UNC){
-    struct ata_smart_values     curval;
-    struct ata_smart_thresholds_pvt *thresh=cfg->smartthres;
-    
-    // Read current attribute values. *drive contains old values and thresholds
-    if (ataReadSmartValues(fd,&curval)){
-      PrintOut(LOG_CRIT, "Device: %s, failed to read SMART Attribute Data\n", name);
-      MailWarning(cfg, 6, "Device: %s, failed to read SMART Attribute Data", name);
-    }
-    else {
-      // look for current or offline pending sectors
-      if (cfg->pending != DONT_MONITOR_UNC) {
-       int64_t rawval;
-       unsigned char currentpending, offlinepending;
-       
-       TranslatePending(cfg->pending, &currentpending, &offlinepending);
-       
-       if (currentpending && (rawval=ATAReturnAttributeRawValue(currentpending, &curval))>0) {
-         // Unreadable pending sectors!!
-         PrintOut(LOG_CRIT,   "Device: %s, %"PRId64" Currently unreadable (pending) sectors\n", name, rawval);
-         MailWarning(cfg, 10, "Device: %s, %"PRId64" Currently unreadable (pending) sectors", name, rawval);
-       }
-       
-       if (offlinepending && (rawval=ATAReturnAttributeRawValue(offlinepending, &curval))>0) {
-         // Unreadable offline sectors!!
-         PrintOut(LOG_CRIT,   "Device: %s, %"PRId64" Offline uncorrectable sectors\n", name, rawval);
-         MailWarning(cfg, 11, "Device: %s, %"PRId64" Offline uncorrectable sectors", name, rawval);
-       }
-      }
-
-      if (cfg->usagefailed || cfg->prefail || cfg->usage) {
-
-       // look for failed usage attributes, or track usage or prefail attributes
-       for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
-         int att;
-         changedattribute_t delta;
-         
-         // This block looks for usage attributes that have failed.
-         // Prefail attributes that have failed are returned with a
-         // positive sign. No failure returns 0. Usage attributes<0.
-         if (cfg->usagefailed && ((att=ataCheckAttribute(&curval, thresh, i))<0)){
-           
-           // are we ignoring failures of this attribute?
-           att *= -1;
-           if (!IsAttributeOff(att, &cfg->monitorattflags, 0, MONITOR_FAILUSE, __LINE__)){
-             char attname[64], *loc=attname;
-             
-             // get attribute name & skip white space
-             ataPrintSmartAttribName(loc, att, cfg->attributedefs);
-             while (*loc && *loc==' ') loc++;
-             
-             // warning message
-             PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %s.\n", name, loc);
-             MailWarning(cfg, 2, "Device: %s, Failed SMART usage Attribute: %s.", name, loc);
-           }
-         }
-         
-         // This block tracks usage or prefailure attributes to see if
-         // they are changing.  It also looks for changes in RAW values
-         // if this has been requested by user.
-         if ((cfg->usage || cfg->prefail) && ATACompareValues(&delta, &curval, cfg->smartval, thresh, i, name)){
-           unsigned char id=delta.id;
-           
-           // if the only change is the raw value, and we're not
-           // tracking raw value, then continue loop over attributes
-           if (!delta.sameraw && delta.newval==delta.oldval && !IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAW, __LINE__))
-             continue;
-           
-           // are we tracking this attribute?
-           if (!IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_IGNORE, __LINE__)){
-             char newrawstring[64], oldrawstring[64], attname[64], *loc=attname;
-             
-             // get attribute name, skip spaces
-             ataPrintSmartAttribName(loc, id, cfg->attributedefs);
-             while (*loc && *loc==' ') loc++;
-             
-             // has the user asked for us to print raw values?
-             if (IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAWPRINT, __LINE__)) {
-               // get raw values (as a string) and add to printout
-               char rawstring[64];
-               ataPrintSmartAttribRawValue(rawstring, curval.vendor_attributes+i, cfg->attributedefs);
-               sprintf(newrawstring, " [Raw %s]", rawstring);
-               ataPrintSmartAttribRawValue(rawstring, cfg->smartval->vendor_attributes+i, cfg->attributedefs);
-               sprintf(oldrawstring, " [Raw %s]", rawstring);
-             }
-             else
-               newrawstring[0]=oldrawstring[0]='\0';
-             
-             // prefailure attribute
-             if (cfg->prefail && delta.prefail)
-               PrintOut(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %d%s to %d%s\n",
-                        name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
-             
-             // usage attribute
-             if (cfg->usage && !delta.prefail)
-               PrintOut(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %d%s to %d%s\n",
-                        name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
-           }
-         } // endof block tracking usage or prefailure
-       } // end of loop over attributes
-       
-       // Save the new values into *drive for the next time around
-       *(cfg->smartval)=curval;
-      }
-    }
-  }
-  
-  // check if number of selftest errors has increased (note: may also DECREASE)
-  if (cfg->selftest)
-    CheckSelfTestLogs(cfg, SelfTestErrorCount(fd, name));
-  
-  // check if number of ATA errors has increased
-  if (cfg->errorlog){
-
-    int new,old=cfg->ataerrorcount;
-
-    // new number of errors
-    new=ATAErrorCount(fd, name);
-
-    // did command fail?
-    if (new<0)
-      // lack of PrintOut here is INTENTIONAL
-      MailWarning(cfg, 7, "Device: %s, Read SMART Error Log Failed", name);
-
-    // has error count increased?
-    if (new>old){
-      PrintOut(LOG_CRIT, "Device: %s, ATA error count increased from %d to %d\n",
-               name, old, new);
-      MailWarning(cfg, 4, "Device: %s, ATA error count increased from %d to %d",
-                   name, old, new);
-    }
-    
-    // this last line is probably not needed, count always increases
-    if (new>=0)
-      cfg->ataerrorcount=new;
-  }
-  
-  // if the user has asked, and device is capable (or we're not yet
-  // sure) carry out scheduled self-tests.
-  if (cfg->testdata) {
-    // long test
-    if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L', 0)>0)
-      DoATASelfTest(fd, cfg, 'L');    
-    // short test
-    else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S', 0)>0)
-      DoATASelfTest(fd, cfg, 'S');
-    // conveyance test
-    else if (!cfg->testdata->not_cap_conveyance && DoTestNow(cfg, 'C', 0)>0)
-      DoATASelfTest(fd, cfg, 'C');
-    // offline immediate
-    else if (!cfg->testdata->not_cap_offline && DoTestNow(cfg, 'O', 0)>0)
-      DoATASelfTest(fd, cfg, 'O');  
-  }
-  
-  // Don't leave device open -- the OS/user may want to access it
-  // before the next smartd cycle!
-  CloseDevice(fd, name);
-  return 0;
-}
-
-#define DEF_SCSI_REPORT_TEMPERATURE_DELTA 2
-static int scsi_report_temperature_delta = DEF_SCSI_REPORT_TEMPERATURE_DELTA;
-
-int SCSICheckDevice(cfgfile *cfg)
-{
-    UINT8 asc, ascq;
-    UINT8 currenttemp;
-    UINT8 triptemp;
-    int fd;
-    char *name=cfg->name;
-    const char *cp;
-
-    // If the user has asked for it, test the email warning system
-    if (cfg->mailwarn && cfg->mailwarn->emailtest)
-      MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name);
-
-    // if we can't open device, fail gracefully rather than hard --
-    // perhaps the next time around we'll be able to open it
-    if ((fd=OpenDevice(name, "SCSI", 0))<0) {
-      // Lack of PrintOut() here is intentional!
-      MailWarning(cfg, 9, "Device: %s, unable to open device", name);
-      return 1;
-    }
-    currenttemp = 0;
-    asc = 0;
-    ascq = 0;
-    if (! cfg->SuppressReport) {
-        if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
-                        &asc, &ascq, &currenttemp, &triptemp)) {
-            PrintOut(LOG_INFO, "Device: %s, failed to read SMART values\n",
-                      name);
-            MailWarning(cfg, 6, "Device: %s, failed to read SMART values", name);
-            cfg->SuppressReport = 1;
-        }
-    }
-    if (asc > 0) {
-        cp = scsiGetIEString(asc, ascq);
-        if (cp) {
-            PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp);
-            MailWarning(cfg, 1,"Device: %s, SMART Failure: %s", name, cp); 
-        }
-    } else if (debugmode)
-        PrintOut(LOG_INFO,"Device: %s, Acceptable asc,ascq: %d,%d\n", 
-                 name, (int)asc, (int)ascq);  
-  
-    if (currenttemp && currenttemp!=255) {
-        if (cfg->Temperature) {
-            if (abs(((int)currenttemp - (int)cfg->Temperature)) >= 
-                scsi_report_temperature_delta) {
-                PrintOut(LOG_INFO, "Device: %s, Temperature changed %d Celsius "
-                         "to %d Celsius since last report\n", name, 
-                         (int)(currenttemp - cfg->Temperature), 
-                         (int)currenttemp);
-                cfg->Temperature = currenttemp;
-            }
-        }
-        else {
-            PrintOut(LOG_INFO, "Device: %s, initial Temperature is %d "
-                     "Celsius\n", name, (int)currenttemp);
-           if (triptemp)
-                PrintOut(LOG_INFO, "    [trip Temperature is %d Celsius]\n",
-                         (int)triptemp);
-            cfg->Temperature = currenttemp;
-            cfg->Temperature = currenttemp;
-        }
-    }
-    
-    // check if number of selftest errors has increased (note: may also DECREASE)
-    if (cfg->selftest)
-      CheckSelfTestLogs(cfg, scsiCountFailedSelfTests(fd, 0));
-    
-    if (cfg->testdata) {
-      // long (extended) background test
-      if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L', 0)>0)
-        DoSCSISelfTest(fd, cfg, 'L');
-      // short background test
-      else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S', 0)>0)
-        DoSCSISelfTest(fd, cfg, 'S');
-    }
-    CloseDevice(fd, name);
-    return 0;
-}
-
-// Checks the SMART status of all ATA and SCSI devices
-void CheckDevicesOnce(cfgfile **atadevices, cfgfile **scsidevices){
-  int i;
-  
-  for (i=0; i<numdevata; i++) 
-    ATACheckDevice(atadevices[i]);
-  
-  for (i=0; i<numdevscsi; i++)
-    SCSICheckDevice(scsidevices[i]);
-
-  return;
-}
-
-#if SCSITIMEOUT
-// This alarm means that a SCSI USB device was hanging
-void AlarmHandler(int signal) {
-  longjmp(registerscsienv, 1);
-}
-#endif
-
-// Does initialization right after fork to daemon mode
-void Initialize(time_t *wakeuptime){
-
-  // install goobye message and remove pidfile handler
-  atexit(Goodbye);
-  
-  // write PID file only after installing exit handler
-  if (!debugmode)
-    WritePidFile();
-  
-  // install signal handlers.  On Solaris, can't use signal() because
-  // it resets the handler to SIG_DFL after each call.  So use sigset()
-  // instead.  So SIGNALFN()==signal() or SIGNALFN()==sigset().
-  
-  // normal and abnormal exit
-  if (SIGNALFN(SIGTERM, sighandler)==SIG_IGN)
-    SIGNALFN(SIGTERM, SIG_IGN);
-  if (SIGNALFN(SIGQUIT, sighandler)==SIG_IGN)
-    SIGNALFN(SIGQUIT, SIG_IGN);
-  
-  // in debug mode, <CONTROL-C> ==> HUP
-  if (SIGNALFN(SIGINT, debugmode?HUPhandler:sighandler)==SIG_IGN)
-    SIGNALFN(SIGINT, SIG_IGN);
-  
-  // Catch HUP and USR1
-  if (SIGNALFN(SIGHUP, HUPhandler)==SIG_IGN)
-    SIGNALFN(SIGHUP, SIG_IGN);
-  if (SIGNALFN(SIGUSR1, USR1handler)==SIG_IGN)
-    SIGNALFN(SIGUSR1, SIG_IGN);
-#ifdef _WIN32
-  if (SIGNALFN(SIGUSR2, USR2handler)==SIG_IGN)
-    SIGNALFN(SIGUSR2, SIG_IGN);
-#endif
-
-  // initialize wakeup time to CURRENT time
-  *wakeuptime=time(NULL);
-  
-  return;
-}
-
-#ifdef _WIN32
-// Toggle debug mode implemented for native windows only
-// (there is no easy way to reopen tty on *nix)
-static void ToggleDebugMode()
-{
-  if (!debugmode) {
-    PrintOut(LOG_INFO,"Signal USR2 - enabling debug mode\n");
-    if (!daemon_enable_console("smartd [Debug]")) {
-      debugmode = 1;
-      daemon_signal(SIGINT, HUPhandler);
-      PrintOut(LOG_INFO,"smartd debug mode enabled, PID=%d\n", getpid());
-    }
-    else
-      PrintOut(LOG_INFO,"enable console failed\n");
-  }
-  else if (debugmode == 1) {
-    daemon_disable_console();
-    debugmode = 0;
-    daemon_signal(SIGINT, sighandler);
-    PrintOut(LOG_INFO,"Signal USR2 - debug mode disabled\n");
-  }
-  else
-    PrintOut(LOG_INFO,"Signal USR2 - debug mode %d not changed\n", debugmode);
-}
-#endif
-
-time_t dosleep(time_t wakeuptime){
-  time_t timenow=0;
-  
-  // If past wake-up-time, compute next wake-up-time
-  timenow=time(NULL);
-  while (wakeuptime<=timenow){
-    int intervals=1+(timenow-wakeuptime)/checktime;
-    wakeuptime+=intervals*checktime;
-  }
-  
-  // sleep until we catch SIGUSR1 or have completed sleeping
-  while (timenow<wakeuptime && !caughtsigUSR1 && !caughtsigHUP && !caughtsigEXIT){
-    
-    // protect user again system clock being adjusted backwards
-    if (wakeuptime>timenow+checktime){
-      PrintOut(LOG_CRIT, "System clock time adjusted to the past. Resetting next wakeup time.\n");
-      wakeuptime=timenow+checktime;
-    }
-    
-    // Exit sleep when time interval has expired or a signal is received
-    sleep(wakeuptime-timenow);
-
-#ifdef _WIN32
-    // toggle debug mode?
-    if (caughtsigUSR2) {
-      ToggleDebugMode();
-      caughtsigUSR2 = 0;
-    }
-#endif
-
-    timenow=time(NULL);
-  }
-  // if we caught a SIGUSR1 then print message and clear signal
-  if (caughtsigUSR1){
-    PrintOut(LOG_INFO,"Signal USR1 - checking devices now rather than in %d seconds.\n",
-             wakeuptime-timenow>0?(int)(wakeuptime-timenow):0);
-    caughtsigUSR1=0;
-  }
-  
-  // return adjusted wakeuptime
-  return wakeuptime;
-}
-
-// Print out a list of valid arguments for the Directive d
-void printoutvaliddirectiveargs(int priority, char d) {
-  char *s=NULL;
-
-  switch (d) {
-  case 'n':
-    PrintOut(priority, "never[,q], sleep[,q], standby[,q], idle[,q]");
-    break;
-  case 's':
-    PrintOut(priority, "valid_regular_expression");
-    break;
-  case 'd':
-    PrintOut(priority, "ata, scsi, marvell, removable, 3ware,N");
-    break;
-  case 'T':
-    PrintOut(priority, "normal, permissive");
-    break;
-  case 'o':
-  case 'S':
-    PrintOut(priority, "on, off");
-    break;
-  case 'l':
-    PrintOut(priority, "error, selftest");
-    break;
-  case 'M':
-    PrintOut(priority, "\"once\", \"daily\", \"diminishing\", \"test\", \"exec\"");
-    break;
-  case 'v':
-    if (!(s = create_vendor_attribute_arg_list())) {
-      PrintOut(LOG_CRIT,"Insufficient memory to construct argument list\n");
-      EXIT(EXIT_NOMEM);
-    }
-    PrintOut(priority, "\n%s\n", s);
-    s=CheckFree(s, __LINE__,filenameandversion);
-    break;
-  case 'P':
-    PrintOut(priority, "use, ignore, show, showall");
-    break;
-  case 'F':
-    PrintOut(priority, "none, samsung, samsung2");
-    break;
-  }
-}
-
-// exits with an error message, or returns integer value of token
-int GetInteger(char *arg, char *name, char *token, int lineno, char *configfile, int min, int max){
-  char *endptr;
-  int val;
-  
-  // check input range
-  if (min<0){
-    PrintOut(LOG_CRIT, "min =%d passed to GetInteger() must be >=0\n", min);
-    return -1;
-  }
-
-  // make sure argument is there
-  if (!arg) {
-    PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes integer argument from %d to %d.\n",
-             configfile, lineno, name, token, min, max);
-    return -1;
-  }
-  
-  // get argument value (base 10), check that it's integer, and in-range
-  val=strtol(arg,&endptr,10);
-  if (*endptr!='\0' || val<min || val>max )  {
-    PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs integer from %d to %d.\n",
-             configfile, lineno, name, token, arg, min, max);
-    return -1;
-  }
-
-  // all is well; return value
-  return val;
-}
-
-// This function returns 1 if it has correctly parsed one token (and
-// any arguments), else zero if no tokens remain.  It returns -1 if an
-// error was encountered.
-int ParseToken(char *token,cfgfile *cfg){
-  char sym;
-  char *name=cfg->name;
-  int lineno=cfg->lineno;
-  char *delim = " \n\t";
-  int badarg = 0;
-  int missingarg = 0;
-  char *arg = NULL;
-  int makemail=0;
-  maildata *mdat=NULL, tempmail;
-
-  // is the rest of the line a comment
-  if (*token=='#')
-    return 1;
-  
-  // is the token not recognized?
-  if (*token!='-' || strlen(token)!=2) {
-    PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
-             configfile, lineno, name, token);
-    PrintOut(LOG_CRIT, "Run smartd -D to print a list of valid Directives.\n");
-    return -1;
-  }
-  
-  // token we will be parsing:
-  sym=token[1];
-
-  // create temporary maildata structure.  This means we can postpone
-  // allocating space in the data segment until we are sure there are
-  // no errors.
-  if ('m'==sym || 'M'==sym){
-    if (!cfg->mailwarn){
-      memset(&tempmail, 0, sizeof(maildata));
-      mdat=&tempmail;
-      makemail=1;
-    }
-    else
-      mdat=cfg->mailwarn;
-  }
-
-  // parse the token and swallow its argument
-  switch (sym) {
-    int val;
-
-  case 'C':
-    // monitor current pending sector count (default 197)
-    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0)
-      return -1;
-    if (val==CUR_UNC_DEFAULT)
-      val=0;
-    else if (val==0)
-      val=CUR_UNC_DEFAULT;
-    // set bottom 8 bits to correct value
-    cfg->pending &= 0xff00;
-    cfg->pending |= val;
-    break;
-  case 'U':
-    // monitor offline uncorrectable sectors (default 198)
-    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0)
-      return -1;
-    if (val==OFF_UNC_DEFAULT)
-      val=0;
-    else if (val==0)
-      val=OFF_UNC_DEFAULT;
-    // turn off top 8 bits, then set to correct value
-    cfg->pending &= 0xff;
-    cfg->pending |= (val<<8);
-    break;
-  case 'T':
-    // Set tolerance level for SMART command failures
-    if ((arg = strtok(NULL, delim)) == NULL) {
-      missingarg = 1;
-    } else if (!strcmp(arg, "normal")) {
-      // Normal mode: exit on failure of a mandatory S.M.A.R.T. command, but
-      // not on failure of an optional S.M.A.R.T. command.
-      // This is the default so we don't need to actually do anything here.
-      cfg->permissive=0;
-    } else if (!strcmp(arg, "permissive")) {
-      // Permissive mode; ignore errors from Mandatory SMART commands
-      cfg->permissive=1;
-    } else {
-      badarg = 1;
-    }
-    break;
-  case 'd':
-    // specify the device type
-    if ((arg = strtok(NULL, delim)) == NULL) {
-      missingarg = 1;
-    } else if (!strcmp(arg, "ata")) {
-      cfg->controller_port = 0;
-      cfg->controller_type = CONTROLLER_ATA;
-    } else if (!strcmp(arg, "scsi")) {
-      cfg->controller_port =0;
-      cfg->controller_type = CONTROLLER_SCSI;
-    } else if (!strcmp(arg, "marvell")) {
-      cfg->controller_port =0;
-      cfg->controller_type = CONTROLLER_MARVELL_SATA;
-    } else if (!strcmp(arg, "removable")) {
-      cfg->removable = 1;
-    } else {
-      // look 3ware,N RAID device
-      int i;
-      char *s;
-      
-      // make a copy of the string to mess with
-      if (!(s = strdup(arg))) {
-        PrintOut(LOG_CRIT,
-                 "No memory to copy argument to -d option - exiting\n");
-        EXIT(EXIT_NOMEM);
-      } else if (strncmp(s,"3ware,",6)) {
-        badarg=1;
-      } else if (split_report_arg2(s, &i)){
-        PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N requires N integer\n",
-                 configfile, lineno, name);
-        badarg=1;
-      } else if ( i<0 || i>15) {
-        PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N (N=%d) must have 0 <= N <= 15\n",
-                 configfile, lineno, name, i);
-        badarg=1;
-      } else {
-       // determine type of escalade device from name of device
-       cfg->controller_type = guess_device_type(name);
-       if (cfg->controller_type!=CONTROLLER_3WARE_9000_CHAR && cfg->controller_type!=CONTROLLER_3WARE_678K_CHAR)
-         cfg->controller_type=CONTROLLER_3WARE_678K;
-           
-        // NOTE: controller_port == disk number + 1
-        cfg->controller_port = i+1;
-      }
-      s=CheckFree(s, __LINE__,filenameandversion); 
-    }
-    break;
-  case 'F':
-    // fix firmware bug
-    if ((arg = strtok(NULL, delim)) == NULL) {
-      missingarg = 1;
-    } else if (!strcmp(arg, "none")) {
-      cfg->fixfirmwarebug = FIX_NONE;
-    } else if (!strcmp(arg, "samsung")) {
-      cfg->fixfirmwarebug = FIX_SAMSUNG;
-    } else if (!strcmp(arg, "samsung2")) {
-      cfg->fixfirmwarebug = FIX_SAMSUNG2;
-    } else {
-      badarg = 1;
-    }
-    break;
-  case 'H':
-    // check SMART status
-    cfg->smartcheck=1;
-    break;
-  case 'f':
-    // check for failure of usage attributes
-    cfg->usagefailed=1;
-    break;
-  case 't':
-    // track changes in all vendor attributes
-    cfg->prefail=1;
-    cfg->usage=1;
-    break;
-  case 'p':
-    // track changes in prefail vendor attributes
-    cfg->prefail=1;
-    break;
-  case 'u':
-    //  track changes in usage vendor attributes
-    cfg->usage=1;
-    break;
-  case 'l':
-    // track changes in SMART logs
-    if ((arg = strtok(NULL, delim)) == NULL) {
-      missingarg = 1;
-    } else if (!strcmp(arg, "selftest")) {
-      // track changes in self-test log
-      cfg->selftest=1;
-    } else if (!strcmp(arg, "error")) {
-      // track changes in ATA error log
-      cfg->errorlog=1;
-    } else {
-      badarg = 1;
-    }
-    break;
-  case 'a':
-    // monitor everything
-    cfg->smartcheck=1;
-    cfg->prefail=1;
-    cfg->usagefailed=1;
-    cfg->usage=1;
-    cfg->selftest=1;
-    cfg->errorlog=1;
-    break;
-  case 'o':
-    // automatic offline testing enable/disable
-    if ((arg = strtok(NULL, delim)) == NULL) {
-      missingarg = 1;
-    } else if (!strcmp(arg, "on")) {
-      cfg->autoofflinetest = 2;
-    } else if (!strcmp(arg, "off")) {
-      cfg->autoofflinetest = 1;
-    } else {
-      badarg = 1;
-    }
-    break;
-  case 'n':
-    // skip disk check if in idle or standby mode
-    if (!(arg = strtok(NULL, delim)))
-      missingarg = 1;
-    else if (!strcmp(arg, "never")   || !strcmp(arg, "never,q"))
-      cfg->powermode = 0;
-    else if (!strcmp(arg, "sleep")   || !strcmp(arg, "sleep,q"))
-      cfg->powermode = 1;
-    else if (!strcmp(arg, "standby") || !strcmp(arg, "standby,q"))
-      cfg->powermode = 2;
-    else if (!strcmp(arg, "idle")    || !strcmp(arg, "idle,q"))
-      cfg->powermode = 3;
-    else
-      badarg = 1;
-    cfg->powerquiet = !!strchr(arg, ',');
-    break;
-  case 'S':
-    // automatic attribute autosave enable/disable
-    if ((arg = strtok(NULL, delim)) == NULL) {
-      missingarg = 1;
-    } else if (!strcmp(arg, "on")) {
-      cfg->autosave = 2;
-    } else if (!strcmp(arg, "off")) {
-      cfg->autosave = 1;
-    } else {
-      badarg = 1;
-    }
-    break;
-  case 's':
-    // warn user, and delete any previously given -s REGEXP Directives
-    if (cfg->testdata){
-      PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Test Directive -s %s\n",
-               configfile, lineno, name, cfg->testdata->regex);
-      cfg->testdata=FreeTestData(cfg->testdata);
-    }
-    // check for missing argument
-    if (!(arg = strtok(NULL, delim))) {
-      missingarg = 1;
-    }
-    // allocate space for structure and string
-    else if (!(cfg->testdata=(testinfo *)Calloc(1, sizeof(testinfo))) || !(cfg->testdata->regex=CustomStrDup(arg, 1, __LINE__,filenameandversion))) {
-      PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create Test Directive -s %s!\n",
-               configfile, lineno, name, arg);
-      EXIT(EXIT_NOMEM);
-    }
-    else if ((val=regcomp(&(cfg->testdata->cregex), arg, REG_EXTENDED))) {
-      char errormsg[512];
-      // not a valid regular expression!
-      regerror(val, &(cfg->testdata->cregex), errormsg, 512);
-      PrintOut(LOG_CRIT, "File %s line %d (drive %s): -s argument \"%s\" is INVALID extended regular expression. %s.\n",
-               configfile, lineno, name, arg, errormsg);
-      cfg->testdata=FreeTestData(cfg->testdata);
-      return -1;
-    }
-    // Do a bit of sanity checking and warn user if we think that
-    // their regexp is "strange". User probably confused about shell
-    // glob(3) syntax versus regular expression syntax regexp(7).
-    if ((int)strlen(arg) != (val=strspn(arg,"0123456789/.-+*|()?^$[]SLCO")))
-      PrintOut(LOG_INFO,  "File %s line %d (drive %s): warning, character %d (%c) looks odd in extended regular expression %s\n",
-               configfile, lineno, name, val+1, arg[val], arg);
-    break;
-  case 'm':
-    // send email to address that follows
-    if (!(arg = strtok(NULL,delim)))
-      missingarg = 1;
-    else {
-      if (mdat->address) {
-        PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n",
-                 configfile, lineno, name, mdat->address);
-        mdat->address=FreeNonZero(mdat->address, -1,__LINE__,filenameandversion);
-      }
-      mdat->address=CustomStrDup(arg, 1, __LINE__,filenameandversion);
-    }
-    break;
-  case 'M':
-    // email warning options
-    if (!(arg = strtok(NULL, delim)))
-      missingarg = 1;
-    else if (!strcmp(arg, "once"))
-      mdat->emailfreq = 1;
-    else if (!strcmp(arg, "daily"))
-      mdat->emailfreq = 2;
-    else if (!strcmp(arg, "diminishing"))
-      mdat->emailfreq = 3;
-    else if (!strcmp(arg, "test"))
-      mdat->emailtest = 1;
-    else if (!strcmp(arg, "exec")) {
-      // Get the next argument (the command line)
-      if (!(arg = strtok(NULL, delim))) {
-        PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n",
-                 configfile, lineno, name, token);
-        return -1;
-      }
-      // Free the last cmd line given if any, and copy new one
-      if (mdat->emailcmdline) {
-        PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous mail Directive -M exec %s\n",
-                 configfile, lineno, name, mdat->emailcmdline);
-        mdat->emailcmdline=FreeNonZero(mdat->emailcmdline, -1,__LINE__,filenameandversion);
-      }
-      mdat->emailcmdline=CustomStrDup(arg, 1, __LINE__,filenameandversion);
-    } 
-    else
-      badarg = 1;
-    break;
-  case 'i':
-    // ignore failure of usage attribute
-    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
-      return -1;
-    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_FAILUSE, __LINE__);
-    break;
-  case 'I':
-    // ignore attribute for tracking purposes
-    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
-      return -1;
-    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_IGNORE, __LINE__);
-    break;
-  case 'r':
-    // print raw value when tracking
-    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
-      return -1;
-    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__);
-    break;
-  case 'R':
-    // track changes in raw value (forces printing of raw value)
-    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
-      return -1;
-    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__);
-    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAW, __LINE__);
-    break;
-  case 'v':
-    // non-default vendor-specific attribute meaning
-    if (!(arg=strtok(NULL,delim))) {
-      missingarg = 1;
-    } else if (parse_attribute_def(arg, &cfg->attributedefs)){   
-      badarg = 1;
-    }
-    break;
-  case 'P':
-    // Define use of drive-specific presets.
-    if (!(arg = strtok(NULL, delim))) {
-      missingarg = 1;
-    } else if (!strcmp(arg, "use")) {
-      cfg->ignorepresets = FALSE;
-    } else if (!strcmp(arg, "ignore")) {
-      cfg->ignorepresets = TRUE;
-    } else if (!strcmp(arg, "show")) {
-      cfg->showpresets = TRUE;
-    } else if (!strcmp(arg, "showall")) {
-      showallpresets();
-    } else {
-      badarg = 1;
-    }
-    break;
-  default:
-    // Directive not recognized
-    PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
-             configfile, lineno, name, token);
-    Directives();
-    return -1;
-  }
-  if (missingarg) {
-    PrintOut(LOG_CRIT, "File %s line %d (drive %s): Missing argument to %s Directive\n",
-             configfile, lineno, name, token);
-  }
-  if (badarg) {
-    PrintOut(LOG_CRIT, "File %s line %d (drive %s): Invalid argument to %s Directive: %s\n",
-             configfile, lineno, name, token, arg);
-  }
-  if (missingarg || badarg) {
-    PrintOut(LOG_CRIT, "Valid arguments to %s Directive are: ", token);
-    printoutvaliddirectiveargs(LOG_CRIT, sym);
-    PrintOut(LOG_CRIT, "\n");
-    return -1;
-  }
-
-  // If this did something to fill the mail structure, and that didn't
-  // already exist, create it and copy.
-  if (makemail) {
-    if (!(cfg->mailwarn=(maildata *)Calloc(1, sizeof(maildata)))) {
-      PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create mail warning entry!\n",
-               configfile, lineno, name);
-      EXIT(EXIT_NOMEM);
-    }
-    memcpy(cfg->mailwarn, mdat, sizeof(maildata));
-  }
-  
-  return 1;
-}
-
-// Allocate storage for a new cfgfile entry.  If original!=NULL, it's
-// a copy of the original, but with private data storage.  Else all is
-// zeroed.  Returns address, and fails if non memory available.
-
-cfgfile *CreateConfigEntry(cfgfile *original){
-  cfgfile *add;
-  
-  // allocate memory for new structure
-  if (!(add=(cfgfile *)Calloc(1,sizeof(cfgfile))))
-    goto badexit;
-  
-  // if old structure was pointed to, copy it
-  if (original)
-    memcpy(add, original, sizeof(cfgfile));
-  
-  // make private copies of data items ONLY if they are in use (non
-  // NULL)
-  add->name         = CustomStrDup(add->name,         0, __LINE__,filenameandversion);
-
-  if (add->testdata) {
-    int val;
-    if (!(add->testdata=(testinfo *)Calloc(1,sizeof(testinfo))))
-      goto badexit;
-    memcpy(add->testdata, original->testdata, sizeof(testinfo));
-    add->testdata->regex = CustomStrDup(add->testdata->regex,   1, __LINE__,filenameandversion);
-    // only POSIX-portable way to make fresh copy of compiled regex is
-    // to recompile it completely.  There is no POSIX
-    // compiled-regex-copy command.
-    if ((val=regcomp(&(add->testdata->cregex), add->testdata->regex, REG_EXTENDED))) {
-      char errormsg[512];
-      regerror(val, &(add->testdata->cregex), errormsg, 512);
-      PrintOut(LOG_CRIT, "unable to recompile regular expression %s. %s\n", add->testdata->regex, errormsg);
-      goto badexit;
-    }
-  }
-  
-  if (add->mailwarn) {
-    if (!(add->mailwarn=(maildata *)Calloc(1,sizeof(maildata))))
-      goto badexit;
-    memcpy(add->mailwarn, original->mailwarn, sizeof(maildata));
-    add->mailwarn->address      = CustomStrDup(add->mailwarn->address,      0, __LINE__,filenameandversion);
-    add->mailwarn->emailcmdline = CustomStrDup(add->mailwarn->emailcmdline, 0, __LINE__,filenameandversion);
-  }
-
-  if (add->attributedefs) {
-    if (!(add->attributedefs=(unsigned char *)Calloc(MAX_ATTRIBUTE_NUM,1)))
-      goto badexit;
-    memcpy(add->attributedefs, original->attributedefs, MAX_ATTRIBUTE_NUM);
-  }
-  
-  if (add->monitorattflags) {
-    if (!(add->monitorattflags=(unsigned char *)Calloc(NMONITOR*32, 1)))
-      goto badexit;
-    memcpy(add->monitorattflags, original->monitorattflags, NMONITOR*32);
-  }
-  
-  if (add->smartval) {
-    if (!(add->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values))))
-      goto badexit;
-  }
-  
-  if (add->smartthres) {
-    if (!(add->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt))))
-      goto badexit;
-  }
-
-  return add;
-
- badexit:
-  PrintOut(LOG_CRIT, "No memory to create entry from configuration file\n");
-  EXIT(EXIT_NOMEM);
-}
-
-
-
-// This is the routine that adds things to the cfgentries list. To
-// prevent memory leaks when re-reading the configuration file many
-// times, this routine MUST deallocate any memory other than that
-// pointed to within cfg-> before it returns.
-//
-// Return values are:
-//  1: parsed a normal line
-//  0: found comment or blank line
-// -1: found SCANDIRECTIVE line
-// -2: found an error
-//
-// Note: this routine modifies *line from the caller!
-int ParseConfigLine(int entry, int lineno,char *line){
-  char *token=NULL;
-  char *name=NULL;
-  char *delim = " \n\t";
-  cfgfile *cfg=NULL;
-  int devscan=0;
-
-  // get first token: device name. If a comment, skip line
-  if (!(name=strtok(line,delim)) || *name=='#') {
-    return 0;
-  }
-
-  // Have we detected the SCANDIRECTIVE directive?
-  if (!strcmp(SCANDIRECTIVE,name)){
-    devscan=1;
-    if (entry) {
-      PrintOut(LOG_INFO,"Scan Directive %s (line %d) must be the first entry in %s\n",name, lineno, configfile);
-      return -2;
-    }
-  }
-  
-  // Is there space for another entry?  If not, allocate more
-  while (entry>=cfgentries_max)
-    cfgentries=AllocateMoreSpace(cfgentries, &cfgentries_max, "configuration file device");
-
-  // We've got a legit entry, make space to store it
-  cfg=cfgentries[entry]=CreateConfigEntry(NULL);
-  cfg->name = CustomStrDup(name, 1, __LINE__,filenameandversion);
-
-  // Store line number, and by default check for both device types.
-  cfg->lineno=lineno;
-  
-  // Try and recognize if a IDE or SCSI device.  These can be
-  // overwritten by configuration file directives.
-  if (cfg->controller_type==CONTROLLER_UNKNOWN)
-    cfg->controller_type = guess_device_type(cfg->name);
-  
-  // parse tokens one at a time from the file.
-  while ((token=strtok(NULL,delim))){
-    int retval=ParseToken(token,cfg);
-    
-    if (retval==0)
-      // No tokens left:
-      break;
-    
-    if (retval>0) {
-      // Parsed token  
-#if (0)
-      PrintOut(LOG_INFO,"Parsed token %s\n",token);
-#endif
-      continue;
-    }
-    
-    if (retval<0) {
-      // error found on the line
-      return -2;
-    }
-  }
-  
-  // If we found 3ware controller, then modify device name by adding a SPACE
-  if (cfg->controller_port){
-    int len=17+strlen(cfg->name);
-    char *newname;
-    
-    if (devscan){
-      PrintOut(LOG_CRIT, "smartd: can not scan for 3ware devices (line %d of file %s)\n",
-               lineno, configfile);
-      return -2;
-    }
-    
-    if (!(newname=(char *)calloc(len,1))) {
-      PrintOut(LOG_INFO,"No memory to parse file: %s line %d, %s\n", configfile, lineno, strerror(errno));
-      EXIT(EXIT_NOMEM);
-    }
-    
-    // Make new device name by adding a space then RAID disk number
-    snprintf(newname, len, "%s [3ware_disk_%02d]", cfg->name, cfg->controller_port-1);
-    cfg->name=CheckFree(cfg->name, __LINE__,filenameandversion);
-    cfg->name=newname;
-    bytes+=16;
-  }
-
-  // If NO monitoring directives are set, then set all of them.
-  if (!(cfg->smartcheck || cfg->usagefailed || cfg->prefail  || 
-        cfg->usage      || cfg->selftest    || cfg->errorlog   )){
-    
-    PrintOut(LOG_INFO,"Drive: %s, implied '-a' Directive on line %d of file %s\n",
-             cfg->name, cfg->lineno, configfile);
-    
-    cfg->smartcheck=1;
-    cfg->usagefailed=1;
-    cfg->prefail=1;
-    cfg->usage=1;
-    cfg->selftest=1;
-    cfg->errorlog=1;
-  }
-  
-  // additional sanity check. Has user set -M options without -m?
-  if (cfg->mailwarn && !cfg->mailwarn->address && (cfg->mailwarn->emailcmdline || cfg->mailwarn->emailfreq || cfg->mailwarn->emailtest)){
-    PrintOut(LOG_CRIT,"Drive: %s, -M Directive(s) on line %d of file %s need -m ADDRESS Directive\n",
-             cfg->name, cfg->lineno, configfile);
-    return -2;
-  }
-  
-  // has the user has set <nomailer>?
-  if (cfg->mailwarn && cfg->mailwarn->address && !strcmp(cfg->mailwarn->address,"<nomailer>")){
-    // check that -M exec is also set
-    if (!cfg->mailwarn->emailcmdline){
-      PrintOut(LOG_CRIT,"Drive: %s, -m <nomailer> Directive on line %d of file %s needs -M exec Directive\n",
-               cfg->name, cfg->lineno, configfile);
-      return -2;
-    }
-    // now free memory.  From here on the sign of <nomailer> is
-    // address==NULL and cfg->emailcmdline!=NULL
-    cfg->mailwarn->address=FreeNonZero(cfg->mailwarn->address, -1,__LINE__,filenameandversion);
-  }
-
-  // set cfg->emailfreq to 1 (once) if user hasn't set it
-  if (cfg->mailwarn && !cfg->mailwarn->emailfreq)
-    cfg->mailwarn->emailfreq = 1;
-
-  entry++;
-
-  if (devscan)
-    return -1;
-  else
-    return 1;
-}
-
-// clean up utility for ParseConfigFile()
-void cleanup(FILE **fpp, int is_stdin){
-  if (*fpp){
-    // (*fpp != stdin) does not work here if stdin has been closed & reopened
-    if (!is_stdin)
-      fclose(*fpp);
-    *fpp=NULL;
-  }
-
-  return;
-}
-
-
-// Parses a configuration file.  Return values are:
-//  N=>0: found N entries
-// -1:    syntax error in config file
-// -2:    config file does not exist
-// -3:    config file exists but cannot be read
-//
-// In the case where the return value is 0, there are three
-// possiblities:
-// Empty configuration file ==> cfgentries==NULL
-// No configuration file    ==> cfgentries[0]->lineno == 0
-// SCANDIRECTIVE found      ==> cfgentries[0]->lineno != 0
-int ParseConfigFile(){
-  FILE *fp=NULL;
-  int entry=0,lineno=1,cont=0,contlineno=0;
-  char line[MAXLINELEN+2];
-  char fullline[MAXCONTLINE+1];
-
-  int is_stdin = (configfile == configfile_stdin); // pointer comparison ok here
-
-  // Open config file, if it exists and is not <stdin>
-  if (!is_stdin) {
-    fp=fopen(configfile,"r");
-    if (fp==NULL && (errno!=ENOENT || configfile_alt)) {
-      // file exists but we can't read it or it should exist due to '-c' option
-      int ret = (errno!=ENOENT ? -3 : -2);
-      PrintOut(LOG_CRIT,"%s: Unable to open configuration file %s\n",
-               strerror(errno),configfile);
-      return ret;
-    }
-  }
-  else // read from stdin ('-c -' option)
-    fp = stdin;
-  
-  // No configuration file found -- use fake one
-  if (fp==NULL) {
-    int len=strlen(SCANDIRECTIVE)+4;
-    char *fakeconfig=(char *)calloc(len,1);
-  
-    if (!fakeconfig || 
-        (len-1) != snprintf(fakeconfig, len, "%s -a", SCANDIRECTIVE) ||
-        -1 != ParseConfigLine(entry, 0, fakeconfig)
-        ) {
-      PrintOut(LOG_CRIT,"Internal error in ParseConfigFile() at line %d of file %s\n%s", 
-               __LINE__, filenameandversion, reportbug);
-      EXIT(EXIT_BADCODE);
-    }
-    fakeconfig=CheckFree(fakeconfig, __LINE__,filenameandversion);
-    return 0;
-  }
-
-#ifdef __CYGWIN__
-  setmode(fileno(fp), O_TEXT); // Allow files with \r\n
-#endif
-
-  // configuration file exists
-  PrintOut(LOG_INFO,"Opened configuration file %s\n",configfile);
-
-  // parse config file line by line
-  while (1) {
-    int len=0,scandevice;
-    char *lastslash;
-    char *comment;
-    char *code;
-
-    // make debugging simpler
-    memset(line,0,sizeof(line));
-
-    // get a line
-    code=fgets(line,MAXLINELEN+2,fp);
-    
-    // are we at the end of the file?
-    if (!code){
-      if (cont) {
-        scandevice=ParseConfigLine(entry,contlineno,fullline);
-        // See if we found a SCANDIRECTIVE directive
-        if (scandevice==-1) {
-          cleanup(&fp, is_stdin);
-          return 0;
-        }
-        // did we find a syntax error
-        if (scandevice==-2) {
-          cleanup(&fp, is_stdin);
-          return -1;
-        }
-        // the final line is part of a continuation line
-        cont=0;
-        entry+=scandevice;
-      }
-      break;
-    }
-
-    // input file line number
-    contlineno++;
-    
-    // See if line is too long
-    len=strlen(line);
-    if (len>MAXLINELEN){
-      char *warn;
-      if (line[len-1]=='\n')
-        warn="(including newline!) ";
-      else
-        warn="";
-      PrintOut(LOG_CRIT,"Error: line %d of file %s %sis more than MAXLINELEN=%d characters.\n",
-               (int)contlineno,configfile,warn,(int)MAXLINELEN);
-      cleanup(&fp, is_stdin);
-      return -1;
-    }
-
-    // Ignore anything after comment symbol
-    if ((comment=strchr(line,'#'))){
-      *comment='\0';
-      len=strlen(line);
-    }
-
-    // is the total line (made of all continuation lines) too long?
-    if (cont+len>MAXCONTLINE){
-      PrintOut(LOG_CRIT,"Error: continued line %d (actual line %d) of file %s is more than MAXCONTLINE=%d characters.\n",
-               lineno, (int)contlineno, configfile, (int)MAXCONTLINE);
-      cleanup(&fp, is_stdin);
-      return -1;
-    }
-    
-    // copy string so far into fullline, and increment length
-    strcpy(fullline+cont,line);
-    cont+=len;
-
-    // is this a continuation line.  If so, replace \ by space and look at next line
-    if ( (lastslash=strrchr(line,'\\')) && !strtok(lastslash+1," \n\t")){
-      *(fullline+(cont-len)+(lastslash-line))=' ';
-      continue;
-    }
-
-    // Not a continuation line. Parse it
-    scandevice=ParseConfigLine(entry,contlineno,fullline);
-
-    // did we find a scandevice directive?
-    if (scandevice==-1) {
-      cleanup(&fp, is_stdin);
-      return 0;
-    }
-    // did we find a syntax error
-    if (scandevice==-2) {
-      cleanup(&fp, is_stdin);
-      return -1;
-    }
-
-    entry+=scandevice;
-    lineno++;
-    cont=0;
-  }
-  cleanup(&fp, is_stdin);
-  
-  // note -- may be zero if syntax of file OK, but no valid entries!
-  return entry;
-}
-
-
-// Prints copyright, license and version information
-void PrintCopyleft(void){
-  debugmode=1;
-  PrintHead();
-  PrintCVS();
-  return;
-}
-
-/* Prints the message "=======> VALID ARGUMENTS ARE: <LIST>  <=======\n", where
-   <LIST> is the list of valid arguments for option opt. */
-void PrintValidArgs(char opt) {
-  const char *s;
-
-  PrintOut(LOG_CRIT, "=======> VALID ARGUMENTS ARE: ");
-  if (!(s = GetValidArgList(opt)))
-    PrintOut(LOG_CRIT, "Error constructing argument list for option %c", opt);
-  else
-    PrintOut(LOG_CRIT, (char *)s);
-  PrintOut(LOG_CRIT, " <=======\n");
-}
-
-// Parses input line, prints usage message and
-// version/license/copyright messages
-void ParseOpts(int argc, char **argv){
-  extern char *optarg;
-  extern int  optopt, optind, opterr;
-  int optchar;
-  int badarg;
-  char *tailptr;
-  long lchecktime;
-  // Please update GetValidArgList() if you edit shortopts
-  const char *shortopts = "c:l:q:dDi:p:r:Vh?";
-#ifdef HAVE_GETOPT_LONG
-  char *arg;
-  // Please update GetValidArgList() if you edit longopts
-  struct option longopts[] = {
-    { "configfile",     required_argument, 0, 'c' },
-    { "logfacility",    required_argument, 0, 'l' },
-    { "quit",           required_argument, 0, 'q' },
-    { "debug",          no_argument,       0, 'd' },
-    { "showdirectives", no_argument,       0, 'D' },
-    { "interval",       required_argument, 0, 'i' },
-    { "pidfile",        required_argument, 0, 'p' },
-    { "report",         required_argument, 0, 'r' },
-#if defined(_WIN32) || defined(__CYGWIN__)
-    { "service",        no_argument,       0, 'S' },
-#endif
-    { "version",        no_argument,       0, 'V' },
-    { "license",        no_argument,       0, 'V' },
-    { "copyright",      no_argument,       0, 'V' },
-    { "help",           no_argument,       0, 'h' },
-    { "usage",          no_argument,       0, 'h' },
-    { 0,                0,                 0, 0   }
-  };
-#endif
-  
-  opterr=optopt=0;
-  badarg=FALSE;
-  
-  // Parse input options.  This horrible construction is so that emacs
-  // indents properly.  Sorry.
-  while (-1 != (optchar = 
-#ifdef HAVE_GETOPT_LONG
-                getopt_long(argc, argv, shortopts, longopts, NULL)
-#else
-                getopt(argc, argv, shortopts)
-#endif
-                )) {
-    
-    switch(optchar) {
-    case 'q':
-      // when to quit
-      if (!(strcmp(optarg,"nodev"))) {
-        quit=0;
-      } else if (!(strcmp(optarg,"nodevstartup"))) {
-        quit=1;
-      } else if (!(strcmp(optarg,"never"))) {
-        quit=2;
-      } else if (!(strcmp(optarg,"onecheck"))) {
-        quit=3;
-        debugmode=1;
-      } else if (!(strcmp(optarg,"showtests"))) {
-        quit=4;
-        debugmode=1;
-      } else if (!(strcmp(optarg,"errors"))) {
-        quit=5;
-      } else {
-        badarg = TRUE;
-      }
-      break;
-    case 'l':
-      // set the log facility level
-      if (!strcmp(optarg, "daemon"))
-        facility=LOG_DAEMON;
-      else if (!strcmp(optarg, "local0"))
-        facility=LOG_LOCAL0;
-      else if (!strcmp(optarg, "local1"))
-        facility=LOG_LOCAL1;
-      else if (!strcmp(optarg, "local2"))
-        facility=LOG_LOCAL2;
-      else if (!strcmp(optarg, "local3"))
-        facility=LOG_LOCAL3;
-      else if (!strcmp(optarg, "local4"))
-        facility=LOG_LOCAL4;
-      else if (!strcmp(optarg, "local5"))
-        facility=LOG_LOCAL5;
-      else if (!strcmp(optarg, "local6"))
-        facility=LOG_LOCAL6;
-      else if (!strcmp(optarg, "local7"))
-        facility=LOG_LOCAL7;
-      else
-        badarg = TRUE;
-      break;
-    case 'd':
-      // enable debug mode
-      debugmode = TRUE;
-      break;
-    case 'D':
-      // print summary of all valid directives
-      debugmode = TRUE;
-      Directives();
-      EXIT(0);
-      break;
-    case 'i':
-      // Period (time interval) for checking
-      // strtol will set errno in the event of overflow, so we'll check it.
-      errno = 0;
-      lchecktime = strtol(optarg, &tailptr, 10);
-      if (*tailptr != '\0' || lchecktime < 10 || lchecktime > INT_MAX || errno) {
-        debugmode=1;
-        PrintHead();
-        PrintOut(LOG_CRIT, "======> INVALID INTERVAL: %s <=======\n", optarg);
-        PrintOut(LOG_CRIT, "======> INTERVAL MUST BE INTEGER BETWEEN %d AND %d <=======\n", 10, INT_MAX);
-        PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
-        EXIT(EXIT_BADCMD);
-      }
-      checktime = (int)lchecktime;
-      break;
-    case 'r':
-      // report IOCTL transactions
-      {
-        int i;
-        char *s;
-
-        // split_report_arg() may modify its first argument string, so use a
-        // copy of optarg in case we want optarg for an error message.
-        if (!(s = strdup(optarg))) {
-          PrintOut(LOG_CRIT, "No memory to process -r option - exiting\n");
-          EXIT(EXIT_NOMEM);
-        }
-        if (split_report_arg(s, &i)) {
-          badarg = TRUE;
-        } else if (i<1 || i>3) {
-          debugmode=1;
-          PrintHead();
-          PrintOut(LOG_CRIT, "======> INVALID REPORT LEVEL: %s <=======\n", optarg);
-          PrintOut(LOG_CRIT, "======> LEVEL MUST BE INTEGER BETWEEN 1 AND 3<=======\n");
-          EXIT(EXIT_BADCMD);
-        } else if (!strcmp(s,"ioctl")) {
-          con->reportataioctl  = con->reportscsiioctl = i;
-        } else if (!strcmp(s,"ataioctl")) {
-          con->reportataioctl = i;
-        } else if (!strcmp(s,"scsiioctl")) {
-          con->reportscsiioctl = i;
-        } else {
-          badarg = TRUE;
-        }
-        s=CheckFree(s, __LINE__,filenameandversion);
-      }
-      break;
-    case 'c':
-      // alternate configuration file
-      if (strcmp(optarg,"-"))
-        configfile=configfile_alt=CustomStrDup(optarg, 1, __LINE__,filenameandversion);
-      else // read from stdin
-        configfile=configfile_stdin;
-      break;
-    case 'p':
-      // output file with PID number
-      pid_file=CustomStrDup(optarg, 1, __LINE__,filenameandversion);
-      break;
-#if defined(_WIN32) || defined(__CYGWIN__)
-    case 'S':
-      // running as service
-#ifdef __CYGWIN__ // On Windows, option is already handled by daemon_main(), so ignore it
-      is_service = 1;
-#endif
-      break;
-#endif // _WIN32 || __CYGWIN__
-    case 'V':
-      // print version and CVS info
-      PrintCopyleft();
-      EXIT(0);
-      break;
-    case 'h':
-      // help: print summary of command-line options
-      debugmode=1;
-      PrintHead();
-      Usage();
-      EXIT(0);
-      break;
-    case '?':
-    default:
-      // unrecognized option
-      debugmode=1;
-      PrintHead();
-#ifdef HAVE_GETOPT_LONG
-      // Point arg to the argument in which this option was found.
-      arg = argv[optind-1];
-      // Check whether the option is a long option that doesn't map to -h.
-      if (arg[1] == '-' && optchar != 'h') {
-        // Iff optopt holds a valid option then argument must be missing.
-        if (optopt && (strchr(shortopts, optopt) != NULL)) {
-          PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n",arg+2);
-          PrintValidArgs(optopt);
-        } else {
-          PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %s <=======\n\n",arg+2);
-        }
-        PrintOut(LOG_CRIT, "\nUse smartd --help to get a usage summary\n\n");
-        EXIT(EXIT_BADCMD);
-      }
-#endif
-      if (optopt) {
-        // Iff optopt holds a valid option then argument must be missing.
-        if (strchr(shortopts, optopt) != NULL){
-          PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %c <=======\n",optopt);
-          PrintValidArgs(optopt);
-        } else {
-          PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt);
-        }
-        PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
-        EXIT(EXIT_BADCMD);
-      }
-      Usage();
-      EXIT(0);
-    }
-
-    // Check to see if option had an unrecognized or incorrect argument.
-    if (badarg) {
-      debugmode=1;
-      PrintHead();
-      // It would be nice to print the actual option name given by the user
-      // here, but we just print the short form.  Please fix this if you know
-      // a clean way to do it.
-      PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <======= \n", optchar, optarg);
-      PrintValidArgs(optchar);
-      PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
-      EXIT(EXIT_BADCMD);
-    }
-  }
-
-  // non-option arguments are not allowed
-  if (argc > optind) {
-    debugmode=1;
-    PrintHead();
-    PrintOut(LOG_CRIT, "=======> UNRECOGNIZED ARGUMENT: %s <=======\n\n", argv[optind]);
-    PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
-    EXIT(EXIT_BADCMD);
-  }
-
-  // no pidfile in debug mode
-  if (debugmode && pid_file) {
-    debugmode=1;
-    PrintHead();
-    PrintOut(LOG_CRIT, "=======> INVALID CHOICE OF OPTIONS: -d and -p <======= \n\n");
-    PrintOut(LOG_CRIT, "Error: pid file %s not written in debug (-d) mode\n\n", pid_file);
-    pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion);
-    EXIT(EXIT_BADCMD);
-  }
-  
-  // print header
-  PrintHead();
-  
-  return;
-}
-
-// Function we call if no configuration file was found or if the
-// SCANDIRECTIVE Directive was found.  It makes entries for device
-// names returned by make_device_names() in os_OSNAME.c
-int MakeConfigEntries(const char *type, int start){
-  int i;
-  int num;
-  char** devlist = NULL;
-  cfgfile *first=cfgentries[0],*cfg=first;
-
-  // make list of devices
-  if ((num=make_device_names(&devlist,type))<0)
-    PrintOut(LOG_CRIT,"Problem creating device name scan list\n");
-  
-  // if no devices, or error constructing list, return
-  if (num<=0)
-    return 0;
-  
-  // loop over entries to create
-  for (i=0; i<num; i++){
-    
-    // make storage and copy for all but first entry
-    if (start+i) {
-      // allocate more storage if needed
-      while (cfgentries_max<=start+i)
-        cfgentries=AllocateMoreSpace(cfgentries, &cfgentries_max, "simulated configuration file device");
-      cfg=cfgentries[start+i]=CreateConfigEntry(first);
-    }
-
-    // ATA or SCSI?
-    if (!strcmp(type,"ATA") )
-      cfg->controller_type = CONTROLLER_ATA;
-    if (!strcmp(type,"SCSI") ) 
-      cfg->controller_type = CONTROLLER_SCSI;
-    
-    // remove device name, if it's there, and put in correct one
-    cfg->name=FreeNonZero(cfg->name, -1,__LINE__,filenameandversion);
-    // save pointer to the device name created within
-    // make_device_names
-    cfg->name=devlist[i];
-  }
-  
-  // If needed, free memory used for devlist: pointers now in
-  // cfgentries[]->names.  If num==0 we never get to this point, but
-  // that's OK.  If we realloc()d the array length in
-  // make_device_names() that was ALREADY equivalent to calling
-  // free().
-  devlist = FreeNonZero(devlist,(sizeof (char*) * num),__LINE__, filenameandversion);
-  
-  return num;
-}
-void CanNotRegister(char *name, char *type, int line, int scandirective){
-  if( !debugmode && scandirective == 1 ) { return; }
-  if (line)
-    PrintOut(scandirective?LOG_INFO:LOG_CRIT,
-             "Unable to register %s device %s at line %d of file %s\n",
-             type, name, line, configfile);
-  else
-    PrintOut(LOG_INFO,"Unable to register %s device %s\n",
-             type, name);
-  return;
-}
-
-// Returns negative value (see ParseConfigFile()) if config file
-// had errors, else number of entries which may be zero or positive. 
-// If we found no configuration file, or it contained SCANDIRECTIVE,
-// then *scanning is set to 1, else 0.
-int ReadOrMakeConfigEntries(int *scanning){
-  int entries;
-  
-  // deallocate any cfgfile data structures in memory
-  RmAllConfigEntries();
-  
-  // parse configuration file configfile (normally /etc/smartd.conf)  
-  if ((entries=ParseConfigFile())<0) {
-    // There was an error reading the configuration file.
-    RmAllConfigEntries();
-    if (entries == -1)
-      PrintOut(LOG_CRIT, "Configuration file %s has fatal syntax errors.\n", configfile);
-    return entries;
-  }
-
-  // did we find entries or scan?
-  *scanning=0;
-  
-  // no error parsing config file.
-  if (entries) {
-    // we did not find a SCANDIRECTIVE and did find valid entries
-    PrintOut(LOG_INFO, "Configuration file %s parsed.\n", configfile);
-  }
-  else if (cfgentries && cfgentries[0]) {
-    // we found a SCANDIRECTIVE or there was no configuration file so
-    // scan.  Configuration file's first entry contains all options
-    // that were set
-    cfgfile *first=cfgentries[0];
-    int doata  = !(first->controller_type==CONTROLLER_SCSI);
-    int doscsi = !(first->controller_type==CONTROLLER_ATA);
-    
-    *scanning=1;
-    
-    if (first->lineno)
-      PrintOut(LOG_INFO,"Configuration file %s was parsed, found %s, scanning devices\n", configfile, SCANDIRECTIVE);
-    else
-      PrintOut(LOG_INFO,"No configuration file %s found, scanning devices\n", configfile);
-    
-    // make config list of ATA devices to search for
-    if (doata)
-      entries+=MakeConfigEntries("ATA", entries);
-    // make config list of SCSI devices to search for
-    if (doscsi)
-      entries+=MakeConfigEntries("SCSI", entries);
-
-    // warn user if scan table found no devices
-    if (!entries) {
-      PrintOut(LOG_CRIT,"In the system's table of devices NO devices found to scan\n");
-      // get rid of fake entry with SCANDIRECTIVE as name
-      RmConfigEntry(cfgentries, __LINE__);
-    }
-  } 
-  else
-    PrintOut(LOG_CRIT,"Configuration file %s parsed but has no entries (like /dev/hda)\n",configfile);
-  
-  return entries;
-}
-
-
-// This function tries devices from cfgentries.  Each one that can be
-// registered is moved onto the [ata|scsi]devices lists and removed
-// from the cfgentries list, else it's memory is deallocated.
-void RegisterDevices(int scanning){
-  int i;
-  
-  // start by clearing lists/memory of ALL existing devices
-  RmAllDevEntries();
-  numdevata=numdevscsi=0;
-  
-  // Register entries
-  for (i=0; i<cfgentries_max ; i++){
-    
-    cfgfile *ent=cfgentries[i];
-    
-    // skip any NULL entries (holes)
-    if (!ent)
-      continue;
-    
-    // register ATA devices
-    if (ent->controller_type!=CONTROLLER_SCSI){
-      if (ATADeviceScan(ent, scanning))
-        CanNotRegister(ent->name, "ATA", ent->lineno, scanning);
-      else {
-        // move onto the list of ata devices
-        cfgentries[i]=NULL;
-        while (numdevata>=atadevlist_max)
-          atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device");
-        atadevlist[numdevata++]=ent;
-      }
-    }
-    
-    // then register SCSI devices
-    if (ent->controller_type==CONTROLLER_SCSI || ent->controller_type==CONTROLLER_UNKNOWN){
-      int retscsi=0;
-
-#if SCSITIMEOUT
-      struct sigaction alarmAction, defaultaction;
-
-      // Set up an alarm handler to catch USB devices that hang on
-      // SCSI scanning...
-      alarmAction.sa_handler= AlarmHandler;
-      alarmAction.sa_flags  = SA_RESTART;
-      if (sigaction(SIGALRM, &alarmAction, &defaultaction)) {
-        // if we can't set timeout, just scan device
-        PrintOut(LOG_CRIT, "Unable to initialize SCSI timeout mechanism.\n");
-        retscsi=SCSIDeviceScan(ent, scanning);
-      }
-      else {
-        // prepare return point in case of bad SCSI device
-        if (setjmp(registerscsienv))
-          // SCSI device timed out!
-          retscsi=-1;
-        else {
-        // Set alarm, make SCSI call, reset alarm
-          alarm(SCSITIMEOUT);
-          retscsi=SCSIDeviceScan(ent, scanning);
-          alarm(0);
-        }
-        if (sigaction(SIGALRM, &defaultaction, NULL)){
-          PrintOut(LOG_CRIT, "Unable to clear SCSI timeout mechanism.\n");
-        }
-      }
-#else
-      retscsi=SCSIDeviceScan(ent, scanning);
-#endif   
-
-      // Now scan SCSI device...
-      if (retscsi){
-        if (retscsi<0)
-          PrintOut(LOG_CRIT, "Device %s timed out (poorly-implemented USB device?)\n", ent->name);
-        CanNotRegister(ent->name, "SCSI", ent->lineno, scanning);
-      }
-      else {
-        // move onto the list of scsi devices
-        cfgentries[i]=NULL;
-        while (numdevscsi>=scsidevlist_max)
-          scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device");
-        scsidevlist[numdevscsi++]=ent;
-      }
-    }
-    
-    // if device is explictly listed and we can't register it, then
-    // exit unless the user has specified that the device is removable
-    if (cfgentries[i]  && !scanning){
-      if (ent->removable || quit==2)
-        PrintOut(LOG_INFO, "Device %s not available\n", ent->name);
-      else {
-        PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", ent->name);
-        EXIT(EXIT_BADDEV);
-      }
-    }
-    
-    // free up memory if device could not be registered
-    RmConfigEntry(cfgentries+i, __LINE__);
-  }
-  
-  return;
-}
-
-
-#ifndef _WIN32
-// Main function
-int main(int argc, char **argv)
-#else
-// Windows: internal main function started direct or by service control manager
-static int smartd_main(int argc, char **argv)
-#endif
-{
-  // external control variables for ATA disks
-  smartmonctrl control;
-
-  // is it our first pass through?
-  int firstpass=1;
-
-  // next time to wake up
-  time_t wakeuptime;
-
-  // for simplicity, null all global communications variables/lists
-  con=&control;
-  memset(con,        0,sizeof(control));
-
-  // parse input and print header and usage info if needed
-  ParseOpts(argc,argv);
-  
-  // do we mute printing from ataprint commands?
-  con->printing_switchable=0;
-  con->dont_print=debugmode?0:1;
-  
-  // don't exit on bad checksums
-  con->checksumfail=0;
-  
-  // the main loop of the code
-  while (1){
-
-    // are we exiting from a signal?
-    if (caughtsigEXIT) {
-      // are we exiting with SIGTERM?
-      int isterm=(caughtsigEXIT==SIGTERM);
-      int isquit=(caughtsigEXIT==SIGQUIT);
-      int isok=debugmode?isterm || isquit:isterm;
-      
-      PrintOut(isok?LOG_INFO:LOG_CRIT, "smartd received signal %d: %s\n",
-               caughtsigEXIT, strsignal(caughtsigEXIT));
-      
-      EXIT(isok?0:EXIT_SIGNAL);
-    }
-
-    // Should we (re)read the config file?
-    if (firstpass || caughtsigHUP){
-      int entries, scanning=0;
-
-      if (!firstpass) {
-#ifdef __CYGWIN__
-        // Workaround for missing SIGQUIT via keyboard on Cygwin
-        if (caughtsigHUP==2) {
-          // Simulate SIGQUIT if another SIGINT arrives soon
-          caughtsigHUP=0;
-          sleep(1);
-          if (caughtsigHUP==2) {
-            caughtsigEXIT=SIGQUIT;
-            continue;
-          }
-          caughtsigHUP=2;
-        }
-#endif
-        PrintOut(LOG_INFO,
-                 caughtsigHUP==1?
-                 "Signal HUP - rereading configuration file %s\n":
-                 "\a\nSignal INT - rereading configuration file %s ("SIGQUIT_KEYNAME" quits)\n\n",
-                 configfile);
-      }
-
-      // clears cfgentries, (re)reads config file, makes >=0 entries
-      entries=ReadOrMakeConfigEntries(&scanning);
-
-      if (entries>=0) {
-        // checks devices, then moves onto ata/scsi list or deallocates.
-        RegisterDevices(scanning);
-      }
-      else if (quit==2 || ((quit==0 || quit==1) && !firstpass)) {
-        // user has asked to continue on error in configuration file
-        if (!firstpass)
-          PrintOut(LOG_INFO,"Reusing previous configuration\n");
-      }
-      else {
-        // exit with configuration file error status
-        int status = (entries==-3 ? EXIT_READCONF : entries==-2 ? EXIT_NOCONF : EXIT_BADCONF);
-        EXIT(status);
-      }
-
-      // Log number of devices we are monitoring...
-      if (numdevata+numdevscsi || quit==2 || (quit==1 && !firstpass))
-        PrintOut(LOG_INFO,"Monitoring %d ATA and %d SCSI devices\n",
-                 numdevata, numdevscsi);
-      else {
-        PrintOut(LOG_INFO,"Unable to monitor any SMART enabled devices. Try debug (-d) option. Exiting...\n");
-        EXIT(EXIT_NODEV);
-      }
-
-      if (quit==4) {
-        // user has asked to print test schedule
-        PrintTestSchedule(atadevlist, scsidevlist);
-        EXIT(0);
-      }
-      
-      // reset signal
-      caughtsigHUP=0;
-    }
-
-    // check all devices once
-    CheckDevicesOnce(atadevlist, scsidevlist); 
-    
-    // user has asked us to exit after first check
-    if (quit==3) {
-      PrintOut(LOG_INFO,"Started with '-q onecheck' option. All devices sucessfully checked once.\n"
-               "smartd is exiting (exit status 0)\n");
-      EXIT(0);
-    }
-    
-    // fork into background if needed
-    if (firstpass && !debugmode) {
-#ifdef __CYGWIN__
-     if (!is_service) // don't fork() if running as service via cygrunsrv
-#endif
-      DaemonInit();
-    }
-
-    // set exit and signal handlers, write PID file, set wake-up time
-    if (firstpass){
-      Initialize(&wakeuptime);
-      firstpass=0;
-    }
-    
-    // sleep until next check time, or a signal arrives
-    wakeuptime=dosleep(wakeuptime);
-  }
-}
-
-
-#ifdef _WIN32
-// Main function for Windows
-int main(int argc, char **argv){
-  // Options for smartd windows service
-  static const daemon_winsvc_options svc_opts = {
-    "--service", // cmd_opt
-    "smartd", "SmartD Service", // servicename, displayname
-    // description
-    "Controls and monitors storage devices using the Self-Monitoring, "
-    "Analysis and Reporting Technology System (S.M.A.R.T.) "
-    "built into ATA and SCSI Hard Drives. "
-    PACKAGE_HOMEPAGE
-  };
-  // daemon_main() handles daemon and service specific commands
-  // and starts smartd_main() direct, from a new process,
-  // or via service control manager
-  return daemon_main("smartd", &svc_opts , smartd_main, argc, argv);
-}
-#endif
index f8216833b1d6dec2754a3cfb946acf63a41db028..7cd6d84a418970b60266248516b397e9316f389a 100644 (file)
@@ -2,7 +2,7 @@
 
 # Home page is: http://smartmontools.sourceforge.net
 
-# $Id: smartd.conf,v 1.40 2006/04/12 14:00:12 ballen4705 Exp $
+# $Id: smartd.conf,v 1.42 2006/09/15 08:01:20 sxzzsf Exp $
 
 # smartd will re-read the configuration file if it receives a HUP
 # signal
@@ -32,15 +32,21 @@ DEVICESCAN
 # changes in all attributes except for attribute 194
 #/dev/hdb -H -l error -l selftest -t -I 194 
 
+# Monitor all attributes except normalized Temperature (usually 194),
+# but track Temperature changes >= 4 Celsius, report Temperatures
+# >= 45 Celsius and changes in Raw value of Reallocated_Sector_Ct (5).
+# Send mail on SMART failures or when Temperature is >= 55 Celsius.
+#/dev/hdc -a -I 194 -W 4,45,55 -R 5 -m admin@example.com
+
 # Linux-specific example: monitor a SATA (Serial ATA) disk which uses
 # the libata driver.  This requires a Linux 2.6.15 or later kernel.
 # Note that the disk is addressed via a SCSI device, but the
 # underlying disk type is actually ATA
-# /dev/sda -a -d ata
+#/dev/sda -a -d ata
 
 # A very silent check.  Only report SMART health status if it fails
 # But send an email in this case
-#/dev/hdc -H -m admin@example.com
+#/dev/hdc -H -C 0 -U 0 -m admin@example.com
 
 # First two SCSI disks.  This will monitor everything that smartd can
 # monitor.  Start extended self-tests Wednesdays between 6-7pm and
@@ -64,10 +70,21 @@ DEVICESCAN
 #/dev/twa0 -d 3ware,0 -a -s L/../../2/01
 #/dev/twa0 -d 3ware,1 -a -s L/../../2/03
 
+# Monitor 3 ATA disks directly connected to a HighPoint RocketRAID. Start long
+# self-tests Sundays between 1-2, 2-3, and 3-4 am. 
+#/dev/sdd -d hpt,1/1 -a -s L/../../7/01
+#/dev/sdd -d hpt,1/2 -a -s L/../../7/02
+#/dev/sdd -d hpt,1/3 -a -s L/../../7/03
+
+# Monitor 2 ATA disks connected to the same PMPort which connected to the
+# HighPoint RocketRAID. Start long self-tests Tuesdays between 1-2 and 3-4 am
+#/dev/sdd -d hpt,1/4/1 -a -s L/../../2/01
+#/dev/sdd -d hpt,1/4/2 -a -s L/../../2/03
+
 # HERE IS A LIST OF DIRECTIVES FOR THIS CONFIGURATION FILE.
 # PLEASE SEE THE smartd.conf MAN PAGE FOR DETAILS
 #
-#   -d TYPE Set the device type: ata, scsi, marvell, removable, 3ware,N
+#   -d TYPE Set the device type: ata, scsi, marvell, removable, 3ware,N, hpt,L/M/N
 #   -T TYPE set the tolerance to one of: normal, permissive
 #   -o VAL  Enable/disable automatic offline tests (on/off)
 #   -S VAL  Enable/disable attribute autosave (on/off)
@@ -87,6 +104,7 @@ DEVICESCAN
 #   -I ID   Ignore Attribute ID for -p, -u or -t Directive
 #   -C ID   Report if Current Pending Sector count non-zero
 #   -U ID   Report if Offline Uncorrectable count non-zero
+#   -W D,I,C Monitor Temperature D)ifference, I)nformal limit, C)ritical limit
 #   -v N,ST Modifies labeling of Attribute N (see man page)
 #   -a      Default: equivalent to -H -f -t -l error -l selftest -C 197 -U 198
 #   -F TYPE Use firmware bug workaround. Type is one of: none, samsung
index d8ddc717f7f077c20ca4bfd3304119d23ee47d5f..f0819a95f031d19977643c2dceb7f52ae63c5498 100644 (file)
@@ -1,7 +1,7 @@
 .ig
 Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
-$Id: smartd.conf.5.in,v 1.73 2006/04/12 14:03:14 ballen4705 Exp $
+$Id: smartd.conf.5.in,v 1.77 2006/09/15 08:01:20 sxzzsf Exp $
 
 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
@@ -118,7 +118,11 @@ Section below!
 .B # This is an example smartd startup config file
 .B # /usr/local/etc/smartd.conf for monitoring three
 .B # ATA disks, three SCSI disks, six ATA disks
-.B # behind two 3ware controllers and one SATA disk
+.B # behind two 3ware controllers, three SATA disks
+.B # directly connected to the highpoint rocket-
+.B # raid controller, two SATA disks connected to
+.B # the highpoint rocketraid controller via a pmport
+.B # device and one SATA disk.
 .B #
 .nf
 .B # First ATA disk on two different interfaces. On
@@ -167,6 +171,21 @@ Section below!
 .B \ \ /dev/twa0 -d 3ware,1 -a -s L/../../7/02
 .B #
 .nf
+.B # Three SATA disks on a highpoint rocketraid controller.
+.B # Start short self-tests daily between 1-2, 2-3, and
+.B # 3-4 am.
+.B \ \ /dev/sde -d hpt,1/1 -a -s S/../.././01
+.B \ \ /dev/sde -d hpt,1/2 -a -s S/../.././02
+.B \ \ /dev/sde -d hpt,1/3 -a -s S/../.././03
+.B #
+.nf
+.B # Two SATA disks connected to a highpoint rocketraid 
+.B # via a pmport device.  Start long self-tests Sundays
+.B # between midnight and 1am and 2-3 am.
+.B \ \ /dev/sde -d hpt,1/4/1 -a -s L/../../7/00
+.B \ \ /dev/sde -d hpt,1/4/2 -a -s L/../../7/02
+.B #
+.nf
 .B # The following line enables monitoring of the 
 .B # ATA Error Log and the Self-Test Error Log.  
 .B # It also tracks changes in both Prefailure
@@ -234,7 +253,7 @@ directives can be used for these disks (but see note below).
 .TP
 .B \-d TYPE
 Specifies the type of the device.  This Directive may be used multiple
-times for one device, but the arguments \fIata\fP, \fIscsi\fP,
+times for one device, but the arguments \fIata\fP, \fIscsi\fP, \fIsat\fP,
 \fImarvell\fP, and \fI3ware,N\fP are mutually-exclusive. If more than
 one is given then \fBsmartd\fP will use the last one which appears.
 
@@ -259,6 +278,18 @@ from issuing SCSI commands to an ATA device.
 \fBsmartd\fP
 from issuing ATA commands to a SCSI device.
 
+.I sat
+\- the device type is SCSI to ATA Translation (SAT).
+\fBsmartd\fP
+will generate ATA (smart) commands and then package them in
+the SAT defined ATA PASS THROUGH SCSI commands. The commands
+are then routed through the SCSI pass through interface to the
+operating system. There are two types of ATA PASS THROUGH
+SCSI commands: a 12 byte and 16 byte variant.
+\fBsmartd\fP
+can use either and defaults to the 16 byte variant. This can
+be overridden with this syntax: \'\-d sat,12\' or \'\-d sat,16\'.
+
 .I marvell
 \- Under Linux, interact with SATA disks behind Marvell chip-set
 controllers (using the Marvell rather than libata driver).
@@ -306,6 +337,19 @@ controllers).
 
 .B 3ware controllers are currently ONLY supported under Linux.
 
+.I hpt,L/M/N
+\- the device consists of one or more ATA disks connected to a HighPoint
+RocketRAID controller.  The integer L is the controller id, the integer M
+is the channel number, and the integer N is the PMPort number if it is
+available. The allowed values of L are from 1 to 4 inclusive, M are from
+1 to 8 inclusive and N from 1 to 4 if PMPort available.  And also these
+values are limited by the model of the HighPoint RocketRAID controller.
+In log files and email messages this disk will be identified as
+hpt_X/X/X and X/X/X is the same as L/M/N, note if no N indicated, N set
+to the default value 1.
+
+.B HighPoint RocketRAID controllers are currently ONLY supported under Linux.
+
 .I removable
 \- the device or its media is removable.  This indicates to
 \fBsmartd\fP
@@ -360,6 +404,9 @@ this is probably what you want.
 In the IDLE state, most disks are still spinning, so this is probably
 not what you want.
 
+When a self test is scheduled (see \'\-s\' Directive below), the
+\'\fB\-n\fP\' Directive is ignored, and all tests are carried out.
+
 When a periodic test is skipped, \fBsmartd\fP normally writes an
 informal log message. The message can be suppressed by appending
 the option \',q\' to POWERMODE (like \'\-n standby,q\').
@@ -669,13 +716,15 @@ is set to the argument of \-M exec, if present or else to \'mail\'
 .IP \fBSMARTD_DEVICE\fP 4
 is set to the device path (examples: /dev/hda, /dev/sdb).
 .IP \fBSMARTD_DEVICETYPE\fP 4
-is set to the device type (possible values: ata, scsi, 3ware,N). Here
-N=0,...,15 denotes the ATA disk behind a 3ware RAID controller.
+is set to the device type (possible values: ata, scsi, 3ware,N, hpt,L/M/N).
+Here N=0,...,15 denotes the ATA disk behind a 3ware RAID controller and
+L/M/N denotes the SATA disk behind a HighPoint RocketRAID controller.
 .IP \fBSMARTD_DEVICESTRING\fP 4
 is set to the device description.  For SMARTD_DEVICETYPE of ata or
 scsi, this is the same as SMARTD_DEVICE.  For 3ware RAID controllers,
-the form used is \'/dev/sdc [3ware_disk_01]\'. In this case the device
-string contains a space and is NOT quoted.  So to use
+the form used is \'/dev/sdc [3ware_disk_01]\'.  For HighPoint RocketRAID
+controller, the form is \'/dev/sdd [hpt_1/1/1]\'.  In these cases the
+device string contains a space and is NOT quoted.  So to use
 $SMARTD_DEVICESTRING in a bash script you should probably enclose it
 in double quotes.
 .IP \fBSMARTD_FAILTYPE\fP 4
@@ -915,6 +964,41 @@ to know, because if you have data stored in this disk sector, and you
 need to read it, the read will fail.  Please see the previous \'\-C\'
 option for more details.
 
+.TP
+.B \-W DIFF[,INFO[,CRIT]]
+Report if the current temperature had changed by at least \fBDIFF\fP
+degrees since last report. Report or Warn if the temperature is greater
+or equal than one of \fBINFO\fP or \fBCRIT\fP degrees Celsius. If the
+limit \fBCRIT\fP is reached, a message with loglevel
+\fB\'LOG_CRITICAL\'\fP will be logged to syslog and a warning email
+will be send if '-m' is specified. If only the limit \fBINFO\fP is
+reached, a message with loglevel \fB\'LOG_INFO\'\fP will be logged.
+
+To disable any of the 3 reports, set the corresponding limit to 0.
+Trailing zero arguments may be omitted. By default, all temperature
+reports are disabled (\'-W 0\').
+
+To track temperature changes of at least 2 degrees, use:
+.nf
+\fB \-W 2
+.fi
+To log informal messages on temperatures of at least 40 degrees, use:
+.nf
+\fB \-W 0,40
+.fi
+For warning messages/mails on temperatures of at least 45 degrees, use:
+.nf
+\fB \-W 0,0,45
+.fi
+To combine all of the above reports, use:
+.nf
+\fB \-W 2,40,45
+.fi
+
+For ATA devices, smartd interprets Attribute 194 as Temperature Celsius
+by default. This can be changed to Attribute 9 or 220 by the drive
+database or by the \'-v\' directive, see below.
+
 .TP
 .B \-F TYPE
 [ATA only] Modifies the behavior of \fBsmartd\fP to compensate for
@@ -1250,4 +1334,4 @@ SEE ALSO:
 
 .SH
 CVS ID OF THIS PAGE:
-$Id: smartd.conf.5.in,v 1.73 2006/04/12 14:03:14 ballen4705 Exp $
+$Id: smartd.conf.5.in,v 1.77 2006/09/15 08:01:20 sxzzsf Exp $
diff --git a/smartd.cpp b/smartd.cpp
new file mode 100644 (file)
index 0000000..05725c4
--- /dev/null
@@ -0,0 +1,4357 @@
+/*
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+// unconditionally included files
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>   // umask
+#ifndef _WIN32
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <limits.h>
+
+#if SCSITIMEOUT
+#include <setjmp.h>
+#endif
+
+// see which system files to conditionally include
+#include "config.h"
+
+// conditionally included files
+#ifdef HAVE_GETOPT_LONG
+#include <getopt.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef _WIN32
+#ifdef _MSC_VER
+#pragma warning(disable:4761) // "conversion supplied"
+typedef unsigned short mode_t;
+typedef int pid_t;
+#endif
+#include <io.h> // umask()
+#include <process.h> // getpid()
+#endif // _WIN32
+
+#ifdef __CYGWIN__
+// From <windows.h>:
+// BOOL WINAPI FreeConsole(void);
+extern "C" int __stdcall FreeConsole(void);
+#include <io.h> // setmode()
+#endif // __CYGWIN__
+
+// locally included files
+#include "int64.h"
+#include "atacmds.h"
+#include "ataprint.h"
+#include "extern.h"
+#include "knowndrives.h"
+#include "scsicmds.h"
+#include "smartd.h"
+#include "utility.h"
+
+#ifdef _WIN32
+#include "hostname_win32.h" // gethost/domainname()
+#define HAVE_GETHOSTNAME   1
+#define HAVE_GETDOMAINNAME 1
+// fork()/signal()/initd simulation for native Windows
+#include "daemon_win32.h" // daemon_main/detach/signal()
+#undef SIGNALFN
+#define SIGNALFN  daemon_signal
+#define strsignal daemon_strsignal
+#define sleep     daemon_sleep
+#undef EXIT // see utility.h
+#define EXIT(x)  { exitstatus = daemon_winsvc_exitcode = (x); exit((x)); }
+// SIGQUIT does not exits, CONTROL-Break signals SIGBREAK.
+#define SIGQUIT SIGBREAK
+#define SIGQUIT_KEYNAME "CONTROL-Break"
+#else  // _WIN32
+#ifdef __CYGWIN__
+// 2x CONTROL-C simulates missing SIGQUIT via keyboard
+#define SIGQUIT_KEYNAME "2x CONTROL-C"
+#else // __CYGWIN__
+#define SIGQUIT_KEYNAME "CONTROL-\\"
+#endif // __CYGWIN__
+#endif // _WIN32
+
+#if defined (__SVR4) && defined (__sun)
+extern "C" int getdomainname(char *, int); // no declaration in header files!
+#endif
+
+#define ARGUSED(x) ((void)(x))
+
+// These are CVS identification information for *.cpp and *.h files
+extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *escalade_c_cvsid, 
+                  *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid;
+
+static const char *filenameandversion="$Id: smartd.cpp,v 1.378 2006/09/20 16:17:31 shattered Exp $";
+#ifdef NEED_SOLARIS_ATA_CODE
+extern const char *os_solaris_ata_s_cvsid;
+#endif
+#ifdef _WIN32
+extern const char *daemon_win32_c_cvsid, *hostname_win32_c_cvsid, *syslog_win32_c_cvsid;
+#endif
+const char *smartd_c_cvsid="$Id: smartd.cpp,v 1.378 2006/09/20 16:17:31 shattered Exp $" 
+ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID
+#ifdef DAEMON_WIN32_H_CVSID
+DAEMON_WIN32_H_CVSID
+#endif
+EXTERN_H_CVSID INT64_H_CVSID
+#ifdef HOSTNAME_WIN32_H_CVSID
+HOSTNAME_WIN32_H_CVSID
+#endif
+KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID
+#ifdef SYSLOG_H_CVSID
+SYSLOG_H_CVSID
+#endif
+UTILITY_H_CVSID;
+
+extern const char *reportbug;
+
+// GNU copyleft statement.  Needed for GPL purposes.
+const char *copyleftstring="smartd comes with ABSOLUTELY NO WARRANTY. This is\n"
+                           "free software, and you are welcome to redistribute it\n"
+                           "under the terms of the GNU General Public License\n"
+                           "Version 2. See http://www.gnu.org for further details.\n\n";
+
+extern unsigned char debugmode;
+
+// command-line: how long to sleep between checks
+static int checktime=CHECKTIME;
+
+// command-line: name of PID file (NULL for no pid file)
+static char* pid_file=NULL;
+
+// configuration file name
+#ifndef _WIN32
+static char* configfile = SMARTMONTOOLS_SYSCONFDIR "/" CONFIGFILENAME ;
+#else
+static char* configfile = "./" CONFIGFILENAME ;
+#endif
+// configuration file "name" if read from stdin
+static /*const*/ char * const configfile_stdin = "<stdin>";
+// allocated memory for alternate configuration file name
+static char* configfile_alt = NULL;
+
+// command-line: when should we exit?
+static int quit=0;
+
+// command-line; this is the default syslog(3) log facility to use.
+static int facility=LOG_DAEMON;
+
+#ifdef __CYGWIN__
+// command-line: running as service, so don't fork()
+static int is_service=0;
+#endif
+
+// used for control of printing, passing arguments to atacmds.c
+smartmonctrl *con=NULL;
+
+// pointers to (real or simulated) entries in configuration file, and
+// maximum space currently allocated for these entries.
+cfgfile **cfgentries=NULL;
+int cfgentries_max=0;
+
+// pointers to ATA and SCSI devices being monitored, maximum and
+// actual numbers
+cfgfile **atadevlist=NULL, **scsidevlist=NULL;
+int atadevlist_max=0, scsidevlist_max=0;
+int numdevata=0, numdevscsi=0;
+
+// track memory usage
+extern int64_t bytes;
+
+// exit status
+extern int exitstatus;
+
+// set to one if we catch a USR1 (check devices now)
+volatile int caughtsigUSR1=0;
+
+#ifdef _WIN32
+// set to one if we catch a USR2 (toggle debug mode)
+volatile int caughtsigUSR2=0;
+#endif
+
+// set to one if we catch a HUP (reload config file). In debug mode,
+// set to two, if we catch INT (also reload config file).
+volatile int caughtsigHUP=0;
+
+// set to signal value if we catch INT, QUIT, or TERM
+volatile int caughtsigEXIT=0;
+
+#if SCSITIMEOUT
+// stack environment if we time out during SCSI access (USB devices)
+jmp_buf registerscsienv;
+#endif
+
+// tranlate cfg->pending into the correct Attribute numbers
+void TranslatePending(unsigned short pending, unsigned char *current, unsigned char *offline) {
+
+  unsigned char curr = CURR_PEND(pending);
+  unsigned char off =  OFF_PEND(pending);
+
+  // look for special value of CUR_UNC_DEFAULT that means DONT
+  // monitor. 0 means DO test.
+  if (curr==CUR_UNC_DEFAULT)
+    curr=0;
+  else if (curr==0)
+    curr=CUR_UNC_DEFAULT;
+       
+  // look for special value of OFF_UNC_DEFAULT that means DONT
+  // monitor.  0 means DO TEST.
+  if (off==OFF_UNC_DEFAULT)
+    off=0;
+  else if (off==0)
+    off=OFF_UNC_DEFAULT;
+
+  *current=curr;
+  *offline=off;
+
+  return;
+}
+
+
+// free all memory associated with selftest part of configfile entry.  Return NULL
+testinfo* FreeTestData(testinfo *data){
+  
+  // make sure we have something to do.
+  if (!data)
+    return NULL;
+  
+  // free space for text pattern
+  data->regex=FreeNonZero(data->regex, -1, __LINE__, filenameandversion);
+  
+  // free compiled expression
+  regfree(&(data->cregex));
+
+  // make sure that no sign of the compiled expression is left behind
+  // (just in case, to help detect bugs if we ever try and refer to
+  // that again).
+  memset(&(data->cregex), '0', sizeof(regex_t));
+
+  // free remaining memory space
+  data=FreeNonZero(data, sizeof(testinfo), __LINE__, filenameandversion);
+
+  return NULL;
+}
+
+cfgfile **AllocateMoreSpace(cfgfile **oldarray, int *oldsize, char *listname){
+  // for now keep BLOCKSIZE small to help detect coding problems.
+  // Perhaps increase in the future.
+  const int BLOCKSIZE=8;
+  int i;
+  int olds = *oldsize;
+  int news = olds + BLOCKSIZE;
+  cfgfile **newptr=(cfgfile **)realloc(oldarray, news*sizeof(cfgfile *));
+  
+  // did we get more space?
+  if (newptr) {
+
+    // clear remaining entries ala calloc()
+    for (i=olds; i<news; i++)
+      newptr[i]=NULL;
+    
+    bytes += BLOCKSIZE*sizeof(cfgfile *);
+    
+    *oldsize=news;
+    
+#if 0
+    PrintOut(LOG_INFO, "allocating %d slots for %s\n", BLOCKSIZE, listname);
+#endif
+
+    return newptr;
+  }
+  
+  PrintOut(LOG_CRIT, "out of memory for allocating %s list\n", listname);
+  EXIT(EXIT_NOMEM);
+}
+
+void PrintOneCVS(const char *a_cvs_id){
+  char out[CVSMAXLEN];
+  printone(out,a_cvs_id);
+  PrintOut(LOG_INFO,"%s",out);
+  return;
+}
+
+// prints CVS identity information for the executable
+void PrintCVS(void){
+  char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]";
+
+  PrintOut(LOG_INFO,(char *)copyleftstring);
+  PrintOut(LOG_INFO,"CVS version IDs of files used to build this code are:\n");
+  PrintOneCVS(atacmdnames_c_cvsid);
+  PrintOneCVS(atacmds_c_cvsid);
+  PrintOneCVS(ataprint_c_cvsid);
+#ifdef _WIN32
+  PrintOneCVS(daemon_win32_c_cvsid);
+#endif
+#ifdef _WIN32
+  PrintOneCVS(hostname_win32_c_cvsid);
+#endif
+  PrintOneCVS(knowndrives_c_cvsid);
+  PrintOneCVS(os_XXXX_c_cvsid);
+#ifdef NEED_SOLARIS_ATA_CODE
+  PrintOneCVS( os_solaris_ata_s_cvsid);
+#endif
+  PrintOneCVS(scsicmds_c_cvsid);
+  PrintOneCVS(smartd_c_cvsid);
+#ifdef _WIN32
+  PrintOneCVS(syslog_win32_c_cvsid);
+#endif
+  PrintOneCVS(utility_c_cvsid);
+  PrintOut(LOG_INFO, "\nsmartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n");
+  PrintOut(LOG_INFO, "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n");
+  PrintOut(LOG_INFO, "smartmontools build configured: " SMARTMONTOOLS_CONFIGURE_DATE "\n");
+  PrintOut(LOG_INFO, "smartd compile dated " __DATE__ " at "__TIME__ "\n");
+  PrintOut(LOG_INFO, "smartmontools configure arguments: %s\n", configargs);
+  return;
+}
+
+// Removes config file entry, freeing all memory
+void RmConfigEntry(cfgfile **anentry, int whatline){
+  
+  cfgfile *cfg;
+
+  // pointer should never be null!
+  if (!anentry){
+    PrintOut(LOG_CRIT,"Internal error in RmConfigEntry() at line %d of file %s\n%s",
+             whatline, filenameandversion, reportbug);    
+    EXIT(EXIT_BADCODE);
+  }
+  
+  // only remove entries that exist!
+  if (!(cfg=*anentry))
+    return;
+
+  // entry exists -- free all of its memory  
+  cfg->name            = FreeNonZero(cfg->name,           -1,__LINE__,filenameandversion);
+  cfg->smartthres      = FreeNonZero(cfg->smartthres,      sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion);
+  cfg->smartval        = FreeNonZero(cfg->smartval,        sizeof(struct ata_smart_values),__LINE__,filenameandversion);
+  cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion);
+  cfg->attributedefs   = FreeNonZero(cfg->attributedefs,   MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion);
+  if (cfg->mailwarn){
+    cfg->mailwarn->address         = FreeNonZero(cfg->mailwarn->address,        -1,__LINE__,filenameandversion);
+    cfg->mailwarn->emailcmdline    = FreeNonZero(cfg->mailwarn->emailcmdline,   -1,__LINE__,filenameandversion);
+    cfg->mailwarn                  = FreeNonZero(cfg->mailwarn,   sizeof(maildata),__LINE__,filenameandversion);
+  }
+  cfg->testdata        = FreeTestData(cfg->testdata);
+  *anentry             = FreeNonZero(cfg,                  sizeof(cfgfile),__LINE__,filenameandversion);
+
+  return;
+}
+
+// deallocates all memory associated with cfgentries list
+void RmAllConfigEntries(){
+  int i;
+
+  for (i=0; i<cfgentries_max; i++)
+    RmConfigEntry(cfgentries+i, __LINE__);
+
+  cfgentries=FreeNonZero(cfgentries, sizeof(cfgfile *)*cfgentries_max, __LINE__, filenameandversion);
+  cfgentries_max=0;
+
+  return;
+}
+
+// deallocates all memory associated with ATA/SCSI device lists
+void RmAllDevEntries(){
+  int i;
+  
+  for (i=0; i<atadevlist_max; i++)
+    RmConfigEntry(atadevlist+i, __LINE__);
+
+  atadevlist=FreeNonZero(atadevlist, sizeof(cfgfile *)*atadevlist_max, __LINE__, filenameandversion);
+  atadevlist_max=0;
+
+  for (i=0; i<scsidevlist_max; i++)
+    RmConfigEntry(scsidevlist+i, __LINE__);
+  
+  scsidevlist=FreeNonZero(scsidevlist, sizeof(cfgfile *)*scsidevlist_max, __LINE__, filenameandversion);
+  scsidevlist_max=0;
+
+  return;
+}
+
+// remove the PID file
+void RemovePidFile(){
+  if (pid_file) {
+    if ( -1==unlink(pid_file) )
+      PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n", 
+               pid_file, strerror(errno));
+    pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion);
+  }
+  return;
+}
+
+
+//  Note if we catch a SIGUSR1
+void USR1handler(int sig){
+  if (SIGUSR1==sig)
+    caughtsigUSR1=1;
+  return;
+}
+
+#ifdef _WIN32
+//  Note if we catch a SIGUSR2
+void USR2handler(int sig){
+  if (SIGUSR2==sig)
+    caughtsigUSR2=1;
+  return;
+}
+#endif
+
+// Note if we catch a HUP (or INT in debug mode)
+void HUPhandler(int sig){
+  if (sig==SIGHUP)
+    caughtsigHUP=1;
+  else
+    caughtsigHUP=2;
+  return;
+}
+
+// signal handler for TERM, QUIT, and INT (if not in debug mode)
+void sighandler(int sig){
+  if (!caughtsigEXIT)
+    caughtsigEXIT=sig;
+  return;
+}
+
+
+// signal handler that prints Goodbye message and removes pidfile
+void Goodbye(void){
+  
+  // clean up memory -- useful for debugging
+  RmAllConfigEntries();
+  RmAllDevEntries();
+
+  // delete PID file, if one was created
+  RemovePidFile();
+
+  // remove alternate configfile name
+  configfile_alt=FreeNonZero(configfile_alt, -1,__LINE__,filenameandversion);
+
+  // useful for debugging -- have we managed memory correctly?
+  if (debugmode || (bytes && exitstatus!=EXIT_NOMEM))
+    PrintOut(LOG_INFO, "Memory still allocated for devices at exit is %" PRId64 " bytes.\n", bytes);
+
+  // if we are exiting because of a code bug, tell user
+  if (exitstatus==EXIT_BADCODE || (bytes && exitstatus!=EXIT_NOMEM))
+        PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n");
+
+  if (exitstatus==0 && bytes)
+    exitstatus=EXIT_BADCODE;
+
+  // and this should be the final output from smartd before it exits
+  PrintOut(exitstatus?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", exitstatus);
+
+  return;
+}
+
+#define ENVLENGTH 1024
+
+// a replacement for setenv() which is not available on all platforms.
+// Note that the string passed to putenv must not be freed or made
+// invalid, since a pointer to it is kept by putenv(). This means that
+// it must either be a static buffer or allocated off the heap. The
+// string can be freed if the environment variable is redefined or
+// deleted via another call to putenv(). So we keep these on the stack
+// as long as the popen() call is underway.
+int exportenv(char* stackspace, const char *name, const char *value){
+  snprintf(stackspace,ENVLENGTH, "%s=%s", name, value);
+  return putenv(stackspace);
+}
+
+char* dnsdomain(const char* hostname) {
+  char *p = NULL;
+#ifdef HAVE_GETHOSTBYNAME
+  struct hostent *hp;
+  
+  if ((hp = gethostbyname(hostname))) {
+    // Does this work if gethostbyname() returns an IPv6 name in
+    // colon/dot notation?  [BA]
+    if ((p = strchr(hp->h_name, '.')))
+      p++; // skip "."
+  }
+#else
+  ARGUSED(hostname);
+#endif
+  return p;
+}
+
+#define EBUFLEN 1024
+
+// If either address or executable path is non-null then send and log
+// a warning email, or execute executable
+void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
+  char command[2048], message[256], hostname[256], domainname[256], additional[256],fullmessage[1024];
+  char original[256], further[256], nisdomain[256], subject[256],dates[DATEANDEPOCHLEN];
+  char environ_strings[11][ENVLENGTH];
+  time_t epoch;
+  va_list ap;
+  const int day=24*3600;
+  int days=0;
+  char *whichfail[]={
+    "EmailTest",                  // 0
+    "Health",                     // 1
+    "Usage",                      // 2
+    "SelfTest",                   // 3
+    "ErrorCount",                 // 4
+    "FailedHealthCheck",          // 5
+    "FailedReadSmartData",        // 6
+    "FailedReadSmartErrorLog",    // 7
+    "FailedReadSmartSelfTestLog", // 8
+    "FailedOpenDevice",           // 9
+    "CurrentPendingSector",       // 10
+    "OfflineUncorrectableSector", // 11
+    "Temperature"                 // 12
+  };
+  
+  char *address, *executable;
+  mailinfo *mail;
+  maildata* data=cfg->mailwarn;
+#ifndef _WIN32
+  FILE *pfp=NULL;
+#else
+  char stdinbuf[1024]; int boxmsgoffs, boxtype;
+#endif
+  const char *newadd=NULL, *newwarn=NULL;
+  const char *unknown="[Unknown]";
+
+  // See if user wants us to send mail
+  if(!data)
+    return;
+  
+  address=data->address;
+  executable=data->emailcmdline;
+  
+  if (!address && !executable)
+    return;
+  
+  // which type of mail are we sending?
+  mail=(data->maillog)+which;
+  
+  // checks for sanity
+  if (data->emailfreq<1 || data->emailfreq>3) {
+    PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg->mailwarn->emailfreq=%d\n",data->emailfreq);
+    return;
+  }
+  if (which<0 || which>=SMARTD_NMAIL || sizeof(whichfail)!=SMARTD_NMAIL*sizeof(char *)) {
+    PrintOut(LOG_CRIT,"Contact " PACKAGE_BUGREPORT "; internal error in MailWarning(): which=%d, size=%d\n",
+             which, (int)sizeof(whichfail));
+    return;
+  }
+  
+  // Return if a single warning mail has been sent.
+  if ((data->emailfreq==1) && mail->logged)
+    return;
+
+  // Return if this is an email test and one has already been sent.
+  if (which == 0 && mail->logged)
+    return;
+  
+  // To decide if to send mail, we need to know what time it is.
+  epoch=time(NULL);
+
+  // Return if less than one day has gone by
+  if (data->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day))
+    return;
+
+  // Return if less than 2^(logged-1) days have gone by
+  if (data->emailfreq==3 && mail->logged){
+    days=0x01<<(mail->logged-1);
+    days*=day;
+    if  (epoch<(mail->lastsent+days))
+      return;
+  }
+
+  // record the time of this mail message, and the first mail message
+  if (!mail->logged)
+    mail->firstsent=epoch;
+  mail->lastsent=epoch;
+  
+  // get system host & domain names (not null terminated if length=MAX) 
+#ifdef HAVE_GETHOSTNAME
+  if (gethostname(hostname, 256))
+    strcpy(hostname, unknown);
+  else {
+    char *p=NULL;
+    hostname[255]='\0';
+    p = dnsdomain(hostname);
+    if (p && *p) {
+      strncpy(domainname, p, 255);
+      domainname[255]='\0';
+    } else
+      strcpy(domainname, unknown);
+  }
+#else
+  strcpy(hostname, unknown);
+  strcpy(domainname, unknown);
+#endif
+  
+#ifdef HAVE_GETDOMAINNAME
+  if (getdomainname(nisdomain, 256))
+    strcpy(nisdomain, unknown);
+  else
+    nisdomain[255]='\0';
+#else
+  strcpy(nisdomain, unknown);
+#endif
+  
+  // print warning string into message
+  va_start(ap, fmt);
+  vsnprintf(message, 256, fmt, ap);
+  va_end(ap);
+
+  // appropriate message about further information
+  additional[0]=original[0]=further[0]='\0';
+  if (which) {
+    sprintf(further,"You can also use the smartctl utility for further investigation.\n");
+
+    switch (data->emailfreq){
+    case 1:
+      sprintf(additional,"No additional email messages about this problem will be sent.\n");
+      break;
+    case 2:
+      sprintf(additional,"Another email message will be sent in 24 hours if the problem persists.\n");
+      break;
+    case 3:
+      sprintf(additional,"Another email message will be sent in %d days if the problem persists\n",
+              (0x01)<<mail->logged);
+      break;
+    }
+    if (data->emailfreq>1 && mail->logged){
+      dateandtimezoneepoch(dates, mail->firstsent);
+      sprintf(original,"The original email about this issue was sent at %s\n", dates);
+    }
+  }
+  
+  snprintf(subject, 256,"SMART error (%s) detected on host: %s", whichfail[which], hostname);
+
+  // If the user has set cfg->emailcmdline, use that as mailer, else "mail" or "mailx".
+  if (!executable)
+#ifdef DEFAULT_MAILER
+    executable = DEFAULT_MAILER ;
+#else
+#ifndef _WIN32
+    executable = "mail";
+#else
+    executable = "blat"; // http://blat.sourceforge.net/
+#endif
+#endif
+
+  // make a private copy of address with commas replaced by spaces
+  // to separate recipients
+  if (address) {
+    address=CustomStrDup(data->address, 1, __LINE__, filenameandversion);
+#ifndef _WIN32 // blat mailer needs comma
+    {
+      char *comma=address;
+      while ((comma=strchr(comma, ',')))
+        *comma=' ';
+    }
+#endif
+  }
+
+  // Export information in environment variables that will be useful
+  // for user scripts
+  exportenv(environ_strings[0], "SMARTD_MAILER", executable);
+  exportenv(environ_strings[1], "SMARTD_MESSAGE", message);
+  exportenv(environ_strings[2], "SMARTD_SUBJECT", subject);
+  dateandtimezoneepoch(dates, mail->firstsent);
+  exportenv(environ_strings[3], "SMARTD_TFIRST", dates);
+  snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent);
+  exportenv(environ_strings[4], "SMARTD_TFIRSTEPOCH", dates);
+  exportenv(environ_strings[5], "SMARTD_FAILTYPE", whichfail[which]);
+  if (address)
+    exportenv(environ_strings[6], "SMARTD_ADDRESS", address);
+  exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg->name);
+
+  switch (cfg->controller_type) {
+  case CONTROLLER_3WARE_678K:
+  case CONTROLLER_3WARE_9000_CHAR:
+  case CONTROLLER_3WARE_678K_CHAR:
+    {
+      char *s,devicetype[16];
+      sprintf(devicetype, "3ware,%d", cfg->controller_port-1);
+      exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype);
+      if ((s=strchr(cfg->name, ' ')))
+       *s='\0';
+      exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
+      if (s)
+       *s=' ';
+    }
+    break;
+  case CONTROLLER_ATA:
+    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "ata");
+    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
+    break;
+  case CONTROLLER_MARVELL_SATA:
+    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "marvell");
+    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
+    break;
+  case CONTROLLER_SCSI:
+    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "scsi");
+    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
+  case CONTROLLER_SAT:
+    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "sat");
+    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
+  case CONTROLLER_HPT:
+    {
+      char *s,devicetype[16];
+      sprintf(devicetype, "hpt,%d/%d/%d", cfg->hpt_data[0],
+              cfg->hpt_data[1], cfg->hpt_data[2]);
+      exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype);
+      if ((s=strchr(cfg->name, ' ')))
+        *s='\0';
+      exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
+      if (s)
+        *s=' ';
+    }
+  }
+
+  snprintf(fullmessage, 1024,
+             "This email was generated by the smartd daemon running on:\n\n"
+             "   host name: %s\n"
+             "  DNS domain: %s\n"
+             "  NIS domain: %s\n\n"
+             "The following warning/error was logged by the smartd daemon:\n\n"
+             "%s\n\n"
+             "For details see host's SYSLOG (default: /var/log/messages).\n\n"
+             "%s%s%s",
+            hostname, domainname, nisdomain, message, further, original, additional);
+  exportenv(environ_strings[10], "SMARTD_FULLMESSAGE", fullmessage);
+
+  // now construct a command to send this as EMAIL
+#ifndef _WIN32
+  if (address)
+    snprintf(command, 2048, 
+             "$SMARTD_MAILER -s '%s' %s 2>&1 << \"ENDMAIL\"\n"
+            "%sENDMAIL\n", subject, address, fullmessage);
+  else
+    snprintf(command, 2048, "%s 2>&1", executable);
+  
+  // tell SYSLOG what we are about to do...
+  newadd=address?address:"<nomailer>";
+  newwarn=which?"Warning via":"Test of";
+
+  PrintOut(LOG_INFO,"%s %s to %s ...\n",
+           which?"Sending warning via":"Executing test of", executable, newadd);
+  
+  // issue the command to send mail or to run the user's executable
+  errno=0;
+  if (!(pfp=popen(command, "r")))
+    // failed to popen() mail process
+    PrintOut(LOG_CRIT,"%s %s to %s: failed (fork or pipe failed, or no memory) %s\n", 
+            newwarn,  executable, newadd, errno?strerror(errno):"");
+  else {
+    // pipe suceeded!
+    int len, status;
+    char buffer[EBUFLEN];
+
+    // if unexpected output on stdout/stderr, null terminate, print, and flush
+    if ((len=fread(buffer, 1, EBUFLEN, pfp))) {
+      int count=0;
+      int newlen = len<EBUFLEN ? len : EBUFLEN-1;
+      buffer[newlen]='\0';
+      PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%s%d bytes) to STDOUT/STDERR: \n%s\n", 
+              newwarn, executable, newadd, len!=newlen?"here truncated to ":"", newlen, buffer);
+      
+      // flush pipe if needed
+      while (fread(buffer, 1, EBUFLEN, pfp) && count<EBUFLEN)
+       count++;
+
+      // tell user that pipe was flushed, or that something is really wrong
+      if (count && count<EBUFLEN)
+       PrintOut(LOG_CRIT,"%s %s to %s: flushed remaining STDOUT/STDERR\n", 
+                newwarn, executable, newadd);
+      else if (count)
+       PrintOut(LOG_CRIT,"%s %s to %s: more than 1 MB STDOUT/STDERR flushed, breaking pipe\n", 
+                newwarn, executable, newadd);
+    }
+    
+    // if something went wrong with mail process, print warning
+    errno=0;
+    if (-1==(status=pclose(pfp)))
+      PrintOut(LOG_CRIT,"%s %s to %s: pclose(3) failed %s\n", newwarn, executable, newadd,
+              errno?strerror(errno):"");
+    else {
+      // mail process apparently succeeded. Check and report exit status
+      int status8;
+
+      if (WIFEXITED(status)) {
+       // exited 'normally' (but perhaps with nonzero status)
+       status8=WEXITSTATUS(status);
+       
+       if (status8>128)  
+         PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d) perhaps caught signal %d [%s]\n", 
+                  newwarn, executable, newadd, status, status8, status8-128, strsignal(status8-128));
+       else if (status8)  
+         PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d)\n", 
+                  newwarn, executable, newadd, status, status8);
+       else
+         PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
+      }
+      
+      if (WIFSIGNALED(status))
+       PrintOut(LOG_INFO,"%s %s to %s: exited because of uncaught signal %d [%s]\n", 
+                newwarn, executable, newadd, WTERMSIG(status), strsignal(WTERMSIG(status)));
+      
+      // this branch is probably not possible. If subprocess is
+      // stopped then pclose() should not return.
+      if (WIFSTOPPED(status)) 
+       PrintOut(LOG_CRIT,"%s %s to %s: process STOPPED because it caught signal %d [%s]\n",
+                newwarn, executable, newadd, WSTOPSIG(status), strsignal(WSTOPSIG(status)));
+      
+    }
+  }
+  
+#else // _WIN32
+
+  // No "here-documents" on Windows, so must use separate commandline and stdin
+  command[0] = stdinbuf[0] = 0;
+  boxtype = -1; boxmsgoffs = 0;
+  newadd = "<nomailer>";
+  if (address) {
+    // address "[sys]msgbox ..." => show warning (also) as [system modal ]messagebox
+    int addroffs = (!strncmp(address, "sys", 3) ? 3 : 0);
+    if (!strncmp(address+addroffs, "msgbox", 6) && (!address[addroffs+6] || address[addroffs+6] == ',')) {
+      boxtype = (addroffs > 0 ? 1 : 0);
+      addroffs += 6;
+      if (address[addroffs])
+        addroffs++;
+    }
+    else
+      addroffs = 0;
+
+    if (address[addroffs]) {
+      // Use "blat" parameter syntax (TODO: configure via -M for other mailers)
+      snprintf(command, sizeof(command),
+               "%s - -q -subject \"%s\" -to \"%s\"",
+               executable, subject, address+addroffs);
+      newadd = address+addroffs;
+    }
+    // Message for mail [0...] and messagebox [boxmsgoffs...]
+    snprintf(stdinbuf, sizeof(stdinbuf),
+             "This email was generated by the smartd daemon running on:\n\n"
+             "   host name: %s\n"
+             "  DNS domain: %s\n"
+//           "  NIS domain: %s\n"
+             "\n%n"
+             "The following warning/error was logged by the smartd daemon:\n\n"
+             "%s\n\n"
+             "For details see the event log or log file of smartd.\n\n"
+             "%s%s%s"
+             "\n",
+             hostname, /*domainname, */ nisdomain, &boxmsgoffs, message, further, original, additional);
+  }
+  else
+    snprintf(command, sizeof(command), "%s", executable);
+
+  newwarn=which?"Warning via":"Test of";
+  if (boxtype >= 0) {
+    // show message box
+    daemon_messagebox(boxtype, subject, stdinbuf+boxmsgoffs);
+    PrintOut(LOG_INFO,"%s message box\n", newwarn);
+  }
+  if (command[0]) {
+    char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog()
+    int rc;
+    // run command
+    PrintOut(LOG_INFO,"%s %s to %s ...\n",
+             (which?"Sending warning via":"Executing test of"), executable, newadd);
+    rc = daemon_spawn(command, stdinbuf, strlen(stdinbuf), stdoutbuf, sizeof(stdoutbuf));
+    if (rc >= 0 && stdoutbuf[0])
+      PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%d bytes) to STDOUT/STDERR:\n%s\n",
+        newwarn, executable, newadd, strlen(stdoutbuf), stdoutbuf);
+    if (rc != 0)
+      PrintOut(LOG_CRIT,"%s %s to %s: failed, exit status %d\n",
+        newwarn, executable, newadd, rc);
+    else
+      PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
+  }
+
+#endif // _WIN32
+
+  // increment mail sent counter
+  mail->logged++;
+  
+  // free copy of address (without commas)
+  address=FreeNonZero(address, -1, __LINE__, filenameandversion);
+
+  return;
+}
+
+// Printing function for watching ataprint commands, or losing them
+// [From GLIBC Manual: Since the prototype doesn't specify types for
+// optional arguments, in a call to a variadic function the default
+// argument promotions are performed on the optional argument
+// values. This means the objects of type char or short int (whether
+// signed or not) are promoted to either int or unsigned int, as
+// appropriate.]
+void pout(const char *fmt, ...){
+  va_list ap;
+
+  // get the correct time in syslog()
+  FixGlibcTimeZoneBug();
+  // initialize variable argument list 
+  va_start(ap,fmt);
+  // in debug==1 mode we will print the output from the ataprint.o functions!
+  if (debugmode && debugmode!=2)
+#ifdef _WIN32
+   if (facility == LOG_LOCAL1) // logging to stdout
+    vfprintf(stderr,fmt,ap);
+   else   
+#endif
+    vprintf(fmt,ap);
+  // in debug==2 mode we print output from knowndrives.o functions
+  else if (debugmode==2 || con->reportataioctl || con->reportscsiioctl || con->controller_port) {
+    openlog("smartd", LOG_PID, facility);
+    vsyslog(LOG_INFO, fmt, ap);
+    closelog();
+  }
+  va_end(ap);
+  fflush(NULL);
+  return;
+}
+
+// This function prints either to stdout or to the syslog as needed.
+// This function is also used by utility.cpp to report LOG_CRIT errors.
+void PrintOut(int priority, const char *fmt, ...){
+  va_list ap;
+  
+  // get the correct time in syslog()
+  FixGlibcTimeZoneBug();
+  // initialize variable argument list 
+  va_start(ap,fmt);
+  if (debugmode) 
+#ifdef _WIN32
+   if (facility == LOG_LOCAL1) // logging to stdout
+    vfprintf(stderr,fmt,ap);
+   else   
+#endif
+    vprintf(fmt,ap);
+  else {
+    openlog("smartd", LOG_PID, facility);
+    vsyslog(priority,fmt,ap);
+    closelog();
+  }
+  va_end(ap);
+  return;
+}
+
+// Forks new process, closes ALL file descriptors, redirects stdin,
+// stdout, and stderr.  Not quite daemon().  See
+// http://www.iar.unlp.edu.ar/~fede/revistas/lj/Magazines/LJ47/2335.html
+// for a good description of why we do things this way.
+void DaemonInit(){
+#ifndef _WIN32
+  pid_t pid;
+  int i;  
+
+  // flush all buffered streams.  Else we might get two copies of open
+  // streams since both parent and child get copies of the buffers.
+  fflush(NULL);
+  
+  if ((pid=fork()) < 0) {
+    // unable to fork!
+    PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
+    EXIT(EXIT_STARTUP);
+  }
+  else if (pid)
+    // we are the parent process -- exit cleanly
+    EXIT(0);
+  
+  // from here on, we are the child process.
+  setsid();
+
+  // Fork one more time to avoid any possibility of having terminals
+  if ((pid=fork()) < 0) {
+    // unable to fork!
+    PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
+    EXIT(EXIT_STARTUP);
+  }
+  else if (pid)
+    // we are the parent process -- exit cleanly
+    EXIT(0);
+
+  // Now we are the child's child...
+
+  // close any open file descriptors
+  for (i=getdtablesize();i>=0;--i)
+    close(i);
+  
+#ifdef __CYGWIN__
+  // Cygwin's setsid() does not detach the process from Windows console
+  FreeConsole();
+#endif // __CYGWIN__
+
+  // redirect any IO attempts to /dev/null for stdin
+  i=open("/dev/null",O_RDWR);
+  // stdout
+  dup(i);
+  // stderr
+  dup(i);
+  umask(0);
+  chdir("/");
+  
+  PrintOut(LOG_INFO, "smartd has fork()ed into background mode. New PID=%d.\n", (int)getpid());
+
+#else // _WIN32
+
+  // No fork() on native Win32
+  // Detach this process from console
+  fflush(NULL);
+  if (daemon_detach("smartd")) {
+    PrintOut(LOG_CRIT,"smartd unable to detach from console!\n");
+    EXIT(EXIT_STARTUP);
+  }
+  // stdin/out/err now closed if not redirected
+
+#endif // _WIN32
+  return;
+}
+
+// create a PID file containing the current process id
+void WritePidFile() {
+  if (pid_file) {
+    int error = 0;
+    pid_t pid = getpid();
+    mode_t old_umask;
+    FILE* fp; 
+
+#ifndef __CYGWIN__
+    old_umask = umask(0077); // rwx------
+#else
+    // Cygwin: smartd service runs on system account, ensure PID file can be read by admins
+    old_umask = umask(0033); // rwxr--r--
+#endif
+    fp = fopen(pid_file, "w");
+    umask(old_umask);
+    if (fp == NULL) {
+      error = 1;
+    } else if (fprintf(fp, "%d\n", (int)pid) <= 0) {
+      error = 1;
+    } else if (fclose(fp) != 0) {
+      error = 1;
+    }
+    if (error) {
+      PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file);
+      EXIT(EXIT_PID);
+    }
+    PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file, (int)pid);
+  }
+  return;
+}
+
+// Prints header identifying version of code and home
+void PrintHead(){
+#ifdef HAVE_GET_OS_VERSION_STR
+  const char * ver = get_os_version_str();
+#else
+  const char * ver = SMARTMONTOOLS_BUILD_HOST;
+#endif
+  PrintOut(LOG_INFO,"smartd version %s [%s] Copyright (C) 2002-6 Bruce Allen\n", PACKAGE_VERSION, ver);
+  PrintOut(LOG_INFO,"Home page is " PACKAGE_HOMEPAGE "\n\n");
+  return;
+}
+
+// prints help info for configuration file Directives
+void Directives() {
+  PrintOut(LOG_INFO,
+           "Configuration file (%s) Directives (after device name):\n"
+           "  -d TYPE Set the device type: ata, scsi, marvell, removable, sat, 3ware,N, hpt,L/M/N\n"
+           "  -T TYPE Set the tolerance to one of: normal, permissive\n"
+           "  -o VAL  Enable/disable automatic offline tests (on/off)\n"
+           "  -S VAL  Enable/disable attribute autosave (on/off)\n"
+           "  -n MODE No check if: never[,q], sleep[,q], standby[,q], idle[,q]\n"
+           "  -H      Monitor SMART Health Status, report if failed\n"
+           "  -s REG  Do Self-Test at time(s) given by regular expression REG\n"
+           "  -l TYPE Monitor SMART log.  Type is one of: error, selftest\n"
+           "  -f      Monitor 'Usage' Attributes, report failures\n"
+           "  -m ADD  Send email warning to address ADD\n"
+           "  -M TYPE Modify email warning behavior (see man page)\n"
+           "  -p      Report changes in 'Prefailure' Attributes\n"
+           "  -u      Report changes in 'Usage' Attributes\n"
+           "  -t      Equivalent to -p and -u Directives\n"
+           "  -r ID   Also report Raw values of Attribute ID with -p, -u or -t\n"
+           "  -R ID   Track changes in Attribute ID Raw value with -p, -u or -t\n"
+           "  -i ID   Ignore Attribute ID for -f Directive\n"
+           "  -I ID   Ignore Attribute ID for -p, -u or -t Directive\n"
+          "  -C ID   Monitor Current Pending Sectors in Attribute ID\n"
+          "  -U ID   Monitor Offline Uncorrectable Sectors in Attribute ID\n"
+           "  -W D,I,C Monitor Temperature D)ifference, I)nformal limit, C)ritical limit\n"
+           "  -v N,ST Modifies labeling of Attribute N (see man page)  \n"
+           "  -P TYPE Drive-specific presets: use, ignore, show, showall\n"
+           "  -a      Default: -H -f -t -l error -l selftest -C 197 -U 198\n"
+           "  -F TYPE Firmware bug workaround: none, samsung, samsung2\n"
+           "   #      Comment: text after a hash sign is ignored\n"
+           "   \\      Line continuation character\n"
+           "Attribute ID is a decimal integer 1 <= ID <= 255\n"
+          "Use ID = 0 to turn off -C and/or -U Directives\n"
+           "Example: /dev/hda -a\n", 
+           configfile);
+  return;
+}
+
+/* Returns a pointer to a static string containing a formatted list of the valid
+   arguments to the option opt or NULL on failure. */
+const char *GetValidArgList(char opt) {
+  switch (opt) {
+  case 'c':
+    return "<FILE_NAME>, -";
+  case 's':
+    return "valid_regular_expression";
+  case 'l':
+    return "daemon, local0, local1, local2, local3, local4, local5, local6, local7";
+  case 'q':
+    return "nodev, errors, nodevstartup, never, onecheck, showtests";
+  case 'r':
+    return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
+  case 'p':
+    return "<FILE_NAME>";
+  case 'i':
+    return "<INTEGER_SECONDS>";
+  default:
+    return NULL;
+  }
+}
+
+/* prints help information for command syntax */
+void Usage (void){
+  PrintOut(LOG_INFO,"Usage: smartd [options]\n\n");
+#ifdef HAVE_GETOPT_LONG
+  PrintOut(LOG_INFO,"  -c NAME|-, --configfile=NAME|-\n");
+  PrintOut(LOG_INFO,"        Read configuration file NAME or stdin [default is %s]\n\n", configfile);
+  PrintOut(LOG_INFO,"  -d, --debug\n");
+  PrintOut(LOG_INFO,"        Start smartd in debug mode\n\n");
+  PrintOut(LOG_INFO,"  -D, --showdirectives\n");
+  PrintOut(LOG_INFO,"        Print the configuration file Directives and exit\n\n");
+  PrintOut(LOG_INFO,"  -h, --help, --usage\n");
+  PrintOut(LOG_INFO,"        Display this help and exit\n\n");
+  PrintOut(LOG_INFO,"  -i N, --interval=N\n");
+  PrintOut(LOG_INFO,"        Set interval between disk checks to N seconds, where N >= 10\n\n");
+  PrintOut(LOG_INFO,"  -l local[0-7], --logfacility=local[0-7]\n");
+#ifndef _WIN32
+  PrintOut(LOG_INFO,"        Use syslog facility local0 - local7 or daemon [default]\n\n");
+#else
+  PrintOut(LOG_INFO,"        Log to \"./smartd.log\", stdout, stderr [default is event log]\n\n");
+#endif
+  PrintOut(LOG_INFO,"  -p NAME, --pidfile=NAME\n");
+  PrintOut(LOG_INFO,"        Write PID file NAME\n\n");
+  PrintOut(LOG_INFO,"  -q WHEN, --quit=WHEN\n");
+  PrintOut(LOG_INFO,"        Quit on one of: %s\n\n", GetValidArgList('q'));
+  PrintOut(LOG_INFO,"  -r, --report=TYPE\n");
+  PrintOut(LOG_INFO,"        Report transactions for one of: %s\n\n", GetValidArgList('r'));
+#if defined(_WIN32) || defined(__CYGWIN__)
+  PrintOut(LOG_INFO,"  --service\n");
+  PrintOut(LOG_INFO,"        Running as windows service (see man page), install with:\n");
+#ifdef _WIN32
+  PrintOut(LOG_INFO,"          smartd install [options]\n");
+  PrintOut(LOG_INFO,"        Remove service with:\n");
+  PrintOut(LOG_INFO,"          smartd remove\n\n");
+#else
+  PrintOut(LOG_INFO,"          /etc/rc.d/init.d/smartd install [options]\n");
+  PrintOut(LOG_INFO,"        Remove service with:\n");
+  PrintOut(LOG_INFO,"          /etc/rc.d/init.d/smartd remove\n\n");
+#endif
+#endif // _WIN32 || __CYGWIN__
+  PrintOut(LOG_INFO,"  -V, --version, --license, --copyright\n");
+  PrintOut(LOG_INFO,"        Print License, Copyright, and version information\n");
+#else
+  PrintOut(LOG_INFO,"  -c NAME|-  Read configuration file NAME or stdin [default is %s]\n", configfile);
+  PrintOut(LOG_INFO,"  -d         Start smartd in debug mode\n");
+  PrintOut(LOG_INFO,"  -D         Print the configuration file Directives and exit\n");
+  PrintOut(LOG_INFO,"  -h         Display this help and exit\n");
+  PrintOut(LOG_INFO,"  -i N       Set interval between disk checks to N seconds, where N >= 10\n");
+  PrintOut(LOG_INFO,"  -l local?  Use syslog facility local0 - local7, or daemon\n");
+  PrintOut(LOG_INFO,"  -p NAME    Write PID file NAME\n");
+  PrintOut(LOG_INFO,"  -q WHEN    Quit on one of: %s\n", GetValidArgList('q'));
+  PrintOut(LOG_INFO,"  -r TYPE    Report transactions for one of: %s\n", GetValidArgList('r'));
+  PrintOut(LOG_INFO,"  -V         Print License, Copyright, and version information\n");
+#endif
+}
+
+// returns negative if problem, else fd>=0
+static int OpenDevice(char *device, char *mode, int scanning) {
+  int fd;
+  char *s=device;
+  
+  // If there is an ASCII "space" character in the device name,
+  // terminate string there.  This is for 3ware and highpoint devices only.
+  if ((s=strchr(device,' ')))
+    *s='\0';
+
+  // open the device
+  fd = deviceopen(device, mode);
+
+  // if we removed a space, put it back in please
+  if (s)
+    *s=' ';
+
+  // if we failed to open the device, complain!
+  if (fd < 0) {
+
+    // For linux+devfs, a nonexistent device gives a strange error
+    // message.  This makes the error message a bit more sensible.
+    // If no debug and scanning - don't print errors
+    if (debugmode || !scanning) {
+      if (errno==ENOENT || errno==ENOTDIR)
+        errno=ENODEV;
+
+      PrintOut(LOG_INFO,"Device: %s, %s, open() failed\n",
+              device, strerror(errno));
+    }
+    return -1;
+  }
+  // device opened sucessfully
+  return fd;
+}
+
+int CloseDevice(int fd, char *name){
+  if (deviceclose(fd)){
+    PrintOut(LOG_INFO,"Device: %s, %s, close(%d) failed\n", name, strerror(errno), fd);
+    return 1;
+  }
+  // device sucessfully closed
+  return 0;
+}
+
+// returns <0 on failure
+int ATAErrorCount(int fd, char *name){
+  struct ata_smart_errorlog log;
+  
+  if (-1==ataReadErrorLog(fd,&log)){
+    PrintOut(LOG_INFO,"Device: %s, Read SMART Error Log Failed\n",name);
+    return -1;
+  }
+  
+  // return current number of ATA errors
+  return log.error_log_pointer?log.ata_error_count:0;
+}
+
+// returns <0 if problem.  Otherwise, bottom 8 bits are the self test
+// error count, and top bits are the power-on hours of the last error.
+int SelfTestErrorCount(int fd, char *name){
+  struct ata_smart_selftestlog log;
+
+  if (-1==ataReadSelfTestLog(fd,&log)){
+    PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name);
+    return -1;
+  }
+  
+  // return current number of self-test errors
+  return ataPrintSmartSelfTestlog(&log,0);
+}
+
+// scan to see what ata devices there are, and if they support SMART
+int ATADeviceScan(cfgfile *cfg, int scanning){
+  int fd, supported=0;
+  struct ata_identify_device drive;
+  char *name=cfg->name;
+  int retainsmartdata=0;
+  int retid;
+  char *mode;
+  
+  // should we try to register this as an ATA device?
+  switch (cfg->controller_type) {
+  case CONTROLLER_ATA:
+  case CONTROLLER_3WARE_678K:
+  case CONTROLLER_MARVELL_SATA:
+  case CONTROLLER_HPT:
+  case CONTROLLER_UNKNOWN:
+    mode="ATA";
+    break;
+  case CONTROLLER_3WARE_678K_CHAR:
+    mode="ATA_3WARE_678K";
+    break;
+  case CONTROLLER_3WARE_9000_CHAR:
+    mode="ATA_3WARE_9000";
+    break;
+  case CONTROLLER_SAT:
+    mode="SCSI";
+    break;
+  default:
+    // not a recognized ATA or SATA device.  We should never enter
+    // this branch.
+    return 1;
+  }
+  
+  // open the device
+  if ((fd=OpenDevice(name, mode, scanning))<0)
+    // device open failed
+    return 1;
+  PrintOut(LOG_INFO,"Device: %s, opened\n", name);
+  
+  // pass user settings on to low-level ATA commands
+  con->controller_port=cfg->controller_port;
+  con->hpt_data[0]=cfg->hpt_data[0];
+  con->hpt_data[1]=cfg->hpt_data[1];
+  con->hpt_data[2]=cfg->hpt_data[2];
+  con->controller_type=cfg->controller_type;
+  con->controller_explicit=cfg->controller_explicit;
+  con->fixfirmwarebug = cfg->fixfirmwarebug;
+  con->satpassthrulen = cfg->satpassthrulen;
+  
+  // Get drive identity structure
+  if ((retid=ataReadHDIdentity (fd,&drive))){
+    if (retid<0)
+      // Unable to read Identity structure
+      PrintOut(LOG_INFO,"Device: %s, not ATA, no IDENTIFY DEVICE Structure\n",name);
+    else
+      PrintOut(LOG_INFO,"Device: %s, packet devices [this device %s] not SMART capable\n",
+               name, packetdevicetype(retid-1));
+    CloseDevice(fd, name);
+    return 2; 
+  }
+
+  // Show if device in database, and use preset vendor attribute
+  // options unless user has requested otherwise.
+  if (cfg->ignorepresets)
+    PrintOut(LOG_INFO, "Device: %s, smartd database not searched (Directive: -P ignore).\n", name);
+  else {
+    // do whatever applypresets decides to do. Will allocate memory if
+    // cfg->attributedefs is needed.
+    if (applypresets(&drive, &cfg->attributedefs, con)<0)
+      PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name);
+    else
+      PrintOut(LOG_INFO, "Device: %s, found in smartd database.\n", name);
+    
+    // then save the correct state of the flag (applypresets may have changed it)
+    cfg->fixfirmwarebug = con->fixfirmwarebug;
+  }
+  
+  // If requested, show which presets would be used for this drive
+  if (cfg->showpresets) {
+    int savedebugmode=debugmode;
+    PrintOut(LOG_INFO, "Device %s: presets are:\n", name);
+    if (!debugmode)
+      debugmode=2;
+    showpresets(&drive);
+    debugmode=savedebugmode;
+  }
+
+  // see if drive supports SMART
+  supported=ataSmartSupport(&drive);
+  if (supported!=1) {
+    if (supported==0)
+      // drive does NOT support SMART
+      PrintOut(LOG_INFO,"Device: %s, lacks SMART capability\n",name);
+    else
+      // can't tell if drive supports SMART
+      PrintOut(LOG_INFO,"Device: %s, ATA IDENTIFY DEVICE words 82-83 don't specify if SMART capable.\n",name);
+  
+    // should we proceed anyway?
+    if (cfg->permissive){
+      PrintOut(LOG_INFO,"Device: %s, proceeding since '-T permissive' Directive given.\n",name);
+    }
+    else {
+      PrintOut(LOG_INFO,"Device: %s, to proceed anyway, use '-T permissive' Directive.\n",name);
+      CloseDevice(fd, name);
+      return 2;
+    }
+  }
+  
+  if (ataEnableSmart(fd)){
+    // Enable SMART command has failed
+    PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name);
+    CloseDevice(fd, name);
+    return 2; 
+  }
+  
+  // disable device attribute autosave...
+  if (cfg->autosave==1){
+    if (ataDisableAutoSave(fd))
+      PrintOut(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",name);
+    else
+      PrintOut(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",name);
+  }
+
+  // or enable device attribute autosave
+  if (cfg->autosave==2){
+    if (ataEnableAutoSave(fd))
+      PrintOut(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",name);
+    else
+      PrintOut(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",name);
+  }
+
+  // capability check: SMART status
+  if (cfg->smartcheck && ataSmartStatus2(fd)==-1){
+    PrintOut(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",name);
+    cfg->smartcheck=0;
+  }
+  
+  // capability check: Read smart values and thresholds.  Note that
+  // smart values are ALSO needed even if we ONLY want to know if the
+  // device is self-test log or error-log capable!  After ATA-5, this
+  // information was ALSO reproduced in the IDENTIFY DEVICE response,
+  // but sadly not for ATA-5.  Sigh.
+
+  // do we need to retain SMART data after returning from this routine?
+  retainsmartdata=cfg->usagefailed || cfg->prefail || cfg->usage || cfg->tempdiff || cfg->tempinfo || cfg->tempcrit;
+  
+  // do we need to get SMART data?
+  if (retainsmartdata || cfg->autoofflinetest || cfg->selftest || cfg->errorlog || cfg->pending!=DONT_MONITOR_UNC) {
+
+    unsigned char currentpending, offlinepending;
+
+    cfg->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values));
+    cfg->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt));
+    
+    if (!cfg->smartval || !cfg->smartthres){
+      PrintOut(LOG_CRIT,"Not enough memory to obtain SMART data\n");
+      EXIT(EXIT_NOMEM);
+    }
+    
+    if (ataReadSmartValues(fd,cfg->smartval) ||
+        ataReadSmartThresholds (fd,cfg->smartthres)){
+      PrintOut(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",name);
+      retainsmartdata=cfg->usagefailed=cfg->prefail=cfg->usage=0;
+      cfg->tempdiff = cfg->tempinfo = cfg->tempcrit = 0;
+      cfg->pending=DONT_MONITOR_UNC;
+    }
+    
+    // see if the necessary Attribute is there to monitor offline or
+    // current pending sectors or temperature
+    TranslatePending(cfg->pending, &currentpending, &offlinepending);
+    
+    if (currentpending && ATAReturnAttributeRawValue(currentpending, cfg->smartval)<0) {
+      PrintOut(LOG_INFO,"Device: %s, can't monitor Current Pending Sector count - no Attribute %d\n",
+              name, (int)currentpending);
+      cfg->pending &= 0xff00;
+      cfg->pending |= CUR_UNC_DEFAULT;
+    }
+    
+    if (offlinepending && ATAReturnAttributeRawValue(offlinepending, cfg->smartval)<0) {
+      PrintOut(LOG_INFO,"Device: %s, can't monitor Offline Uncorrectable Sector count  - no Attribute %d\n",
+              name, (int)offlinepending);
+      cfg->pending &= 0x00ff;
+      cfg->pending |= OFF_UNC_DEFAULT<<8;
+    }
+
+    if (   (cfg->tempdiff || cfg->tempinfo || cfg->tempcrit)
+        && !ATAReturnTemperatureValue(cfg->smartval, cfg->attributedefs)) {
+      PrintOut(LOG_CRIT, "Device: %s, can't monitor Temperature, ignoring -W Directive\n", name);
+      cfg->tempdiff = cfg->tempinfo = cfg->tempcrit = 0;
+    }
+  }
+  
+  // enable/disable automatic on-line testing
+  if (cfg->autoofflinetest){
+    // is this an enable or disable request?
+    const char *what=(cfg->autoofflinetest==1)?"disable":"enable";
+    if (!cfg->smartval)
+      PrintOut(LOG_INFO,"Device: %s, could not %s SMART Automatic Offline Testing.\n",name, what);
+    else {
+      // if command appears unsupported, issue a warning...
+      if (!isSupportAutomaticTimer(cfg->smartval))
+        PrintOut(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",name);
+      // ... but then try anyway
+      if ((cfg->autoofflinetest==1)?ataDisableAutoOffline(fd):ataEnableAutoOffline(fd))
+        PrintOut(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", name, what);
+      else
+        PrintOut(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", name, what);
+    }
+  }
+  
+  // capability check: self-test-log
+  if (cfg->selftest){
+    int retval;
+    
+    // start with service disabled, and re-enable it if all works OK
+    cfg->selftest=0;
+    cfg->selflogcount=0;
+    cfg->selfloghour=0;
+
+    if (!cfg->smartval)
+      PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log (SMART READ DATA failed); disabling -l selftest\n", name);
+    else if (!cfg->permissive && !isSmartTestLogCapable(cfg->smartval, &drive))
+      PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Self-Test log; disabling -l selftest (override with -T permissive Directive)\n", name);
+    else if ((retval=SelfTestErrorCount(fd, name))<0)
+      PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log; remove -l selftest Directive from smartd.conf\n", name);
+    else {
+      cfg->selftest=1;
+      cfg->selflogcount=SELFTEST_ERRORCOUNT(retval);
+      cfg->selfloghour =SELFTEST_ERRORHOURS(retval);
+    }
+  }
+  
+  // capability check: ATA error log
+  if (cfg->errorlog){
+    int val;
+
+    // start with service disabled, and re-enable it if all works OK
+    cfg->errorlog=0;
+    cfg->ataerrorcount=0;
+
+    if (!cfg->smartval)
+      PrintOut(LOG_INFO, "Device: %s, no SMART Error log (SMART READ DATA failed); disabling -l error\n", name);
+    else if (!cfg->permissive && !isSmartErrorLogCapable(cfg->smartval, &drive))
+      PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Error log; disabling -l error (override with -T permissive Directive)\n", name);
+    else if ((val=ATAErrorCount(fd, name))<0)
+      PrintOut(LOG_INFO, "Device: %s, no SMART Error log; remove -l error Directive from smartd.conf\n", name);
+    else {
+        cfg->errorlog=1;
+        cfg->ataerrorcount=val;
+    }
+  }
+  
+  // If we don't need to save SMART data, get rid of it now
+  if (!retainsmartdata) {
+    if (cfg->smartval) {
+      cfg->smartval=CheckFree(cfg->smartval, __LINE__,filenameandversion);
+      bytes-=sizeof(struct ata_smart_values);
+    }
+    if (cfg->smartthres) {
+      cfg->smartthres=CheckFree(cfg->smartthres, __LINE__,filenameandversion);
+      bytes-=sizeof(struct ata_smart_thresholds_pvt);
+    }
+  }
+
+  // capabilities check -- does it support powermode?
+  if (cfg->powermode) {
+    int powermode=ataCheckPowerMode(fd);
+    
+    if (-1 == powermode) {
+      PrintOut(LOG_CRIT, "Device: %s, no ATA CHECK POWER STATUS support, ignoring -n Directive\n", name);
+      cfg->powermode=0;
+    } 
+    else if (powermode!=0 && powermode!=0x80 && powermode!=0xff) {
+      PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
+              name, powermode);
+      cfg->powermode=0;
+    }
+  }
+
+  // If no tests available or selected, return
+  if (!(cfg->errorlog    || cfg->selftest || cfg->smartcheck || 
+        cfg->usagefailed || cfg->prefail  || cfg->usage      ||
+        cfg->tempdiff    || cfg->tempinfo || cfg->tempcrit     )) {
+    CloseDevice(fd, name);
+    return 3;
+  }
+  
+  // Do we still have entries available?
+  while (numdevata>=atadevlist_max)
+    atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device");
+  
+  // register device
+  PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",name);
+  
+    // record number of device, type of device, increment device count
+  if (cfg->controller_type == CONTROLLER_UNKNOWN)
+    cfg->controller_type=CONTROLLER_ATA;
+  
+  // close file descriptor
+  CloseDevice(fd, name);
+  return 0;
+}
+
+// Returns 1 if device recognised as one we do not want to treat as a general
+// SCSI device. Also returns 1 if INQUIRY fails (all "SCSI" devices should
+// respond to INQUIRY). Otherwise returns 0 (i.e. normal SCSI device).
+static int SCSIFilterKnown(int fd, char * device)
+{
+  char req_buff[256];
+  char di_buff[256];
+  int req_len, avail_len, len;
+
+  memset(req_buff, 0, 96);
+  req_len = 36;
+  if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) {
+    /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
+    /* watch this spot ... other devices could lock up here */
+    req_len = 64;
+    if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) {
+      PrintOut(LOG_INFO, "Device: %s, failed on INQUIRY; skip device\n", device);
+      // device doesn't like INQUIRY commands
+      return 1;
+    }
+  }
+  avail_len = req_buff[4] + 5;
+  len = (avail_len < req_len) ? avail_len : req_len;
+  if (len >= 36) {
+    if (0 == strncmp(req_buff + 8, "3ware", 5) || 0 == strncmp(req_buff + 8, "AMCC", 4) ) {
+      PrintOut(LOG_INFO, "Device %s, please try adding '-d 3ware,N'\n", device);
+      PrintOut(LOG_INFO, "Device %s, you may need to replace %s with /dev/twaN or /dev/tweN\n", device, device);
+      return 1;
+    } else if ((len >= 42) && (0 == strncmp(req_buff + 36, "MVSATA", 6))) {
+      PrintOut(LOG_INFO, "Device %s, please try '-d marvell'\n", device);
+      return 1;
+    } else if ((avail_len >= 96) && (0 == strncmp(req_buff + 8, "ATA", 3))) {
+      /* <<<< This is Linux specific code to detect SATA disks using a
+              SCSI-ATA command translation layer. This may be generalized
+              later when the t10.org SAT project matures. >>>> */
+      req_len = 252;
+      memset(di_buff, 0, req_len);
+      if (scsiInquiryVpd(fd, 0x83, (unsigned char *)di_buff, req_len)) {
+        return 0;    // guess it is normal device
+      }
+      avail_len = ((di_buff[2] << 8) + di_buff[3]) + 4;
+      len = (avail_len < req_len) ? avail_len : req_len;
+      if (isLinuxLibAta((unsigned char *)di_buff, len)) {
+        PrintOut(LOG_INFO, "Device %s: SATA disks accessed via libata are "
+                 "supported by Linux\nkernel versions 2.6.15-rc1 and above. "
+                 "Try adding '-d ata' or\n'-d sat' to the smartd.conf "
+                 "config file line.\n", device);
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+
+// on success, return 0. On failure, return >0.  Never return <0,
+// please.
+static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
+  int k, fd, err; 
+  char *device = cfg->name;
+  struct scsi_iec_mode_page iec;
+  UINT8  tBuf[64];
+  
+  // should we try to register this as a SCSI device?
+  switch (cfg->controller_type) {
+  case CONTROLLER_SCSI:
+  case CONTROLLER_UNKNOWN:
+    break;
+  default:
+    return 1;
+  }
+  
+  // open the device
+  if ((fd = OpenDevice(device, "SCSI", scanning)) < 0)
+    return 1;
+  PrintOut(LOG_INFO,"Device: %s, opened\n", device);
+
+  // early skip if device known and needs to be handled by some other
+  // device type (e.g. '-d 3ware,<n>')
+  if (SCSIFilterKnown(fd, device)) {
+    CloseDevice(fd, device);
+    return 2; 
+  }
+    
+  // check that device is ready for commands. IE stores its stuff on
+  // the media.
+  if ((err = scsiTestUnitReady(fd))) {
+    if (SIMPLE_ERR_NOT_READY == err)
+      PrintOut(LOG_INFO, "Device: %s, NOT READY (e.g. spun down); skip device\n", device);
+    else if (SIMPLE_ERR_NO_MEDIUM == err)
+      PrintOut(LOG_INFO, "Device: %s, NO MEDIUM present; skip device\n", device);
+    else if (SIMPLE_ERR_BECOMING_READY == err)
+      PrintOut(LOG_INFO, "Device: %s, BECOMING (but not yet) READY; skip device\n", device);
+    else
+      PrintOut(LOG_CRIT, "Device: %s, failed Test Unit Ready [err=%d]\n", device, err);
+    CloseDevice(fd, device);
+    return 2; 
+  }
+  
+  // Badly-conforming USB storage devices may fail this check.
+  // The response to the following IE mode page fetch (current and
+  // changeable values) is carefully examined. It has been found
+  // that various USB devices that malform the response will lock up
+  // if asked for a log page (e.g. temperature) so it is best to
+  // bail out now.
+  if (!(err = scsiFetchIECmpage(fd, &iec, cfg->modese_len)))
+    cfg->modese_len = iec.modese_len;
+  else if (SIMPLE_ERR_BAD_FIELD == err)
+    ;  /* continue since it is reasonable not to support IE mpage */
+  else { /* any other error (including malformed response) unreasonable */
+    PrintOut(LOG_INFO, 
+             "Device: %s, Bad IEC (SMART) mode page, err=%d, skip device\n", 
+             device, err);
+    CloseDevice(fd, device);
+    return 3;
+  }
+  
+  // N.B. The following is passive (i.e. it doesn't attempt to turn on
+  // smart if it is off). This may change to be the same as the ATA side.
+  if (!scsi_IsExceptionControlEnabled(&iec)) {
+    PrintOut(LOG_INFO, "Device: %s, IE (SMART) not enabled, skip device\n"
+                      "Try 'smartctl -s on %s' to turn on SMART features\n", 
+                        device, device);
+    CloseDevice(fd, device);
+    return 3;
+  }
+  
+  // Device exists, and does SMART.  Add to list (allocating more space if needed)
+  while (numdevscsi >= scsidevlist_max)
+    scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device");
+  
+  // Flag that certain log pages are supported (information may be
+  // available from other sources).
+  if (0 == scsiLogSense(fd, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 0)) {
+    for (k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) {
+      switch (tBuf[k]) { 
+      case TEMPERATURE_LPAGE:
+        cfg->TempPageSupported = 1;
+        break;
+      case IE_LPAGE:
+        cfg->SmartPageSupported = 1;
+        break;
+      default:
+        break;
+      }
+    }   
+  }
+  
+  // record type of device
+  cfg->controller_type = CONTROLLER_SCSI;
+  
+  // get rid of allocated memory only needed for ATA devices.  These
+  // might have been allocated if the user specified Ignore options or
+  // other ATA-only Attribute-specific options on the DEVICESCAN line.
+  cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion);
+  cfg->attributedefs   = FreeNonZero(cfg->attributedefs,   MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion);
+  cfg->smartval        = FreeNonZero(cfg->smartval,        sizeof(struct ata_smart_values),__LINE__,filenameandversion);
+  cfg->smartthres      = FreeNonZero(cfg->smartthres,      sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion);
+  
+  // Check if scsiCheckIE() is going to work
+  {
+    UINT8 asc = 0;
+    UINT8 ascq = 0;
+    UINT8 currenttemp = 0;
+    UINT8 triptemp = 0;
+    
+    if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
+                    &asc, &ascq, &currenttemp, &triptemp)) {
+      PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device);
+      cfg->SuppressReport = 1;
+      if (cfg->tempdiff || cfg->tempinfo || cfg->tempcrit) {
+        PrintOut(LOG_CRIT, "Device: %s, can't monitor Temperature, ignoring -W Directive\n", device);
+        cfg->tempdiff = cfg->tempinfo = cfg->tempcrit = 0;
+      }
+    }
+  }
+  
+  // capability check: self-test-log
+  if (cfg->selftest){
+    int retval=scsiCountFailedSelfTests(fd, 0);
+    if (retval<0) {
+      // no self-test log, turn off monitoring
+      PrintOut(LOG_INFO, "Device: %s, does not support SMART Self-Test Log.\n", device);
+      cfg->selftest=0;
+      cfg->selflogcount=0;
+      cfg->selfloghour=0;
+    }
+    else {
+      // register starting values to watch for changes
+      cfg->selflogcount=SELFTEST_ERRORCOUNT(retval);
+      cfg->selfloghour =SELFTEST_ERRORHOURS(retval);
+    }
+  }
+  
+  // disable autosave (set GLTSD bit)
+  if (cfg->autosave==1){
+    if (scsiSetControlGLTSD(fd, 1, cfg->modese_len))
+      PrintOut(LOG_INFO,"Device: %s, could not disable autosave (set GLTSD bit).\n",device);
+    else
+      PrintOut(LOG_INFO,"Device: %s, disabled autosave (set GLTSD bit).\n",device);
+  }
+
+  // or enable autosave (clear GLTSD bit)
+  if (cfg->autosave==2){
+    if (scsiSetControlGLTSD(fd, 0, cfg->modese_len))
+      PrintOut(LOG_INFO,"Device: %s, could not enable autosave (clear GLTSD bit).\n",device);
+    else
+      PrintOut(LOG_INFO,"Device: %s, enabled autosave (cleared GLTSD bit).\n",device);
+  }
+  
+  // tell user we are registering device
+  PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device);
+  
+  // close file descriptor
+  CloseDevice(fd, device);
+  return 0;
+}
+
+// We compare old and new values of the n'th attribute.  Note that n
+// is NOT the attribute ID number.. If (Normalized & Raw) equal,
+// then return 0, else nonzero.
+int  ATACompareValues(changedattribute_t *delta,
+                            struct ata_smart_values *newv,
+                            struct ata_smart_values *oldv,
+                            struct ata_smart_thresholds_pvt *thresholds,
+                            int n, char *name){
+  struct ata_smart_attribute *now,*was;
+  struct ata_smart_threshold_entry *thre;
+  unsigned char oldval,newval;
+  int sameraw;
+
+  // check that attribute number in range, and no null pointers
+  if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !newv || !oldv || !thresholds)
+    return 0;
+  
+  // pointers to disk's values and vendor's thresholds
+  now=newv->vendor_attributes+n;
+  was=oldv->vendor_attributes+n;
+  thre=thresholds->thres_entries+n;
+
+  // consider only valid attributes
+  if (!now->id || !was->id || !thre->id)
+    return 0;
+  
+  
+  // issue warning if they don't have the same ID in all structures:
+  if ( (now->id != was->id) || (now->id != thre->id) ){
+    PrintOut(LOG_INFO,"Device: %s, same Attribute has different ID numbers: %d = %d = %d\n",
+             name, (int)now->id, (int)was->id, (int)thre->id);
+    return 0;
+  }
+
+  // new and old values of Normalized Attributes
+  newval=now->current;
+  oldval=was->current;
+
+  // See if the RAW values are unchanged (ie, the same)
+  if (memcmp(now->raw, was->raw, 6))
+    sameraw=0;
+  else
+    sameraw=1;
+  
+  // if any values out of the allowed range, or if the values haven't
+  // changed, return 0
+  if (!newval || !oldval || newval>0xfe || oldval>0xfe || (oldval==newval && sameraw))
+    return 0;
+  
+  // values have changed.  Construct output and return
+  delta->newval=newval;
+  delta->oldval=oldval;
+  delta->id=now->id;
+  delta->prefail=ATTRIBUTE_FLAGS_PREFAILURE(now->flags);
+  delta->sameraw=sameraw;
+
+  return 1;
+}
+
+// This looks to see if the corresponding bit of the 32 bytes is set.
+// This wastes a few bytes of storage but eliminates all searching and
+// sorting functions! Entry is ZERO <==> the attribute ON. Calling
+// with set=0 tells you if the attribute is being tracked or not.
+// Calling with set=1 turns the attribute OFF.
+int IsAttributeOff(unsigned char attr, unsigned char **datap, int set, int which, int whatline){
+  unsigned char *data;
+  int loc=attr>>3;
+  int bit=attr & 0x07;
+  unsigned char mask=0x01<<bit;
+
+  if (which>=NMONITOR || which < 0){
+    PrintOut(LOG_CRIT, "Internal error in IsAttributeOff() at line %d of file %s (which=%d)\n%s",
+             whatline, filenameandversion, which, reportbug);
+    EXIT(EXIT_BADCODE);
+  }
+
+  if (*datap == NULL){
+    // NULL data implies Attributes are ON...
+    if (!set)
+      return 0;
+    
+    // we are writing
+    if (!(*datap=(unsigned char *)Calloc(NMONITOR*32, 1))){
+      PrintOut(LOG_CRIT,"No memory to create monattflags\n");
+      EXIT(EXIT_NOMEM);
+    }
+  }
+  
+  // pointer to the 256 bits that we need
+  data=*datap+which*32;
+
+  // attribute zero is always OFF
+  if (!attr)
+    return 1;
+
+  if (!set)
+    return (data[loc] & mask);
+  
+  data[loc]|=mask;
+
+  // return value when setting has no sense
+  return 0;
+}
+
+// If the self-test log has got more self-test errors (or more recent
+// self-test errors) recorded, then notify user.
+void CheckSelfTestLogs(cfgfile *cfg, int newi){
+  char *name=cfg->name;
+
+  if (newi<0)
+    // command failed
+    MailWarning(cfg, 8, "Device: %s, Read SMART Self-Test Log Failed", name);
+  else {      
+    // old and new error counts
+    int oldc=cfg->selflogcount;
+    int newc=SELFTEST_ERRORCOUNT(newi);
+    
+    // old and new error timestamps in hours
+    int oldh=cfg->selfloghour;
+    int newh=SELFTEST_ERRORHOURS(newi);
+    
+    if (oldc<newc) {
+      // increase in error count
+      PrintOut(LOG_CRIT, "Device: %s, Self-Test Log error count increased from %d to %d\n",
+               name, oldc, newc);
+      MailWarning(cfg, 3, "Device: %s, Self-Test Log error count increased from %d to %d",
+                   name, oldc, newc);
+    } else if (oldh!=newh) {
+      // more recent error
+      // a 'more recent' error might actually be a smaller hour number,
+      // if the hour number has wrapped.
+      // There's still a bug here.  You might just happen to run a new test
+      // exactly 32768 hours after the previous failure, and have run exactly
+      // 20 tests between the two, in which case smartd will miss the
+      // new failure.
+      PrintOut(LOG_CRIT, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
+               name, newh);
+      MailWarning(cfg, 3, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
+                   name, newh);
+    }
+    
+    // Needed since self-test error count may DECREASE.  Hour might
+    // also have changed.
+    cfg->selflogcount= newc;
+    cfg->selfloghour = newh;
+  }
+  return;
+}
+
+// returns 1 if time to do test of type testtype, 0 if not time to do
+// test, < 0 if error
+int DoTestNow(cfgfile *cfg, char testtype, time_t testtime) {
+  // start by finding out the time:
+  struct tm *timenow;
+  time_t epochnow;
+  char matchpattern[16];
+  regmatch_t substring;
+  int weekday, length;
+  unsigned short hours;
+  testinfo *dat=cfg->testdata;
+
+  // check that self-testing has been requested
+  if (!dat)
+    return 0;
+  
+  // since we are about to call localtime(), be sure glibc is informed
+  // of any timezone changes we make.
+  if (!testtime)
+    FixGlibcTimeZoneBug();
+  
+  // construct pattern containing the month, day of month, day of
+  // week, and hour
+  epochnow = (!testtime ? time(NULL) : testtime);
+  timenow=localtime(&epochnow);
+  
+  // tm_wday is 0 (Sunday) to 6 (Saturday).  We use 1 (Monday) to 7
+  // (Sunday).
+  weekday=timenow->tm_wday?timenow->tm_wday:7;
+  sprintf(matchpattern, "%c/%02d/%02d/%1d/%02d", testtype, timenow->tm_mon+1, 
+          timenow->tm_mday, weekday, timenow->tm_hour);
+  
+  // if no match, we are done
+  if (regexec(&(dat->cregex), matchpattern, 1, &substring, 0))
+    return 0;
+  
+  // must match the ENTIRE type/date/time string
+  length=strlen(matchpattern);
+  if (substring.rm_so!=0 || substring.rm_eo!=length)
+    return 0;
+  
+  // never do a second test in the same hour as another test (the % 7 ensures
+  // that the RHS will never be greater than 65535 and so will always fit into
+  // an unsigned short)
+  hours=1+timenow->tm_hour+24*(timenow->tm_yday+366*(timenow->tm_year % 7));
+  if (hours==dat->hour) {
+    if (!testtime && testtype!=dat->testtype)
+      PrintOut(LOG_INFO, "Device: %s, did test of type %c in current hour, skipping test of type %c\n",
+              cfg->name, dat->testtype, testtype);
+    return 0;
+  }
+  
+  // save time and type of the current test; we are ready to do a test
+  dat->hour=hours;
+  dat->testtype=testtype;
+  return 1;
+}
+
+// Print a list of future tests.
+void PrintTestSchedule(cfgfile **atadevices, cfgfile **scsidevices){
+  int i, t;
+  cfgfile * cfg;
+  char datenow[DATEANDEPOCHLEN], date[DATEANDEPOCHLEN];
+  time_t now; long seconds;
+  int numdev = numdevata+numdevscsi;
+  typedef int cnt_t[4];
+  cnt_t * testcnts; // testcnts[numdev][4]
+  if (numdev <= 0)
+    return;
+  testcnts = (cnt_t *)calloc(numdev, sizeof(testcnts[0]));
+  if (!testcnts)
+    return;
+
+  PrintOut(LOG_INFO, "\nNext scheduled self tests (at most 5 of each type per device):\n");
+
+  // FixGlibcTimeZoneBug(); // done in PrintOut()
+  now=time(NULL);
+  dateandtimezoneepoch(datenow, now);
+  for (seconds=0; seconds<3600L*24*90; seconds+=checktime) {
+    // Check for each device whether a test will be run
+    time_t testtime = now + seconds;
+    for (i=0; i<numdev; i++) {
+      cfg = (i<numdevata? atadevices[i] : scsidevices[i-numdevata]);
+      for (t=0; t<(i<numdevata?4:2); t++) {
+        char testtype = "LSCO"[t];
+        if (DoTestNow(cfg, testtype, testtime)) {
+          // Report at most 5 tests of each type
+          if (++testcnts[i][t] <= 5) {
+            dateandtimezoneepoch(date, testtime);
+            PrintOut(LOG_INFO, "Device: %s, will do test %d of type %c at %s\n", cfg->name,
+              testcnts[i][t], testtype, date);
+          }
+        }
+      }
+    }
+  }
+
+  // Report totals
+  dateandtimezoneepoch(date, now+seconds);
+  PrintOut(LOG_INFO, "\nTotals [%s - %s]:\n", datenow, date);
+  for (i=0; i<numdev; i++) {
+    cfg = (i<numdevata? atadevices[i] : scsidevices[i-numdevata]);
+    for (t=0; t<(i<numdevata?4:2); t++) {
+      PrintOut(LOG_INFO, "Device: %s, will do %3d test%s of type %c\n", cfg->name, testcnts[i][t],
+        (testcnts[i][t]==1?"":"s"), "LSCO"[t]);
+    }
+  }
+
+  free(testcnts);
+}
+
+// Return zero on success, nonzero on failure. Perform offline (background)
+// short or long (extended) self test on given scsi device.
+int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) {
+  int retval = 0;
+  char *testname = NULL;
+  char *name = cfg->name;
+  int inProgress;
+
+  if (scsiSelfTestInProgress(fd, &inProgress)) {
+    PrintOut(LOG_CRIT, "Device: %s, does not support Self-Tests\n", name);
+    cfg->testdata->not_cap_short=cfg->testdata->not_cap_long=1;
+    return 1;
+  }
+
+  if (1 == inProgress) {
+    PrintOut(LOG_INFO, "Device: %s, skip since Self-Test already in "
+             "progress.\n", name);
+    return 1;
+  }
+
+  switch (testtype) {
+  case 'S':
+    testname = "Short Self";
+    retval = scsiSmartShortSelfTest(fd);
+    break;
+  case 'L':
+    testname = "Long Self";
+    retval = scsiSmartExtendSelfTest(fd);
+    break;
+  }
+  // If we can't do the test, exit
+  if (NULL == testname) {
+    PrintOut(LOG_CRIT, "Device: %s, not capable of %c Self-Test\n", name, 
+             testtype);
+    return 1;
+  }
+  if (retval) {
+    if ((SIMPLE_ERR_BAD_OPCODE == retval) || 
+        (SIMPLE_ERR_BAD_FIELD == retval)) {
+      PrintOut(LOG_CRIT, "Device: %s, not capable of %s-Test\n", name, 
+               testname);
+      if ('L'==testtype)
+        cfg->testdata->not_cap_long=1;
+      else
+        cfg->testdata->not_cap_short=1;
+     
+      return 1;
+    }
+    PrintOut(LOG_CRIT, "Device: %s, execute %s-Test failed (err: %d)\n", name, 
+             testname, retval);
+    return 1;
+  }
+  
+  PrintOut(LOG_INFO, "Device: %s, starting scheduled %s-Test.\n", name, testname);
+  
+  return 0;
+}
+
+// Do an offline immediate or self-test.  Return zero on success,
+// nonzero on failure.
+int DoATASelfTest(int fd, cfgfile *cfg, char testtype) {
+  
+  struct ata_smart_values data;
+  char *testname=NULL;
+  int retval, dotest=-1;
+  char *name=cfg->name;
+  
+  // Read current smart data and check status/capability
+  if (ataReadSmartValues(fd, &data) || !(data.offline_data_collection_capability)) {
+    PrintOut(LOG_CRIT, "Device: %s, not capable of Offline or Self-Testing.\n", name);
+    return 1;
+  }
+  
+  // Check for capability to do the test
+  switch (testtype) {
+  case 'O':
+    testname="Offline Immediate ";
+    if (isSupportExecuteOfflineImmediate(&data))
+      dotest=OFFLINE_FULL_SCAN;
+    else
+      cfg->testdata->not_cap_offline=1;
+    break;
+  case 'C':
+    testname="Conveyance Self-";
+    if (isSupportConveyanceSelfTest(&data))
+      dotest=CONVEYANCE_SELF_TEST;
+    else
+      cfg->testdata->not_cap_conveyance=1;
+    break;
+  case 'S':
+    testname="Short Self-";
+    if (isSupportSelfTest(&data))
+      dotest=SHORT_SELF_TEST;
+    else
+      cfg->testdata->not_cap_short=1;
+    break;
+  case 'L':
+    testname="Long Self-";
+    if (isSupportSelfTest(&data))
+      dotest=EXTEND_SELF_TEST;
+    else
+      cfg->testdata->not_cap_long=1;
+    break;
+  }
+  
+  // If we can't do the test, exit
+  if (dotest<0) {
+    PrintOut(LOG_CRIT, "Device: %s, not capable of %sTest\n", name, testname);
+    return 1;
+  }
+  
+  // If currently running a self-test, do not interrupt it to start another.
+  if (15==(data.self_test_exec_status >> 4)) {
+    PrintOut(LOG_INFO, "Device: %s, skip scheduled %sTest; %1d0%% remaining of current Self-Test.\n",
+             name, testname, (int)(data.self_test_exec_status & 0x0f));
+    return 1;
+  }
+
+  // else execute the test, and return status
+  if ((retval=smartcommandhandler(fd, IMMEDIATE_OFFLINE, dotest, NULL)))
+    PrintOut(LOG_CRIT, "Device: %s, execute %sTest failed.\n", name, testname);
+  else
+    PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname);
+  
+  return retval;
+}
+
+// Check Temperature limits
+static void CheckTemperature(cfgfile * cfg, unsigned char currtemp, unsigned char triptemp)
+{
+  const char *minchg = "", *maxchg = "";
+  if (!(0 < currtemp && currtemp < 255)) {
+    PrintOut(LOG_INFO, "Device: %s, failed to read Temperature\n", cfg->name);
+    return;
+  }
+
+  if (!cfg->temperature) {
+    PrintOut(LOG_INFO, "Device: %s, initial Temperature is %d Celsius\n",
+      cfg->name, (int)currtemp);
+    if (triptemp)
+      PrintOut(LOG_INFO, "    [trip Temperature is %d Celsius]\n", (int)triptemp);
+    cfg->temperature = cfg->tempmin = cfg->tempmax = currtemp;
+  }
+  else {
+    // Update [min,max]
+    if (currtemp < cfg->tempmin) {
+      cfg->tempmin = currtemp; minchg = "!";
+      cfg->tempmininc = 0;
+    }
+    else if (cfg->tempmininc) {
+      // increase min Temperature during first 30 minutes
+      cfg->tempmin = currtemp;
+      cfg->tempmininc--;
+    }
+    if (currtemp > cfg->tempmax) {
+      cfg->tempmax = currtemp; maxchg = "!";
+    }
+
+    // Track changes
+    if (cfg->tempdiff && (*minchg || *maxchg || abs((int)currtemp - (int)cfg->temperature) >= cfg->tempdiff)) {
+      PrintOut(LOG_INFO, "Device: %s, Temperature changed %+d Celsius to %u Celsius (Min/Max %u%s/%u%s)\n",
+        cfg->name, (int)currtemp-(int)cfg->temperature, currtemp, cfg->tempmin, minchg, cfg->tempmax, maxchg);
+      cfg->temperature = currtemp;
+    }
+  }
+
+  // Check limits
+  if (cfg->tempcrit && currtemp >= cfg->tempcrit) {
+    PrintOut(LOG_CRIT, "Device: %s, Temperature %u Celsius reached critical limit of %u Celsius (Min/Max %u%s/%u%s)\n",
+      cfg->name, currtemp, cfg->tempcrit, cfg->tempmin, minchg, cfg->tempmax, maxchg);
+    MailWarning(cfg, 12, "Device: %s, Temperature %d Celsius reached critical limit of %u Celsius (Min/Max %u%s/%u%s)\n",
+      cfg->name, currtemp, cfg->tempcrit, cfg->tempmin, minchg, cfg->tempmax, maxchg);
+  }
+  else if (cfg->tempinfo && currtemp >= cfg->tempinfo) {
+    PrintOut(LOG_INFO, "Device: %s, Temperature %u Celsius reached limit of %u Celsius (Min/Max %u%s/%u%s)\n",
+      cfg->name, currtemp, cfg->tempinfo, cfg->tempmin, minchg, cfg->tempmax, maxchg);
+  }
+}
+
+int ATACheckDevice(cfgfile *cfg){
+  int fd,i;
+  char *name=cfg->name;
+  char *mode="ATA";
+  char testtype=0;
+  
+  // fix firmware bug if requested
+  con->fixfirmwarebug=cfg->fixfirmwarebug;
+  con->controller_port=cfg->controller_port;
+  con->controller_type=cfg->controller_type;
+  con->controller_explicit=cfg->controller_explicit;
+
+  // If user has asked, test the email warning system
+  if (cfg->mailwarn && cfg->mailwarn->emailtest)
+    MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name);
+
+  if (cfg->controller_type == CONTROLLER_3WARE_9000_CHAR)
+    mode="ATA_3WARE_9000";
+  
+  if (cfg->controller_type == CONTROLLER_3WARE_678K_CHAR)
+    mode="ATA_3WARE_678K";
+
+  // if we can't open device, fail gracefully rather than hard --
+  // perhaps the next time around we'll be able to open it.  ATAPI
+  // cd/dvd devices will hang awaiting media if O_NONBLOCK is not
+  // given (see linux cdrom driver).
+  if ((fd=OpenDevice(name, mode, 0))<0){
+    MailWarning(cfg, 9, "Device: %s, unable to open device", name);
+    return 1;
+  }
+
+  // if the user has asked, and device is capable (or we're not yet
+  // sure) check whether a self test should be done now.
+  // This check is done before powermode check to avoid missing self
+  // tests on idle or sleeping disks.
+  if (cfg->testdata) {
+    // long test
+    if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L', 0)>0)
+      testtype = 'L';
+    // short test
+    else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S', 0)>0)
+      testtype = 'S';
+    // conveyance test
+    else if (!cfg->testdata->not_cap_conveyance && DoTestNow(cfg, 'C', 0)>0)
+      testtype = 'C';
+    // offline immediate
+    else if (!cfg->testdata->not_cap_offline && DoTestNow(cfg, 'O', 0)>0)
+      testtype = 'O';
+  }
+
+  // user may have requested (with the -n Directive) to leave the disk
+  // alone if it is in idle or sleeping mode.  In this case check the
+  // power mode and exit without check if needed
+  if (cfg->powermode){
+    int dontcheck=0, powermode=ataCheckPowerMode(fd);
+    char *mode=NULL;
+    if (0 <= powermode && powermode < 0xff) {
+      // wait for possible spin up and check again
+      int powermode2;
+      sleep(5);
+      powermode2 = ataCheckPowerMode(fd);
+      if (powermode2 > powermode)
+        PrintOut(LOG_INFO, "Device: %s, CHECK POWER STATUS spins up disk (0x%02x -> 0x%02x)\n", name, powermode, powermode2);
+      powermode = powermode2;
+    }
+        
+    switch (powermode){
+    case -1:
+      // SLEEP
+      mode="SLEEP";
+      if (cfg->powermode>=1)
+        dontcheck=1;
+      break;
+    case 0:
+      // STANDBY
+      mode="STANDBY";
+      if (cfg->powermode>=2)
+        dontcheck=1;
+      break;
+    case 0x80:
+      // IDLE
+      mode="IDLE";
+      if (cfg->powermode>=3)
+        dontcheck=1;
+      break;
+    case 0xff:
+      // ACTIVE/IDLE
+      mode="ACTIVE or IDLE";
+      break;
+    default:
+      // UNKNOWN
+      PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
+        name, powermode);
+      cfg->powermode=0;
+      break;
+    }
+
+    // if we are going to skip a check, return now
+    if (dontcheck){
+      // but ignore powermode on scheduled selftest
+      if (!testtype) {
+        CloseDevice(fd, name);
+        if (!cfg->powerskipcnt && !cfg->powerquiet) // report first only and avoid waking up system disk
+          PrintOut(LOG_INFO, "Device: %s, is in %s mode, suspending checks\n", name, mode);
+        cfg->powerskipcnt++;
+        return 0;
+      }
+      PrintOut(LOG_INFO, "Device: %s, %s mode ignored due to scheduled self test (%d check%s skipped)\n",
+        name, mode, cfg->powerskipcnt, (cfg->powerskipcnt==1?"":"s"));
+      cfg->powerskipcnt = 0;
+    }
+    else if (cfg->powerskipcnt) {
+      PrintOut(LOG_INFO, "Device: %s, is back in %s mode, resuming checks (%d check%s skipped)\n",
+        name, mode, cfg->powerskipcnt, (cfg->powerskipcnt==1?"":"s"));
+      cfg->powerskipcnt = 0;
+    }
+  }
+
+  // check smart status
+  if (cfg->smartcheck){
+    int status=ataSmartStatus2(fd);
+    if (status==-1){
+      PrintOut(LOG_INFO,"Device: %s, not capable of SMART self-check\n",name);
+      MailWarning(cfg, 5, "Device: %s, not capable of SMART self-check", name);
+    }
+    else if (status==1){
+      PrintOut(LOG_CRIT, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!\n", name);
+      MailWarning(cfg, 1, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!", name);
+    }
+  }
+  
+  // Check everything that depends upon SMART Data (eg, Attribute values)
+  if (   cfg->usagefailed || cfg->prefail || cfg->usage || cfg->pending!=DONT_MONITOR_UNC
+      || cfg->tempdiff || cfg->tempinfo || cfg->tempcrit                                 ){
+    struct ata_smart_values     curval;
+    struct ata_smart_thresholds_pvt *thresh=cfg->smartthres;
+    
+    // Read current attribute values. *drive contains old values and thresholds
+    if (ataReadSmartValues(fd,&curval)){
+      PrintOut(LOG_CRIT, "Device: %s, failed to read SMART Attribute Data\n", name);
+      MailWarning(cfg, 6, "Device: %s, failed to read SMART Attribute Data", name);
+    }
+    else {
+      // look for current or offline pending sectors
+      if (cfg->pending != DONT_MONITOR_UNC) {
+       int64_t rawval;
+       unsigned char currentpending, offlinepending;
+       
+       TranslatePending(cfg->pending, &currentpending, &offlinepending);
+       
+       if (currentpending && (rawval=ATAReturnAttributeRawValue(currentpending, &curval))>0) {
+         // Unreadable pending sectors!!
+         PrintOut(LOG_CRIT,   "Device: %s, %"PRId64" Currently unreadable (pending) sectors\n", name, rawval);
+         MailWarning(cfg, 10, "Device: %s, %"PRId64" Currently unreadable (pending) sectors", name, rawval);
+       }
+       
+       if (offlinepending && (rawval=ATAReturnAttributeRawValue(offlinepending, &curval))>0) {
+         // Unreadable offline sectors!!
+         PrintOut(LOG_CRIT,   "Device: %s, %"PRId64" Offline uncorrectable sectors\n", name, rawval);
+         MailWarning(cfg, 11, "Device: %s, %"PRId64" Offline uncorrectable sectors", name, rawval);
+       }
+      }
+
+      // check temperature limits
+      if (cfg->tempdiff || cfg->tempinfo || cfg->tempcrit)
+        CheckTemperature(cfg, ATAReturnTemperatureValue(&curval, cfg->attributedefs), 0);
+
+      if (cfg->usagefailed || cfg->prefail || cfg->usage) {
+
+       // look for failed usage attributes, or track usage or prefail attributes
+       for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
+         int att;
+         changedattribute_t delta;
+         
+         // This block looks for usage attributes that have failed.
+         // Prefail attributes that have failed are returned with a
+         // positive sign. No failure returns 0. Usage attributes<0.
+         if (cfg->usagefailed && ((att=ataCheckAttribute(&curval, thresh, i))<0)){
+           
+           // are we ignoring failures of this attribute?
+           att *= -1;
+           if (!IsAttributeOff(att, &cfg->monitorattflags, 0, MONITOR_FAILUSE, __LINE__)){
+             char attname[64], *loc=attname;
+             
+             // get attribute name & skip white space
+             ataPrintSmartAttribName(loc, att, cfg->attributedefs);
+             while (*loc && *loc==' ') loc++;
+             
+             // warning message
+             PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %s.\n", name, loc);
+             MailWarning(cfg, 2, "Device: %s, Failed SMART usage Attribute: %s.", name, loc);
+           }
+         }
+         
+         // This block tracks usage or prefailure attributes to see if
+         // they are changing.  It also looks for changes in RAW values
+         // if this has been requested by user.
+         if ((cfg->usage || cfg->prefail) && ATACompareValues(&delta, &curval, cfg->smartval, thresh, i, name)){
+           unsigned char id=delta.id;
+           
+           // if the only change is the raw value, and we're not
+           // tracking raw value, then continue loop over attributes
+           if (!delta.sameraw && delta.newval==delta.oldval && !IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAW, __LINE__))
+             continue;
+           
+           // are we tracking this attribute?
+           if (!IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_IGNORE, __LINE__)){
+             char newrawstring[64], oldrawstring[64], attname[64], *loc=attname;
+             
+             // get attribute name, skip spaces
+             ataPrintSmartAttribName(loc, id, cfg->attributedefs);
+             while (*loc && *loc==' ') loc++;
+             
+             // has the user asked for us to print raw values?
+             if (IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAWPRINT, __LINE__)) {
+               // get raw values (as a string) and add to printout
+               char rawstring[64];
+               ataPrintSmartAttribRawValue(rawstring, curval.vendor_attributes+i, cfg->attributedefs);
+               sprintf(newrawstring, " [Raw %s]", rawstring);
+               ataPrintSmartAttribRawValue(rawstring, cfg->smartval->vendor_attributes+i, cfg->attributedefs);
+               sprintf(oldrawstring, " [Raw %s]", rawstring);
+             }
+             else
+               newrawstring[0]=oldrawstring[0]='\0';
+             
+             // prefailure attribute
+             if (cfg->prefail && delta.prefail)
+               PrintOut(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %d%s to %d%s\n",
+                        name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
+             
+             // usage attribute
+             if (cfg->usage && !delta.prefail)
+               PrintOut(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %d%s to %d%s\n",
+                        name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
+           }
+         } // endof block tracking usage or prefailure
+       } // end of loop over attributes
+       
+       // Save the new values into *drive for the next time around
+       *(cfg->smartval)=curval;
+      }
+    }
+  }
+  
+  // check if number of selftest errors has increased (note: may also DECREASE)
+  if (cfg->selftest)
+    CheckSelfTestLogs(cfg, SelfTestErrorCount(fd, name));
+  
+  // check if number of ATA errors has increased
+  if (cfg->errorlog){
+
+    int newc,oldc=cfg->ataerrorcount;
+
+    // new number of errors
+    newc=ATAErrorCount(fd, name);
+
+    // did command fail?
+    if (newc<0)
+      // lack of PrintOut here is INTENTIONAL
+      MailWarning(cfg, 7, "Device: %s, Read SMART Error Log Failed", name);
+
+    // has error count increased?
+    if (newc>oldc){
+      PrintOut(LOG_CRIT, "Device: %s, ATA error count increased from %d to %d\n",
+               name, oldc, newc);
+      MailWarning(cfg, 4, "Device: %s, ATA error count increased from %d to %d",
+                   name, oldc, newc);
+    }
+    
+    // this last line is probably not needed, count always increases
+    if (newc>=0)
+      cfg->ataerrorcount=newc;
+  }
+  
+  // carry out scheduled self-test
+  if (testtype)
+    DoATASelfTest(fd, cfg, testtype);
+  
+  // Don't leave device open -- the OS/user may want to access it
+  // before the next smartd cycle!
+  CloseDevice(fd, name);
+  return 0;
+}
+
+int SCSICheckDevice(cfgfile *cfg)
+{
+    UINT8 asc, ascq;
+    UINT8 currenttemp;
+    UINT8 triptemp;
+    int fd;
+    char *name=cfg->name;
+    const char *cp;
+
+    // If the user has asked for it, test the email warning system
+    if (cfg->mailwarn && cfg->mailwarn->emailtest)
+      MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name);
+
+    // if we can't open device, fail gracefully rather than hard --
+    // perhaps the next time around we'll be able to open it
+    if ((fd=OpenDevice(name, "SCSI", 0))<0) {
+      // Lack of PrintOut() here is intentional!
+      MailWarning(cfg, 9, "Device: %s, unable to open device", name);
+      return 1;
+    }
+    currenttemp = 0;
+    asc = 0;
+    ascq = 0;
+    if (! cfg->SuppressReport) {
+        if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
+                        &asc, &ascq, &currenttemp, &triptemp)) {
+            PrintOut(LOG_INFO, "Device: %s, failed to read SMART values\n",
+                      name);
+            MailWarning(cfg, 6, "Device: %s, failed to read SMART values", name);
+            cfg->SuppressReport = 1;
+        }
+    }
+    if (asc > 0) {
+        cp = scsiGetIEString(asc, ascq);
+        if (cp) {
+            PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp);
+            MailWarning(cfg, 1,"Device: %s, SMART Failure: %s", name, cp); 
+        }
+    } else if (debugmode)
+        PrintOut(LOG_INFO,"Device: %s, Acceptable asc,ascq: %d,%d\n", 
+                 name, (int)asc, (int)ascq);  
+
+    // check temperature limits
+    if (cfg->tempdiff || cfg->tempinfo || cfg->tempcrit)
+      CheckTemperature(cfg, currenttemp, triptemp);
+
+    // check if number of selftest errors has increased (note: may also DECREASE)
+    if (cfg->selftest)
+      CheckSelfTestLogs(cfg, scsiCountFailedSelfTests(fd, 0));
+    
+    if (cfg->testdata) {
+      // long (extended) background test
+      if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L', 0)>0)
+        DoSCSISelfTest(fd, cfg, 'L');
+      // short background test
+      else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S', 0)>0)
+        DoSCSISelfTest(fd, cfg, 'S');
+    }
+    CloseDevice(fd, name);
+    return 0;
+}
+
+// Checks the SMART status of all ATA and SCSI devices
+void CheckDevicesOnce(cfgfile **atadevices, cfgfile **scsidevices){
+  int i;
+  
+  for (i=0; i<numdevata; i++) 
+    ATACheckDevice(atadevices[i]);
+  
+  for (i=0; i<numdevscsi; i++)
+    SCSICheckDevice(scsidevices[i]);
+
+  return;
+}
+
+#if SCSITIMEOUT
+// This alarm means that a SCSI USB device was hanging
+void AlarmHandler(int signal) {
+  longjmp(registerscsienv, 1);
+}
+#endif
+
+// Does initialization right after fork to daemon mode
+void Initialize(time_t *wakeuptime){
+
+  // install goobye message and remove pidfile handler
+  atexit(Goodbye);
+  
+  // write PID file only after installing exit handler
+  if (!debugmode)
+    WritePidFile();
+  
+  // install signal handlers.  On Solaris, can't use signal() because
+  // it resets the handler to SIG_DFL after each call.  So use sigset()
+  // instead.  So SIGNALFN()==signal() or SIGNALFN()==sigset().
+  
+  // normal and abnormal exit
+  if (SIGNALFN(SIGTERM, sighandler)==SIG_IGN)
+    SIGNALFN(SIGTERM, SIG_IGN);
+  if (SIGNALFN(SIGQUIT, sighandler)==SIG_IGN)
+    SIGNALFN(SIGQUIT, SIG_IGN);
+  
+  // in debug mode, <CONTROL-C> ==> HUP
+  if (SIGNALFN(SIGINT, debugmode?HUPhandler:sighandler)==SIG_IGN)
+    SIGNALFN(SIGINT, SIG_IGN);
+  
+  // Catch HUP and USR1
+  if (SIGNALFN(SIGHUP, HUPhandler)==SIG_IGN)
+    SIGNALFN(SIGHUP, SIG_IGN);
+  if (SIGNALFN(SIGUSR1, USR1handler)==SIG_IGN)
+    SIGNALFN(SIGUSR1, SIG_IGN);
+#ifdef _WIN32
+  if (SIGNALFN(SIGUSR2, USR2handler)==SIG_IGN)
+    SIGNALFN(SIGUSR2, SIG_IGN);
+#endif
+
+  // initialize wakeup time to CURRENT time
+  *wakeuptime=time(NULL);
+  
+  return;
+}
+
+#ifdef _WIN32
+// Toggle debug mode implemented for native windows only
+// (there is no easy way to reopen tty on *nix)
+static void ToggleDebugMode()
+{
+  if (!debugmode) {
+    PrintOut(LOG_INFO,"Signal USR2 - enabling debug mode\n");
+    if (!daemon_enable_console("smartd [Debug]")) {
+      debugmode = 1;
+      daemon_signal(SIGINT, HUPhandler);
+      PrintOut(LOG_INFO,"smartd debug mode enabled, PID=%d\n", getpid());
+    }
+    else
+      PrintOut(LOG_INFO,"enable console failed\n");
+  }
+  else if (debugmode == 1) {
+    daemon_disable_console();
+    debugmode = 0;
+    daemon_signal(SIGINT, sighandler);
+    PrintOut(LOG_INFO,"Signal USR2 - debug mode disabled\n");
+  }
+  else
+    PrintOut(LOG_INFO,"Signal USR2 - debug mode %d not changed\n", debugmode);
+}
+#endif
+
+time_t dosleep(time_t wakeuptime){
+  time_t timenow=0;
+  
+  // If past wake-up-time, compute next wake-up-time
+  timenow=time(NULL);
+  while (wakeuptime<=timenow){
+    int intervals=1+(timenow-wakeuptime)/checktime;
+    wakeuptime+=intervals*checktime;
+  }
+  
+  // sleep until we catch SIGUSR1 or have completed sleeping
+  while (timenow<wakeuptime && !caughtsigUSR1 && !caughtsigHUP && !caughtsigEXIT){
+    
+    // protect user again system clock being adjusted backwards
+    if (wakeuptime>timenow+checktime){
+      PrintOut(LOG_CRIT, "System clock time adjusted to the past. Resetting next wakeup time.\n");
+      wakeuptime=timenow+checktime;
+    }
+    
+    // Exit sleep when time interval has expired or a signal is received
+    sleep(wakeuptime-timenow);
+
+#ifdef _WIN32
+    // toggle debug mode?
+    if (caughtsigUSR2) {
+      ToggleDebugMode();
+      caughtsigUSR2 = 0;
+    }
+#endif
+
+    timenow=time(NULL);
+  }
+  // if we caught a SIGUSR1 then print message and clear signal
+  if (caughtsigUSR1){
+    PrintOut(LOG_INFO,"Signal USR1 - checking devices now rather than in %d seconds.\n",
+             wakeuptime-timenow>0?(int)(wakeuptime-timenow):0);
+    caughtsigUSR1=0;
+  }
+  
+  // return adjusted wakeuptime
+  return wakeuptime;
+}
+
+// Print out a list of valid arguments for the Directive d
+void printoutvaliddirectiveargs(int priority, char d) {
+  char *s=NULL;
+
+  switch (d) {
+  case 'n':
+    PrintOut(priority, "never[,q], sleep[,q], standby[,q], idle[,q]");
+    break;
+  case 's':
+    PrintOut(priority, "valid_regular_expression");
+    break;
+  case 'd':
+    PrintOut(priority, "ata, scsi, marvell, removable, sat, 3ware,N, hpt,L/M/N");
+    break;
+  case 'T':
+    PrintOut(priority, "normal, permissive");
+    break;
+  case 'o':
+  case 'S':
+    PrintOut(priority, "on, off");
+    break;
+  case 'l':
+    PrintOut(priority, "error, selftest");
+    break;
+  case 'M':
+    PrintOut(priority, "\"once\", \"daily\", \"diminishing\", \"test\", \"exec\"");
+    break;
+  case 'v':
+    if (!(s = create_vendor_attribute_arg_list())) {
+      PrintOut(LOG_CRIT,"Insufficient memory to construct argument list\n");
+      EXIT(EXIT_NOMEM);
+    }
+    PrintOut(priority, "\n%s\n", s);
+    s=CheckFree(s, __LINE__,filenameandversion);
+    break;
+  case 'P':
+    PrintOut(priority, "use, ignore, show, showall");
+    break;
+  case 'F':
+    PrintOut(priority, "none, samsung, samsung2");
+    break;
+  }
+}
+
+// exits with an error message, or returns integer value of token
+int GetInteger(char *arg, char *name, char *token, int lineno, char *configfile, int min, int max){
+  char *endptr;
+  int val;
+  
+  // check input range
+  if (min<0){
+    PrintOut(LOG_CRIT, "min =%d passed to GetInteger() must be >=0\n", min);
+    return -1;
+  }
+
+  // make sure argument is there
+  if (!arg) {
+    PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes integer argument from %d to %d.\n",
+             configfile, lineno, name, token, min, max);
+    return -1;
+  }
+  
+  // get argument value (base 10), check that it's integer, and in-range
+  val=strtol(arg,&endptr,10);
+  if (*endptr!='\0' || val<min || val>max )  {
+    PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs integer from %d to %d.\n",
+             configfile, lineno, name, token, arg, min, max);
+    return -1;
+  }
+
+  // all is well; return value
+  return val;
+}
+
+
+// Get 1-3 small integer(s) for '-W' directive
+int Get3Integers(const char *arg, const char *name, const char *token, int lineno, const char *configfile,
+                 unsigned char * val1, unsigned char * val2, unsigned char * val3){
+  unsigned v1 = 0, v2 = 0, v3 = 0;
+  int n1 = -1, n2 = -1, n3 = -1, len;
+  if (!arg) {
+    PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes 1-3 integer argument(s) from 0 to 255.\n",
+             configfile, lineno, name, token);
+    return -1;
+  }
+
+  len = strlen(arg);
+  if (!(   sscanf(arg, "%u%n,%u%n,%u%n", &v1, &n1, &v2, &n2, &v3, &n3) >= 1
+        && (n1 == len || n2 == len || n3 == len) && v1 <= 255 && v2 <= 255 && v3 <= 255)) {
+    PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs 1-3 integer(s) from 0 to 255.\n",
+             configfile, lineno, name, token, arg);
+    return -1;
+  }
+  *val1 = (unsigned char)v1; *val2 = (unsigned char)v2; *val3 = (unsigned char)v3;
+  return 0;
+}
+
+
+// This function returns 1 if it has correctly parsed one token (and
+// any arguments), else zero if no tokens remain.  It returns -1 if an
+// error was encountered.
+int ParseToken(char *token,cfgfile *cfg){
+  char sym;
+  char *name=cfg->name;
+  int lineno=cfg->lineno;
+  char *delim = " \n\t";
+  int badarg = 0;
+  int missingarg = 0;
+  char *arg = NULL;
+  int makemail=0;
+  maildata *mdat=NULL, tempmail;
+
+  // is the rest of the line a comment
+  if (*token=='#')
+    return 1;
+  
+  // is the token not recognized?
+  if (*token!='-' || strlen(token)!=2) {
+    PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
+             configfile, lineno, name, token);
+    PrintOut(LOG_CRIT, "Run smartd -D to print a list of valid Directives.\n");
+    return -1;
+  }
+  
+  // token we will be parsing:
+  sym=token[1];
+
+  // create temporary maildata structure.  This means we can postpone
+  // allocating space in the data segment until we are sure there are
+  // no errors.
+  if ('m'==sym || 'M'==sym){
+    if (!cfg->mailwarn){
+      memset(&tempmail, 0, sizeof(maildata));
+      mdat=&tempmail;
+      makemail=1;
+    }
+    else
+      mdat=cfg->mailwarn;
+  }
+
+  // parse the token and swallow its argument
+  switch (sym) {
+    int val;
+
+  case 'C':
+    // monitor current pending sector count (default 197)
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0)
+      return -1;
+    if (val==CUR_UNC_DEFAULT)
+      val=0;
+    else if (val==0)
+      val=CUR_UNC_DEFAULT;
+    // set bottom 8 bits to correct value
+    cfg->pending &= 0xff00;
+    cfg->pending |= val;
+    break;
+  case 'U':
+    // monitor offline uncorrectable sectors (default 198)
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0)
+      return -1;
+    if (val==OFF_UNC_DEFAULT)
+      val=0;
+    else if (val==0)
+      val=OFF_UNC_DEFAULT;
+    // turn off top 8 bits, then set to correct value
+    cfg->pending &= 0xff;
+    cfg->pending |= (val<<8);
+    break;
+  case 'T':
+    // Set tolerance level for SMART command failures
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "normal")) {
+      // Normal mode: exit on failure of a mandatory S.M.A.R.T. command, but
+      // not on failure of an optional S.M.A.R.T. command.
+      // This is the default so we don't need to actually do anything here.
+      cfg->permissive=0;
+    } else if (!strcmp(arg, "permissive")) {
+      // Permissive mode; ignore errors from Mandatory SMART commands
+      cfg->permissive=1;
+    } else {
+      badarg = 1;
+    }
+    break;
+  case 'd':
+    // specify the device type
+    cfg->controller_explicit = 1;
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "ata")) {
+      cfg->controller_port = 0;
+      cfg->controller_type = CONTROLLER_ATA;
+    } else if (!strcmp(arg, "scsi")) {
+      cfg->controller_port =0;
+      cfg->controller_type = CONTROLLER_SCSI;
+    } else if (!strcmp(arg, "marvell")) {
+      cfg->controller_port =0;
+      cfg->controller_type = CONTROLLER_MARVELL_SATA;
+    } else if (!strncmp(arg, "sat", 3)) {
+      cfg->controller_type = CONTROLLER_SAT;
+      cfg->controller_port = 0;
+      cfg->satpassthrulen = 0;
+      if (strlen(arg) > 3) {
+        int k;
+        char * cp;
+
+        cp = strchr(arg, ',');
+        if (cp && (1 == sscanf(cp + 1, "%d", &k)) &&
+            ((0 == k) || (12 == k) || (16 == k)))
+          cfg->satpassthrulen = k;
+        else {
+          PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
+                   "'-d sat,<n>' requires <n> to be 0, 12 or 16\n",
+                   configfile, lineno, name);
+          badarg = 1;
+        }
+      }
+    } else if (!strncmp(arg, "hpt", 3)){
+      unsigned char i, slash = 0;
+      cfg->hpt_data[0] = 0;
+      cfg->hpt_data[1] = 0;
+      cfg->hpt_data[2] = 0;
+      cfg->controller_type = CONTROLLER_HPT;
+      for (i=4; i < strlen(arg); i++) {
+        if(arg[i] == '/') {
+          slash++;
+          if(slash == 3) {
+            PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
+                     "'-d hpt,L/M/N' supports 2-3 items\n",
+                     configfile, lineno, name);
+            badarg = TRUE;
+            break;
+          }
+        }
+        else if ((arg[i])>='0' && (arg[i])<='9') {
+          if (cfg->hpt_data[slash]>1) { /* hpt_data[x] max 19 */
+            badarg = TRUE;
+            break;
+          }
+          cfg->hpt_data[slash] = cfg->hpt_data[slash]*10 + arg[i] - '0';
+        }
+        else {
+          badarg = TRUE;
+          break;
+        }
+      }
+      if ( slash == 0 ) {
+        badarg = TRUE;
+      } else if (badarg != TRUE) {
+        if (cfg->hpt_data[0]==0 || cfg->hpt_data[0]>8){
+           PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
+                    "'-d hpt,L/M/N' no/invalid controller id L supplied\n",
+                    configfile, lineno, name);
+           badarg = TRUE;
+        }
+        if (cfg->hpt_data[1]==0 || cfg->hpt_data[1]>8){
+          PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
+                   "'-d hpt,L/M/N' no/invalid channel number M supplied\n",
+                   configfile, lineno, name);
+          badarg = TRUE;
+        }
+        if (slash==2){
+          if (cfg->hpt_data[2]==0 || cfg->hpt_data[2]>15){
+            PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive "
+                     "'-d hpt,L/M/N' no/invalid pmport number N supplied\n",
+                     configfile, lineno, name);
+            badarg = TRUE;
+          }
+        } else { /* no pmport device */
+          cfg->hpt_data[2]=1;
+        }
+      }
+    } else if (!strcmp(arg, "removable")) {
+      cfg->removable = 1;
+    } else {
+      // look 3ware,N RAID device
+      int i;
+      char *s;
+      
+      // make a copy of the string to mess with
+      if (!(s = strdup(arg))) {
+        PrintOut(LOG_CRIT,
+                 "No memory to copy argument to -d option - exiting\n");
+        EXIT(EXIT_NOMEM);
+      } else if (strncmp(s,"3ware,",6)) {
+        badarg=1;
+      } else if (split_report_arg2(s, &i)){
+        PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N requires N integer\n",
+                 configfile, lineno, name);
+        badarg=1;
+      } else if ( i<0 || i>15) {
+        PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N (N=%d) must have 0 <= N <= 15\n",
+                 configfile, lineno, name, i);
+        badarg=1;
+      } else {
+       // determine type of escalade device from name of device
+       cfg->controller_type = guess_device_type(name);
+       if (cfg->controller_type!=CONTROLLER_3WARE_9000_CHAR && cfg->controller_type!=CONTROLLER_3WARE_678K_CHAR)
+         cfg->controller_type=CONTROLLER_3WARE_678K;
+           
+        // NOTE: controller_port == disk number + 1
+        cfg->controller_port = i+1;
+      }
+      s=CheckFree(s, __LINE__,filenameandversion); 
+    }
+    break;
+  case 'F':
+    // fix firmware bug
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "none")) {
+      cfg->fixfirmwarebug = FIX_NONE;
+    } else if (!strcmp(arg, "samsung")) {
+      cfg->fixfirmwarebug = FIX_SAMSUNG;
+    } else if (!strcmp(arg, "samsung2")) {
+      cfg->fixfirmwarebug = FIX_SAMSUNG2;
+    } else {
+      badarg = 1;
+    }
+    break;
+  case 'H':
+    // check SMART status
+    cfg->smartcheck=1;
+    break;
+  case 'f':
+    // check for failure of usage attributes
+    cfg->usagefailed=1;
+    break;
+  case 't':
+    // track changes in all vendor attributes
+    cfg->prefail=1;
+    cfg->usage=1;
+    break;
+  case 'p':
+    // track changes in prefail vendor attributes
+    cfg->prefail=1;
+    break;
+  case 'u':
+    //  track changes in usage vendor attributes
+    cfg->usage=1;
+    break;
+  case 'l':
+    // track changes in SMART logs
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "selftest")) {
+      // track changes in self-test log
+      cfg->selftest=1;
+    } else if (!strcmp(arg, "error")) {
+      // track changes in ATA error log
+      cfg->errorlog=1;
+    } else {
+      badarg = 1;
+    }
+    break;
+  case 'a':
+    // monitor everything
+    cfg->smartcheck=1;
+    cfg->prefail=1;
+    cfg->usagefailed=1;
+    cfg->usage=1;
+    cfg->selftest=1;
+    cfg->errorlog=1;
+    break;
+  case 'o':
+    // automatic offline testing enable/disable
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "on")) {
+      cfg->autoofflinetest = 2;
+    } else if (!strcmp(arg, "off")) {
+      cfg->autoofflinetest = 1;
+    } else {
+      badarg = 1;
+    }
+    break;
+  case 'n':
+    // skip disk check if in idle or standby mode
+    if (!(arg = strtok(NULL, delim)))
+      missingarg = 1;
+    else if (!strcmp(arg, "never")   || !strcmp(arg, "never,q"))
+      cfg->powermode = 0;
+    else if (!strcmp(arg, "sleep")   || !strcmp(arg, "sleep,q"))
+      cfg->powermode = 1;
+    else if (!strcmp(arg, "standby") || !strcmp(arg, "standby,q"))
+      cfg->powermode = 2;
+    else if (!strcmp(arg, "idle")    || !strcmp(arg, "idle,q"))
+      cfg->powermode = 3;
+    else
+      badarg = 1;
+    cfg->powerquiet = !!strchr(arg, ',');
+    break;
+  case 'S':
+    // automatic attribute autosave enable/disable
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "on")) {
+      cfg->autosave = 2;
+    } else if (!strcmp(arg, "off")) {
+      cfg->autosave = 1;
+    } else {
+      badarg = 1;
+    }
+    break;
+  case 's':
+    // warn user, and delete any previously given -s REGEXP Directives
+    if (cfg->testdata){
+      PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Test Directive -s %s\n",
+               configfile, lineno, name, cfg->testdata->regex);
+      cfg->testdata=FreeTestData(cfg->testdata);
+    }
+    // check for missing argument
+    if (!(arg = strtok(NULL, delim))) {
+      missingarg = 1;
+    }
+    // allocate space for structure and string
+    else if (!(cfg->testdata=(testinfo *)Calloc(1, sizeof(testinfo))) || !(cfg->testdata->regex=CustomStrDup(arg, 1, __LINE__,filenameandversion))) {
+      PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create Test Directive -s %s!\n",
+               configfile, lineno, name, arg);
+      EXIT(EXIT_NOMEM);
+    }
+    else if ((val=regcomp(&(cfg->testdata->cregex), arg, REG_EXTENDED))) {
+      char errormsg[512];
+      // not a valid regular expression!
+      regerror(val, &(cfg->testdata->cregex), errormsg, 512);
+      PrintOut(LOG_CRIT, "File %s line %d (drive %s): -s argument \"%s\" is INVALID extended regular expression. %s.\n",
+               configfile, lineno, name, arg, errormsg);
+      cfg->testdata=FreeTestData(cfg->testdata);
+      return -1;
+    }
+    // Do a bit of sanity checking and warn user if we think that
+    // their regexp is "strange". User probably confused about shell
+    // glob(3) syntax versus regular expression syntax regexp(7).
+    if ((int)strlen(arg) != (val=strspn(arg,"0123456789/.-+*|()?^$[]SLCO")))
+      PrintOut(LOG_INFO,  "File %s line %d (drive %s): warning, character %d (%c) looks odd in extended regular expression %s\n",
+               configfile, lineno, name, val+1, arg[val], arg);
+    break;
+  case 'm':
+    // send email to address that follows
+    if (!(arg = strtok(NULL,delim)))
+      missingarg = 1;
+    else {
+      if (mdat->address) {
+        PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n",
+                 configfile, lineno, name, mdat->address);
+        mdat->address=FreeNonZero(mdat->address, -1,__LINE__,filenameandversion);
+      }
+      mdat->address=CustomStrDup(arg, 1, __LINE__,filenameandversion);
+    }
+    break;
+  case 'M':
+    // email warning options
+    if (!(arg = strtok(NULL, delim)))
+      missingarg = 1;
+    else if (!strcmp(arg, "once"))
+      mdat->emailfreq = 1;
+    else if (!strcmp(arg, "daily"))
+      mdat->emailfreq = 2;
+    else if (!strcmp(arg, "diminishing"))
+      mdat->emailfreq = 3;
+    else if (!strcmp(arg, "test"))
+      mdat->emailtest = 1;
+    else if (!strcmp(arg, "exec")) {
+      // Get the next argument (the command line)
+      if (!(arg = strtok(NULL, delim))) {
+        PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n",
+                 configfile, lineno, name, token);
+        return -1;
+      }
+      // Free the last cmd line given if any, and copy new one
+      if (mdat->emailcmdline) {
+        PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous mail Directive -M exec %s\n",
+                 configfile, lineno, name, mdat->emailcmdline);
+        mdat->emailcmdline=FreeNonZero(mdat->emailcmdline, -1,__LINE__,filenameandversion);
+      }
+      mdat->emailcmdline=CustomStrDup(arg, 1, __LINE__,filenameandversion);
+    } 
+    else
+      badarg = 1;
+    break;
+  case 'i':
+    // ignore failure of usage attribute
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
+      return -1;
+    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_FAILUSE, __LINE__);
+    break;
+  case 'I':
+    // ignore attribute for tracking purposes
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
+      return -1;
+    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_IGNORE, __LINE__);
+    break;
+  case 'r':
+    // print raw value when tracking
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
+      return -1;
+    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__);
+    break;
+  case 'R':
+    // track changes in raw value (forces printing of raw value)
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
+      return -1;
+    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__);
+    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAW, __LINE__);
+    break;
+  case 'W':
+    // track Temperature
+    if ((val=Get3Integers(arg=strtok(NULL,delim), name, token, lineno, configfile,
+                          &cfg->tempdiff, &cfg->tempinfo, &cfg->tempcrit))<0)
+      return -1;
+    // increase min Temperature during first 30 minutes
+    if (!(cfg->tempmininc = (unsigned char)(CHECKTIME / checktime)))
+      cfg->tempmininc = 1;
+    break;
+  case 'v':
+    // non-default vendor-specific attribute meaning
+    if (!(arg=strtok(NULL,delim))) {
+      missingarg = 1;
+    } else if (parse_attribute_def(arg, &cfg->attributedefs)){   
+      badarg = 1;
+    }
+    break;
+  case 'P':
+    // Define use of drive-specific presets.
+    if (!(arg = strtok(NULL, delim))) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "use")) {
+      cfg->ignorepresets = FALSE;
+    } else if (!strcmp(arg, "ignore")) {
+      cfg->ignorepresets = TRUE;
+    } else if (!strcmp(arg, "show")) {
+      cfg->showpresets = TRUE;
+    } else if (!strcmp(arg, "showall")) {
+      showallpresets();
+    } else {
+      badarg = 1;
+    }
+    break;
+  default:
+    // Directive not recognized
+    PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
+             configfile, lineno, name, token);
+    Directives();
+    return -1;
+  }
+  if (missingarg) {
+    PrintOut(LOG_CRIT, "File %s line %d (drive %s): Missing argument to %s Directive\n",
+             configfile, lineno, name, token);
+  }
+  if (badarg) {
+    PrintOut(LOG_CRIT, "File %s line %d (drive %s): Invalid argument to %s Directive: %s\n",
+             configfile, lineno, name, token, arg);
+  }
+  if (missingarg || badarg) {
+    PrintOut(LOG_CRIT, "Valid arguments to %s Directive are: ", token);
+    printoutvaliddirectiveargs(LOG_CRIT, sym);
+    PrintOut(LOG_CRIT, "\n");
+    return -1;
+  }
+
+  // If this did something to fill the mail structure, and that didn't
+  // already exist, create it and copy.
+  if (makemail) {
+    if (!(cfg->mailwarn=(maildata *)Calloc(1, sizeof(maildata)))) {
+      PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create mail warning entry!\n",
+               configfile, lineno, name);
+      EXIT(EXIT_NOMEM);
+    }
+    memcpy(cfg->mailwarn, mdat, sizeof(maildata));
+  }
+  
+  return 1;
+}
+
+// Allocate storage for a new cfgfile entry.  If original!=NULL, it's
+// a copy of the original, but with private data storage.  Else all is
+// zeroed.  Returns address, and fails if non memory available.
+
+cfgfile *CreateConfigEntry(cfgfile *original){
+  cfgfile *add;
+  
+  // allocate memory for new structure
+  if (!(add=(cfgfile *)Calloc(1,sizeof(cfgfile))))
+    goto badexit;
+  
+  // if old structure was pointed to, copy it
+  if (original)
+    memcpy(add, original, sizeof(cfgfile));
+  
+  // make private copies of data items ONLY if they are in use (non
+  // NULL)
+  add->name         = CustomStrDup(add->name,         0, __LINE__,filenameandversion);
+
+  if (add->testdata) {
+    int val;
+    if (!(add->testdata=(testinfo *)Calloc(1,sizeof(testinfo))))
+      goto badexit;
+    memcpy(add->testdata, original->testdata, sizeof(testinfo));
+    add->testdata->regex = CustomStrDup(add->testdata->regex,   1, __LINE__,filenameandversion);
+    // only POSIX-portable way to make fresh copy of compiled regex is
+    // to recompile it completely.  There is no POSIX
+    // compiled-regex-copy command.
+    if ((val=regcomp(&(add->testdata->cregex), add->testdata->regex, REG_EXTENDED))) {
+      char errormsg[512];
+      regerror(val, &(add->testdata->cregex), errormsg, 512);
+      PrintOut(LOG_CRIT, "unable to recompile regular expression %s. %s\n", add->testdata->regex, errormsg);
+      goto badexit;
+    }
+  }
+  
+  if (add->mailwarn) {
+    if (!(add->mailwarn=(maildata *)Calloc(1,sizeof(maildata))))
+      goto badexit;
+    memcpy(add->mailwarn, original->mailwarn, sizeof(maildata));
+    add->mailwarn->address      = CustomStrDup(add->mailwarn->address,      0, __LINE__,filenameandversion);
+    add->mailwarn->emailcmdline = CustomStrDup(add->mailwarn->emailcmdline, 0, __LINE__,filenameandversion);
+  }
+
+  if (add->attributedefs) {
+    if (!(add->attributedefs=(unsigned char *)Calloc(MAX_ATTRIBUTE_NUM,1)))
+      goto badexit;
+    memcpy(add->attributedefs, original->attributedefs, MAX_ATTRIBUTE_NUM);
+  }
+  
+  if (add->monitorattflags) {
+    if (!(add->monitorattflags=(unsigned char *)Calloc(NMONITOR*32, 1)))
+      goto badexit;
+    memcpy(add->monitorattflags, original->monitorattflags, NMONITOR*32);
+  }
+  
+  if (add->smartval) {
+    if (!(add->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values))))
+      goto badexit;
+  }
+  
+  if (add->smartthres) {
+    if (!(add->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt))))
+      goto badexit;
+  }
+
+  return add;
+
+ badexit:
+  PrintOut(LOG_CRIT, "No memory to create entry from configuration file\n");
+  EXIT(EXIT_NOMEM);
+}
+
+
+
+// This is the routine that adds things to the cfgentries list. To
+// prevent memory leaks when re-reading the configuration file many
+// times, this routine MUST deallocate any memory other than that
+// pointed to within cfg-> before it returns.
+//
+// Return values are:
+//  1: parsed a normal line
+//  0: found comment or blank line
+// -1: found SCANDIRECTIVE line
+// -2: found an error
+//
+// Note: this routine modifies *line from the caller!
+int ParseConfigLine(int entry, int lineno,char *line){
+  char *token=NULL;
+  char *name=NULL;
+  char *delim = " \n\t";
+  cfgfile *cfg=NULL;
+  int devscan=0;
+
+  // get first token: device name. If a comment, skip line
+  if (!(name=strtok(line,delim)) || *name=='#') {
+    return 0;
+  }
+
+  // Have we detected the SCANDIRECTIVE directive?
+  if (!strcmp(SCANDIRECTIVE,name)){
+    devscan=1;
+    if (entry) {
+      PrintOut(LOG_INFO,"Scan Directive %s (line %d) must be the first entry in %s\n",name, lineno, configfile);
+      return -2;
+    }
+  }
+  
+  // Is there space for another entry?  If not, allocate more
+  while (entry>=cfgentries_max)
+    cfgentries=AllocateMoreSpace(cfgentries, &cfgentries_max, "configuration file device");
+
+  // We've got a legit entry, make space to store it
+  cfg=cfgentries[entry]=CreateConfigEntry(NULL);
+  cfg->name = CustomStrDup(name, 1, __LINE__,filenameandversion);
+
+  // Store line number, and by default check for both device types.
+  cfg->lineno=lineno;
+  
+  // Try and recognize if a IDE or SCSI device.  These can be
+  // overwritten by configuration file directives.
+  if (cfg->controller_type==CONTROLLER_UNKNOWN)
+    cfg->controller_type = guess_device_type(cfg->name);
+  
+  // parse tokens one at a time from the file.
+  while ((token=strtok(NULL,delim))){
+    int retval=ParseToken(token,cfg);
+    
+    if (retval==0)
+      // No tokens left:
+      break;
+    
+    if (retval>0) {
+      // Parsed token  
+#if (0)
+      PrintOut(LOG_INFO,"Parsed token %s\n",token);
+#endif
+      continue;
+    }
+    
+    if (retval<0) {
+      // error found on the line
+      return -2;
+    }
+  }
+  
+  // If we found 3ware controller, then modify device name by adding a SPACE
+  if (cfg->controller_port){
+    int len=17+strlen(cfg->name);
+    char *newname;
+    
+    if (devscan){
+      PrintOut(LOG_CRIT, "smartd: can not scan for 3ware devices (line %d of file %s)\n",
+               lineno, configfile);
+      return -2;
+    }
+    
+    if (!(newname=(char *)calloc(len,1))) {
+      PrintOut(LOG_INFO,"No memory to parse file: %s line %d, %s\n", configfile, lineno, strerror(errno));
+      EXIT(EXIT_NOMEM);
+    }
+    
+    // Make new device name by adding a space then RAID disk number
+    snprintf(newname, len, "%s [3ware_disk_%02d]", cfg->name, cfg->controller_port-1);
+    cfg->name=CheckFree(cfg->name, __LINE__,filenameandversion);
+    cfg->name=newname;
+    bytes+=16;
+  }
+
+  if (cfg->hpt_data[0]) {
+      int len=17+strlen(cfg->name);
+      char *newname;
+
+      if (devscan){
+        PrintOut(LOG_CRIT, "smartd: can not scan for highpoint devices (line %d of file %s)\n",
+                 lineno, configfile);
+        return -2;
+      }
+
+      if (!(newname=(char *)calloc(len,1))) {
+        PrintOut(LOG_INFO,"No memory to parse file: %s line %d, %s\n", configfile, lineno, strerror(errno));
+        EXIT(EXIT_NOMEM);
+      }
+
+      // Make new device name by adding a space then RAID disk number
+      snprintf(newname, len, "%s [hpt_%d/%d/%d]", cfg->name, cfg->hpt_data[0],
+               cfg->hpt_data[1], cfg->hpt_data[2]);
+      cfg->name=CheckFree(cfg->name, __LINE__,filenameandversion);
+      cfg->name=newname;
+      bytes+=16;
+  }
+
+  // If NO monitoring directives are set, then set all of them.
+  if (!(cfg->smartcheck || cfg->usagefailed || cfg->prefail  || 
+        cfg->usage      || cfg->selftest    || cfg->errorlog ||  
+        cfg->tempdiff   || cfg->tempinfo    || cfg->tempcrit   )) {
+    
+    PrintOut(LOG_INFO,"Drive: %s, implied '-a' Directive on line %d of file %s\n",
+             cfg->name, cfg->lineno, configfile);
+    
+    cfg->smartcheck=1;
+    cfg->usagefailed=1;
+    cfg->prefail=1;
+    cfg->usage=1;
+    cfg->selftest=1;
+    cfg->errorlog=1;
+  }
+  
+  // additional sanity check. Has user set -M options without -m?
+  if (cfg->mailwarn && !cfg->mailwarn->address && (cfg->mailwarn->emailcmdline || cfg->mailwarn->emailfreq || cfg->mailwarn->emailtest)){
+    PrintOut(LOG_CRIT,"Drive: %s, -M Directive(s) on line %d of file %s need -m ADDRESS Directive\n",
+             cfg->name, cfg->lineno, configfile);
+    return -2;
+  }
+  
+  // has the user has set <nomailer>?
+  if (cfg->mailwarn && cfg->mailwarn->address && !strcmp(cfg->mailwarn->address,"<nomailer>")){
+    // check that -M exec is also set
+    if (!cfg->mailwarn->emailcmdline){
+      PrintOut(LOG_CRIT,"Drive: %s, -m <nomailer> Directive on line %d of file %s needs -M exec Directive\n",
+               cfg->name, cfg->lineno, configfile);
+      return -2;
+    }
+    // now free memory.  From here on the sign of <nomailer> is
+    // address==NULL and cfg->emailcmdline!=NULL
+    cfg->mailwarn->address=FreeNonZero(cfg->mailwarn->address, -1,__LINE__,filenameandversion);
+  }
+
+  // set cfg->emailfreq to 1 (once) if user hasn't set it
+  if (cfg->mailwarn && !cfg->mailwarn->emailfreq)
+    cfg->mailwarn->emailfreq = 1;
+
+  entry++;
+
+  if (devscan)
+    return -1;
+  else
+    return 1;
+}
+
+// clean up utility for ParseConfigFile()
+void cleanup(FILE **fpp, int is_stdin){
+  if (*fpp){
+    // (*fpp != stdin) does not work here if stdin has been closed & reopened
+    if (!is_stdin)
+      fclose(*fpp);
+    *fpp=NULL;
+  }
+
+  return;
+}
+
+
+// Parses a configuration file.  Return values are:
+//  N=>0: found N entries
+// -1:    syntax error in config file
+// -2:    config file does not exist
+// -3:    config file exists but cannot be read
+//
+// In the case where the return value is 0, there are three
+// possiblities:
+// Empty configuration file ==> cfgentries==NULL
+// No configuration file    ==> cfgentries[0]->lineno == 0
+// SCANDIRECTIVE found      ==> cfgentries[0]->lineno != 0
+int ParseConfigFile(){
+  FILE *fp=NULL;
+  int entry=0,lineno=1,cont=0,contlineno=0;
+  char line[MAXLINELEN+2];
+  char fullline[MAXCONTLINE+1];
+
+  int is_stdin = (configfile == configfile_stdin); // pointer comparison ok here
+
+  // Open config file, if it exists and is not <stdin>
+  if (!is_stdin) {
+    fp=fopen(configfile,"r");
+    if (fp==NULL && (errno!=ENOENT || configfile_alt)) {
+      // file exists but we can't read it or it should exist due to '-c' option
+      int ret = (errno!=ENOENT ? -3 : -2);
+      PrintOut(LOG_CRIT,"%s: Unable to open configuration file %s\n",
+               strerror(errno),configfile);
+      return ret;
+    }
+  }
+  else // read from stdin ('-c -' option)
+    fp = stdin;
+  
+  // No configuration file found -- use fake one
+  if (fp==NULL) {
+    int len=strlen(SCANDIRECTIVE)+4;
+    char *fakeconfig=(char *)calloc(len,1);
+  
+    if (!fakeconfig || 
+        (len-1) != snprintf(fakeconfig, len, "%s -a", SCANDIRECTIVE) ||
+        -1 != ParseConfigLine(entry, 0, fakeconfig)
+        ) {
+      PrintOut(LOG_CRIT,"Internal error in ParseConfigFile() at line %d of file %s\n%s", 
+               __LINE__, filenameandversion, reportbug);
+      EXIT(EXIT_BADCODE);
+    }
+    fakeconfig=CheckFree(fakeconfig, __LINE__,filenameandversion);
+    return 0;
+  }
+
+#ifdef __CYGWIN__
+  setmode(fileno(fp), O_TEXT); // Allow files with \r\n
+#endif
+
+  // configuration file exists
+  PrintOut(LOG_INFO,"Opened configuration file %s\n",configfile);
+
+  // parse config file line by line
+  while (1) {
+    int len=0,scandevice;
+    char *lastslash;
+    char *comment;
+    char *code;
+
+    // make debugging simpler
+    memset(line,0,sizeof(line));
+
+    // get a line
+    code=fgets(line,MAXLINELEN+2,fp);
+    
+    // are we at the end of the file?
+    if (!code){
+      if (cont) {
+        scandevice=ParseConfigLine(entry,contlineno,fullline);
+        // See if we found a SCANDIRECTIVE directive
+        if (scandevice==-1) {
+          cleanup(&fp, is_stdin);
+          return 0;
+        }
+        // did we find a syntax error
+        if (scandevice==-2) {
+          cleanup(&fp, is_stdin);
+          return -1;
+        }
+        // the final line is part of a continuation line
+        cont=0;
+        entry+=scandevice;
+      }
+      break;
+    }
+
+    // input file line number
+    contlineno++;
+    
+    // See if line is too long
+    len=strlen(line);
+    if (len>MAXLINELEN){
+      char *warn;
+      if (line[len-1]=='\n')
+        warn="(including newline!) ";
+      else
+        warn="";
+      PrintOut(LOG_CRIT,"Error: line %d of file %s %sis more than MAXLINELEN=%d characters.\n",
+               (int)contlineno,configfile,warn,(int)MAXLINELEN);
+      cleanup(&fp, is_stdin);
+      return -1;
+    }
+
+    // Ignore anything after comment symbol
+    if ((comment=strchr(line,'#'))){
+      *comment='\0';
+      len=strlen(line);
+    }
+
+    // is the total line (made of all continuation lines) too long?
+    if (cont+len>MAXCONTLINE){
+      PrintOut(LOG_CRIT,"Error: continued line %d (actual line %d) of file %s is more than MAXCONTLINE=%d characters.\n",
+               lineno, (int)contlineno, configfile, (int)MAXCONTLINE);
+      cleanup(&fp, is_stdin);
+      return -1;
+    }
+    
+    // copy string so far into fullline, and increment length
+    strcpy(fullline+cont,line);
+    cont+=len;
+
+    // is this a continuation line.  If so, replace \ by space and look at next line
+    if ( (lastslash=strrchr(line,'\\')) && !strtok(lastslash+1," \n\t")){
+      *(fullline+(cont-len)+(lastslash-line))=' ';
+      continue;
+    }
+
+    // Not a continuation line. Parse it
+    scandevice=ParseConfigLine(entry,contlineno,fullline);
+
+    // did we find a scandevice directive?
+    if (scandevice==-1) {
+      cleanup(&fp, is_stdin);
+      return 0;
+    }
+    // did we find a syntax error
+    if (scandevice==-2) {
+      cleanup(&fp, is_stdin);
+      return -1;
+    }
+
+    entry+=scandevice;
+    lineno++;
+    cont=0;
+  }
+  cleanup(&fp, is_stdin);
+  
+  // note -- may be zero if syntax of file OK, but no valid entries!
+  return entry;
+}
+
+
+// Prints copyright, license and version information
+void PrintCopyleft(void){
+  debugmode=1;
+  PrintHead();
+  PrintCVS();
+  return;
+}
+
+/* Prints the message "=======> VALID ARGUMENTS ARE: <LIST>  <=======\n", where
+   <LIST> is the list of valid arguments for option opt. */
+void PrintValidArgs(char opt) {
+  const char *s;
+
+  PrintOut(LOG_CRIT, "=======> VALID ARGUMENTS ARE: ");
+  if (!(s = GetValidArgList(opt)))
+    PrintOut(LOG_CRIT, "Error constructing argument list for option %c", opt);
+  else
+    PrintOut(LOG_CRIT, (char *)s);
+  PrintOut(LOG_CRIT, " <=======\n");
+}
+
+// Parses input line, prints usage message and
+// version/license/copyright messages
+void ParseOpts(int argc, char **argv){
+  extern char *optarg;
+  extern int  optopt, optind, opterr;
+  int optchar;
+  int badarg;
+  char *tailptr;
+  long lchecktime;
+  // Please update GetValidArgList() if you edit shortopts
+  const char *shortopts = "c:l:q:dDi:p:r:Vh?";
+#ifdef HAVE_GETOPT_LONG
+  char *arg;
+  // Please update GetValidArgList() if you edit longopts
+  struct option longopts[] = {
+    { "configfile",     required_argument, 0, 'c' },
+    { "logfacility",    required_argument, 0, 'l' },
+    { "quit",           required_argument, 0, 'q' },
+    { "debug",          no_argument,       0, 'd' },
+    { "showdirectives", no_argument,       0, 'D' },
+    { "interval",       required_argument, 0, 'i' },
+    { "pidfile",        required_argument, 0, 'p' },
+    { "report",         required_argument, 0, 'r' },
+#if defined(_WIN32) || defined(__CYGWIN__)
+    { "service",        no_argument,       0, 'S' },
+#endif
+    { "version",        no_argument,       0, 'V' },
+    { "license",        no_argument,       0, 'V' },
+    { "copyright",      no_argument,       0, 'V' },
+    { "help",           no_argument,       0, 'h' },
+    { "usage",          no_argument,       0, 'h' },
+    { 0,                0,                 0, 0   }
+  };
+#endif
+  
+  opterr=optopt=0;
+  badarg=FALSE;
+  
+  // Parse input options.  This horrible construction is so that emacs
+  // indents properly.  Sorry.
+  while (-1 != (optchar = 
+#ifdef HAVE_GETOPT_LONG
+                getopt_long(argc, argv, shortopts, longopts, NULL)
+#else
+                getopt(argc, argv, shortopts)
+#endif
+                )) {
+    
+    switch(optchar) {
+    case 'q':
+      // when to quit
+      if (!(strcmp(optarg,"nodev"))) {
+        quit=0;
+      } else if (!(strcmp(optarg,"nodevstartup"))) {
+        quit=1;
+      } else if (!(strcmp(optarg,"never"))) {
+        quit=2;
+      } else if (!(strcmp(optarg,"onecheck"))) {
+        quit=3;
+        debugmode=1;
+      } else if (!(strcmp(optarg,"showtests"))) {
+        quit=4;
+        debugmode=1;
+      } else if (!(strcmp(optarg,"errors"))) {
+        quit=5;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'l':
+      // set the log facility level
+      if (!strcmp(optarg, "daemon"))
+        facility=LOG_DAEMON;
+      else if (!strcmp(optarg, "local0"))
+        facility=LOG_LOCAL0;
+      else if (!strcmp(optarg, "local1"))
+        facility=LOG_LOCAL1;
+      else if (!strcmp(optarg, "local2"))
+        facility=LOG_LOCAL2;
+      else if (!strcmp(optarg, "local3"))
+        facility=LOG_LOCAL3;
+      else if (!strcmp(optarg, "local4"))
+        facility=LOG_LOCAL4;
+      else if (!strcmp(optarg, "local5"))
+        facility=LOG_LOCAL5;
+      else if (!strcmp(optarg, "local6"))
+        facility=LOG_LOCAL6;
+      else if (!strcmp(optarg, "local7"))
+        facility=LOG_LOCAL7;
+      else
+        badarg = TRUE;
+      break;
+    case 'd':
+      // enable debug mode
+      debugmode = TRUE;
+      break;
+    case 'D':
+      // print summary of all valid directives
+      debugmode = TRUE;
+      Directives();
+      EXIT(0);
+      break;
+    case 'i':
+      // Period (time interval) for checking
+      // strtol will set errno in the event of overflow, so we'll check it.
+      errno = 0;
+      lchecktime = strtol(optarg, &tailptr, 10);
+      if (*tailptr != '\0' || lchecktime < 10 || lchecktime > INT_MAX || errno) {
+        debugmode=1;
+        PrintHead();
+        PrintOut(LOG_CRIT, "======> INVALID INTERVAL: %s <=======\n", optarg);
+        PrintOut(LOG_CRIT, "======> INTERVAL MUST BE INTEGER BETWEEN %d AND %d <=======\n", 10, INT_MAX);
+        PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
+        EXIT(EXIT_BADCMD);
+      }
+      checktime = (int)lchecktime;
+      break;
+    case 'r':
+      // report IOCTL transactions
+      {
+        int i;
+        char *s;
+
+        // split_report_arg() may modify its first argument string, so use a
+        // copy of optarg in case we want optarg for an error message.
+        if (!(s = strdup(optarg))) {
+          PrintOut(LOG_CRIT, "No memory to process -r option - exiting\n");
+          EXIT(EXIT_NOMEM);
+        }
+        if (split_report_arg(s, &i)) {
+          badarg = TRUE;
+        } else if (i<1 || i>3) {
+          debugmode=1;
+          PrintHead();
+          PrintOut(LOG_CRIT, "======> INVALID REPORT LEVEL: %s <=======\n", optarg);
+          PrintOut(LOG_CRIT, "======> LEVEL MUST BE INTEGER BETWEEN 1 AND 3<=======\n");
+          EXIT(EXIT_BADCMD);
+        } else if (!strcmp(s,"ioctl")) {
+          con->reportataioctl  = con->reportscsiioctl = i;
+        } else if (!strcmp(s,"ataioctl")) {
+          con->reportataioctl = i;
+        } else if (!strcmp(s,"scsiioctl")) {
+          con->reportscsiioctl = i;
+        } else {
+          badarg = TRUE;
+        }
+        s=CheckFree(s, __LINE__,filenameandversion);
+      }
+      break;
+    case 'c':
+      // alternate configuration file
+      if (strcmp(optarg,"-"))
+        configfile=configfile_alt=CustomStrDup(optarg, 1, __LINE__,filenameandversion);
+      else // read from stdin
+        configfile=configfile_stdin;
+      break;
+    case 'p':
+      // output file with PID number
+      pid_file=CustomStrDup(optarg, 1, __LINE__,filenameandversion);
+      break;
+#if defined(_WIN32) || defined(__CYGWIN__)
+    case 'S':
+      // running as service
+#ifdef __CYGWIN__ // On Windows, option is already handled by daemon_main(), so ignore it
+      is_service = 1;
+#endif
+      break;
+#endif // _WIN32 || __CYGWIN__
+    case 'V':
+      // print version and CVS info
+      PrintCopyleft();
+      EXIT(0);
+      break;
+    case 'h':
+      // help: print summary of command-line options
+      debugmode=1;
+      PrintHead();
+      Usage();
+      EXIT(0);
+      break;
+    case '?':
+    default:
+      // unrecognized option
+      debugmode=1;
+      PrintHead();
+#ifdef HAVE_GETOPT_LONG
+      // Point arg to the argument in which this option was found.
+      arg = argv[optind-1];
+      // Check whether the option is a long option that doesn't map to -h.
+      if (arg[1] == '-' && optchar != 'h') {
+        // Iff optopt holds a valid option then argument must be missing.
+        if (optopt && (strchr(shortopts, optopt) != NULL)) {
+          PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n",arg+2);
+          PrintValidArgs(optopt);
+        } else {
+          PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %s <=======\n\n",arg+2);
+        }
+        PrintOut(LOG_CRIT, "\nUse smartd --help to get a usage summary\n\n");
+        EXIT(EXIT_BADCMD);
+      }
+#endif
+      if (optopt) {
+        // Iff optopt holds a valid option then argument must be missing.
+        if (strchr(shortopts, optopt) != NULL){
+          PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %c <=======\n",optopt);
+          PrintValidArgs(optopt);
+        } else {
+          PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt);
+        }
+        PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
+        EXIT(EXIT_BADCMD);
+      }
+      Usage();
+      EXIT(0);
+    }
+
+    // Check to see if option had an unrecognized or incorrect argument.
+    if (badarg) {
+      debugmode=1;
+      PrintHead();
+      // It would be nice to print the actual option name given by the user
+      // here, but we just print the short form.  Please fix this if you know
+      // a clean way to do it.
+      PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <======= \n", optchar, optarg);
+      PrintValidArgs(optchar);
+      PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
+      EXIT(EXIT_BADCMD);
+    }
+  }
+
+  // non-option arguments are not allowed
+  if (argc > optind) {
+    debugmode=1;
+    PrintHead();
+    PrintOut(LOG_CRIT, "=======> UNRECOGNIZED ARGUMENT: %s <=======\n\n", argv[optind]);
+    PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
+    EXIT(EXIT_BADCMD);
+  }
+
+  // no pidfile in debug mode
+  if (debugmode && pid_file) {
+    debugmode=1;
+    PrintHead();
+    PrintOut(LOG_CRIT, "=======> INVALID CHOICE OF OPTIONS: -d and -p <======= \n\n");
+    PrintOut(LOG_CRIT, "Error: pid file %s not written in debug (-d) mode\n\n", pid_file);
+    pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion);
+    EXIT(EXIT_BADCMD);
+  }
+  
+  // print header
+  PrintHead();
+  
+  return;
+}
+
+// Function we call if no configuration file was found or if the
+// SCANDIRECTIVE Directive was found.  It makes entries for device
+// names returned by make_device_names() in os_OSNAME.c
+int MakeConfigEntries(const char *type, int start){
+  int i;
+  int num;
+  char** devlist = NULL;
+  cfgfile *first=cfgentries[0],*cfg=first;
+
+  // make list of devices
+  if ((num=make_device_names(&devlist,type))<0)
+    PrintOut(LOG_CRIT,"Problem creating device name scan list\n");
+  
+  // if no devices, or error constructing list, return
+  if (num<=0)
+    return 0;
+  
+  // loop over entries to create
+  for (i=0; i<num; i++){
+    
+    // make storage and copy for all but first entry
+    if (start+i) {
+      // allocate more storage if needed
+      while (cfgentries_max<=start+i)
+        cfgentries=AllocateMoreSpace(cfgentries, &cfgentries_max, "simulated configuration file device");
+      cfg=cfgentries[start+i]=CreateConfigEntry(first);
+    }
+
+    // ATA or SCSI?
+    if (!strcmp(type,"ATA") )
+      cfg->controller_type = CONTROLLER_ATA;
+    if (!strcmp(type,"SCSI") ) 
+      cfg->controller_type = CONTROLLER_SCSI;
+    
+    // remove device name, if it's there, and put in correct one
+    cfg->name=FreeNonZero(cfg->name, -1,__LINE__,filenameandversion);
+    // save pointer to the device name created within
+    // make_device_names
+    cfg->name=devlist[i];
+  }
+  
+  // If needed, free memory used for devlist: pointers now in
+  // cfgentries[]->names.  If num==0 we never get to this point, but
+  // that's OK.  If we realloc()d the array length in
+  // make_device_names() that was ALREADY equivalent to calling
+  // free().
+  devlist = FreeNonZero(devlist,(sizeof (char*) * num),__LINE__, filenameandversion);
+  
+  return num;
+}
+void CanNotRegister(char *name, char *type, int line, int scandirective){
+  if( !debugmode && scandirective == 1 ) { return; }
+  if (line)
+    PrintOut(scandirective?LOG_INFO:LOG_CRIT,
+             "Unable to register %s device %s at line %d of file %s\n",
+             type, name, line, configfile);
+  else
+    PrintOut(LOG_INFO,"Unable to register %s device %s\n",
+             type, name);
+  return;
+}
+
+// Returns negative value (see ParseConfigFile()) if config file
+// had errors, else number of entries which may be zero or positive. 
+// If we found no configuration file, or it contained SCANDIRECTIVE,
+// then *scanning is set to 1, else 0.
+int ReadOrMakeConfigEntries(int *scanning){
+  int entries;
+  
+  // deallocate any cfgfile data structures in memory
+  RmAllConfigEntries();
+  
+  // parse configuration file configfile (normally /etc/smartd.conf)  
+  if ((entries=ParseConfigFile())<0) {
+    // There was an error reading the configuration file.
+    RmAllConfigEntries();
+    if (entries == -1)
+      PrintOut(LOG_CRIT, "Configuration file %s has fatal syntax errors.\n", configfile);
+    return entries;
+  }
+
+  // did we find entries or scan?
+  *scanning=0;
+  
+  // no error parsing config file.
+  if (entries) {
+    // we did not find a SCANDIRECTIVE and did find valid entries
+    PrintOut(LOG_INFO, "Configuration file %s parsed.\n", configfile);
+  }
+  else if (cfgentries && cfgentries[0]) {
+    // we found a SCANDIRECTIVE or there was no configuration file so
+    // scan.  Configuration file's first entry contains all options
+    // that were set
+    cfgfile *first=cfgentries[0];
+    int doata  = !(first->controller_type==CONTROLLER_SCSI);
+    int doscsi = !(first->controller_type==CONTROLLER_ATA);
+    
+    *scanning=1;
+    
+    if (first->lineno)
+      PrintOut(LOG_INFO,"Configuration file %s was parsed, found %s, scanning devices\n", configfile, SCANDIRECTIVE);
+    else
+      PrintOut(LOG_INFO,"No configuration file %s found, scanning devices\n", configfile);
+    
+    // make config list of ATA devices to search for
+    if (doata)
+      entries+=MakeConfigEntries("ATA", entries);
+    // make config list of SCSI devices to search for
+    if (doscsi)
+      entries+=MakeConfigEntries("SCSI", entries);
+
+    // warn user if scan table found no devices
+    if (!entries) {
+      PrintOut(LOG_CRIT,"In the system's table of devices NO devices found to scan\n");
+      // get rid of fake entry with SCANDIRECTIVE as name
+      RmConfigEntry(cfgentries, __LINE__);
+    }
+  } 
+  else
+    PrintOut(LOG_CRIT,"Configuration file %s parsed but has no entries (like /dev/hda)\n",configfile);
+  
+  return entries;
+}
+
+
+// This function tries devices from cfgentries.  Each one that can be
+// registered is moved onto the [ata|scsi]devices lists and removed
+// from the cfgentries list, else it's memory is deallocated.
+void RegisterDevices(int scanning){
+  int i;
+  
+  // start by clearing lists/memory of ALL existing devices
+  RmAllDevEntries();
+  numdevata=numdevscsi=0;
+  
+  // Register entries
+  for (i=0; i<cfgentries_max ; i++){
+    
+    cfgfile *ent=cfgentries[i];
+    
+    // skip any NULL entries (holes)
+    if (!ent)
+      continue;
+    
+    // register ATA devices
+    if (ent->controller_type!=CONTROLLER_SCSI){
+      if (ATADeviceScan(ent, scanning))
+        CanNotRegister(ent->name, "ATA", ent->lineno, scanning);
+      else {
+        // move onto the list of ata devices
+        cfgentries[i]=NULL;
+        while (numdevata>=atadevlist_max)
+          atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device");
+        atadevlist[numdevata++]=ent;
+      }
+    }
+    
+    // then register SCSI devices
+    if (ent->controller_type==CONTROLLER_SCSI || ent->controller_type==CONTROLLER_UNKNOWN){
+      int retscsi=0;
+
+#if SCSITIMEOUT
+      struct sigaction alarmAction, defaultaction;
+
+      // Set up an alarm handler to catch USB devices that hang on
+      // SCSI scanning...
+      alarmAction.sa_handler= AlarmHandler;
+      alarmAction.sa_flags  = SA_RESTART;
+      if (sigaction(SIGALRM, &alarmAction, &defaultaction)) {
+        // if we can't set timeout, just scan device
+        PrintOut(LOG_CRIT, "Unable to initialize SCSI timeout mechanism.\n");
+        retscsi=SCSIDeviceScan(ent, scanning);
+      }
+      else {
+        // prepare return point in case of bad SCSI device
+        if (setjmp(registerscsienv))
+          // SCSI device timed out!
+          retscsi=-1;
+        else {
+        // Set alarm, make SCSI call, reset alarm
+          alarm(SCSITIMEOUT);
+          retscsi=SCSIDeviceScan(ent, scanning);
+          alarm(0);
+        }
+        if (sigaction(SIGALRM, &defaultaction, NULL)){
+          PrintOut(LOG_CRIT, "Unable to clear SCSI timeout mechanism.\n");
+        }
+      }
+#else
+      retscsi=SCSIDeviceScan(ent, scanning);
+#endif   
+
+      // Now scan SCSI device...
+      if (retscsi){
+        if (retscsi<0)
+          PrintOut(LOG_CRIT, "Device %s timed out (poorly-implemented USB device?)\n", ent->name);
+        CanNotRegister(ent->name, "SCSI", ent->lineno, scanning);
+      }
+      else {
+        // move onto the list of scsi devices
+        cfgentries[i]=NULL;
+        while (numdevscsi>=scsidevlist_max)
+          scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device");
+        scsidevlist[numdevscsi++]=ent;
+      }
+    }
+    
+    // if device is explictly listed and we can't register it, then
+    // exit unless the user has specified that the device is removable
+    if (cfgentries[i]  && !scanning){
+      if (ent->removable || quit==2)
+        PrintOut(LOG_INFO, "Device %s not available\n", ent->name);
+      else {
+        PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", ent->name);
+        EXIT(EXIT_BADDEV);
+      }
+    }
+    
+    // free up memory if device could not be registered
+    RmConfigEntry(cfgentries+i, __LINE__);
+  }
+  
+  return;
+}
+
+
+#ifndef _WIN32
+// Main function
+int main(int argc, char **argv)
+#else
+// Windows: internal main function started direct or by service control manager
+static int smartd_main(int argc, char **argv)
+#endif
+{
+  // external control variables for ATA disks
+  smartmonctrl control;
+
+  // is it our first pass through?
+  int firstpass=1;
+
+  // next time to wake up
+  time_t wakeuptime;
+
+  // for simplicity, null all global communications variables/lists
+  con=&control;
+  memset(con,        0,sizeof(control));
+
+  // parse input and print header and usage info if needed
+  ParseOpts(argc,argv);
+  
+  // do we mute printing from ataprint commands?
+  con->printing_switchable=0;
+  con->dont_print=debugmode?0:1;
+  
+  // don't exit on bad checksums
+  con->checksumfail=0;
+  
+  // the main loop of the code
+  while (1){
+
+    // are we exiting from a signal?
+    if (caughtsigEXIT) {
+      // are we exiting with SIGTERM?
+      int isterm=(caughtsigEXIT==SIGTERM);
+      int isquit=(caughtsigEXIT==SIGQUIT);
+      int isok=debugmode?isterm || isquit:isterm;
+      
+      PrintOut(isok?LOG_INFO:LOG_CRIT, "smartd received signal %d: %s\n",
+               caughtsigEXIT, strsignal(caughtsigEXIT));
+      
+      EXIT(isok?0:EXIT_SIGNAL);
+    }
+
+    // Should we (re)read the config file?
+    if (firstpass || caughtsigHUP){
+      int entries, scanning=0;
+
+      if (!firstpass) {
+#ifdef __CYGWIN__
+        // Workaround for missing SIGQUIT via keyboard on Cygwin
+        if (caughtsigHUP==2) {
+          // Simulate SIGQUIT if another SIGINT arrives soon
+          caughtsigHUP=0;
+          sleep(1);
+          if (caughtsigHUP==2) {
+            caughtsigEXIT=SIGQUIT;
+            continue;
+          }
+          caughtsigHUP=2;
+        }
+#endif
+        PrintOut(LOG_INFO,
+                 caughtsigHUP==1?
+                 "Signal HUP - rereading configuration file %s\n":
+                 "\a\nSignal INT - rereading configuration file %s ("SIGQUIT_KEYNAME" quits)\n\n",
+                 configfile);
+      }
+
+      // clears cfgentries, (re)reads config file, makes >=0 entries
+      entries=ReadOrMakeConfigEntries(&scanning);
+
+      if (entries>=0) {
+        // checks devices, then moves onto ata/scsi list or deallocates.
+        RegisterDevices(scanning);
+      }
+      else if (quit==2 || ((quit==0 || quit==1) && !firstpass)) {
+        // user has asked to continue on error in configuration file
+        if (!firstpass)
+          PrintOut(LOG_INFO,"Reusing previous configuration\n");
+      }
+      else {
+        // exit with configuration file error status
+        int status = (entries==-3 ? EXIT_READCONF : entries==-2 ? EXIT_NOCONF : EXIT_BADCONF);
+        EXIT(status);
+      }
+
+      // Log number of devices we are monitoring...
+      if (numdevata+numdevscsi || quit==2 || (quit==1 && !firstpass))
+        PrintOut(LOG_INFO,"Monitoring %d ATA and %d SCSI devices\n",
+                 numdevata, numdevscsi);
+      else {
+        PrintOut(LOG_INFO,"Unable to monitor any SMART enabled devices. Try debug (-d) option. Exiting...\n");
+        EXIT(EXIT_NODEV);
+      }
+
+      if (quit==4) {
+        // user has asked to print test schedule
+        PrintTestSchedule(atadevlist, scsidevlist);
+        EXIT(0);
+      }
+      
+      // reset signal
+      caughtsigHUP=0;
+    }
+
+    // check all devices once
+    CheckDevicesOnce(atadevlist, scsidevlist); 
+    
+    // user has asked us to exit after first check
+    if (quit==3) {
+      PrintOut(LOG_INFO,"Started with '-q onecheck' option. All devices sucessfully checked once.\n"
+               "smartd is exiting (exit status 0)\n");
+      EXIT(0);
+    }
+    
+    // fork into background if needed
+    if (firstpass && !debugmode) {
+#ifdef __CYGWIN__
+     if (!is_service) // don't fork() if running as service via cygrunsrv
+#endif
+      DaemonInit();
+    }
+
+    // set exit and signal handlers, write PID file, set wake-up time
+    if (firstpass){
+      Initialize(&wakeuptime);
+      firstpass=0;
+    }
+    
+    // sleep until next check time, or a signal arrives
+    wakeuptime=dosleep(wakeuptime);
+  }
+}
+
+
+#ifdef _WIN32
+// Main function for Windows
+int main(int argc, char **argv){
+  // Options for smartd windows service
+  static const daemon_winsvc_options svc_opts = {
+    "--service", // cmd_opt
+    "smartd", "SmartD Service", // servicename, displayname
+    // description
+    "Controls and monitors storage devices using the Self-Monitoring, "
+    "Analysis and Reporting Technology System (S.M.A.R.T.) "
+    "built into ATA and SCSI Hard Drives. "
+    PACKAGE_HOMEPAGE
+  };
+  // daemon_main() handles daemon and service specific commands
+  // and starts smartd_main() direct, from a new process,
+  // or via service control manager
+  return daemon_main("smartd", &svc_opts , smartd_main, argc, argv);
+}
+#endif
index c1b5dd195d394a1c666f8690b786dcbe747e5983..563a2c1723d0e98a26502c4aa5bc88afd52233e9 100644 (file)
--- a/smartd.h
+++ b/smartd.h
@@ -32,7 +32,7 @@
 
 
 #ifndef SMARTD_H_CVSID
-#define SMARTD_H_CVSID "$Id: smartd.h,v 1.76 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define SMARTD_H_CVSID "$Id: smartd.h,v 1.83 2006/09/15 08:01:21 sxzzsf Exp $\n"
 #endif
 
 // Configuration file
@@ -64,7 +64,7 @@
 
 
 // Number of allowed mail message types
-#define SMARTD_NMAIL 12
+#define SMARTD_NMAIL 13
 
 typedef struct mailinfo_s {
   int logged;// number of times an email has been sent
@@ -168,8 +168,11 @@ typedef struct configfile_s {
   // REGISTERED AND WE LEARN ITS CAPABILITIES.
   int lineno;                             // Line number of entry in file
   char *name;                             // Device name (+ optional [3ware_disk_XX])
-  unsigned char controller_type;          // Controller type, ATA/SCSI/3Ware/(more to come)
+  unsigned char controller_explicit;      // Controller (device) type has been specified explicitly
+  unsigned char controller_type;          // Controller type, ATA/SCSI/SAT/3Ware/(more to come)
   unsigned char controller_port;          // 1 + (disk number in controller). 0 means controller only handles one disk.
+  unsigned char hpt_data[3];              // combined controller/channle/pmport for highpoint rocketraid controller
+  unsigned char satpassthrulen;           // length of SAT ata pass through scsi commands (12, 16 or 0 (platform choice))
   char smartcheck;                        // Check SMART status
   char usagefailed;                       // Check for failed Usage Attributes
   char prefail;                           // Track changes in Prefail Attributes
@@ -185,6 +188,9 @@ typedef struct configfile_s {
   char removable;                         // Device may disappear (not be present)
   char powermode;                         // skip check, if disk in idle or standby mode
   char powerquiet;                        // skip powermode 'skipping checks' message
+  unsigned char tempdiff;                 // Track Temperature changes >= this limit
+  unsigned char tempinfo, tempcrit;       // Track Temperatures >= these limits as LOG_INFO, LOG_CRIT+mail
+  unsigned char tempmin, tempmax;         // Min/Max Temperatures
   unsigned char selflogcount;             // total number of self-test errors
   unsigned short selfloghour;             // lifetime hours of last self-test error
   testinfo *testdata;                     // Pointer to data on scheduled testing
@@ -193,11 +199,13 @@ typedef struct configfile_s {
   
   // THE NEXT SET OF ENTRIES ALSO TRACK DEVICE STATE AND ARE DYNAMIC
   maildata *mailwarn;                     // non-NULL: info about sending mail or executing script
+  unsigned char temperature;              // last recorded Temperature (in Celsius)
+  unsigned char tempmininc;               // #checks where Min Temperature is increased after powerup
+  int powerskipcnt;                       // Number of checks skipped due to idle or standby mode
 
   // SCSI ONLY
   unsigned char SmartPageSupported;       // has log sense IE page (0x2f)
   unsigned char TempPageSupported;        // has log sense temperature page (0xd)
-  unsigned char Temperature;              // last recorded figure (in Celsius)
   unsigned char SuppressReport;           // minimize nuisance reports
   unsigned char modese_len;               // mode sense/select cmd len: 0 (don't
                                           // know yet) 6 or 10
@@ -241,7 +249,7 @@ typedef struct changedattribute_s {
 #ifndef __GNUC__
 #define __attribute__(x)      /* nothing */
 #endif
-void PrintOut(int priority,char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
+void PrintOut(int priority, const char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
 
 void PrintAndMail(cfgfile *cfg, int which, int priority, char *fmt, ...) __attribute__ ((format(printf, 4, 5)));   
 
index 35816d2270cc2a2fa4df233638fbc097e50a80a0..1a706df75c7722ded567da9db3d5514b1f9ae0ef 100644 (file)
@@ -8,7 +8,7 @@ Summary(pt):    smartmontools - para monitorar discos e dispositivos S.M.A.R.T.
 Summary(it):   smartmontools - per monitare dischi e dispositivi S.M.A.R.T.
 Summary(pl):   Monitorowanie i kontrola dysków u¿ywaj±æ S.M.A.R.T.
 Name:          smartmontools
-Version:       5.36
+Version:       5.37
 License:       GPL
 Group:         Applications/System
 Group(de):     Applikationen/System
@@ -26,16 +26,19 @@ Obsoletes:  ucsc-smartsuite
 Obsoletes:      smartsuite
 Packager:       Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
-%define redhat      %(test ! -f /etc/redhat-release ; echo $?)
-%define redhat      %(test ! -f /etc/fedora-release ; echo $?)
 %define mandrake    %(test ! -f /etc/mandrake-release ; echo $?)
 %define suse        %(test ! -f /etc/SuSE-release ; echo $?)
+%define redhat      %(test ! -f /etc/redhat-release ; echo $?)
+%define fedora      %(test ! -f /etc/fedora-release ; echo $?)
+%if %{fedora}                                                                                                                                                             
+   %define redhat 1                                                                                                                                                       
+%endif
 
 # Source code can be found at:
 # http://ftp1.sourceforge.net/smartmontools/smartmontools-%{version}-%{release}.tar.gz
 
 # CVS ID of this file is:
-# $Id: smartmontools.spec,v 1.167 2006/04/12 17:39:32 ballen4705 Exp $
+# $Id: smartmontools.spec,v 1.169 2006/09/09 02:59:03 ballen4705 Exp $
 
 # Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 # Home page: http://smartmontools.sourceforge.net/
diff --git a/utility.c b/utility.c
deleted file mode 100644 (file)
index 19c9f80..0000000
--- a/utility.c
+++ /dev/null
@@ -1,681 +0,0 @@
-/*
- * utility.c
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
- *
- * 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, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
- */
-
-// THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO
-// BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD,
-// SMARTCTL, OR BOTH.
-
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <syslog.h>
-#include <stdarg.h>
-#include <sys/stat.h>
-#ifdef _WIN32
-#include <mbstring.h> // _mbsinc()
-#endif
-
-#include "config.h"
-#include "int64.h"
-#include "utility.h"
-
-// Any local header files should be represented by a CVSIDX just below.
-const char* utility_c_cvsid="$Id: utility.c,v 1.61 2006/04/12 14:54:28 ballen4705 Exp $"
-CONFIG_H_CVSID INT64_H_CVSID UTILITY_H_CVSID;
-
-const char * packet_types[] = {
-        "Direct-access (disk)",
-        "Sequential-access (tape)",
-        "Printer",
-        "Processor",
-        "Write-once (optical disk)",
-        "CD/DVD",
-        "Scanner",
-        "Optical memory (optical disk)",
-        "Medium changer",
-        "Communications",
-        "Graphic arts pre-press (10)",
-        "Graphic arts pre-press (11)",
-        "Array controller",
-        "Enclosure services",
-        "Reduced block command (simplified disk)",
-        "Optical card reader/writer"
-};
-
-// Whenever exit() status is EXIT_BADCODE, please print this message
-const char *reportbug="Please report this bug to the Smartmontools developers at " PACKAGE_BUGREPORT ".\n";
-
-
-// hang on to exit code, so we can make use of more generic 'atexit()'
-// functionality and still check our exit code
-int exitstatus = 0;
-
-// command-line argument: are we running in debug mode?.
-unsigned char debugmode = 0;
-
-
-// Solaris only: Get site-default timezone. This is called from
-// UpdateTimezone() when TZ environment variable is unset at startup.
-#if defined (__SVR4) && defined (__sun)
-static const char *TIMEZONE_FILE = "/etc/TIMEZONE";
-
-static char *ReadSiteDefaultTimezone(){
-  FILE *fp;
-  char buf[512], *tz;
-  int n;
-
-  tz = NULL;
-  fp = fopen(TIMEZONE_FILE, "r");
-  if(fp == NULL) return NULL;
-  while(fgets(buf, sizeof(buf), fp)) {
-    if (strncmp(buf, "TZ=", 3))    // searches last "TZ=" line
-      continue;
-    n = strlen(buf) - 1;
-    if (buf[n] == '\n') buf[n] = 0;
-    if (tz) free(tz);
-    tz = strdup(buf);
-  }
-  fclose(fp);
-  return tz;
-}
-#endif
-
-// Make sure that this executable is aware if the user has changed the
-// time-zone since the last time we polled devices. The cannonical
-// example is a user who starts smartd on a laptop, then flies across
-// time-zones with a laptop, and then changes the timezone, WITHOUT
-// restarting smartd. This is a work-around for a bug in
-// GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and
-// thanks to Ian Redfern for posting a workaround.
-
-// Please refer to the smartd manual page, in the section labeled LOG
-// TIMESTAMP TIMEZONE.
-void FixGlibcTimeZoneBug(){
-#if __GLIBC__  
-  if (!getenv("TZ")) {
-    putenv("TZ=GMT");
-    tzset();
-    putenv("TZ");
-    tzset();
-  }
-#elif _WIN32
-  if (!getenv("TZ")) {
-    putenv("TZ=GMT");
-    tzset();
-    putenv("TZ=");  // empty value removes TZ, putenv("TZ") does nothing
-    tzset();
-  }
-#elif defined (__SVR4) && defined (__sun)
-  // In Solaris, putenv("TZ=") sets null string and invalid timezone.
-  // putenv("TZ") does nothing.  With invalid TZ, tzset() do as if
-  // TZ=GMT.  With TZ unset, /etc/TIMEZONE will be read only _once_ at
-  // first tzset() call.  Conclusion: Unlike glibc, dynamic
-  // configuration of timezone can be done only by changing actual
-  // value of TZ environment value.
-  enum tzstate { NOT_CALLED_YET, USER_TIMEZONE, TRACK_TIMEZONE };
-  static enum tzstate state = NOT_CALLED_YET;
-
-  static struct stat prev_stat;
-  static char *prev_tz;
-  struct stat curr_stat;
-  char *curr_tz;
-
-  if(state == NOT_CALLED_YET) {
-    if(getenv("TZ")) {
-      state = USER_TIMEZONE; // use supplied timezone
-    } else {
-      state = TRACK_TIMEZONE;
-      if(stat(TIMEZONE_FILE, &prev_stat)) {
-       state = USER_TIMEZONE;  // no TZ, no timezone file; use GMT forever
-      } else {
-       prev_tz = ReadSiteDefaultTimezone(); // track timezone file change
-       if(prev_tz) putenv(prev_tz);
-      }
-    }
-    tzset();
-  } else if(state == TRACK_TIMEZONE) {
-    if(stat(TIMEZONE_FILE, &curr_stat) == 0
-       && (curr_stat.st_ctime != prev_stat.st_ctime
-           || curr_stat.st_mtime != prev_stat.st_mtime)) {
-      // timezone file changed
-      curr_tz = ReadSiteDefaultTimezone();
-      if(curr_tz) {
-       putenv(curr_tz);
-       if(prev_tz) free(prev_tz);
-       prev_tz = curr_tz; prev_stat = curr_stat; 
-      }
-    }
-    tzset();
-  }
-#endif
-  // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED.  PLEASE TRY TO
-  // KEEP THEM INDEPENDENT.
-  return;
-}
-
-#ifdef _WIN32
-// Fix strings in tzname[] to avoid long names with non-ascii characters.
-// If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the
-// national language timezone names returned by GetTimezoneInformation().
-static char * fixtzname(char * dest, int destsize, const char * src)
-{
-  int i = 0, j = 0;
-  while (src[i] && j < destsize-1) {
-    int i2 = (const char *)_mbsinc((const unsigned char *)src+i) - src;
-    if (i2 > i+1)
-      i = i2; // Ignore multibyte chars
-    else {
-      if ('A' <= src[i] && src[i] <= 'Z')
-        dest[j++] = src[i]; // "Pacific Standard Time" => "PST"
-      i++;
-    }
-  }
-  if (j < 2)
-    j = 0;
-  dest[j] = 0;
-  return dest;
-}
-#endif // _WIN32
-
-// This value follows the peripheral device type value as defined in
-// SCSI Primary Commands, ANSI INCITS 301:1997.  It is also used in
-// the ATA standard for packet devices to define the device type.
-const char *packetdevicetype(int type){
-  if (type<0x10)
-    return packet_types[type];
-  
-  if (type<0x20)
-    return "Reserved";
-  
-  return "Unknown";
-}
-
-
-// Returns 1 if machine is big endian, else zero.  This is a run-time
-// rather than a compile-time function.  We could do it at
-// compile-time but in principle there are architectures that can run
-// with either byte-ordering.
-int isbigendian(){
-  short i=0x0100;
-  char *tmp=(char *)&i;
-  return *tmp;
-}
-
-// Utility function prints date and time and timezone into a character
-// buffer of length>=64.  All the fuss is needed to get the right
-// timezone info (sigh).
-void dateandtimezoneepoch(char *buffer, time_t tval){
-  struct tm *tmval;
-  char *timezonename;
-  char datebuffer[DATEANDEPOCHLEN];
-  int lenm1;
-#ifdef _WIN32
-  char tzfixbuf[6+1];
-#endif
-
-  FixGlibcTimeZoneBug();
-  
-  // Get the time structure.  We need this to determine if we are in
-  // daylight savings time or not.
-  tmval=localtime(&tval);
-  
-  // Convert to an ASCII string, put in datebuffer
-  // same as: asctime_r(tmval, datebuffer);
-  strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN);
-  datebuffer[DATEANDEPOCHLEN-1]='\0';
-  
-  // Remove newline
-  lenm1=strlen(datebuffer)-1;
-  datebuffer[lenm1>=0?lenm1:0]='\0';
-  
-  // correct timezone name
-  if (tmval->tm_isdst==0)
-    // standard time zone
-    timezonename=tzname[0];
-  else if (tmval->tm_isdst>0)
-    // daylight savings in effect
-    timezonename=tzname[1];
-  else
-    // unable to determine if daylight savings in effect
-    timezonename="";
-
-#ifdef _WIN32
-  // Fix long non-ascii timezone names
-  if (!getenv("TZ"))
-    timezonename=fixtzname(tzfixbuf, sizeof(tzfixbuf), timezonename);
-#endif
-  
-  // Finally put the information into the buffer as needed.
-  snprintf(buffer, DATEANDEPOCHLEN, "%s %s", datebuffer, timezonename);
-  
-  return;
-}
-
-// Date and timezone gets printed into string pointed to by buffer
-void dateandtimezone(char *buffer){
-  
-  // Get the epoch (time in seconds since Jan 1 1970)
-  time_t tval=time(NULL);
-  
-  dateandtimezoneepoch(buffer, tval);
-  return;
-}
-
-// These are two utility functions for printing CVS IDs. Massagecvs()
-// returns distance that it has moved ahead in the input string
-int massagecvs(char *out, const char *cvsid){
-  char *copy,*filename,*date,*version;
-  int retVal=0;
-  const char delimiters[] = " ,$";
-
-  // make a copy on the heap, go to first token,
-  if (!(copy=strdup(cvsid)))
-    return 0;
-
-  if (!(filename=strtok(copy, delimiters)))
-    goto endmassage;
-
-  // move to first instance of "Id:"
-  while (strcmp(filename,"Id:"))
-    if (!(filename=strtok(NULL, delimiters)))
-      goto endmassage;
-  
-  // get filename, skip "v", get version and date
-  if (!(  filename=strtok(NULL, delimiters)  ) ||
-      !(           strtok(NULL, delimiters)  ) ||
-      !(   version=strtok(NULL, delimiters)  ) ||
-      !(      date=strtok(NULL, delimiters)  ) )
-    goto endmassage;
-  
-  sprintf(out,"%-16s revision: %-5s date: %-15s", filename, version, date);
-  retVal = (date-copy)+strlen(date);
-  
- endmassage:
-  free(copy);
-  return retVal;
-}
-
-// prints a single set of CVS ids
-void printone(char *block, const char *cvsid){
-  char strings[CVSMAXLEN];
-  const char *here=cvsid;
-  int bi=0, len=strlen(cvsid)+1;
-
-  // check that the size of the output block is sufficient
-  if (len>=CVSMAXLEN) {
-    pout("CVSMAXLEN=%d must be at least %d\n",CVSMAXLEN,len+1);
-    EXIT(1);
-  }
-
-  // loop through the different strings
-  while (bi<CVSMAXLEN && (len=massagecvs(strings,here))){
-    bi+=snprintf(block+bi,CVSMAXLEN-bi,"%s %s\n",(bi==0?"Module:":"  uses:"),strings);
-    here+=len;
-  }
-  return;
-}
-
-
-// A replacement for perror() that sends output to our choice of
-// printing. If errno not set then just print message.
-void syserror(const char *message){
-  
-  if (errno) {
-    // Get the correct system error message:
-    const char *errormessage=strerror(errno);
-    
-    // Check that caller has handed a sensible string, and provide
-    // appropriate output. See perrror(3) man page to understand better.
-    if (message && *message)
-      pout("%s: %s\n",message, errormessage);
-    else
-      pout("%s\n",errormessage);
-  }
-  else if (message && *message)
-    pout("%s\n",message);
-  
-  return;
-}
-
-// Prints a warning message for a failed regular expression compilation from
-// regcomp().
-void printregexwarning(int errcode, regex_t *compiled){
-  size_t length = regerror(errcode, compiled, NULL, 0);
-  char *buffer = malloc(length);
-  if (!buffer){
-    pout("Out of memory in printregexwarning()\n");
-    return;
-  }
-  regerror(errcode, compiled, buffer, length);
-  pout("%s\n", buffer);
-  free(buffer);
-  return;
-}
-
-// POSIX extended regular expressions interpret unmatched ')' ordinary:
-// "The close-parenthesis shall be considered special in this context
-//  only if matched with a preceding open-parenthesis."
-//
-// Actual '(...)' nesting errors remain undetected on strict POSIX
-// implementations (glibc) but an error is reported on others (Cygwin).
-// 
-// The check below is rather incomplete because it does not handle
-// e.g. '\)' '[)]'.
-// But it should work for the regex subset used in drive database.
-static int check_regex_nesting(const char * pattern)
-{
-  int level = 0, i;
-  for (i = 0; pattern[i] && level >= 0; i++) {
-    switch (pattern[i]) {
-      case '(': level++; break;
-      case ')': level--; break;
-    }
-  }
-  return level;
-}
-
-// A wrapper for regcomp().  Returns zero for success, non-zero otherwise.
-int compileregex(regex_t *compiled, const char *pattern, int cflags)
-{ 
-  int errorcode;
-
-  if (   (errorcode = regcomp(compiled, pattern, cflags))
-      || check_regex_nesting(pattern) < 0                ) {
-    pout("Internal error: unable to compile regular expression \"%s\" ", pattern);
-    if (errorcode)
-      printregexwarning(errorcode, compiled);
-    else
-      pout("Unmatched ')'\n");
-    pout("Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n");
-    return 1;
-  }
-  return 0;
-}
-
-// Splits an argument to the -r option into a name part and an (optional) 
-// positive integer part.  s is a pointer to a string containing the
-// argument.  After the call, s will point to the name part and *i the
-// integer part if there is one or 1 otherwise.  Note that the string s may
-// be changed by this function.  Returns zero if successful and non-zero
-// otherwise.
-int split_report_arg(char *s, int *i)
-{
-  if ((s = strchr(s, ','))) {
-    // Looks like there's a name part and an integer part.
-    char *tailptr;
-
-    *s++ = '\0';
-    if (*s == '0' || !isdigit((int)*s))  // The integer part must be positive
-      return 1;
-    errno = 0;
-    *i = (int) strtol(s, &tailptr, 10);
-    if (errno || *tailptr != '\0')
-      return 1;
-  } else {
-    // There's no integer part.
-    *i = 1;
-  }
-
-  return 0;
-}
-
-// same as above but sets *i to -1 if missing , argument
-int split_report_arg2(char *s, int *i){
-  char *tailptr;
-  s+=6;
-
-  if (*s=='\0' || !isdigit((int)*s)) { 
-    // What's left must be integer
-    *i=-1;
-    return 1;
-  }
-
-  errno = 0;
-  *i = (int) strtol(s, &tailptr, 10);
-  if (errno || *tailptr != '\0') {
-    *i=-1;
-    return 1;
-  }
-
-  return 0;
-}
-
-#ifndef HAVE_STRTOULL
-// Replacement for missing strtoull() (Linux with libc < 6, MSVC 6.0)
-// Functionality reduced to split_selective_arg()'s requirements.
-
-static uint64_t strtoull(const char * p, char * * endp, int base)
-{
-  uint64_t result, maxres;
-  int i = 0;
-  char c = p[i++];
-  // assume base == 0
-  if (c == '0') {
-    if (p[i] == 'x' || p[i] == 'X') {
-      base = 16; i++;
-    }
-    else
-      base = 8;
-    c = p[i++];
-  }
-  else
-    base = 10;
-
-  result = 0;
-  maxres = ~(uint64_t)0 / (unsigned)base;
-  for (;;) {
-    unsigned digit;
-    if ('0' <= c && c <= '9')
-      digit = c - '0';
-    else if ('A' <= c && c <= 'Z')
-      digit = c - 'A' + 10;
-    else if ('a' <= c && c <= 'z')
-      digit = c - 'a' + 10;
-    else
-      break;
-    if (digit >= (unsigned)base)
-      break;
-    if (!(   result < maxres
-          || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) {
-      result = ~(uint64_t)0; errno = ERANGE; // return on overflow
-      break;
-    }
-    result = result * (unsigned)base + digit;
-    c = p[i++];
-  }
-  *endp = (char *)p + i - 1;
-  return result;
-}
-#endif // HAVE_STRTOLL
-
-// Splits an argument to the -t option that is assumed to be of the form
-// "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
-// are allowed).  The first long long int is assigned to *start and the second
-// to *stop.  Returns zero if successful and non-zero otherwise.
-int split_selective_arg(char *s, uint64_t *start,
-                        uint64_t *stop)
-{
-  char *tailptr;
-
-  if (!(s = strchr(s, ',')))
-    return 1;
-  if (!isdigit((int)(*++s)))
-    return 1;
-  errno = 0;
-  // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
-  // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
-  *start = strtoull(s, &tailptr, 0);
-
-  s = tailptr;
-  if (errno || *s++ != '-')
-    return 1;
-  *stop = strtoull(s, &tailptr, 0);
-  if (errno || *tailptr != '\0')
-    return 1;
-  return 0;
-}
-
-int64_t bytes = 0;
-// Helps debugging.  If the second argument is non-negative, then
-// decrement bytes by that amount.  Else decrement bytes by (one plus)
-// length of null terminated string.
-void *FreeNonZero(void *address, int size, int line, const char* file){
-  if (address) {
-    if (size<0)
-      bytes-=1+strlen(address);
-    else
-      bytes-=size;
-    return CheckFree(address, line, file);
-  }
-  return NULL;
-}
-
-// To help with memory checking.  Use when it is known that address is
-// NOT null.
-void *CheckFree(void *address, int whatline, const char* file){
-  if (address){
-    free(address);
-    return NULL;
-  }
-  
-  PrintOut(LOG_CRIT, "Internal error in CheckFree() at line %d of file %s\n%s", 
-           whatline, file, reportbug);
-  EXIT(EXIT_BADCODE);
-}
-
-// A custom version of calloc() that tracks memory use
-void *Calloc(size_t nmemb, size_t size) { 
-  void *ptr=calloc(nmemb, size);
-  
-  if (ptr)
-    bytes+=nmemb*size;
-
-  return ptr;
-}
-
-// A custom version of strdup() that keeps track of how much memory is
-// being allocated. If mustexist is set, it also throws an error if we
-// try to duplicate a NULL string.
-char *CustomStrDup(char *ptr, int mustexist, int whatline, const char* file){
-  char *tmp;
-
-  // report error if ptr is NULL and mustexist is set
-  if (ptr==NULL){
-    if (mustexist) {
-      PrintOut(LOG_CRIT, "Internal error in CustomStrDup() at line %d of file %s\n%s", 
-               whatline, file, reportbug);
-      EXIT(EXIT_BADCODE);
-    }
-    else
-      return NULL;
-  }
-
-  // make a copy of the string...
-  tmp=strdup(ptr);
-  
-  if (!tmp) {
-    PrintOut(LOG_CRIT, "No memory to duplicate string %s at line %d of file %s\n", ptr, whatline, file);
-    EXIT(EXIT_NOMEM);
-  }
-  
-  // and track memory usage
-  bytes+=1+strlen(ptr);
-  
-  return tmp;
-}
-
-// Returns nonzero if region of memory contains non-zero entries
-int nonempty(unsigned char *testarea,int n){
-  int i;
-  for (i=0;i<n;i++)
-    if (testarea[i])
-      return 1;
-  return 0;
-}
-
-
-// This routine converts an integer number of milliseconds into a test
-// string of the form Xd+Yh+Zm+Ts.msec.  The resulting text string is
-// written to the array.
-void MsecToText(unsigned int msec, char *txt){
-  int start=0;
-  unsigned int days, hours, min, sec;
-
-  days       = msec/86400000U;
-  msec      -= days*86400000U;
-
-  hours      = msec/3600000U; 
-  msec      -= hours*3600000U;
-
-  min        = msec/60000U;
-  msec      -= min*60000U;
-
-  sec        = msec/1000U;
-  msec      -= sec*1000U;
-
-  if (days) {
-    txt += sprintf(txt, "%2dd+", (int)days);
-    start=1;
-  }
-
-  sprintf(txt, "%02d:%02d:%02d.%03d", (int)hours, (int)min, (int)sec, (int)msec);  
-  return;
-}
-
-
-#ifndef HAVE_WORKING_SNPRINTF
-// Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
-// and/or return -1 on overflow (old Linux).
-// Below are sane replacements substituted by #define in utility.h.
-
-#undef vsnprintf
-#if defined(_WIN32) && defined(_MSC_VER)
-#define vsnprintf _vsnprintf
-#endif
-
-int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap)
-{
-  int i;
-  if (size <= 0)
-    return 0;
-  i = vsnprintf(buf, size, fmt, ap);
-  if (0 <= i && i < size)
-    return i;
-  buf[size-1] = 0;
-  return strlen(buf); // Note: cannot detect for overflow, not necessary here.
-}
-
-int safe_snprintf(char *buf, int size, const char *fmt, ...)
-{
-  int i; va_list ap;
-  va_start(ap, fmt);
-  i = safe_vsnprintf(buf, size, fmt, ap);
-  va_end(ap);
-  return i;
-}
-
-#endif
diff --git a/utility.cpp b/utility.cpp
new file mode 100644 (file)
index 0000000..d5b0a3f
--- /dev/null
@@ -0,0 +1,681 @@
+/*
+ * utility.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * 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, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+// THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO
+// BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD,
+// SMARTCTL, OR BOTH.
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#ifdef _WIN32
+#include <mbstring.h> // _mbsinc()
+#endif
+
+#include "config.h"
+#include "int64.h"
+#include "utility.h"
+
+// Any local header files should be represented by a CVSIDX just below.
+const char* utility_c_cvsid="$Id: utility.cpp,v 1.62 2006/08/09 20:40:20 chrfranke Exp $"
+CONFIG_H_CVSID INT64_H_CVSID UTILITY_H_CVSID;
+
+const char * packet_types[] = {
+        "Direct-access (disk)",
+        "Sequential-access (tape)",
+        "Printer",
+        "Processor",
+        "Write-once (optical disk)",
+        "CD/DVD",
+        "Scanner",
+        "Optical memory (optical disk)",
+        "Medium changer",
+        "Communications",
+        "Graphic arts pre-press (10)",
+        "Graphic arts pre-press (11)",
+        "Array controller",
+        "Enclosure services",
+        "Reduced block command (simplified disk)",
+        "Optical card reader/writer"
+};
+
+// Whenever exit() status is EXIT_BADCODE, please print this message
+const char *reportbug="Please report this bug to the Smartmontools developers at " PACKAGE_BUGREPORT ".\n";
+
+
+// hang on to exit code, so we can make use of more generic 'atexit()'
+// functionality and still check our exit code
+int exitstatus = 0;
+
+// command-line argument: are we running in debug mode?.
+unsigned char debugmode = 0;
+
+
+// Solaris only: Get site-default timezone. This is called from
+// UpdateTimezone() when TZ environment variable is unset at startup.
+#if defined (__SVR4) && defined (__sun)
+static const char *TIMEZONE_FILE = "/etc/TIMEZONE";
+
+static char *ReadSiteDefaultTimezone(){
+  FILE *fp;
+  char buf[512], *tz;
+  int n;
+
+  tz = NULL;
+  fp = fopen(TIMEZONE_FILE, "r");
+  if(fp == NULL) return NULL;
+  while(fgets(buf, sizeof(buf), fp)) {
+    if (strncmp(buf, "TZ=", 3))    // searches last "TZ=" line
+      continue;
+    n = strlen(buf) - 1;
+    if (buf[n] == '\n') buf[n] = 0;
+    if (tz) free(tz);
+    tz = strdup(buf);
+  }
+  fclose(fp);
+  return tz;
+}
+#endif
+
+// Make sure that this executable is aware if the user has changed the
+// time-zone since the last time we polled devices. The cannonical
+// example is a user who starts smartd on a laptop, then flies across
+// time-zones with a laptop, and then changes the timezone, WITHOUT
+// restarting smartd. This is a work-around for a bug in
+// GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and
+// thanks to Ian Redfern for posting a workaround.
+
+// Please refer to the smartd manual page, in the section labeled LOG
+// TIMESTAMP TIMEZONE.
+void FixGlibcTimeZoneBug(){
+#if __GLIBC__  
+  if (!getenv("TZ")) {
+    putenv("TZ=GMT");
+    tzset();
+    putenv("TZ");
+    tzset();
+  }
+#elif _WIN32
+  if (!getenv("TZ")) {
+    putenv("TZ=GMT");
+    tzset();
+    putenv("TZ=");  // empty value removes TZ, putenv("TZ") does nothing
+    tzset();
+  }
+#elif defined (__SVR4) && defined (__sun)
+  // In Solaris, putenv("TZ=") sets null string and invalid timezone.
+  // putenv("TZ") does nothing.  With invalid TZ, tzset() do as if
+  // TZ=GMT.  With TZ unset, /etc/TIMEZONE will be read only _once_ at
+  // first tzset() call.  Conclusion: Unlike glibc, dynamic
+  // configuration of timezone can be done only by changing actual
+  // value of TZ environment value.
+  enum tzstate { NOT_CALLED_YET, USER_TIMEZONE, TRACK_TIMEZONE };
+  static enum tzstate state = NOT_CALLED_YET;
+
+  static struct stat prev_stat;
+  static char *prev_tz;
+  struct stat curr_stat;
+  char *curr_tz;
+
+  if(state == NOT_CALLED_YET) {
+    if(getenv("TZ")) {
+      state = USER_TIMEZONE; // use supplied timezone
+    } else {
+      state = TRACK_TIMEZONE;
+      if(stat(TIMEZONE_FILE, &prev_stat)) {
+       state = USER_TIMEZONE;  // no TZ, no timezone file; use GMT forever
+      } else {
+       prev_tz = ReadSiteDefaultTimezone(); // track timezone file change
+       if(prev_tz) putenv(prev_tz);
+      }
+    }
+    tzset();
+  } else if(state == TRACK_TIMEZONE) {
+    if(stat(TIMEZONE_FILE, &curr_stat) == 0
+       && (curr_stat.st_ctime != prev_stat.st_ctime
+           || curr_stat.st_mtime != prev_stat.st_mtime)) {
+      // timezone file changed
+      curr_tz = ReadSiteDefaultTimezone();
+      if(curr_tz) {
+       putenv(curr_tz);
+       if(prev_tz) free(prev_tz);
+       prev_tz = curr_tz; prev_stat = curr_stat; 
+      }
+    }
+    tzset();
+  }
+#endif
+  // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED.  PLEASE TRY TO
+  // KEEP THEM INDEPENDENT.
+  return;
+}
+
+#ifdef _WIN32
+// Fix strings in tzname[] to avoid long names with non-ascii characters.
+// If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the
+// national language timezone names returned by GetTimezoneInformation().
+static char * fixtzname(char * dest, int destsize, const char * src)
+{
+  int i = 0, j = 0;
+  while (src[i] && j < destsize-1) {
+    int i2 = (const char *)_mbsinc((const unsigned char *)src+i) - src;
+    if (i2 > i+1)
+      i = i2; // Ignore multibyte chars
+    else {
+      if ('A' <= src[i] && src[i] <= 'Z')
+        dest[j++] = src[i]; // "Pacific Standard Time" => "PST"
+      i++;
+    }
+  }
+  if (j < 2)
+    j = 0;
+  dest[j] = 0;
+  return dest;
+}
+#endif // _WIN32
+
+// This value follows the peripheral device type value as defined in
+// SCSI Primary Commands, ANSI INCITS 301:1997.  It is also used in
+// the ATA standard for packet devices to define the device type.
+const char *packetdevicetype(int type){
+  if (type<0x10)
+    return packet_types[type];
+  
+  if (type<0x20)
+    return "Reserved";
+  
+  return "Unknown";
+}
+
+
+// Returns 1 if machine is big endian, else zero.  This is a run-time
+// rather than a compile-time function.  We could do it at
+// compile-time but in principle there are architectures that can run
+// with either byte-ordering.
+int isbigendian(){
+  short i=0x0100;
+  char *tmp=(char *)&i;
+  return *tmp;
+}
+
+// Utility function prints date and time and timezone into a character
+// buffer of length>=64.  All the fuss is needed to get the right
+// timezone info (sigh).
+void dateandtimezoneepoch(char *buffer, time_t tval){
+  struct tm *tmval;
+  char *timezonename;
+  char datebuffer[DATEANDEPOCHLEN];
+  int lenm1;
+#ifdef _WIN32
+  char tzfixbuf[6+1];
+#endif
+
+  FixGlibcTimeZoneBug();
+  
+  // Get the time structure.  We need this to determine if we are in
+  // daylight savings time or not.
+  tmval=localtime(&tval);
+  
+  // Convert to an ASCII string, put in datebuffer
+  // same as: asctime_r(tmval, datebuffer);
+  strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN);
+  datebuffer[DATEANDEPOCHLEN-1]='\0';
+  
+  // Remove newline
+  lenm1=strlen(datebuffer)-1;
+  datebuffer[lenm1>=0?lenm1:0]='\0';
+  
+  // correct timezone name
+  if (tmval->tm_isdst==0)
+    // standard time zone
+    timezonename=tzname[0];
+  else if (tmval->tm_isdst>0)
+    // daylight savings in effect
+    timezonename=tzname[1];
+  else
+    // unable to determine if daylight savings in effect
+    timezonename="";
+
+#ifdef _WIN32
+  // Fix long non-ascii timezone names
+  if (!getenv("TZ"))
+    timezonename=fixtzname(tzfixbuf, sizeof(tzfixbuf), timezonename);
+#endif
+  
+  // Finally put the information into the buffer as needed.
+  snprintf(buffer, DATEANDEPOCHLEN, "%s %s", datebuffer, timezonename);
+  
+  return;
+}
+
+// Date and timezone gets printed into string pointed to by buffer
+void dateandtimezone(char *buffer){
+  
+  // Get the epoch (time in seconds since Jan 1 1970)
+  time_t tval=time(NULL);
+  
+  dateandtimezoneepoch(buffer, tval);
+  return;
+}
+
+// These are two utility functions for printing CVS IDs. Massagecvs()
+// returns distance that it has moved ahead in the input string
+int massagecvs(char *out, const char *cvsid){
+  char *copy,*filename,*date,*version;
+  int retVal=0;
+  const char delimiters[] = " ,$";
+
+  // make a copy on the heap, go to first token,
+  if (!(copy=strdup(cvsid)))
+    return 0;
+
+  if (!(filename=strtok(copy, delimiters)))
+    goto endmassage;
+
+  // move to first instance of "Id:"
+  while (strcmp(filename,"Id:"))
+    if (!(filename=strtok(NULL, delimiters)))
+      goto endmassage;
+  
+  // get filename, skip "v", get version and date
+  if (!(  filename=strtok(NULL, delimiters)  ) ||
+      !(           strtok(NULL, delimiters)  ) ||
+      !(   version=strtok(NULL, delimiters)  ) ||
+      !(      date=strtok(NULL, delimiters)  ) )
+    goto endmassage;
+  
+  sprintf(out,"%-16s revision: %-5s date: %-15s", filename, version, date);
+  retVal = (date-copy)+strlen(date);
+  
+ endmassage:
+  free(copy);
+  return retVal;
+}
+
+// prints a single set of CVS ids
+void printone(char *block, const char *cvsid){
+  char strings[CVSMAXLEN];
+  const char *here=cvsid;
+  int bi=0, len=strlen(cvsid)+1;
+
+  // check that the size of the output block is sufficient
+  if (len>=CVSMAXLEN) {
+    pout("CVSMAXLEN=%d must be at least %d\n",CVSMAXLEN,len+1);
+    EXIT(1);
+  }
+
+  // loop through the different strings
+  while (bi<CVSMAXLEN && (len=massagecvs(strings,here))){
+    bi+=snprintf(block+bi,CVSMAXLEN-bi,"%s %s\n",(bi==0?"Module:":"  uses:"),strings);
+    here+=len;
+  }
+  return;
+}
+
+
+// A replacement for perror() that sends output to our choice of
+// printing. If errno not set then just print message.
+void syserror(const char *message){
+  
+  if (errno) {
+    // Get the correct system error message:
+    const char *errormessage=strerror(errno);
+    
+    // Check that caller has handed a sensible string, and provide
+    // appropriate output. See perrror(3) man page to understand better.
+    if (message && *message)
+      pout("%s: %s\n",message, errormessage);
+    else
+      pout("%s\n",errormessage);
+  }
+  else if (message && *message)
+    pout("%s\n",message);
+  
+  return;
+}
+
+// Prints a warning message for a failed regular expression compilation from
+// regcomp().
+void printregexwarning(int errcode, regex_t *compiled){
+  size_t length = regerror(errcode, compiled, NULL, 0);
+  char *buffer = (char*)malloc(length);
+  if (!buffer){
+    pout("Out of memory in printregexwarning()\n");
+    return;
+  }
+  regerror(errcode, compiled, buffer, length);
+  pout("%s\n", buffer);
+  free(buffer);
+  return;
+}
+
+// POSIX extended regular expressions interpret unmatched ')' ordinary:
+// "The close-parenthesis shall be considered special in this context
+//  only if matched with a preceding open-parenthesis."
+//
+// Actual '(...)' nesting errors remain undetected on strict POSIX
+// implementations (glibc) but an error is reported on others (Cygwin).
+// 
+// The check below is rather incomplete because it does not handle
+// e.g. '\)' '[)]'.
+// But it should work for the regex subset used in drive database.
+static int check_regex_nesting(const char * pattern)
+{
+  int level = 0, i;
+  for (i = 0; pattern[i] && level >= 0; i++) {
+    switch (pattern[i]) {
+      case '(': level++; break;
+      case ')': level--; break;
+    }
+  }
+  return level;
+}
+
+// A wrapper for regcomp().  Returns zero for success, non-zero otherwise.
+int compileregex(regex_t *compiled, const char *pattern, int cflags)
+{ 
+  int errorcode;
+
+  if (   (errorcode = regcomp(compiled, pattern, cflags))
+      || check_regex_nesting(pattern) < 0                ) {
+    pout("Internal error: unable to compile regular expression \"%s\" ", pattern);
+    if (errorcode)
+      printregexwarning(errorcode, compiled);
+    else
+      pout("Unmatched ')'\n");
+    pout("Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n");
+    return 1;
+  }
+  return 0;
+}
+
+// Splits an argument to the -r option into a name part and an (optional) 
+// positive integer part.  s is a pointer to a string containing the
+// argument.  After the call, s will point to the name part and *i the
+// integer part if there is one or 1 otherwise.  Note that the string s may
+// be changed by this function.  Returns zero if successful and non-zero
+// otherwise.
+int split_report_arg(char *s, int *i)
+{
+  if ((s = strchr(s, ','))) {
+    // Looks like there's a name part and an integer part.
+    char *tailptr;
+
+    *s++ = '\0';
+    if (*s == '0' || !isdigit((int)*s))  // The integer part must be positive
+      return 1;
+    errno = 0;
+    *i = (int) strtol(s, &tailptr, 10);
+    if (errno || *tailptr != '\0')
+      return 1;
+  } else {
+    // There's no integer part.
+    *i = 1;
+  }
+
+  return 0;
+}
+
+// same as above but sets *i to -1 if missing , argument
+int split_report_arg2(char *s, int *i){
+  char *tailptr;
+  s+=6;
+
+  if (*s=='\0' || !isdigit((int)*s)) { 
+    // What's left must be integer
+    *i=-1;
+    return 1;
+  }
+
+  errno = 0;
+  *i = (int) strtol(s, &tailptr, 10);
+  if (errno || *tailptr != '\0') {
+    *i=-1;
+    return 1;
+  }
+
+  return 0;
+}
+
+#ifndef HAVE_STRTOULL
+// Replacement for missing strtoull() (Linux with libc < 6, MSVC 6.0)
+// Functionality reduced to split_selective_arg()'s requirements.
+
+static uint64_t strtoull(const char * p, char * * endp, int base)
+{
+  uint64_t result, maxres;
+  int i = 0;
+  char c = p[i++];
+  // assume base == 0
+  if (c == '0') {
+    if (p[i] == 'x' || p[i] == 'X') {
+      base = 16; i++;
+    }
+    else
+      base = 8;
+    c = p[i++];
+  }
+  else
+    base = 10;
+
+  result = 0;
+  maxres = ~(uint64_t)0 / (unsigned)base;
+  for (;;) {
+    unsigned digit;
+    if ('0' <= c && c <= '9')
+      digit = c - '0';
+    else if ('A' <= c && c <= 'Z')
+      digit = c - 'A' + 10;
+    else if ('a' <= c && c <= 'z')
+      digit = c - 'a' + 10;
+    else
+      break;
+    if (digit >= (unsigned)base)
+      break;
+    if (!(   result < maxres
+          || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) {
+      result = ~(uint64_t)0; errno = ERANGE; // return on overflow
+      break;
+    }
+    result = result * (unsigned)base + digit;
+    c = p[i++];
+  }
+  *endp = (char *)p + i - 1;
+  return result;
+}
+#endif // HAVE_STRTOLL
+
+// Splits an argument to the -t option that is assumed to be of the form
+// "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
+// are allowed).  The first long long int is assigned to *start and the second
+// to *stop.  Returns zero if successful and non-zero otherwise.
+int split_selective_arg(char *s, uint64_t *start,
+                        uint64_t *stop)
+{
+  char *tailptr;
+
+  if (!(s = strchr(s, ',')))
+    return 1;
+  if (!isdigit((int)(*++s)))
+    return 1;
+  errno = 0;
+  // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
+  // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
+  *start = strtoull(s, &tailptr, 0);
+
+  s = tailptr;
+  if (errno || *s++ != '-')
+    return 1;
+  *stop = strtoull(s, &tailptr, 0);
+  if (errno || *tailptr != '\0')
+    return 1;
+  return 0;
+}
+
+int64_t bytes = 0;
+// Helps debugging.  If the second argument is non-negative, then
+// decrement bytes by that amount.  Else decrement bytes by (one plus)
+// length of null terminated string.
+void *FreeNonZero1(void *address, int size, int line, const char* file){
+  if (address) {
+    if (size<0)
+      bytes-=1+strlen((char*)address);
+    else
+      bytes-=size;
+    return CheckFree1(address, line, file);
+  }
+  return NULL;
+}
+
+// To help with memory checking.  Use when it is known that address is
+// NOT null.
+void *CheckFree1(void *address, int whatline, const char* file){
+  if (address){
+    free(address);
+    return NULL;
+  }
+  
+  PrintOut(LOG_CRIT, "Internal error in CheckFree() at line %d of file %s\n%s", 
+           whatline, file, reportbug);
+  EXIT(EXIT_BADCODE);
+}
+
+// A custom version of calloc() that tracks memory use
+void *Calloc(size_t nmemb, size_t size) { 
+  void *ptr=calloc(nmemb, size);
+  
+  if (ptr)
+    bytes+=nmemb*size;
+
+  return ptr;
+}
+
+// A custom version of strdup() that keeps track of how much memory is
+// being allocated. If mustexist is set, it also throws an error if we
+// try to duplicate a NULL string.
+char *CustomStrDup(char *ptr, int mustexist, int whatline, const char* file){
+  char *tmp;
+
+  // report error if ptr is NULL and mustexist is set
+  if (ptr==NULL){
+    if (mustexist) {
+      PrintOut(LOG_CRIT, "Internal error in CustomStrDup() at line %d of file %s\n%s", 
+               whatline, file, reportbug);
+      EXIT(EXIT_BADCODE);
+    }
+    else
+      return NULL;
+  }
+
+  // make a copy of the string...
+  tmp=strdup(ptr);
+  
+  if (!tmp) {
+    PrintOut(LOG_CRIT, "No memory to duplicate string %s at line %d of file %s\n", ptr, whatline, file);
+    EXIT(EXIT_NOMEM);
+  }
+  
+  // and track memory usage
+  bytes+=1+strlen(ptr);
+  
+  return tmp;
+}
+
+// Returns nonzero if region of memory contains non-zero entries
+int nonempty(unsigned char *testarea,int n){
+  int i;
+  for (i=0;i<n;i++)
+    if (testarea[i])
+      return 1;
+  return 0;
+}
+
+
+// This routine converts an integer number of milliseconds into a test
+// string of the form Xd+Yh+Zm+Ts.msec.  The resulting text string is
+// written to the array.
+void MsecToText(unsigned int msec, char *txt){
+  int start=0;
+  unsigned int days, hours, min, sec;
+
+  days       = msec/86400000U;
+  msec      -= days*86400000U;
+
+  hours      = msec/3600000U; 
+  msec      -= hours*3600000U;
+
+  min        = msec/60000U;
+  msec      -= min*60000U;
+
+  sec        = msec/1000U;
+  msec      -= sec*1000U;
+
+  if (days) {
+    txt += sprintf(txt, "%2dd+", (int)days);
+    start=1;
+  }
+
+  sprintf(txt, "%02d:%02d:%02d.%03d", (int)hours, (int)min, (int)sec, (int)msec);  
+  return;
+}
+
+
+#ifndef HAVE_WORKING_SNPRINTF
+// Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
+// and/or return -1 on overflow (old Linux).
+// Below are sane replacements substituted by #define in utility.h.
+
+#undef vsnprintf
+#if defined(_WIN32) && defined(_MSC_VER)
+#define vsnprintf _vsnprintf
+#endif
+
+int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap)
+{
+  int i;
+  if (size <= 0)
+    return 0;
+  i = vsnprintf(buf, size, fmt, ap);
+  if (0 <= i && i < size)
+    return i;
+  buf[size-1] = 0;
+  return strlen(buf); // Note: cannot detect for overflow, not necessary here.
+}
+
+int safe_snprintf(char *buf, int size, const char *fmt, ...)
+{
+  int i; va_list ap;
+  va_start(ap, fmt);
+  i = safe_vsnprintf(buf, size, fmt, ap);
+  va_end(ap);
+  return i;
+}
+
+#endif
index 7229c5a6b7a0f5630a3c95f9bcdb6f0289cfef29..2b23d51e04d1c00505c13d752e1296b0f01e3927 100644 (file)
--- a/utility.h
+++ b/utility.h
@@ -25,7 +25,7 @@
 #ifndef UTILITY_H_
 #define UTILITY_H_
 
-#define UTILITY_H_CVSID "$Id: utility.h,v 1.43 2006/04/12 14:54:28 ballen4705 Exp $\n"
+#define UTILITY_H_CVSID "$Id: utility.h,v 1.46 2006/08/25 06:06:25 sxzzsf Exp $\n"
 
 #include <time.h>
 #include <sys/types.h> // for regex.h (according to POSIX)
@@ -59,7 +59,7 @@ void printone(char *block, const char *cvsid);
 #ifndef __GNUC__
 #define __attribute__(x)      /* nothing */
 #endif
-void pout(char *fmt, ...)  
+void pout(const char *fmt, ...)  
      __attribute__ ((format (printf, 1, 2)));
 
 // replacement for perror() with redirected output.
@@ -96,7 +96,12 @@ int make_device_names (char ***devlist, const char* name);
 void *Calloc(size_t nmemb, size_t size);
 
 // Utility function to free memory
-void *FreeNonZero(void* address, int size, int whatline, const char* file);
+void *FreeNonZero1(void* address, int size, int whatline, const char* file);
+
+// Typesafe version of above
+template <class T>
+inline T * FreeNonZero(T * address, int size, int whatline, const char* file)
+  { return (T *)FreeNonZero1((void *)address, size, whatline, file); }
 
 // A custom version of strdup() that keeps track of how much memory is
 // being allocated. If mustexist is set, it also throws an error if we
@@ -105,7 +110,12 @@ char *CustomStrDup(char *ptr, int mustexist, int whatline, const char* file);
 
 // To help with memory checking.  Use when it is known that address is
 // NOT null.
-void *CheckFree(void *address, int whatline, const char* file);
+void *CheckFree1(void *address, int whatline, const char* file);
+
+// Typesafe version of above
+template <class T>
+inline T * CheckFree(T * address, int whatline, const char* file)
+  { return (T *)CheckFree1((void *)address, whatline, file); }
 
 // This function prints either to stdout or to the syslog as needed
 
@@ -115,7 +125,7 @@ void *CheckFree(void *address, int whatline, const char* file);
 // values. This means the objects of type char or short int (whether
 // signed or not) are promoted to either int or unsigned int, as
 // appropriate.]
-void PrintOut(int priority,char *fmt, ...);
+void PrintOut(int priority, const char *fmt, ...) __attribute__ ((format(printf, 2, 3)));
 
 // run time, determine byte ordering
 int isbigendian();
@@ -174,6 +184,7 @@ void MsecToText(unsigned int msec, char *txt);
 #define CONTROLLER_3WARE_9000_CHAR      0x05  // set by guess_device_type()
 #define CONTROLLER_3WARE_678K_CHAR      0x06  // set by guess_device_type()
 #define CONTROLLER_MARVELL_SATA         0x07  // SATA drives behind Marvell controllers
-
+#define CONTROLLER_SAT                         0x08  // SATA device behind a SCSI ATA Translation (SAT) layer
+#define CONTROLLER_HPT                  0x09  // SATA drives behind HighPoint Raid controllers
 
 #endif