]> git.proxmox.com Git - mirror_smartmontools-debian.git/commitdiff
Imported Upstream version 5.38+svn2879
authorGiuseppe Iuculano <giuseppe@iuculano.it>
Sun, 30 Aug 2009 16:43:15 +0000 (18:43 +0200)
committerGiuseppe Iuculano <giuseppe@iuculano.it>
Sun, 30 Aug 2009 16:43:15 +0000 (18:43 +0200)
80 files changed:
AUTHORS
CHANGELOG
Doxyfile [new file with mode: 0644]
INSTALL
Makefile.am
Makefile.in [deleted file]
NEWS
README
TODO
WARNINGS
aclocal.m4 [deleted file]
atacmdnames.cpp
atacmds.cpp
atacmds.h
ataprint.cpp
ataprint.h
autogen.sh
cciss.cpp
config.guess [deleted file]
config.h.in [deleted file]
config.sub [deleted file]
configure [deleted file]
configure.in
depcomp [deleted file]
dev_ata_cmd_set.cpp [new file with mode: 0644]
dev_ata_cmd_set.h [new file with mode: 0644]
dev_interface.cpp [new file with mode: 0644]
dev_interface.h [new file with mode: 0644]
dev_legacy.cpp [new file with mode: 0644]
dev_tunnelled.h [new file with mode: 0644]
do_release [new file with mode: 0755]
examplescripts/Makefile.am
examplescripts/Makefile.in [deleted file]
extern.h
install-sh [deleted file]
knowndrives.cpp
knowndrives.h
megaraid.h [new file with mode: 0644]
missing [deleted file]
os_darwin.cpp
os_freebsd.cpp
os_freebsd.h
os_generic.cpp
os_linux.cpp
os_netbsd.cpp
os_openbsd.cpp
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_qnxnto.cpp [new file with mode: 0644]
os_qnxnto.h [new file with mode: 0644]
os_solaris.cpp
os_win32.cpp
os_win32/installer.nsi
os_win32/smartctl_vc8.vcproj [new file with mode: 0644]
os_win32/smartd_vc8.vcproj [new file with mode: 0644]
os_win32/smartmontools_vc8.sln [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_vc8.vcproj [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]
scsiata.cpp
scsiata.h [deleted file]
scsicmds.cpp
scsicmds.h
scsiprint.cpp
scsiprint.h
smartctl.8.in
smartctl.cpp
smartctl.h
smartd.8.in
smartd.conf.5.in
smartd.cpp
smartd.h [deleted file]
smartmontools.spec [deleted file]
utility.cpp
utility.h

diff --git a/AUTHORS b/AUTHORS
index d6019d8dd95bb583add3f5a01c3bdc98cbc1be96..26a0c1560c2dae9007beb89e7bc1b4a73a8b29a5 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,4 +1,4 @@
-$Id: AUTHORS,v 1.19 2007/12/08 12:58:53 chrfranke Exp $
+$Id: AUTHORS,v 1.22 2008/09/23 23:55:29 jharg Exp $
 
 This code was originally developed as a Senior Thesis by Michael
 Cornwell at the Concurrent Systems Laboratory (now part of the Storage
@@ -25,6 +25,7 @@ Kai M
 Eduard Martinescu      <martines@rochester.rr.com>
 Frédéric L. W. Meunier <http://www.pervalidus.net/contact.html>
 Keiji Sawada           <card_captor@users.sourceforge.net>
+Manfred Schwarb                <manfred99@gmx.ch>
 David Snyder            <dasnyderx@yahoo.com>
 Sergey Svishchev       <svs@ropnet.ru>
 Phil Williams          <phil@subbacultcha.demon.co.uk>
@@ -33,3 +34,5 @@ Yuri Dario            <mc6530@mclink.it>
 Shengfeng Zhou         <linux@highpoint-tech.com>
 Praveen Chidambaram    <bunchofmails@gmail.com>
 Joerg Hering            <hering.ruegen@gmx.de>
+Tomas Smetana          <tsmetana@redhat.com>
+Jordan Hargrave                <jordan_hargrave@dell.com>
index dfe3dfff2089295ea846760e7096ac4e3db13dc9..c9f682922a8d1275e6d1488be3d5d8730a3b6119 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
 CHANGELOG for smartmontools
 
-$Id: CHANGELOG,v 1.661 2008/03/10 10:44:30 ballen4705 Exp $
+$Id: CHANGELOG 2879 2009-08-29 17:19:00Z chrfranke $
 
 The most recent version of this file is:
-http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/CHANGELOG?view=markup
-
-Maintainers / Developers Key:
-[BA] Bruce Allen
-[EB] Erik Inge Bolsø
-[SB] Stanislav Brabec
-[PC] Peter Cassidy
-[YD] Yuri Dario
-[CD] Casper Dik
-[CF] Christian Franke
-[GF] Guilhem Frézou
-[DG] Douglas Gilbert
-[GG] Guido Guenther
-[GK] Geoff Keating
-[DK] Dr. David Kirkby
-[JH] Joerg Hering
-[KM] Kai Mäkisara
-[EM] Eduard Martinescu
-[FM] Frédéric L. W. Meunier
-[KS] Keiji Sawada
-[DS] David Snyder
-[SS] Sergey Svishchev
-[PW] Phil Williams
-[LW] Leon Woestenberg
-[RZ] Richard Zybert
-[SZ] Sf Zhou
+http://smartmontools.svn.sourceforge.net/viewvc/smartmontools/trunk/smartmontools/CHANGELOG?view=markup
+
+Maintainers / Developers Key (alphabetic order):
+[BA]  Bruce Allen
+[OB]  Oliver Bock
+[EB]  Erik Inge Bolsø
+[SB]  Stanislav Brabec
+[PC]  Peter Cassidy
+[MC]  Matthieu Castet
+[YD]  Yuri Dario
+[CD]  Casper Dik
+[CF]  Christian Franke
+[GF]  Guilhem Frézou
+[DG]  Douglas Gilbert
+[GG]  Guido Guenther
+[JPH] Jordan Powell Hargrave
+[JH]  Joerg Hering
+[GK]  Geoff Keating
+[DK]  Dr. David Kirkby
+[DL]  Dan Lukes
+[KM]  Kai Mäkisara
+[EM]  Eduard Martinescu
+[FM]  Frédéric L. W. Meunier
+[GP]  Gabriele Pohl
+[AR]  Adam Radford
+[KS]  Keiji Sawada
+[MS]  Manfred Schwarb
+[TS]  Tomas Smetana
+[DS]  David Snyder
+[SS]  Sergey Svishchev
+[PW]  Phil Williams
+[LW]  Leon Woestenberg
+[SZ]  Shengfeng Zhou
+[RZ]  Richard Zybert
 
 NOTES FOR FUTURE RELEASES: see TODO file.
 
 <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
 
+  [CF] Fix max number of 3ware devices, 128 devices are supported again.
+       Regression was introduced during migration to new interface.
+       Thanks to Michael Holweg for the problem report.
 
+  [CF] Windows installer: Add 'DisplayVersion' to uninstall registry key.
+
+  [MS] knowndrives.cpp updates:
+       - Marvell SSD SD88SA024BA0
+       - Fujitsu MHZ2 BH series
+       - Fujitsu MHZ2 BJ series
+       - Seagate Maxtor DiamondMax 23
+       - WD Caviar Green: Add some 32MB cache variants
+       - relax OCZ-Vertex pattern
+
+  [CF] Add USB ID of Verbatim FW/USB160.
+
+  [CF] Fix data type bug in checksum test for multi sector logs.
+
+  [CF] Add USB ID of Seagate FreeAgent Go.
+
+  [MS] Add experimental feature to log attribute values at each check 
+       cycle (ATA only), activated with the smartd option 
+       "-A PREFIX" / "--attributelog=PREFIX".
+       Introduce configure options "--enable-attributelog" and
+       "--with-attributelog=PREFIX" to enable feature by default.
+
+  [DG] [SAT] Heads up about a non backwardly compatible change
+       introduced in draft SAT-2 (sat2r8b.pdf) that will break our
+       existing SAT processing code. Action needed if change stands.
+
+  [MS] smartd.cpp: Adjust umask
+
+  [CF] Makefile.am: Remove 'uninstall-docsDATA' target to fix
+       'make distcheck' with automake 1.11. The 'make uninstall'
+       of examplescripts fails if docdir does no longer exist.
+
+  [CF] Remove 'scsiata.h'. The 'scsiata.cpp' module now implements
+       parts of 'dev_interface.h'.
+
+  [CF] smartctl: Don't report an attribute as failed if threshold is 0.
+
+  [CF] Print only one warning on checksum errors in multi sector log.
+       Remove casts from calls of checksum().
+
+  [DG] minor changes to SCSI background scan strings
+
+  [MS] knowndrives.cpp updates:
+       - Fujitsu MHW2 BJ series
+       - WD Caviar Black family
+
+  [MS] Makefile.am: Make creation of svnversion.h independent of
+       locale settings
+
+  [CF] Require to specify PORT parameter of '-d usbjmicron' if two disks
+       are connected.
+
+  [CF] smartctl: Limit default number of printed entries for
+       '-l xerror' to 8, for '-l xselftest' to 25.
+
+  [CF] smartctl: Fix number of entries in '-l xselftest' output.
+
+  [CF] Add USB IDs of a SunplusIT bridge, three WD drives, and an
+       unsupported Iomega drive.
+
+  [CF] Makefile.am: Use 'svnversion' instead of 'svn info' to get
+       the revision number. This also checks for mixed and modified
+       working copies.
+
+  [CF] Remove CVS Id strings from '-V, --version' output.
+
+  [CF] Update CONTRIBUTORS section on man pages.
+
+  [CF] Makefile.am: 'make maintainer-clean' now removes also files
+       generated by './autogen.sh'.
+
+  [CF] Invalidate 'do_release' script, it needs some rework for SVN.
+
+  [CF] Update documentation files for SVN.
+
+  [CF] Rename trunk/sm5 to trunk/smartmontools.
+
+  [CF] Print SVN revision number instead of time in version info line.
+       Get SVN revision number from svn (if available) or guess from
+       Id strings. Rename generated file to svnversion.h.
+
+  [CF] Makefile.am: Modify generation of cvsversion.h for SVN.
+
+  [GP] Convert CVS repository to SVN.
+
+  [CF] smartd: Fix size of monitor flag array from previous commit.
+
+  [CF] Makefile.am: Add missing 'megaraid.h'.
+
+  [CF] smartd: Add '!' flag to '-r' and '-R' directives. If specified,
+       message is logged as LOG_CRIT and warning mail is sent if
+       attribute normalized or raw value changes.
+
+  [CF] Replace global 'con->...' variables used for selective self-tests
+       by local variables.
+
+  [GK] Add names for some attributes used in Samsung MLC drives:
+       178-180 & 183
+
+  [CF] smartctl: Add option '-x, --xall' to print all info including
+       extended SMART logs and non-SMART info.
+
+  [CF] smartctl: Add '-l xerror,error' and '-l xselftest,selftest' to print
+       the old logs if the extended logs are not supported.
+
+  [MS] knowndrives.cpp updates:
+       - Western Digital AV-GP series
+       - Transcend Solid-State Drive and Transcend Solid-State Drive V series
+       - Seagate Momentus 5400.5 series
+
+  [CF] Disable 48-bit ATA commands for JMicron USB bridges by default
+       because these commands do not work with all devices.
+       Add '-d usbjmicron,x' to enable 48-bit commands.
+       Thanks to Alexander Shaduri for the problem report.
+
+  [CF] smartd: Don't ignore the '-n' directive when a self-test is
+       scheduled. Start the self-test later when the disk is active
+       again.
+
+  [DG] SCSI (SAS): implement '-l sasphy,reset' (reset part was stub
+       prior to this)
+
+  [DG] add 'ATA, SCSI command sets and SAT' section to smartctl.8 .
+       [SCSI] add 'number of background medium scans' field
+
+  [DG] SCSI (SAS): add '-l sasphy' and '-l sasphy,reset' into smartctl
+       to output SAS device phy information (from the Protocol specific
+       log page)
+
+  [CF] autogen.sh: Remove 'CYGWIN=check_case:strict', this does no
+       longer work on Cygwin 1.7.  Print warning if Automake version
+       cannot handle case insensitive filesystems.
+
+  [CF] Remove '#define TRUE/FALSE', use 'bool' and 'true/false'.
+
+  [CF] Add 'options' parameter to SCSI printing routine. Move global
+       'con->...' smartctl variables to 'options' parameters of
+       printing routines.
+
+  [CF] Windows: Remove outdated entry about undocumented system calls
+       from WARNINGS file.
+
+  [CF] Print General Purpose Logs even if GPL feature bit is missing.
+       Needed for some older disks which implement READ LOG EXT but
+       do not report the GPL feature set.
+       Change order of the extended log outputs ('-l xerror',
+       '-l xselftest', '-l sataphy'). Extended logs are now printed
+       before their old versions.
+
+  [CF] autogen.sh: automake 1.10.2 and 1.11 are OK.
+
+  [CF] Fix syntax error in prototype of 'safe_snprintf()'.
+       Thanks to Alexander Shaduri for bug report and patch.
+
+  [DG] SCSI: Fetch load-unload cycle counts.
+
+  [CF] Windows: Add Win-7 and Win2008 to get_os_version_str().
+
+  [CF] smartd: Fix '-M test' directive in conjunction with '-s' option.
+       Thanks to Matthias Becher for the problem report.
+
+  [MS] knowndrives.cpp updates:
+       - Add Seagate Barracuda 7200.12 series
+       - Add Seagate Momentus 5400.4 series
+       - Add Hitachi Deskstar 7K1000.B series
+       - Add Transcend SSD TS32GSSD25-M
+       - Add OCZ Vertex 1199
+
+  [CF] knowndrives.cpp updates:
+       Add Samsung S250 series.
+       Add '-v 198,increasing' to Samsung P80.
+       Replace '#if/#endif' by comment to fix configure option
+       '--enable-drivedb'.
+
+  [CF] knowndrives.cpp update:
+       Add Seagate 7200.11 with 'CC' firmware which is unaffected
+       by the bug. Thanks to Bas Mevissen for the patch.
+
+  [CF] Replace global 'con->...' variables used for drive presets
+       by local variables.
+
+  [CF] Simplify '-v' vendor attribute option parsing.
+       Add '-v 197,increasing' and '-v 198,increasing' options
+       to specifiy that an uncorrectable count is never reset.
+       This modifies the printed attribute names and smartd's
+       default setting of '-C' and '-U' directives.
+       Both '-v' options can also be preset in the drive database.
+
+  [CF] Add '+' modifier to smartd '-C' and '-U' directives.
+       If specified, a warning is only printed if the raw value
+       increases.
+
+  [CF] Add smartctl option '-l xselftest[,NUM]' to print
+       ATA SMART Extended Self-test Log (GP Log 0x07).
+
+  [CF] Add experimental option '-d usbsunplus' for drives behind
+       SunplusIT USB bridges. Tested on WinXP with SPIF215(?) in
+       TrekStor DataStation maxi m.u.. Many thanks to SunplusIT
+       tech support for providing the required information.
+
+  [CF] Windows: Provide a non-console version of smartctl.exe
+       as smartctl-nc.exe. This prevents that a new console is
+       opened when smartctl is run from a GUI program with
+       stdio redirected.
+       Used by GSmartControl (http://gsmartcontrol.berlios.de/).
+
+  [CF] Remove support for platforms without getopt_long() in
+       smartctl.cpp and smartd.cpp. If getopt_long() is missing,
+       ./configure aborts with an explanatory message.
+       For now, short option help texts are only removed from
+       os_linux.cpp and os_win32.cpp. HAVE_GETOPT_LONG is still
+       defined in config.h.
+
+  [CF] Add smartctl '-d test' option to print the result of the
+       device type detection.
+
+  [CF] Enhance USB device type autodetection, use bcdDevice if known.
+       Add Cypress CY7C68300B/C (AT2LP) to the table.
+
+  [CF] Linux: Add experimental USB device type autodetection.
+       Uses USB ID info found through symlink "/sys/block/sdX/device".
+
+  [CF] Windows: Add experimental USB device type autodetection.
+       Uses WMI command line tool 'wmic' to query USB ID.
+
+  [CF] Add function smart_interface::get_usb_dev_type_by_id() to map
+       USB vendor:product IDs to '-d type' names. Can be used by
+       platform dependent layer to autodetect USB devices if ID of
+       USB bridge is known.
+
+  [CF] smartd: Log changes of self-test execution status if
+       '-l selftest'is specified.
+
+  [CF] knowndrives.cpp update:
+       Samsung SpinPoint F1 RE series
+
+  [MS] knowndrives.cpp update:
+       Seagate Momentus 5400.6 series
+
+  [CF] Add forgotten SCSI sense checks to class usbjmicron_device.
+
+  [CF] Add new SMART STATUS check command for JMicron USB bridges.
+       Should support also older chip versions and prevents a race
+       condition.
+
+  [CF] Windows: Fix win_scsi_device::scsi_pass_through() for single byte
+       data transfers. Required for JMicron SMART STATUS check.
+
+  [MS] knowndrives.cpp update:
+       Add Hitachi Travelstar C4K60 family (1.8" slim drives)
+
+  [MS] Workaround for huge raw values of attribute 9, needed
+       for Hitachi Travelstar C4K60. For the Power_On_Minutes case,
+       clip the display to 4 bytes and show the remaining part,
+       if existent, in parens.
+
+  [CF] Add experimental option '-d usbjmicron[,PORT]' for drives
+       behind JMicron USB bridges. Tested on WinXP with JM20336 in
+       AixCase AIX-ESU35CD. Many thanks to JMicron tech support
+       for providing the required information.
+
+  [MS] knowndrives.cpp update:
+       Add WD Caviar Green 8MB and 32MB cache variants, stretch to 2TB.
+  [CF] knowndrives.cpp updates: Add more entries for Samsung P80 disks
+       with old and unknown firmware. Remove old entries which would
+       match any new Samsung model reusing old firmware version number.
+
+  [CF] Windows: Add a workaround for missing multi-sector support
+       for ATA READ LOG EXT command.
+
+  [CF] Fix Extended Comprehensive Error Log index base.
+       Add workaround for Samsung disks using reserved byte as index.
+
+  [CF] knowndrives.cpp updates: Update bug warnings for
+       Seagate 7200.11, ES.2 and DiamondMax 22. Add new entries
+       for fixed firmware versions.
+
+  [CF] Add smartctl option '-l xerror[,NUM]' to print
+       ATA SMART Extended Comprehensive Error Log (GP Log 0x03).
+
+  [MS] knowndrives.cpp update:
+       Added remaining WD Scorpio Blue SATA II drives
+
+  [CF] Minor fix to remove ID 0 from 'smartctl -l sataphy ...' output.
+
+  [CF] knowndrives.cpp updates: Add warnings about possible firmware
+       bugs to Seagate 7200.11, ES.2 and DiamondMax 22 entries.
+
+  [CF] knowndrives.cpp updates: Add Samsung SpinPoint F1 series.
+
+  [CF] Windows: Fix return value of scsi_pass_through(). Regression
+       was introduced during migration to new interface. SAT over USB
+       now works on XP (both '-d sat,12' and '-d sat,16').
+
+  [MS] knowndrives.cpp updates:
+       - Added Western Digital RE2-GP family
+       - Added Hitachi Travelstar E5K160 family
+       - Allow uppercase variants of Hitachi 5K160 drives
+
+  [CF] Fix smartctl crash on '-l directory,[gs]'. Allow to override
+       missing GPL feature bit or missing log dir entry with
+       '-T permissive' option.
+
+  [SZ] os_freebsd.cpp, os_freebsd.h updates:
+       Support HighPoint RocketRAID controller under FreeBSD
+
+  [MS] knowndrives.cpp updates:
+       - Added Western Digital RE3 32MB cache variants
+       - Added WD Caviar Green 32MB cache variant (WD10EADS)
+       - Added WD Scorpio Black family
+
+  [DG] Accept half healthy (and half unhealthy) indication from the
+       SMART RETURN STATUS. This makes allowance for SAT implementations
+       (e.g. via USB) that truncate the SCSI sense buffer to 18 bytes.
+       This truncation causes the SMART RETURN STATUS indication to be
+       half health or unhealthy. If the half indication is used, then
+       warn if '-r ioctl' is given.
+
+  [MS] knowndrives.cpp updates:
+       - Added Apple SSD
+       - Added Seagate U8 family
+
+  [DL] os_freebsd.cpp:
+       Added support for CHECK_POWER_MODE and WRITE_LOG commands
+
+  [MS] knowndrives.cpp update:
+       There seem to exist WD Raptors with SATA II interface, add them.
+
+  [MS] knowndrives.cpp updates:
+       - Added remaining Seagate Barracuda 7200.11 drives
+       - Added HP 1TB SATA disk
+
+  [MS] knowndrives.cpp updates:
+       - Added Maxtor 92040U6 (DiamondMax Plus 6800)
+       - Added Seagate Maxtor DiamondMax 21 500GB version
+       - Added QUANTUM FIREBALLlct15 22
+       - Added QUANTUM FIREBALL CR6.4A
+       - Added QUANTUM FIREBALLP LM20.4
+       - Added SUN branded Toshiba MK4019GAX
+       - Added TOSHIBA MK1016GAP and relatives: MK1[05]1[67]GAP
+       - Added Western Digital WD800AB and WD2500AB
+       - Some Hitachi 7K160 drives have garbage at end of name: permit it
+
+  [CF] Add smartd '-n powermode,N' directive parameter to limit the
+       number of skipped checks. Thanks to Michal Hlavinka for the patch.
+
+  [MS] knowndrives.cpp updates:
+       - Added Hitachi Endurastar J4K30/N4K30
+       - Added Hitachi Travelstar 4K120 series
+       - Some Hitachi 7K80 drives have garbage at end of name: permit it
+       - IBM Travelstar 6GN series
+
+  [MS] knowndrives.cpp updates:
+       - Added Quantum Fireball ST4300A
+       - Added Asus-Phison SSD (solid state disk)
+       - Added Seagate DB35.3 Series
+       - Added remaining disks of the Seagate SV35.2 Series
+
+  [MS] Fix trivial compile error with "-pedantic"
+
+  [MS] Workaround for huge raw values of Reallocated_Sector_Ct and
+       Reallocated_Event_Ct for newer Fujitsu disks (only the lower
+       16 bits seem to be meaningful). Clip the display to 16 bits
+       and show the remaining part, if existent, in parens. Patch by [CF].
+
+  [CF] smartd DEVICESCAN: Fix autodetection of SAT devices.
+       Thanks to Stanislav Brabec for bug report and testing.
+
+  [MS] knowndrives.cpp update:
+       Convert file to full string regex: remove "^$" from pattern
+
+  [MS] knowndrives.cpp updates:
+       - Added Seagate Momentus 5400 PSD series (hybrid drives)
+       - Added Seagate Momentus 7200.3 series
+       - Added Hitachi Deskstar 7K250 (SUN branded)
+       - There are Hitachi Travelstar 5K250 drives with capital "HITACHI"
+       - Correct regex for Maxtor VL 30 drives
+
+  [CF] Add configure options '--enable-savestates' and
+       '--with-savestates=PREFIX' to enable smartd persistence
+       ('-s' option) by default.
+
+  [CF] smartd: Add '-s ([cnr]/../.././..)' directive to run scheduled
+       selective self-tests. Useful to perform full tests of large disks
+       not running 24x7.
+
+  [CF] Allow to read local drive database entries from optional file
+       '${sysconfdir}/smart_drivedb.h'.
+       Add configure options '--enable-drivedb' and '--with-drivedbdir=DIR'.
+       If specified, drive database is read from '${drivedbdir}/drivedb.h'.
+       (default '${prefix}/share/smartmontools/drivedb.h'). This file
+       is build from knowndrives.cpp.
+
+  [MS] knowndrives.cpp updates:
+       - Added 640GB variants of Western Digital AAKS and AACS drives
+       - Added Western Digital AV ATA family
+       - Added 160GB variant of Hitachi P7K500
+       - Added 500GB variant of Hitachi 7K1000
+       - Some cleanup for Quantum disks
+       - Added Seagate Maxtor DiamondMax 22 family
+
+  [CF] Use full string match for regexp in drive database.
+
+  [CF] Add option '-d sat+TYPE' to use SAT with controllers which
+       require option '-d TYPE'. Should work with '-d sat+megaraid,N'.
+       As a side effect, '-d usbcypress+TYPE' is also supported.
+
+  [CF] Add parser to read drive database from a file. Add '-B' option
+       to smartctl and smartd to specify database file name. File syntax
+       is identical to the C/C++ syntax used to inialize the internal
+       database array.
+
+  [CF] New syntax for drive database: Specify presets by strings
+       with '-v' and '-F' options. Use empty strings instead of NULL.
+
+  [JPH] Added Linux support for viewing disks behind MegaRAID
+       controllers
+
+  [CF] smartd: Improve min/max temperature recording in conjunction
+       with '-s' option.
+
+  [CF] Add a wrapper class for FILE *.
+
+  [CF] smartd: Add experimental support for state persistence (ATA only).
+       Add option '-s' to specify path prefix for state files.
+       Rework scheduled self-test detection to support persistence.
+       If any test schedules are within downtime, the highest priority
+       test is run after next startup.
+
+  [CF] Remove casts from 'format_ata_string()' calls.
+
+  [CF] Minor changes to fix errors and warnings from Cygwin gcc 4.3.0.
+
+  [CF] smartd: Remove SCSITIMEOUT code. According to smartd.h 1.54 CVS log
+       from 2003-10-27, it did never work.
+
+  [CF] Remove dependencies ataprint.cpp and scsiprint.cpp from smartd.
+       Move common ATA functions from ataprint.cpp to atacmds.cpp.
+       Module scsiprint.cpp was apparently never used in smartd.
+
+  [CF] Move smartd local declarations from smartd.h and utility.h to
+       smartd.cpp. Remove smartd.h.
+
+  [CF] Fixed extra '\n' in "Offline data collection status" output.
+       Thanks to Alexander Shaduri for the patch.
+
+  [CF] smartd: Separate device configuration data from device state data.
+       Use references instead of pointers for configuration and state data.
+
+  [CF] Add const-correctness and static to ATA support functions.
+
+  [CF] Add a wrapper class for regex.
+
+  [CF] Simplify 'create_vendor_attribute_arg_list()'.
+
+  [CF] smartd: Rework of main data structures. Remove explicit memory
+       allocations, use STL containers and structs with value semantics
+       instead. Remove old malloc/free based memory management helper
+       functions unless old interface is still in use.
+
+  [CF] Linux: Cleanup device scan, remove name list, create objects directly.
+
+  [CF] Linux: Cleanup smart_device::open(), type strings are no longer used.
+
+  [CF] Remove CONTROLLER_* defines and variables unless old interface
+       is still in use.
+
+  [CF] Linux: Migrate 3ware interface to 'ata_pass_through()'.
+       Multi-sector support is not complete yet. 48-bit commands
+       possibly work.
+       WARNING: Not tested, please review code before first test!
+
+  [CF] Linux: Migrate os_linux.cpp to new interface.
+
+  [CF] Add direct access to 16-bit registers in 'ata_in/out_regs_48bit'.
+
+  [CF] Add 'ata_cmd_is_ok()' parameter check,
+       remove 'ata_pass_through_28/48bit()' functions.
+
+  [CF] Add CVS date/time from cvsversion.h to man pages also.
+
+  [CF] Add configure option '--with-os-deps='os_module.o ...' to specify
+       alternate OS interface modules. Useful for testing during migration.
+
+  [CF] Remove declarations of 'optarg', 'optind', ..., include <unistd.h>
+       instead. This fixes 'auto-importing' linker warnings on Cygwin.
+
+  [CF] Add '-l sataphy[,reset]' to print SATA Phy Event Counters.
+
+  [CF] Add '-l gplog,ADDR[,FIRST[-LAST|+SIZE]]' and '-l smartlog,...'
+       to dump any log page accessible via GP or SMART read log commands.
+
+  [CF] Enhance '-l directory' to print both GP and SMART Log directories.
+       Add '-l directory[,gs]' modifiers to select GP or SMART log.
+       Enhance 'ata_cmd_in' parameter struct for 48-bit commands.
+
+  [CF] Windows: Add full ATA pass through support including 48-bit commands.
+
+  [CF] Windows: Migrate os_win32.cpp to new interface.
+
+  [CF] SAT: Add full ATA pass through support including 48-bit commands.
+
+  [MS] knowndrives.cpp update
+       - Added FUJITSU MHZ2250BS G2 and family
+
+  [MS] knowndrives.cpp updates
+       - Added Maxtor DiamondMax 60 94098H6
+       - Added Maxtor DiamondMax 1280 84000A6 and family
+       - Added Maxtor DiamondMax VL 30 31536H2 (ATA100) and family
+       - Some Seagate Barracuda 7200.9 have garbage at end of name: permit it
+       - Added Seagate Barracuda ATA ST320430A and family
+       - Regression from previous checkin: add WD RE2 WD...0ABYS again
+       - Added WD RE3 WD5002ABYS and family
+       - Added Quantum Fireball CR13.0A
+       - Added Hitachi Travelstar 5K250 HTS542525K9SA00 and family
+       - Added WD AC420400D and add whole range of AC.... which
+           have 5400rpm or higher (i.e. PIO-only drives omitted)
+
+  [MS] knowndrives.cpp updates
+       - WD: Separated entries for EIDE and SATA
+       - WD: Separated entries for Caviar SE, SE16, RE, RE2
+       - WD Named: WD Caviar AC series
+       - WD Renamed: WD Caviar RE/RE2 -> WD RE/RE2
+       - WD Renamed: WD Caviar SE/SE16 WD....AA[A-Z][A-Z] -> WD Caviar Blue
+       - WD Renamed: WD Scorpio WD....BEV[A-Z] -> WD Scorpio Blue
+       - Added WD Scorpio Blue WD3200BEVT
+       - Added WD RE2 WD5001ABYS and family
+       - Added WD Caviar Green WD5000AACS and family
+       - Added WD VelociRaptor WD3000GLFS and family
+       - Added Seagate Barracuda ES.2 ST31000340NS and family
+       - Added Samsung SP80A4H
+       - Added Maxtor DiamondMax 21 STM3160215AS and STM3320620AS
+       - Added Seagate Barracuda 7200.7 ST380819AS
+       - Added Maxtor DiamondMax 10 6B100P0
+       - Added Seagate SV35.2 Series
+       - Added Fujitsu MHY2120BH and family
+       - Added Fujitsu MHW2080BH PL (PL variant)
+       - Added Toshiba MK3252GSX and family
+
+  [BA] Fix smartctl bug: when running in silent mode '-q errorsonly'
+       do not print the Selective Self-test log.  Any errors will
+       ALREADY appear in the SMART Self-test log.
+
+  [CF] Add missing 'const' and other minor fixes to prevent gcc warnings.
+
+  [OB] Added information message about supported Areca firmware versions.
+       It's displayed in case the ATA device identification fails.
+
+  [CF] Add configuration file for Doxygen.
+
+  [CF] Add new object oriented interface to access ATA and SCSI devices.
+       smartctl and smartd are modified to use the new classes in
+       'dev_interface.{h,cpp}'. The template class in 'dev_tunnelled.h'
+       is used in 'scsiata.cpp'. The code in 'dev_ata_cmd_set.{h,cpp}'
+       supports migration from old function 'ata_command_interface()'.
+       All existing 'os_*.cpp' modules should still work without any changes.
+       The required adapter classes from 'dev_legacy.cpp' are automatically
+       added by configure if necessary.
+
+  [BA] Updated smartd and smartctl and smartd.conf man-page documentation
+       to reflect support for Areca SATA RAID controller cards.
+
+  [OB] Added support for Areca controllers to smartd. Extensive tests
+       as well as documentation are still pending however.
+
+  [OB] Implemented device locking for Areca controllers in smartctl
+
+  [BA] Fixed selective self-test code.  Data structure revision number
+       may be != 1 if no selective self-test has ever been run.  Host
+       MUST set this value (at least at the first selective self-test
+       instance).  Thanks to Curtis Stevens of WDC for clarification.
+
+  [MC] usbcypress autodetection
+
+  [BA] Starting to commit Areca code.  For now just smartctl.
+       More changes and documentation coming soon.
+       Need Areca firmware version 1.45 dated 10 June 2008 or later.
+       May need changes in opening /dev/sg and file locking.
+       Many thanks to Hank Wu!
+
+  [CF] smartd: Fix too small name buffer for 3ware with >100 devices.
+
+  [JH] now C++ Support for QNX Target
+       already tested for QNX 6.3.2 on x86 and armle target
+   
+  [CF] Allow to set BUILD_INFO from make command line.
+
+  [CF] Windows: Add MSVC8 support, remove MSVC6 project files.
+
+  [MC] Add usbcypress device support for smartd.
+
+  [CF] Add output of latest CVS date/time stamp to version info.
+       New file cvsversion.h is generated by Makefile.
+       Move formatting of version info to utility.cpp.
+
+  [AR] Fix bug in 3ware node creation where nodes would be created
+       then deleted, then recreated.
+
+  [BA] Add missing CCISS cvs version tags to '-V' printouts.
+
+  [TS] Linux: Ensure the 3ware device nodes are created with a correct
+       SELinux security context.
+
+  [AR] Add support for up to 128 devices on 3ware controllers.
+
+  [CF] C++: Added new main() with exception handlers, replaced
+       exit(x) with throw(x), removed global variable 'exitstatus'.
+       Necessary for future changes, because exit() does not call
+       any destructors.
+
+  [SS] FreeBSD: plug file descriptor leak in smartd (only happens with
+       CISS devices).  Reported by Vadim Ostranitsyn.
+
+  [SS] FreeBSD: allow smartctl to interact with SCSI /dev/pass devices,
+       thus enabling it to work with RAID controllers that expose disks
+       via these devices.  From scottl@ via FreeBSD ports.
+
+  [MC] Add usbcypress device support for smartctl.
+
+  [KS] INSTALL on Solaris: updated description to use C++ compiler.
+
+  [KS] configure.in on Solaris: added options for Sun's compiler to
+       suppress trivial warnings.
+
+  [KS] configure.in on Solaris: added direction to search suitable
+       libraries for getaddrinfo().
+
+  [TS] smartd on Linux: remove forgotten return from deviceopen();
+       SCSI device descriptors had the FD_CLOEXEC flag unset.
+
+  [CF] Added 'const' to avoid warning on depreciated string constant
+       to 'char *' conversion.
+
+  [CF] autogen.sh: automake 1.10.1 is OK. Updated message texts.
+
+  [BA] removed smartmontools.spec file (now in CVS Attic).  This
+       was not being used by RH or FC anyhow, and was out of
+       date and not maintained.
+
+  [BA] smartd on Linux: sets FD_CLOEXEC on the opened device file
+       descriptor.  The descriptor is otherwise leaked to other
+       applications (mail sender) which may be considered a security
+       risk and may result in AVC messages on SELinux-enabled systems.
+       Thanks to: Tomá Smetana" <tsmetana@redhat.com>.
+
+  [BA] smartd: when sending email, to gather information about the
+       host for the body of the email, eliminate gethostbyname()
+       in favor of the IPv6-friendly function getaddrinfo() if the
+       latter is available.  Info may be found here
+       http://udrepper.livejournal.com/16116.html
+       and here
+       http://people.redhat.com/drepper/userapi-ipv6.html
+       Thanks to: Tomá Smetana" <tsmetana@redhat.com>.
+
+       Smartmontools developers: please check that smartd still LINKS
+       properly on your systems.
+
+  [BA] Fix ugly syntax bug in os_freebsd.cpp.  How did this go
+       undetected for so long??
 
 SMARTMONTOOLS STABLE RELEASE 5.38 2008/03/10
 
diff --git a/Doxyfile b/Doxyfile
new file mode 100644 (file)
index 0000000..50eeac7
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,241 @@
+# Doxyfile 1.5.5
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING      = UTF-8
+PROJECT_NAME           = smartmontools
+PROJECT_NUMBER         = 
+OUTPUT_DIRECTORY       = doc
+CREATE_SUBDIRS         = NO
+OUTPUT_LANGUAGE        = English
+BRIEF_MEMBER_DESC      = YES
+REPEAT_BRIEF           = YES
+ABBREVIATE_BRIEF       = 
+ALWAYS_DETAILED_SEC    = YES
+INLINE_INHERITED_MEMB  = NO
+FULL_PATH_NAMES        = YES
+STRIP_FROM_PATH        = 
+STRIP_FROM_INC_PATH    = 
+SHORT_NAMES            = NO
+JAVADOC_AUTOBRIEF      = YES
+QT_AUTOBRIEF           = NO
+MULTILINE_CPP_IS_BRIEF = NO
+DETAILS_AT_TOP         = NO
+INHERIT_DOCS           = YES
+SEPARATE_MEMBER_PAGES  = NO
+TAB_SIZE               = 8
+ALIASES                = 
+OPTIMIZE_OUTPUT_FOR_C  = NO
+OPTIMIZE_OUTPUT_JAVA   = NO
+OPTIMIZE_FOR_FORTRAN   = NO
+OPTIMIZE_OUTPUT_VHDL   = NO
+BUILTIN_STL_SUPPORT    = NO
+CPP_CLI_SUPPORT        = NO
+SIP_SUPPORT            = NO
+DISTRIBUTE_GROUP_DOC   = NO
+SUBGROUPING            = YES
+TYPEDEF_HIDES_STRUCT   = NO
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL            = YES
+EXTRACT_PRIVATE        = YES
+EXTRACT_STATIC         = YES
+EXTRACT_LOCAL_CLASSES  = YES
+EXTRACT_LOCAL_METHODS  = NO
+EXTRACT_ANON_NSPACES   = YES
+HIDE_UNDOC_MEMBERS     = NO
+HIDE_UNDOC_CLASSES     = NO
+HIDE_FRIEND_COMPOUNDS  = NO
+HIDE_IN_BODY_DOCS      = NO
+INTERNAL_DOCS          = NO
+CASE_SENSE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
+SHOW_INCLUDE_FILES     = YES
+INLINE_INFO            = YES
+SORT_MEMBER_DOCS       = YES
+SORT_BRIEF_DOCS        = NO
+SORT_GROUP_NAMES       = NO
+SORT_BY_SCOPE_NAME     = NO
+GENERATE_TODOLIST      = YES
+GENERATE_TESTLIST      = YES
+GENERATE_BUGLIST       = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS       = 
+MAX_INITIALIZER_LINES  = 30
+SHOW_USED_FILES        = YES
+SHOW_DIRECTORIES       = NO
+FILE_VERSION_FILTER    = 
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET                  = NO
+WARNINGS               = YES
+WARN_IF_UNDOCUMENTED   = NO
+WARN_IF_DOC_ERROR      = YES
+WARN_NO_PARAMDOC       = NO
+WARN_FORMAT            = "$file:$line: $text"
+WARN_LOGFILE           = 
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT                  = 
+INPUT_ENCODING         = UTF-8
+FILE_PATTERNS          = dev*.h \
+                         dev*.cpp \
+                         scsiata.cpp
+RECURSIVE              = NO
+EXCLUDE                = 
+EXCLUDE_SYMLINKS       = NO
+EXCLUDE_PATTERNS       = 
+EXCLUDE_SYMBOLS        = 
+EXAMPLE_PATH           = 
+EXAMPLE_PATTERNS       = 
+EXAMPLE_RECURSIVE      = NO
+IMAGE_PATH             = 
+INPUT_FILTER           = 
+FILTER_PATTERNS        = 
+FILTER_SOURCE_FILES    = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER         = YES
+INLINE_SOURCES         = NO
+STRIP_CODE_COMMENTS    = NO
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION    = NO
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS              = NO
+VERBATIM_HEADERS       = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX     = NO
+COLS_IN_ALPHA_INDEX    = 5
+IGNORE_PREFIX          = 
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML          = YES
+HTML_OUTPUT            = html
+HTML_FILE_EXTENSION    = .html
+HTML_HEADER            = 
+HTML_FOOTER            = 
+HTML_STYLESHEET        = 
+HTML_ALIGN_MEMBERS     = YES
+GENERATE_HTMLHELP      = NO
+GENERATE_DOCSET        = NO
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+HTML_DYNAMIC_SECTIONS  = NO
+CHM_FILE               = 
+HHC_LOCATION           = 
+GENERATE_CHI           = NO
+BINARY_TOC             = NO
+TOC_EXPAND             = NO
+DISABLE_INDEX          = NO
+ENUM_VALUES_PER_LINE   = 4
+GENERATE_TREEVIEW      = NO
+TREEVIEW_WIDTH         = 250
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX         = YES
+LATEX_OUTPUT           = latex
+LATEX_CMD_NAME         = latex
+MAKEINDEX_CMD_NAME     = makeindex
+COMPACT_LATEX          = NO
+PAPER_TYPE             = a4wide
+EXTRA_PACKAGES         = 
+LATEX_HEADER           = 
+PDF_HYPERLINKS         = YES
+USE_PDFLATEX           = YES
+LATEX_BATCHMODE        = NO
+LATEX_HIDE_INDICES     = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF           = NO
+RTF_OUTPUT             = rtf
+COMPACT_RTF            = NO
+RTF_HYPERLINKS         = NO
+RTF_STYLESHEET_FILE    = 
+RTF_EXTENSIONS_FILE    = 
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN           = NO
+MAN_OUTPUT             = man
+MAN_EXTENSION          = .3
+MAN_LINKS              = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML           = NO
+XML_OUTPUT             = xml
+XML_SCHEMA             = 
+XML_DTD                = 
+XML_PROGRAMLISTING     = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF   = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD       = NO
+PERLMOD_LATEX          = NO
+PERLMOD_PRETTY         = YES
+PERLMOD_MAKEVAR_PREFIX = 
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING   = YES
+MACRO_EXPANSION        = NO
+EXPAND_ONLY_PREDEF     = NO
+SEARCH_INCLUDES        = YES
+INCLUDE_PATH           = 
+INCLUDE_FILE_PATTERNS  = 
+PREDEFINED             = 
+EXPAND_AS_DEFINED      = 
+SKIP_FUNCTION_MACROS   = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+TAGFILES               = 
+GENERATE_TAGFILE       = 
+ALLEXTERNALS           = NO
+EXTERNAL_GROUPS        = YES
+PERL_PATH              = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS         = YES
+MSCGEN_PATH            = 
+HIDE_UNDOC_RELATIONS   = YES
+HAVE_DOT               = NO
+CLASS_GRAPH            = YES
+COLLABORATION_GRAPH    = YES
+GROUP_GRAPHS           = YES
+UML_LOOK               = NO
+TEMPLATE_RELATIONS     = NO
+INCLUDE_GRAPH          = YES
+INCLUDED_BY_GRAPH      = YES
+CALL_GRAPH             = NO
+CALLER_GRAPH           = NO
+GRAPHICAL_HIERARCHY    = YES
+DIRECTORY_GRAPH        = YES
+DOT_IMAGE_FORMAT       = png
+DOT_PATH               = 
+DOTFILE_DIRS           = 
+DOT_GRAPH_MAX_NODES    = 50
+MAX_DOT_GRAPH_DEPTH    = 0
+DOT_TRANSPARENT        = YES
+DOT_MULTI_TARGETS      = NO
+GENERATE_LEGEND        = YES
+DOT_CLEANUP            = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+SEARCHENGINE           = NO
diff --git a/INSTALL b/INSTALL
index 9f6bb689ac647461407b9258f7bdd5dc31b84212..5a6d880e46a7b7155f29aa213bf06d15f5e994ed 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,7 +1,7 @@
 Smartmontools installation instructions
 =======================================
 
-$Id: INSTALL,v 1.73 2007/04/27 08:50:00 geoffk1 Exp $
+$Id: INSTALL 2844 2009-07-18 12:59:21Z chrfranke $
 
 Please also see the smartmontools home page:
 http://smartmontools.sourceforge.net/
@@ -9,7 +9,7 @@ http://smartmontools.sourceforge.net/
 Table of contents:
 
 [1] System requirements
-[2] Installing from CVS
+[2] Installing from SVN
 [3] Installing from source tarball
 [4] Guidelines for different Linux distributions
 [5] Guidelines for FreeBSD
@@ -209,23 +209,19 @@ Table of contents:
     Innotek LibC 0.5 runtime is required.
     Currently only ATA disks are supported, SCSI support will be added.
 
-
-[2] Installing from CVS
+[2] Installing from SVN
 =======================
-    Get the sources from the CVS repository:
-    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)
+
+    Get the sources from the SVN repository:
+    svn co https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk/smartmontools smartmontools
 
     Then type:
     ./autogen.sh
     and continue with step [3] below, skipping the "unpack the tarball" step.
 
-    Further details of using CVS can be found at the URL above.
-
     The autogen.sh command is ONLY required when installing from
-    CVS. You need GNU Autoconf (version 2.50 or greater), GNU Automake
-    (version 1.6 or greater) and their dependencies installed in order
+    SVN. You need GNU Autoconf (version 2.50 or greater), GNU Automake
+    (version 1.7 or greater) and their dependencies installed in order
     to run it.  You can get these here:
     http://www.gnu.org/directory/GNU/autoconf.html
     http://www.gnu.org/directory/GNU/automake.html
@@ -233,7 +229,7 @@ Table of contents:
 [3] Installing from the source tarball
 ======================================
 
-    If you are NOT installing from CVS, then unpack the tarball: 
+    If you are NOT installing from SVN, then unpack the tarball:
     tar zxvf smartmontools-5.VERSION.tar.gz
 
     Then:
@@ -263,6 +259,12 @@ Table of contents:
     ./configure, or else do:
     make CFLAGS='your options'
 
+    The first output line of smartctl and smartd provides information
+    about release number, last SVN checkin date and revison, platform,
+    and package. The latter defaults to "(local build)" and can be
+    changed by the variable BUILD_INFO, for example:
+    make BUILD_INFO='"(Debian 5.39-2)"'
+
 [4] Guidelines for different Linux distributions
 ================================================
 
@@ -332,10 +334,6 @@ SuSE:
               --with-docdir=/usr/local/share/doc/smartmontools-VERSION \
              --enable-sample
 
-  Also, it is important that you use GNU make (gmake from /usr/ports/devel/gmake)
-  to build smartmontools, as the default FreeBSD make doesn't know how to build
-  the man pages.
-
   NOTE: --enable-sample will cause the smartd.conf and smartd RC files to
   be installed with the string '.sample' append to the name, so you will end
   up with the following:
@@ -368,17 +366,17 @@ SuSE:
 
     smartmontools has been partially but not completely ported to
     Solaris.  It includes complete SCSI support but no ATA or 3ware
-    support.  It can be compiled with either cc or gcc. To compile
-    with gcc:
+    support.  It can be compiled with either CC (Sun's C++ compiler)
+    or GNU g++.
+
+    To compile with g++:
 
     ./configure [args]
     make
 
-    To compile with Sun cc:
+    To compile with Sun CC:
 
-    setenv CC cc  [csh syntax], or
-    CC=cc         [sh syntax]
-    ./configure [args]
+    env CC=cc CXX=CC ./configure [args]
     make
 
     The correct arguments [args] to configure are:
@@ -426,7 +424,7 @@ Same as Red Hat:
   file type ("binmode" mount option) set. The "autogen.sh" script prints a
   warning if DOS type is selected.
 
-  If installing from CVS, you may check out all files either with CR/LF
+  If installing from SVN, you may check out all files either with CR/LF
   or LF line endings. Starting with release 3.1-7, Cygwin's bash does no
   longer accept scripts with CR/LF by default. To run the initial script
   ./autogen.sh checked out with CR/LF on a "binmode" mount, type:
@@ -469,7 +467,7 @@ To create a Windows installer, use:
 
   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.
+  download location.
 
 To both create and run the (interactive) installer, use:
 
@@ -484,16 +482,22 @@ To both create and run the (interactive) installer, use:
   or a native Win32 release of Info-ZIP, http://www.info-zip.org) are
   necessary but may be not installed by Cygwin's default settings.
 
-  It is also possible to compile smartmontools with MSVC 6.0.
-  The project files (smartmontools_vc6.dsw, smart{ctl,d}_vc6.dsp) are
-  included in CVS (but not in source tarball). The config_vc6.h is no
-  longer maintained in CVS. The command:
+To prepare os_win32 directory for MSVC8, use the following on Cygwin:
+
+  mkdir vctmp && cd vctmp
+  ../configure --build=mingw32
+  make config-vc8
 
-  make config-vc6
+  The MSVC8 project files (os_win32/smartmontools_vc8.sln,
+  os_win32/smart{ctl,d}_vc8.vcproj) are included in SVN (but not in
+  source tarball). The target config-vc8 from a Makefile configured
+  for MinGW creates os_win32/{config,svnversion}_vc8.h from
+  ./{config,svnversion}.h. The configure skript must be run outside
+  of the source directory to avoid inclusion of the original config.h.
 
-  builds config_vc6.h from MinGW's config.h. Unlike MinGW, MSVC 6.0
-  can also be used to build the syslog message file tool syslogevt.exe.
-  See smartd man page for usage information about this tool.
+  Unlike MinGW, MSVC can also be used to build the syslog message file
+  tool syslogevt.exe. See smartd man page for usage information about
+  this tool.
 
 
 [11] Guidelines for OS/2, eComStation
@@ -581,7 +585,7 @@ The following files are installed if ./configure has no options:
 /usr/local/share/man/man8/smartctl.8                    [Manual page]
 /usr/local/share/man/man8/smartd.8                      [Manual page]
 /usr/local/share/doc/smartmontools-5.X/AUTHORS          [Information about the authors and developers]
-/usr/local/share/doc/smartmontools-5.X/CHANGELOG        [A log of changes. Also see CVS]
+/usr/local/share/doc/smartmontools-5.X/CHANGELOG        [A log of changes. Also see SVN]
 /usr/local/share/doc/smartmontools-5.X/COPYING          [GNU General Public License Version 2]
 /usr/local/share/doc/smartmontools-5.X/INSTALL          [Installation instructions: what you're reading!]
 /usr/local/share/doc/smartmontools-5.X/NEWS             [Significant bugs discovered in old versions]
@@ -607,6 +611,19 @@ if groff is available:
 make MAN2HTML='groff -man -Thtml' htmlman
 
 
+Some of the source files are prepared for the documentation
+generator Doxygen (http://www.doxygen.org/). If Doxygen is installed,
+the command:
+
+doxygen
+
+creates HTML documentation in doc/html and LaTeX documentation
+in doc/latex. If TeX is installed, the following command creates
+a documentation file doc/latex/refman.pdf:
+
+( cd doc/latex && make pdf )
+
+
 [14] Detailed description of arguments to configure command
 ===========================================================
 
@@ -628,9 +645,17 @@ OPTIONS              DEFAULT                                      AFFECTS
                                                                   Contents of smartd executable;
                                                                   Contents of smartd/smartd.conf man pages;
                                                                   Directory for rc.d/init.d/smartd init script
---with-initscriptdir  ${sysconfdir}/init.d/rc.d                   Location of init scripts       
---with-docdir        ${prefix}/share/doc/smartmontools-5.X       Location of the documentation
---enable-sample              --disable-sample                            Adds the string '.sample' to the names of the smartd.conf file and the smartd RC file
+--with-initscriptdir  ${sysconfdir}/init.d/rc.d                   Location of init scripts
+--with-docdir         ${prefix}/share/doc/smartmontools-5.X       Location of the documentation
+--enable-sample       --disable-sample                            Adds the string '.sample' to the names of the smartd.conf file and the smartd RC file
+--with-os-deps        os_<guessed>.o                              OS dependent module(s)
+--with-selinux        <not set>                                   Enables SELinux support.  If smartmontools has to create the /dev/tw[ae] device
+                                                                  nodes for 3ware/AMCC controllers, this option ensures that the nodes are created
+                                                                  with correct SELinux file contexts.
+--enable-drivedb      --disable-drivedb                           Enables default drive database file '${drivedbdir}/drivedb.h'
+--with-drivedbdir     ${prefix}/share/smartmontools/drivedb.h     Directory for 'drivedb.h' (specifying this option implies --enable-drivedb)
+--enable-savestates   --disable-savestates                        Enables default smartd state files '${savestates}MODEL-SERIAL.ata.state'
+--with-savestates     ${prefix}/var/lib/smartmontools/smartd.     Prefix for smartd state files (specifying this option implies --enable-savestates)
 
 Here's an example:
 If you set --prefix=/home/joe and none of the other four
index dd3b1f73aac8874eb092b3520387a21acc8cb7f9..15dcdfa4d9bf79227d3564adbbc41e34f6930ae2 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 #
-# $Id: Makefile.am,v 1.81 2007/04/01 16:49:44 shattered Exp $
+# $Id: Makefile.am 2878 2009-08-26 20:03:06Z chrfranke $
 #
 
 @SET_MAKE@
@@ -8,20 +8,33 @@
 # Make sure .cpp takes precedence to avoid compiling old .c file
 SUFFIXES = .cpp .c .s .o
 
+# BUILD_INFO can be provided by package maintainers (see INSTALL file)
+BUILD_INFO= "(local build)"
 
-AM_CPPFLAGS = -DSMARTMONTOOLS_SYSCONFDIR=\"$(sysconfdir)\"
+AM_CPPFLAGS = -DBUILD_INFO='$(BUILD_INFO)' -DSMARTMONTOOLS_SYSCONFDIR='"$(sysconfdir)"'
+if ENABLE_DRIVEDB
+AM_CPPFLAGS += -DSMARTMONTOOLS_DRIVEDBDIR='"$(drivedbdir)"'
+endif
+if ENABLE_SAVESTATES
+AM_CPPFLAGS += -DSMARTMONTOOLS_SAVESTATES='"$(savestates)"'
+endif
+if ENABLE_ATTRIBUTELOG
+AM_CPPFLAGS += -DSMARTMONTOOLS_ATTRIBUTELOG='"$(attributelog)"'
+endif
 
 sbin_PROGRAMS = smartd         \
                smartctl
 
 smartd_SOURCES =  smartd.cpp      \
-                  smartd.h        \
                   atacmdnames.cpp \
                   atacmdnames.h   \
                   atacmds.cpp     \
                   atacmds.h       \
-                  ataprint.cpp    \
-                  ataprint.h      \
+                  dev_ata_cmd_set.cpp \
+                  dev_ata_cmd_set.h   \
+                  dev_interface.cpp   \
+                  dev_interface.h     \
+                  dev_tunnelled.h     \
                   extern.h        \
                   int64.h         \
                   knowndrives.cpp \
@@ -29,9 +42,6 @@ smartd_SOURCES =  smartd.cpp      \
                   scsicmds.cpp    \
                   scsicmds.h      \
                   scsiata.cpp     \
-                  scsiata.h       \
-                  scsiprint.cpp   \
-                  scsiprint.h     \
                   utility.cpp     \
                   utility.h
 
@@ -55,7 +65,9 @@ EXTRA_smartd_SOURCES = os_darwin.cpp    \
                        os_generic.cpp   \
                        os_generic.h     \
                        cciss.cpp        \
-                       cciss.h
+                       cciss.h          \
+                       dev_legacy.cpp   \
+                       megaraid.h
 
 
 if OS_WIN32_MINGW
@@ -87,6 +99,11 @@ smartctl_SOURCES= smartctl.cpp    \
                   atacmds.h       \
                   ataprint.cpp    \
                   ataprint.h      \
+                  dev_ata_cmd_set.cpp \
+                  dev_ata_cmd_set.h   \
+                  dev_interface.cpp   \
+                  dev_interface.h     \
+                  dev_tunnelled.h     \
                   extern.h        \
                   int64.h         \
                   knowndrives.cpp \
@@ -94,12 +111,12 @@ smartctl_SOURCES= smartctl.cpp    \
                   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@
 
@@ -117,7 +134,9 @@ EXTRA_smartctl_SOURCES = os_linux.cpp \
                        os_generic.cpp \
                        os_generic.h   \
                        cciss.cpp      \
-                       cciss.h
+                       cciss.h        \
+                       dev_legacy.cpp \
+                       megaraid.h
 
 if OS_WIN32_MINGW
 
@@ -211,8 +230,7 @@ smartd.conf$(smartd_suffix): smartd.conf
        cp ${srcdir}/smartd.conf smartd.conf$(smartd_suffix)
 endif
 
-EXTRA_DIST = smartmontools.spec                    \
-             smartd.initd.in                       \
+EXTRA_DIST = smartd.initd.in                       \
              smartd.8.in                           \
              smartctl.8.in                         \
              smartd.conf.5.in                      \
@@ -224,7 +242,8 @@ EXTRA_DIST = smartmontools.spec                    \
              os_win32/installer.nsi                \
              $(docs_DATA)
 
-CLEANFILES = smartd.conf.5      \
+CLEANFILES = drivedb.h          \
+             smartd.conf.5      \
              smartd.conf.4      \
              smartd.8           \
              smartd.1m          \
@@ -236,13 +255,70 @@ CLEANFILES = smartd.conf.5      \
              smartctl.8.txt     \
              smartd.conf.5.html \
              smartd.conf.5.txt  \
-             smartd.initd      \
+             smartd.initd       \
+             svnversion.h       \
              SMART
 
 if SMARTD_SUFFIX
 CLEANFILES += smartd.conf$(smartd_suffix)
 endif
 
+# 'make maintainer-clean' also removes files generated by './autogen.sh'
+MAINTAINERCLEANFILES = \
+        $(srcdir)/Makefile.in \
+        $(srcdir)/aclocal.m4 \
+        $(srcdir)/configure \
+        $(srcdir)/config.guess \
+        $(srcdir)/config.h.in \
+        $(srcdir)/config.h.in~ \
+        $(srcdir)/config.sub \
+        $(srcdir)/depcomp \
+        $(srcdir)/install-sh \
+        $(srcdir)/missing \
+        $(srcdir)/mkinstalldirs
+
+utility.o: svnversion.h
+
+if IS_SVN_BUILD
+# Get version info from SVN
+svnversion.h: CHANGELOG Makefile $(srcdir)/.svn/entries
+       echo '/* svnversion.h.  Generated by Makefile from svn info.  */' > $@
+       (cd $(srcdir) \
+        && svnversion 2>/dev/null | sed -n 's,^\([0-9].*\),REV  "\1",p' \
+        && TZ= LC_ALL=C svn info 2>/dev/null \
+        | sed -n 'h;s,^.* Date: *\([^ ]*\) .*$$,DATE "\1",p;g;s,^.* Date: *[^ ]* *\([^ ]*\) .*$$,TIME "\1",p') \
+       | sed 's,^,#define SMARTMONTOOLS_SVN_,' >> $@
+else
+
+# SVN not available, guess version info from Id strings
+svnversion.h: CHANGELOG Makefile
+       echo '/* svnversion.h.  Generated by Makefile from Id strings.  */' > $@
+       (cd $(srcdir) && cat CHANGELOG Makefile.am configure.in smart*.in *.cpp *.h *.s) \
+       | sed -n 's,^.*\$$[I][d]: [^ ]* \([0-9][0-9]* [0-9][-0-9]* [0-9][:0-9]*\)[^:0-9][^$$]*\$$.*$$,\1,p' \
+       | sort -n -r \
+       | sed -n 'h;s,^\([^ ]*\) .*$$,REV  "\1",p;g;s,^[^ ]* \([^ ]*\) .*$$,DATE "\1",p;g;s,^[^ ]* [^ ]* \([^ ]*\)$$,TIME "\1",p;q' \
+       | sed 's,^,#define SMARTMONTOOLS_SVN_,' >> $@
+endif
+
+
+# Drive Database
+drivedb.h: knowndrives.cpp Makefile
+       echo '/* drivedb.h.  Generated from knowndrives.cpp by Makefile.  */' > $@
+       sed '1,/^\/\/ BEGIN drivedb.h/d;/^\/\/ END drivedb.h/,$$d;s/^  //' $(srcdir)/knowndrives.cpp >> $@
+
+if ENABLE_DRIVEDB
+drivedb_DATA = drivedb.h
+endif
+
+if ENABLE_SAVESTATES
+# Create $(savestatesdir) only
+savestates_DATA =
+endif
+
+if ENABLE_ATTRIBUTELOG
+# Create $(attributelogdir) only
+attributelog_DATA =
+endif
 
 smartd.conf.5.in: smartd.8.in
        sed '1,/STARTINCLUDE/ D;/ENDINCLUDE/,$$D' < $(srcdir)/smartd.8.in > $(top_builddir)/tmp.directives
@@ -255,6 +331,7 @@ smartd.conf.5.in: smartd.8.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
 
+if INSTALL_INITSCRIPT
 if OS_DARWIN
 initd_DATA = SMART                            \
        os_darwin/StartupParameters.plist     \
@@ -263,6 +340,7 @@ initd_DATA = SMART                            \
 initd_install_name = SMART
 
 initd_DATA_install = install-initdDATA-darwin
+initd_DATA_uninstall = uninstall-initdDATA-darwin
 
 SMART : os_darwin/SMART.in
        sed "s|/usr/sbin/|$(sbindir)/|" $< > $@
@@ -288,6 +366,9 @@ install-initdDATA-darwin: $(initd_DATA)
        @echo -e "#   to learn about it. A sample configuration file can be found in:\n#   ${docdir}\n#"
        @echo -e "####################################################################\n\n"
 
+uninstall-initdDATA-darwin:
+       rm -rf $(DESTDIR)$(initddir)/$(initd_install_name)
+
 else
 
 initd_DATA = smartd.initd
@@ -298,6 +379,7 @@ smartd.initd: $(srcdir)/smartd.initd.in Makefile
 initd_install_name = smartd$(smartd_suffix)
 
 initd_DATA_install = install-initdDATA-generic
+initd_DATA_uninstall = uninstall-initdDATA-generic
 
 install-initdDATA-generic: $(initd_DATA)
        $(mkinstalldirs) $(DESTDIR)$(initddir)
@@ -310,26 +392,72 @@ install-initdDATA-generic: $(initd_DATA)
        @echo -e "#   to learn about it. A sample configuration file can be found in:\n#   ${docdir}\n#"
        @echo -e "####################################################################\n\n"
 
+
+uninstall-initdDATA-generic:
+       rm -rf $(DESTDIR)$(initddir)/$(initd_install_name)
+endif
+else
+
+initd_DATA_install = install-initdDATA-null
+initd_DATA_uninstall = uninstall-initdDATA-null
+
+install-initdDATA-null:
+uninstall-initdDATA-null:
 endif
 
 install-initdDATA : $(initd_DATA_install)
 
-uninstall-initdDATA:
-       rm -rf $(DESTDIR)$(initddir)/$(initd_install_name)
+uninstall-initdDATA: $(initd_DATA_uninstall)
 
-uninstall-docsDATA:
-       rm -rf $(DESTDIR)$(docsdir)
+if ENABLE_DRIVEDB
+MAN_DRIVEDB = sed "s|/usr/local/share/smartmontools/drivedb\\.h|$(drivedbdir)/drivedb.h|g"
+else
+MAN_DRIVEDB = sed '/BEGIN ENABLE_DRIVEDB/,/END ENABLE_DRIVEDB/d'
+endif
 
-smart%: $(srcdir)/smart%.in Makefile
+if ENABLE_SAVESTATES
+MAN_SAVESTATES = sed "s|/usr/local/var/lib/smartmontools/smartd\\.|$(savestates)|g"
+else
+MAN_SAVESTATES = sed '/BEGIN ENABLE_SAVESTATES/,/END ENABLE_SAVESTATES/d'
+endif
+
+if ENABLE_ATTRIBUTELOG
+MAN_ATTRIBUTELOG = sed "s|/usr/local/var/lib/smartmontools/attrlog\\.|$(attributelog)|g"
+else
+MAN_ATTRIBUTELOG = sed '/BEGIN ENABLE_ATTRIBUTELOG/,/END ENABLE_ATTRIBUTELOG/d'
+endif
+
+if OS_FREEBSD
+.for file in $(man_MANS)
+${file}: $(srcdir)/${file}.in Makefile svnversion.h
+       sed "s|CURRENT_CVS_VERSION|$(releaseversion)|g; \
+            s|CURRENT_CVS_DATE|`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`|g; \
+            s|CURRENT_CVS_TIME|`sed -n 's,^.*TIME[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`|g; \
+            s|/usr/local/share/man/|$(mandir)/|g; \
+            s|/usr/local/sbin/|$(sbindir)/|g; \
+            s|/usr/local/etc/rc\\.d/init.d/|$(initddir)/|g; \
+            s|/usr/local/share/doc/smartmontools-5.1/|$(docsdir)/|g;  \
+            s|/usr/local/etc/smartd\\.conf|$(sysconfdir)/smartd.conf|g; \
+            s|/usr/local/etc/smart_drivedb\\.h|$(sysconfdir)/smart_drivedb\\.h|g" ${.ALLSRC:M*.in} | \
+       $(MAN_DRIVEDB) | \
+       $(MAN_SAVESTATES) | \
+       $(MAN_ATTRIBUTELOG) > $@
+.endfor
+else
+smart%: $(srcdir)/smart%.in Makefile svnversion.h
        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|CURRENT_CVS_DATE|`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`|g" | \
+       sed "s|CURRENT_CVS_TIME|`sed -n 's,^.*TIME[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`|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" > $@
-
+       sed "s|/usr/local/etc/smartd\\.conf|$(sysconfdir)/smartd.conf|g" | \
+       sed "s|/usr/local/etc/smart_drivedb\\.h|$(sysconfdir)/smart_drivedb\\.h|g" | \
+       $(MAN_DRIVEDB) | \
+       $(MAN_SAVESTATES) | \
+       $(MAN_ATTRIBUTELOG) > $@
+endif
 
 # Commands to convert man pages into .html and .txt
 # TODO: configure
@@ -385,6 +513,7 @@ exedir_win32 = $(distdir_win32)/bin
 docdir_win32 = $(distdir_win32)/doc
 
 FILES_WIN32 = $(exedir_win32)/smartctl.exe \
+              $(exedir_win32)/smartctl-nc.exe \
               $(exedir_win32)/smartd.exe \
               $(docdir_win32)/AUTHORS.txt \
               $(docdir_win32)/CHANGELOG.txt \
@@ -402,7 +531,9 @@ FILES_WIN32 = $(exedir_win32)/smartctl.exe \
               $(docdir_win32)/smartd.conf.5.html \
               $(docdir_win32)/smartd.conf.5.txt
 
-CLEANFILES += $(FILES_WIN32) $(exedir_win32)/syslogevt.exe distdir.mkdir syslogevt.check
+CLEANFILES += $(FILES_WIN32) $(exedir_win32)/syslogevt.exe \
+              smartctl-nc.exe smartctl-nc.exe.tmp \
+              distdir.mkdir syslogevt.check
 
 # Textfile converter from cygutils
 UNIX2DOS = unix2dos -D
@@ -435,8 +566,11 @@ $(distinst_win32): $(srcdir)/os_win32/installer.nsi distdir.mkdir $(FILES_WIN32)
            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"
+       date=`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h`; \
+       rev=`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h`; \
+       verstr="$(PACKAGE_VERSION) $$date $$rev "$(BUILD_INFO); \
+       echo "$$makensis /V2 /NOCD /DINPDIR=$(distdir_win32) /DOUTFILE=$(distinst_win32) /DVERSTR='$$verstr' $(srcdir)/os_win32/installer.nsi"; \
+       "$$makensis" /V2 /NOCD /DINPDIR="$(distdir_win32)" /DOUTFILE="$(distinst_win32)" /DVERSTR="$$verstr" "$(srcdir)/os_win32/installer.nsi"
 
 cleandist-win32:
        rm -rf $(distdir_win32) distdir.mkdir syslogevt.check
@@ -473,15 +607,34 @@ $(docdir_win32)/%.conf: $(srcdir)/%.conf
        $(UNIX2DOS) < $< > $@
        touch -r $< $@
 
-
-# Build config_vc6.h for MSVC 6 from MinGW config.h
-
-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\|STRINGS_H\|STRTOULL\|U*INT64_T\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */,' | \
-       sed 's,i.86-pc-mingw32,i686-pc-win32vc6,' > $@
+# Build non-console version of smartctl for GSmartControl.
+# The script below changes the word at offset 220 (Subsystem) from 3
+# (Console) to 2 (GUI) in a copy of smartctl.exe.
+# This will be changed when a tool (like 'editbin') is available in
+# the Cygwin distribution
+smartctl-nc.exe: smartctl.exe
+       @rm -f $@
+       cp -p smartctl.exe $@.tmp
+       @if test `od -A n -j 220 -N 2 -d $@.tmp` -eq 3; then :; \
+         else echo "invalid EXE header"; exit 1; fi
+       @echo "editbin /subsystem:windows $@.tmp"
+       @echo -ne '\002' | dd bs=1 seek=220 count=1 conv=notrunc of=$@.tmp 2>/dev/null
+       @if test `od -A n -j 220 -N 2 -d $@.tmp` -eq 2; then :; \
+         else echo "EXE patch failed"; exit 1; fi
+       mv -f $@.tmp $@
+
+
+# Build {config,svnversion}_vc8.h for MSVC8 from MinGW {config,svnversion}.h
+
+config-vc8: $(srcdir)/os_win32/config_vc8.h  $(srcdir)/os_win32/svnversion_vc8.h
+
+$(srcdir)/os_win32/config_vc8.h: config.h
+       sed '1i/* config_vc8.h.  Generated from config.h by Makefile.  */' $< | \
+       sed 's,^#define HAVE_\(ATTR_PACKED\|INTTYPES_H\|STDINT_H\|STRINGS_H\|STRTOULL\|U*INT64_T\|UNISTD_H\|WORKING_SNPRINTF\) 1$$,/* #undef HAVE_\1 */,' | \
+       sed 's,i.86-pc-mingw32,i686-pc-win32vc8,' > $@
+
+$(srcdir)/os_win32/svnversion_vc8.h: svnversion.h
+       cp svnversion.h $@
 
 endif
 
diff --git a/Makefile.in b/Makefile.in
deleted file mode 100644 (file)
index c2f7ce5..0000000
+++ /dev/null
@@ -1,1397 +0,0 @@
-# Makefile.in generated by automake 1.10 from Makefile.am.
-# @configure_input@
-
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006  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.81 2007/04/01 16:49:44 shattered Exp $
-#
-
-
-VPATH = @srcdir@
-pkgdatadir = $(datadir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-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.cpp   \
-@OS_WIN32_MINGW_TRUE@                os_win32/hostname_win32.h   \
-@OS_WIN32_MINGW_TRUE@                os_win32/hostname_win32.cpp \
-@OS_WIN32_MINGW_TRUE@                os_win32/syslog.h           \
-@OS_WIN32_MINGW_TRUE@                os_win32/syslog_win32.cpp
-
-
-# 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
-subdir = .
-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
-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 config.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.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 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) scsiata.$(OBJEXT) scsiprint.$(OBJEXT) \
-       utility.$(OBJEXT) $(am__objects_1)
-am__EXTRA_smartctl_SOURCES_DIST = 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 cciss.cpp cciss.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.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 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
-@OS_WIN32_MINGW_TRUE@am__objects_2 = 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) scsiata.$(OBJEXT) scsiprint.$(OBJEXT) \
-       utility.$(OBJEXT) $(am__objects_2)
-am__EXTRA_smartd_SOURCES_DIST = os_darwin.cpp os_darwin.h 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_solaris_ata.s os_win32.cpp os_generic.cpp \
-       os_generic.h cciss.cpp cciss.h posix/regcomp.c posix/regexec.c \
-       posix/regex_internal.c posix/regex_internal.h
-smartd_OBJECTS = $(am_smartd_OBJECTS)
-DEFAULT_INCLUDES = -I.@am__isrc@
-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 $@
-CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
-       $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
-CXXLD = $(CXX)
-CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(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-dvi-recursive install-exec-recursive \
-       install-html-recursive install-info-recursive \
-       install-pdf-recursive install-ps-recursive install-recursive \
-       installcheck-recursive installdirs-recursive pdf-recursive \
-       ps-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)
-RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive        \
-  distclean-recursive maintainer-clean-recursive
-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@
-AMTAR = @AMTAR@
-ASFLAGS = @ASFLAGS@
-AUTOCONF = @AUTOCONF@
-AUTOHEADER = @AUTOHEADER@
-AUTOMAKE = @AUTOMAKE@
-AWK = @AWK@
-CC = @CC@
-CCAS = @CCAS@
-CCASDEPMODE = @CCASDEPMODE@
-CCASFLAGS = @CCASFLAGS@
-CCDEPMODE = @CCDEPMODE@
-CFLAGS = @CFLAGS@
-CPPFLAGS = @CPPFLAGS@
-CXX = @CXX@
-CXXCPP = @CXXCPP@
-CXXDEPMODE = @CXXDEPMODE@
-CXXFLAGS = @CXXFLAGS@
-CYGPATH_W = @CYGPATH_W@
-DEFS = @DEFS@
-DEPDIR = @DEPDIR@
-ECHO_C = @ECHO_C@
-ECHO_N = @ECHO_N@
-ECHO_T = @ECHO_T@
-EGREP = @EGREP@
-EXEEXT = @EXEEXT@
-GREP = @GREP@
-INSTALL = @INSTALL@
-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@
-MAKEINFO = @MAKEINFO@
-MKDIR_P = @MKDIR_P@
-OBJEXT = @OBJEXT@
-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@
-STRIP = @STRIP@
-VERSION = @VERSION@
-abs_builddir = @abs_builddir@
-abs_srcdir = @abs_srcdir@
-abs_top_builddir = @abs_top_builddir@
-abs_top_srcdir = @abs_top_srcdir@
-ac_ct_CC = @ac_ct_CC@
-ac_ct_CXX = @ac_ct_CXX@
-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@
-builddir = @builddir@
-datadir = @datadir@
-datarootdir = @datarootdir@
-docdir = @docdir@
-dvidir = @dvidir@
-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@
-htmldir = @htmldir@
-includedir = @includedir@
-infodir = @infodir@
-initddir = @initddir@
-install_sh = @install_sh@
-libc_have_working_snprintf = @libc_have_working_snprintf@
-libdir = @libdir@
-libexecdir = @libexecdir@
-localedir = @localedir@
-localstatedir = @localstatedir@
-mandir = @mandir@
-mkdir_p = @mkdir_p@
-oldincludedir = @oldincludedir@
-os_deps = @os_deps@
-os_libs = @os_libs@
-pdfdir = @pdfdir@
-prefix = @prefix@
-program_transform_name = @program_transform_name@
-psdir = @psdir@
-releaseversion = @releaseversion@
-sbindir = @sbindir@
-sharedstatedir = @sharedstatedir@
-smartd_suffix = @smartd_suffix@
-smartmontools_release_date = @smartmontools_release_date@
-smartmontools_release_time = @smartmontools_release_time@
-srcdir = @srcdir@
-sysconfdir = @sysconfdir@
-target_alias = @target_alias@
-top_builddir = @top_builddir@
-top_srcdir = @top_srcdir@
-
-# Make sure .cpp takes precedence to avoid compiling old .c file
-SUFFIXES = .cpp .c .s .o
-AM_CPPFLAGS = -DSMARTMONTOOLS_SYSCONFDIR=\"$(sysconfdir)\"
-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 $(am__append_1)
-smartd_LDADD = @os_deps@ @os_libs@
-smartd_DEPENDENCIES = @os_deps@
-EXTRA_smartd_SOURCES = os_darwin.cpp os_darwin.h 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_solaris_ata.s os_win32.cpp os_generic.cpp \
-       os_generic.h cciss.cpp cciss.h $(am__append_2)
-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 $(am__append_3)
-smartctl_LDADD = @os_deps@ @os_libs@
-smartctl_DEPENDENCIES = @os_deps@
-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 cciss.cpp cciss.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 \
-             os_win32/installer.nsi                \
-             $(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'
-
-# 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'
-
-
-# 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@distinst_win32 = $(PACKAGE)-$(VERSION).win32-setup.exe
-@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
-@OS_WIN32_MINGW_TRUE@DOS2UNIX = dos2unix -U
-SUBDIRS = . examplescripts
-all: config.h
-       $(MAKE) $(AM_MAKEFLAGS) all-recursive
-
-.SUFFIXES:
-.SUFFIXES: .cpp .c .s .o .obj
-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) $(AM_MAKEFLAGS) 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)
-       $(CXXLINK) $(smartctl_OBJECTS) $(smartctl_LDADD) $(LIBS)
-smartd$(EXEEXT): $(smartd_OBJECTS) $(smartd_DEPENDENCIES) 
-       @rm -f smartd$(EXEEXT)
-       $(CXXLINK) $(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)/cciss.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)/scsiata.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@   $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@   mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@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@   $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@   mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@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@   $(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@   mv -f $(DEPDIR)/regex.Tpo $(DEPDIR)/regex.Po
-@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@   $(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@   mv -f $(DEPDIR)/regex.Tpo $(DEPDIR)/regex.Po
-@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@   $(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@   mv -f $(DEPDIR)/regcomp.Tpo $(DEPDIR)/regcomp.Po
-@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@   $(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@   mv -f $(DEPDIR)/regcomp.Tpo $(DEPDIR)/regcomp.Po
-@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@   $(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@   mv -f $(DEPDIR)/regexec.Tpo $(DEPDIR)/regexec.Po
-@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@   $(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@   mv -f $(DEPDIR)/regexec.Tpo $(DEPDIR)/regexec.Po
-@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@   $(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@   mv -f $(DEPDIR)/regex_internal.Tpo $(DEPDIR)/regex_internal.Po
-@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@   $(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@   mv -f $(DEPDIR)/regex_internal.Tpo $(DEPDIR)/regex_internal.Po
-@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`
-
-.cpp.o:
-@am__fastdepCXX_TRUE@  $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCXX_TRUE@  mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
-
-.cpp.obj:
-@am__fastdepCXX_TRUE@  $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCXX_TRUE@  mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-
-daemon_win32.o: os_win32/daemon_win32.cpp
-@am__fastdepCXX_TRUE@  $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT daemon_win32.o -MD -MP -MF $(DEPDIR)/daemon_win32.Tpo -c -o daemon_win32.o `test -f 'os_win32/daemon_win32.cpp' || echo '$(srcdir)/'`os_win32/daemon_win32.cpp
-@am__fastdepCXX_TRUE@  mv -f $(DEPDIR)/daemon_win32.Tpo $(DEPDIR)/daemon_win32.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='os_win32/daemon_win32.cpp' object='daemon_win32.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o daemon_win32.o `test -f 'os_win32/daemon_win32.cpp' || echo '$(srcdir)/'`os_win32/daemon_win32.cpp
-
-daemon_win32.obj: os_win32/daemon_win32.cpp
-@am__fastdepCXX_TRUE@  $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT daemon_win32.obj -MD -MP -MF $(DEPDIR)/daemon_win32.Tpo -c -o daemon_win32.obj `if test -f 'os_win32/daemon_win32.cpp'; then $(CYGPATH_W) 'os_win32/daemon_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/daemon_win32.cpp'; fi`
-@am__fastdepCXX_TRUE@  mv -f $(DEPDIR)/daemon_win32.Tpo $(DEPDIR)/daemon_win32.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='os_win32/daemon_win32.cpp' object='daemon_win32.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o daemon_win32.obj `if test -f 'os_win32/daemon_win32.cpp'; then $(CYGPATH_W) 'os_win32/daemon_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/daemon_win32.cpp'; fi`
-
-hostname_win32.o: os_win32/hostname_win32.cpp
-@am__fastdepCXX_TRUE@  $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT hostname_win32.o -MD -MP -MF $(DEPDIR)/hostname_win32.Tpo -c -o hostname_win32.o `test -f 'os_win32/hostname_win32.cpp' || echo '$(srcdir)/'`os_win32/hostname_win32.cpp
-@am__fastdepCXX_TRUE@  mv -f $(DEPDIR)/hostname_win32.Tpo $(DEPDIR)/hostname_win32.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='os_win32/hostname_win32.cpp' object='hostname_win32.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o hostname_win32.o `test -f 'os_win32/hostname_win32.cpp' || echo '$(srcdir)/'`os_win32/hostname_win32.cpp
-
-hostname_win32.obj: os_win32/hostname_win32.cpp
-@am__fastdepCXX_TRUE@  $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT hostname_win32.obj -MD -MP -MF $(DEPDIR)/hostname_win32.Tpo -c -o hostname_win32.obj `if test -f 'os_win32/hostname_win32.cpp'; then $(CYGPATH_W) 'os_win32/hostname_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/hostname_win32.cpp'; fi`
-@am__fastdepCXX_TRUE@  mv -f $(DEPDIR)/hostname_win32.Tpo $(DEPDIR)/hostname_win32.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='os_win32/hostname_win32.cpp' object='hostname_win32.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o hostname_win32.obj `if test -f 'os_win32/hostname_win32.cpp'; then $(CYGPATH_W) 'os_win32/hostname_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/hostname_win32.cpp'; fi`
-
-syslog_win32.o: os_win32/syslog_win32.cpp
-@am__fastdepCXX_TRUE@  $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT syslog_win32.o -MD -MP -MF $(DEPDIR)/syslog_win32.Tpo -c -o syslog_win32.o `test -f 'os_win32/syslog_win32.cpp' || echo '$(srcdir)/'`os_win32/syslog_win32.cpp
-@am__fastdepCXX_TRUE@  mv -f $(DEPDIR)/syslog_win32.Tpo $(DEPDIR)/syslog_win32.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='os_win32/syslog_win32.cpp' object='syslog_win32.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o syslog_win32.o `test -f 'os_win32/syslog_win32.cpp' || echo '$(srcdir)/'`os_win32/syslog_win32.cpp
-
-syslog_win32.obj: os_win32/syslog_win32.cpp
-@am__fastdepCXX_TRUE@  $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT syslog_win32.obj -MD -MP -MF $(DEPDIR)/syslog_win32.Tpo -c -o syslog_win32.obj `if test -f 'os_win32/syslog_win32.cpp'; then $(CYGPATH_W) 'os_win32/syslog_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/syslog_win32.cpp'; fi`
-@am__fastdepCXX_TRUE@  mv -f $(DEPDIR)/syslog_win32.Tpo $(DEPDIR)/syslog_win32.Po
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     source='os_win32/syslog_win32.cpp' object='syslog_win32.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o syslog_win32.obj `if test -f 'os_win32/syslog_win32.cpp'; then $(CYGPATH_W) 'os_win32/syslog_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/syslog_win32.cpp'; fi`
-
-.s.o:
-       $(CCASCOMPILE) -c -o $@ $<
-
-.s.obj:
-       $(CCASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
-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):
-       @failcom='exit 1'; \
-       for f in x $$MAKEFLAGS; do \
-         case $$f in \
-           *=* | --[!k]*);; \
-           *k*) failcom='fail=yes';; \
-         esac; \
-       done; \
-       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) \
-         || eval $$failcom; \
-       done; \
-       if test "$$dot_seen" = "no"; then \
-         $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
-       fi; test -z "$$fail"
-
-$(RECURSIVE_CLEAN_TARGETS):
-       @failcom='exit 1'; \
-       for f in x $$MAKEFLAGS; do \
-         case $$f in \
-           *=* | --[!k]*);; \
-           *k*) failcom='fail=yes';; \
-         esac; \
-       done; \
-       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) \
-         || eval $$failcom; \
-       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)
-       test -d $(distdir) || mkdir $(distdir)
-       @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
-       list='$(DISTFILES)'; \
-         dist_files=`for file in $$list; do echo $$file; done | \
-         sed -e "s|^$$srcdirstrip/||;t" \
-             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
-       case $$dist_files in \
-         */*) $(MKDIR_P) `echo "$$dist_files" | \
-                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
-                          sort -u` ;; \
-       esac; \
-       for file in $$dist_files; do \
-         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
-         if test -d $$d/$$file; then \
-           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
-           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" \
-               am__remove_distdir=: \
-               am__skip_length_check=: \
-               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 $(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 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$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-dvi: install-dvi-recursive
-
-install-exec-am: install-sbinPROGRAMS install-sysconfDATA
-
-install-html: install-html-recursive
-
-install-info: install-info-recursive
-
-@OS_SOLARIS_FALSE@install-man: install-man5 install-man8
-
-install-pdf: install-pdf-recursive
-
-install-ps: install-ps-recursive
-
-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-initdDATA uninstall-man \
-       uninstall-sbinPROGRAMS uninstall-sysconfDATA
-
-@OS_SOLARIS_FALSE@uninstall-man: uninstall-man5 uninstall-man8
-
-.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) install-am \
-       install-strip
-
-.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
-       all all-am am--refresh check check-am clean clean-generic \
-       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-tags distcleancheck distdir distuninstallcheck dvi \
-       dvi-am html html-am info info-am install install-am \
-       install-data install-data-am install-docsDATA install-dvi \
-       install-dvi-am install-exec install-exec-am install-html \
-       install-html-am install-info install-info-am install-initdDATA \
-       install-man install-man5 install-man8 install-pdf \
-       install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
-       install-strip install-sysconfDATA installcheck installcheck-am \
-       installdirs installdirs-am maintainer-clean \
-       maintainer-clean-generic mostlyclean mostlyclean-compile \
-       mostlyclean-generic pdf pdf-am ps ps-am tags tags-recursive \
-       uninstall uninstall-am uninstall-docsDATA 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
-
-@OS_WIN32_MINGW_TRUE@%.5.html: %.5
-@OS_WIN32_MINGW_TRUE@  $(DOS2UNIX) < $< | $(MAN2HTML) | $(FIXHTML) > $@
-
-@OS_WIN32_MINGW_TRUE@%.8.html: %.8
-@OS_WIN32_MINGW_TRUE@  $(DOS2UNIX) < $< | $(MAN2HTML) | $(FIXHTML) > $@
-
-@OS_WIN32_MINGW_FALSE@%.5.html: %.5
-@OS_WIN32_MINGW_FALSE@ $(MAN2HTML) $< | $(FIXHTML) > $@
-
-@OS_WIN32_MINGW_FALSE@%.8.html: %.8
-@OS_WIN32_MINGW_FALSE@ $(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@install-win32: $(distinst_win32)
-@OS_WIN32_MINGW_TRUE@  ./$(distinst_win32)
-
-@OS_WIN32_MINGW_TRUE@installer-win32: $(distinst_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) .
-
-# Build NSIS installer, try to locate makensis in default location first
-@OS_WIN32_MINGW_TRUE@$(distinst_win32): $(srcdir)/os_win32/installer.nsi distdir.mkdir $(FILES_WIN32) syslogevt.check
-@OS_WIN32_MINGW_TRUE@  @makensis="$(MAKENSIS)"; if [ -z "$$makensis" ]; then \
-@OS_WIN32_MINGW_TRUE@    if [ ! -z "$$PROGRAMFILES" ] && "$$PROGRAMFILES/NSIS/makensis" /VERSION >/dev/null 2>&1; then \
-@OS_WIN32_MINGW_TRUE@      makensis="$$PROGRAMFILES/NSIS/makensis"; \
-@OS_WIN32_MINGW_TRUE@    elif makensis /VERSION >/dev/null 2>&1; then \
-@OS_WIN32_MINGW_TRUE@      makensis=makensis; \
-@OS_WIN32_MINGW_TRUE@    else \
-@OS_WIN32_MINGW_TRUE@      echo 'makensis: command not found. Please download and install NSIS' 1>&2; \
-@OS_WIN32_MINGW_TRUE@      echo 'from http://nsis.sourceforge.net/Download' 1>&2; exit 1; \
-@OS_WIN32_MINGW_TRUE@    fi; \
-@OS_WIN32_MINGW_TRUE@  fi; \
-@OS_WIN32_MINGW_TRUE@  echo "$$makensis /V2 /NOCD /DINPDIR=$(distdir_win32) /DOUTFILE=$(distinst_win32) $(srcdir)/os_win32/installer.nsi"; \
-@OS_WIN32_MINGW_TRUE@  "$$makensis" /V2 /NOCD /DINPDIR="$(distdir_win32)" /DOUTFILE="$(distinst_win32)" "$(srcdir)/os_win32/installer.nsi"
-
-@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)/%.html: %.html
-@OS_WIN32_MINGW_TRUE@  $(UNIX2DOS) < $< > $@
-@OS_WIN32_MINGW_TRUE@  touch -r $< $@
-
-@OS_WIN32_MINGW_TRUE@$(docdir_win32)/%.txt: %.txt
-@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\|STRINGS_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 bc3683e7d6f40bb8aea87af3a3cb88d6a0fae0e5..0591864c0fc12d58469bc02e66c7c95ff5b44e1a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,16 +1,61 @@
 smartmontools NEWS
 ------------------
-CVS ID: $Id: NEWS,v 1.35 2008/03/10 10:44:30 ballen4705 Exp $
+$Id: NEWS 2844 2009-07-18 12:59:21Z chrfranke $
 
 The most up-to-date version of this file is:
-http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/NEWS?view=markup
+http://smartmontools.svn.sourceforge.net/viewvc/smartmontools/trunk/smartmontools/NEWS?view=markup
+
+Date <Not released yet, please try current SVN>
+Summary: smartmontools release 5.39 (UNSTABLE/EXPERIMENTAL)
+-----------------------------------------------------------
+- Sourcecode repository moved from CVS to SVN
+- Support for USB devices with Cypress, JMicron and Sunplus USB bridges
+- USB device type autodetection for some devices on Linux and Windows
+  (http://smartmontools.wiki.sourceforge.net/overview_USB-Support)
+- Support for Areca controllers on Linux
+- Support for MegaRAID controllers on Linux
+- Support for HighPoint RocketRAID controllers on FreeBSD
+- Support RAID controllers using /dev/pass devices on FreeBSD
+- Support CHECK_POWER_MODE and WRITE_LOG on FreeBSD
+- Support for up to 128 devices on 3ware controllers
+- smartctl option '-l xerror' to print ATA SMART Extended Comprehensive
+  Error Log
+- smartctl option '-l xselftest' to print ATA SMART Extended Self-test Log
+- smartctl option '-l sataphy' to print SATA Phy Event Counters
+- smartctl options '-l gplog,...' and '-l smartlog,...' to print any log page
+- smartctl prints SCSI load/unload cycle counts
+- Improve display of huge raw values of some SMART attributes
+- Option '-d sat+TYPE' to use SAT with controllers which require '-d TYPE'
+- Many additions to drive database
+- New simplified syntax for drive database
+- Option '-B FILE' to read drive database from a file
+- Configure option to add drive database file to distribution
+- smartd can now handle attributes 197 and 198 with increasing raw values
+- smartd logs changes of self-test execution status
+- smartd directive '-n powermode,N' to limit the number of skipped checks
+- smartd supports scheduled Selective Self-Tests
+- Self-tests scheduled during downtime are run after next startup
+- Option '-s PREFIX' to store smartd internal state until next startup
+- Configure option to enable the above by default
+- Change to an object oriented interface to access ATA and SCSI devices
+- Linux and Win32 modules migrated to new interface
+- Rework of smartd data structures
+- Checkin date and SVN revision and optional BUILD_INFO printed in version info
+- Better support for gSmartControl on Windows
+- SELinux fixes to 3ware device node creation
+- Fix CCISS file descriptor leak on FreeBSD
+- Compile fixes for Solaris and FreeBSD
+- Use getaddrinfo() instead of gethostbyname() to support IPv6
+- C++ Support for QNX Target, already tested for QNX 6.3.2 on x86 and
+  armle target
+- Additional support for Samsung MLC flash drives
+
 
 Date 2008-03-10
 Summary: smartmontools release 5.38 (STABLE)
 --------------------------------------------
 This is a stable release of smartmontools.  In addition to changes
 below, it includes:
-
  - Libata/Marvell driver devices no longer need explicit '-d' switch
  - DEVICESCAN automatically detects libata/marvell driver SATA devices 
  - Fixed auto-offline/autosave support in FreeBSD
@@ -38,7 +83,7 @@ below, it includes:
  - Additional command line options for selective self-tests
  - Compilation fixes for various platforms.
 
-See CHANGELOG for more details, or smartmontools CVS for still further
+See CHANGELOG for more details, or smartmontools SVN for still further
 details.
 
 Date 2006-12-20
diff --git a/README b/README
index 646fc707f1f34d55e7ef198727df7afd0a882d07..aa7278ec1215cefc809806ae50b3a0b79a36d98f 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.57 2008/03/04 22:09:47 ballen4705 Exp $
+$Id: README 2844 2009-07-18 12:59:21Z chrfranke $
 
 == HOME ==
 The home for smartmontools is located at:
@@ -19,7 +19,7 @@ You will find a mailing list for support and other questions at:
 
 
 == COPYING ==
-Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+Copyright (C) 2002-9 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
@@ -71,32 +71,19 @@ Source tarballs
 
 http://sourceforge.net/project/showfiles.php?group_id=64297
 
-CVS
+SVN
 ---
 
-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
+svn co https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk/smartmontools smartmontools
 
-This will create a subdirectory called sm5/ containing the code.
+This will create a subdirectory called smartmontools containing the code.
 
-To instead get the 5.1-16 release:
+To instead get the 5.38 release:
 
-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:
-
-cd sm5
-cvs up -r RELEASE_5_1_18
-
-To update any tagged release to the latest development code:
-
-cd sm5
-cvs up -A
+svn co https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/tags/RELEASE_5_38/sm5 smartmontools
 
 You can see what the different tags are by looking at
-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".
+http://smartmontools.svn.sourceforge.net/viewvc/smartmontools/tags/
 
 == BUILDING/INSTALLING SMARTMONTOOLS ==
 
diff --git a/TODO b/TODO
index 22d814bbcf33a7691debe9635ed3a919e02a3ee6..e49a9f3780592f5ffa5a7e7b8ebbff5a24d51ab7 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,34 +1,6 @@
 TODO list for smartmontools:
 
-$Id: TODO,v 1.64 2007/09/03 19:36:58 chrfranke Exp $
-
-SATA devices under Linux
-------------------------
-These work OK if you use the standard IDe drivers in drivers/ide.
-
-The situation is more complicated if you use libata.
-
-Prior to Linux kernel version 2.6.15-rc1, libata does not support the
-HDIO_DRIVE_TASK, HDIO_DRIVE_CMD, and HDIO_DRIVE_TASKFILE ioctl()s that
-are needed by smartmontools.  Support for HDIO_DRIVE_TASK and
-HDIO_DRIVE_CMD was added into libata by Jeff Garzik starting with
-Linux kernel version 2.6.15-rc1.  Starting with this version, you can
-use all the smartmontools commands apart from initiating selective
-self-tests (which also requires HDIO_DRIVE_TASKFILE).  A typical
-command line might look like this:
-  smartctl -a -d ata /dev/sda
-The '-d ata' is required, since otherwise smartmontools will assume
-that the device is SCSI, not ATA/SATA.  Similar syntax will work with
-smartd.
-
-You may be able to patch earlier versions of libata. Please search the
-Linux Kernel Mailing list to find this patch, or look at the thread:
-http://groups.google.de/groups?hl=en&lr=&ie=UTF-8&threadm=2yYBY-4HB-55%40gated-at.bofh.it&rnum=3&prev=/groups%3Fq%3Dsmartmontools%26hl%3Den%26lr%3D%26ie%3DUTF-8%26scoring%3Dd%26selm%3D2yYBY-4HB-55%2540gated-at.bofh.it%26rnum%3D3
-To use this, just use (for example) 'smartctl -a -d ata /dev/sda'.
-
-Since this looks like this patch will become standard, we need to add something
-to smartmontools to automatically recognize the libata, and add the '-d ata'
-automatically.
+$Id: TODO 2845 2009-07-18 13:25:18Z chrfranke $
 
 USB devices under Linux
 -----------------------
@@ -38,42 +10,19 @@ command sets.  Work on improving the detection and bail-out procedures
 for these flawed devices, so that the user sees an informative error
 message and smartd/smartctl don't hang.
 
-ATA-4 (no kidding!)
--------------------
-smartctl: add another -t TESTTYPE option to accomodate old-style ATA-4
-IBM disks (ATA-4 has no self-test commands). See IBM S25L-2426-02 OEM
-HARD DISK DRIVE SPECIFICATIONS for DBCA-203240/204860/206480 2.5-Inch
-Hard Disk Drive with ATA Interface Revision (1.0)
-http://www.hgst.com/tech/techlib.nsf/techdocs/85256AB8006A31E587256A7D00642A1D/$file/dbca_sp.pdf
-section 12.30.1.5 for details.  These disks offer no self-test option,
-and the -t offline command only tests a small part of the disk (a
-'segment').  We need a -t multioffline that:
- (1) issues auto offline immediate command (tests ONE segment)
- (2) waits until estimated completion time
- (3) tests if off-line data collection status is set to 0x02 (all
-     segments completed)
- (4) if not, return to (1)
-
-ATA-6/7
--------
-Support extended error logs
-Support extended self-test logs
+ATA-8
+-----
+Add ability to print counters/values from "Device Statistics" pages
+(General Purpose Log address 0x04).
+See ATA ACS-2 T13/2015-D Revision 1, Section A.5.
 
 smartctl/smartd
 ---------------
 Add additional -v options (corresponding to comments in
 atacmds.c:ataPrintSmartAttribName().
 
-Add interface to Megaraid ATA RAID controllers (Erik)
-
 smartctl: 
 ---------
-Add command line option to issue SMART SAVE ATTRIBUTE VALUES command
-Feature Register value ATA_SMART_SAVE 0xd3
-
-Perhaps modify the -q option (quiet mode) so that it only warns of ATA
-errors if they have (say) taken place in the last 168 hours (week).
-
 Parse and print additional Attribute flag meanings (IBM ones, eg
 performance etc).  These are now documented in atacmds.h -- we just
 need to modify the format of the Attribute table.
@@ -101,6 +50,11 @@ that can be monitored.
 
 Packaging
 ---------
+Rework 'do_release' script for SVN.
+
 Under freebsd and solaris, the following are wrong:
 smartd.conf: has linux device paths
 smart*.in  : man pages have (mostly) linux device paths
+
+configure packages with --enable-drivedb (?)
+configure packages with --enable-savestates (?)
index 2abfe7d0d81a9ea2434672587d0b2ab732383d68..658af7e04bf16495f539c0d88b98335cc3e6404c 100644 (file)
--- a/WARNINGS
+++ b/WARNINGS
@@ -1,7 +1,7 @@
-$Id: WARNINGS,v 1.33 2006/05/19 16:33:33 chrfranke Exp $
+$Id: WARNINGS 2844 2009-07-18 12:59:21Z chrfranke $
 
 The most recent version of this file can be found here:
-http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/WARNINGS?view=markup
+http://smartmontools.svn.sourceforge.net/viewvc/smartmontools/trunk/smartmontools/WARNINGS?view=markup
 
 The following are reports of serious problems (eg system lockup) which
 were due to smartmontools.  There are DARWIN, LINUX, FREEBSD, SOLARIS
@@ -99,17 +99,7 @@ SOLARIS
 CYGWIN and WINDOWS
 ------------------
 
-SYSTEM:   Any Windows NT4, 2000 or XP system.
-PROBLEM:  Use of undocumented system calls for IDE/ATA read log
-          (smartctl -l, --log, -a, --all) may affect system stability.
-REPORTER: Christian Franke <smartmontools-support@lists.sourceforge.net>
-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
-          command SCSIOP_ATA_PASSTHROUGH (0xCC).
-          2000/XP: Undocumented IOCTL_IDE_PASS_THROUGH.
+[No problem reports yet.]
 
 
 DARWIN
diff --git a/aclocal.m4 b/aclocal.m4
deleted file mode 100644 (file)
index ed5812a..0000000
+++ /dev/null
@@ -1,925 +0,0 @@
-# generated automatically by aclocal 1.10 -*- Autoconf -*-
-
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-# 2005, 2006  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.
-
-m4_if(m4_PACKAGE_VERSION, [2.61],,
-[m4_fatal([this file was generated for autoconf 2.61.
-You have another version of autoconf.  If you want to use that,
-you should regenerate the build system entirely.], [63])])
-
-# Copyright (C) 2002, 2003, 2005, 2006  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.
-
-# 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.
-# (This private macro should not be called outside this file.)
-AC_DEFUN([AM_AUTOMAKE_VERSION],
-[am__api_version='1.10'
-dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
-dnl require some minimum version.  Point them to the right macro.
-m4_if([$1], [1.10], [],
-      [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
-])
-
-# _AM_AUTOCONF_VERSION(VERSION)
-# -----------------------------
-# aclocal traces this macro to find the Autoconf version.
-# This is a private macro too.  Using m4_define simplifies
-# the logic in aclocal, which can simply ignore this definition.
-m4_define([_AM_AUTOCONF_VERSION], [])
-
-# AM_SET_CURRENT_AUTOMAKE_VERSION
-# -------------------------------
-# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
-# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
-AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.10])dnl
-_AM_AUTOCONF_VERSION(m4_PACKAGE_VERSION)])
-
-# Figure out how to run the assembler.                      -*- Autoconf -*-
-
-# Copyright (C) 2001, 2003, 2004, 2005, 2006  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.
-
-# serial 5
-
-# 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_IF_OPTION([no-dependencies],, [_AM_DEPENDENCIES([CCAS])])dnl
-])
-
-# AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
-
-# Copyright (C) 2001, 2003, 2005  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.
-
-# 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, 2005, 2006
-# 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.
-
-# serial 8
-
-# 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])dnl
-AC_SUBST([$1_FALSE])dnl
-_AM_SUBST_NOTMAKE([$1_TRUE])dnl
-_AM_SUBST_NOTMAKE([$1_FALSE])dnl
-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])])
-
-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
-# 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.
-
-# serial 9
-
-# 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], UPC,  [depcc="$UPC"  am_compiler_list=],
-       [$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/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
-       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])dnl
-_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
-])
-
-# Generate code to set up dependency tracking.              -*- Autoconf -*-
-
-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
-# 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.
-
-#serial 3
-
-# _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.
-  # Grep'ing the whole file is not good either: AIX grep has a line
-  # limit of 2048, but all sed's we know have understand at least 4000.
-  if sed 10q "$mf" | grep '^#.*generated by automake' > /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"])
-])
-
-# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
-# 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.
-
-# serial 8
-
-# 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 -*-
-
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-# 2005, 2006 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.
-
-# serial 12
-
-# 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.
-
-# 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.60])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
-if test "`cd $srcdir && pwd`" != "`pwd`"; then
-  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
-  # is not polluted with repeated "-I."
-  AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
-  # test to see if srcdir already configured
-  if test -f $srcdir/config.status; then
-    AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
-  fi
-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
-dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
-m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
-  [m4_fatal([AC_INIT should be called with package and version arguments])])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
-AC_PROVIDE_IFELSE([AC_PROG_OBJC],
-                  [_AM_DEPENDENCIES(OBJC)],
-                  [define([AC_PROG_OBJC],
-                          defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])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])
-
-# Copyright (C) 2001, 2003, 2005  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.
-
-# AM_PROG_INSTALL_SH
-# ------------------
-# Define $install_sh.
-AC_DEFUN([AM_PROG_INSTALL_SH],
-[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
-install_sh=${install_sh-"\$(SHELL) $am_aux_dir/install-sh"}
-AC_SUBST(install_sh)])
-
-# Copyright (C) 2003, 2005  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.
-
-# serial 2
-
-# 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.         -*- Autoconf -*-
-# From Jim Meyering
-
-# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005
-# 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.
-
-# serial 4
-
-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, 2005  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.
-
-# serial 3
-
-# 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
-])
-
-# Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
-
-# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005
-# 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.
-
-# serial 5
-
-# 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
-AC_REQUIRE_AUX_FILE([missing])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
-])
-
-# Copyright (C) 2003, 2004, 2005, 2006  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.
-
-# AM_PROG_MKDIR_P
-# ---------------
-# Check for `mkdir -p'.
-AC_DEFUN([AM_PROG_MKDIR_P],
-[AC_PREREQ([2.60])dnl
-AC_REQUIRE([AC_PROG_MKDIR_P])dnl
-dnl Automake 1.8 to 1.9.6 used to define mkdir_p.  We now use MKDIR_P,
-dnl while keeping a definition of mkdir_p for backward compatibility.
-dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
-dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
-dnl Makefile.ins that do not define MKDIR_P, so we do our own
-dnl adjustment using top_builddir (which is defined more often than
-dnl MKDIR_P).
-AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
-case $mkdir_p in
-  [[\\/$]]* | ?:[[\\/]]*) ;;
-  */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
-esac
-])
-
-# Helper functions for option handling.                     -*- Autoconf -*-
-
-# Copyright (C) 2001, 2002, 2003, 2005  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.
-
-# serial 3
-
-# _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.    -*- Autoconf -*-
-
-# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005
-# 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.
-
-# serial 4
-
-# 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)])
-
-# Copyright (C) 2001, 2003, 2005  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.
-
-# AM_PROG_INSTALL_STRIP
-# ---------------------
-# 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="\$(install_sh) -c -s"
-AC_SUBST([INSTALL_STRIP_PROGRAM])])
-
-# Copyright (C) 2006  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.
-
-# _AM_SUBST_NOTMAKE(VARIABLE)
-# ---------------------------
-# Prevent Automake from outputing VARIABLE = @VARIABLE@ in Makefile.in.
-# This macro is traced by Automake.
-AC_DEFUN([_AM_SUBST_NOTMAKE])
-
-# Check how to create a tarball.                            -*- Autoconf -*-
-
-# Copyright (C) 2004, 2005  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.
-
-# serial 2
-
-# _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
-
index d989a515428f5f95b9d0fec22367b56382c5816a..ecd483bbef39bf7c2e7a657ff1d01d31bce56802 100644 (file)
@@ -26,7 +26,7 @@
 
 #define COMMAND_TABLE_SIZE 256
 
-const char *atacmdnames_c_cvsid="$Id: atacmdnames.cpp,v 1.16 2008/03/04 22:09:47 ballen4705 Exp $" ATACMDNAMES_H_CVSID;
+const char *atacmdnames_c_cvsid="$Id: atacmdnames.cpp,v 1.17 2008/03/29 23:41:28 shattered Exp $" ATACMDNAMES_H_CVSID;
 
 const char cmd_reserved[]        = "[RESERVED]";
 const char cmd_vendor_specific[] = "[VENDOR SPECIFIC]";
@@ -289,7 +289,7 @@ const char *command_table[COMMAND_TABLE_SIZE] = {
                             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",
+  "FLUSH CACHE EXT",
   cmd_reserved,
   "IDENTIFY DEVICE",
   "MEDIA EJECT",
index b113373a40961254ecebacbb1dbedc6a0f438181..b5ecd02a2885c3e7bb817c7191cd7642376bf76b 100644 (file)
@@ -3,7 +3,8 @@
  * 
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-9 Christian Franke <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
  * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org>
  *
 #include "config.h"
 #include "int64.h"
 #include "atacmds.h"
-#include "scsiata.h"
 #include "extern.h"
 #include "utility.h"
+#include "dev_ata_cmd_set.h" // for parsed_ata_device
 
-const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.190 2008/03/04 22:09:47 ballen4705 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;
+const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp 2872 2009-08-08 19:54:28Z chrfranke $"
+                                 ATACMDS_H_CVSID;
 
 // for passing global control variables
 extern smartmonctrl *con;
 
+#define SMART_CYL_LOW  0x4F
+#define SMART_CYL_HI   0xC2
+
+// SMART RETURN STATUS yields SMART_CYL_HI,SMART_CYL_LOW to indicate drive
+// is healthy and SRET_STATUS_HI_EXCEEDED,SRET_STATUS_MID_EXCEEDED to
+// indicate that a threshhold exceeded condition has been detected.
+// Those values (byte pairs) are placed in ATA register "LBA 23:8".
+#define SRET_STATUS_HI_EXCEEDED 0x2C
+#define SRET_STATUS_MID_EXCEEDED 0xF4
+
 // 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
@@ -55,7 +63,7 @@ extern smartmonctrl *con;
 #define NOVAL_1                 0xffff
 /* word 81: minor version number */
 #define MINOR_MAX 0x22
-const char *minor_str[] = {                     /* word 81 value: */
+static const char * const 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       */
@@ -99,7 +107,7 @@ const char *minor_str[] = {                     /* word 81 value: */
 // vendor values in sensible format.
 
 // Negative values below are because it doesn't support SMART
-const int actual_ver[] = { 
+static const int actual_ver[] = { 
   /* word 81 value: */
   0,            /* 0x0000       WARNING:        */
   1,            /* 0x0001       WARNING:        */
@@ -140,50 +148,54 @@ const int actual_ver[] = {
 
 // 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
+// 1 -- if needed, modify ataPrintSmartAttribRawValue()
+// 2 -  if needed, modify ataPrintSmartAttribName()
+// 3 -- add drive in question into builtin_knowndrives[] table in knowndrives.cpp
+// 4 -- update smartctl.8
+// 5 -- update smartd.8
+// 6 -- do "make smartd.conf.5" to update smartd.conf.5
+// 7 -- update CHANGELOG file
+
+struct vendor_attr_arg_entry
+{
+  unsigned char id;  // attribute ID, 0 for all
+  const char * name; // attribute name
+  unsigned char val; // value for attribute defs array
+};
+
+// The order of these entries is (only) relevant for '-v help' output.
+const vendor_attr_arg_entry vendor_attribute_args[] = {
+  {  9,"halfminutes", 4},
+  {  9,"minutes", 1},
+  {  9,"seconds", 3},
+  {  9,"temp", 2},
+  {192,"emergencyretractcyclect", 1},
+  {193,"loadunload", 1},
+  {194,"10xCelsius", 1},
+  {194,"unknown", 2},
+  {197,"increasing", 1},
+  {198,"offlinescanuncsectorct", 2},
+  {198,"increasing", 1},
+  {200,"writeerrorcount", 1},
+  {201,"detectedtacount", 1},
+  {220,"temp", 1},
+  {  0,"raw8", 253},
+  {  0,"raw16", 254},
+  {  0,"raw48", 255},
 };
 
+const unsigned num_vendor_attribute_args = sizeof(vendor_attribute_args)/sizeof(vendor_attribute_args[0]);
+
+// Get ID and increase flag of current pending or offline
+// uncorrectable attribute.
+unsigned char get_unc_attr_id(bool offline, const unsigned char * defs,
+                              bool & increase)
+{
+  unsigned char id = (!offline ? 197 : 198);
+  increase = (defs[id] == 1);
+  return id;
+}
+
 // 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
@@ -213,275 +225,59 @@ const char *SelfTestFailureCodeName(unsigned char which){
 // 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)
+int parse_attribute_def(const char * pair, unsigned char * defs)
 {
-  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;
+  int id = 0, nc = -1;
+  char name[32+1];
+  if (pair[0] == 'N') {
+    // "N,name"
+    if (!(sscanf(pair, "N,%32s%n", name, &nc) == 1 && nc == (int)strlen(pair)))
+      return 1;
+  }
+  else {
+    // "attr,name"
+    if (!(   sscanf(pair, "%d,%32s%n", &id, name, &nc) == 2
+          && 1 <= id && id <= 255 && nc == (int)strlen(pair)))
+      return 1;
+  }
+
+  // Find pair
+  unsigned i;
+  for (i = 0; ; i++) {
+    if (i >= num_vendor_attribute_args)
+      return 1; // Not found
+    if (   (!vendor_attribute_args[i].id || vendor_attribute_args[i].id == id)
+        && !strcmp(vendor_attribute_args[i].name, name)                       )
+      break;
   }
-  *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;
+  if (!id) {
+    // "N,name" -> set all entries
+    for (int j = 0; j < MAX_ATTRIBUTE_NUM; j++)
+      defs[j] = vendor_attribute_args[i].val;
   }
+  else
+    // "attr,name"
+    defs[id] = vendor_attribute_args[i].val;
 
-  return t;
+  return 0;
 }
 
-// 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
+// Return a multiline string containing a list of valid arguments for
+// parse_attribute_def().  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");
+std::string create_vendor_attribute_arg_list()
+{
+  std::string s;
+  for (unsigned i = 0; i < num_vendor_attribute_args; i++) {
+    if (i > 0)
+      s += '\n';
+    if (!vendor_attribute_args[i].id)
+      s += "\tN,";
+    else
+      s += strprintf("\t%d,", vendor_attribute_args[i].id);
+    s += vendor_attribute_args[i].name;
   }
-
-  free((char **)sorted);
-
-  // Return a pointer to the string
   return s;
 }
 
@@ -533,7 +329,7 @@ static void invalidate_serno(ata_identify_device * id){
 #endif
 }
 
-static char *commandstrings[]={
+static const char * const commandstrings[]={
   "SMART ENABLE",
   "SMART DISABLE",
   "SMART AUTOMATIC ATTRIBUTE SAVE",
@@ -551,6 +347,33 @@ static char *commandstrings[]={
   "WARNING (UNDEFINED COMMAND -- CONTACT DEVELOPERS AT " PACKAGE_BUGREPORT ")\n"
 };
 
+
+static const char * preg(const ata_register & r, char * buf)
+{
+  if (!r.is_set())
+    //return "n/a ";
+    return "....";
+  sprintf(buf, "0x%02x", r.val()); return buf;
+}
+
+void print_regs(const char * prefix, const ata_in_regs & r, const char * suffix = "\n")
+{
+  char bufs[7][4+1+13];
+  pout("%s FR=%s, SC=%s, LL=%s, LM=%s, LH=%s, DEV=%s, CMD=%s%s", prefix,
+    preg(r.features, bufs[0]), preg(r.sector_count, bufs[1]), preg(r.lba_low, bufs[2]),
+    preg(r.lba_mid, bufs[3]), preg(r.lba_high, bufs[4]), preg(r.device, bufs[5]),
+    preg(r.command, bufs[6]), suffix);
+}
+
+void print_regs(const char * prefix, const ata_out_regs & r, const char * suffix = "\n")
+{
+  char bufs[7][4+1+13];
+  pout("%sERR=%s, SC=%s, LL=%s, LM=%s, LH=%s, DEV=%s, STS=%s%s", prefix,
+    preg(r.error, bufs[0]), preg(r.sector_count, bufs[1]), preg(r.lba_low, bufs[2]),
+    preg(r.lba_mid, bufs[3]), preg(r.lba_high, bufs[4]), preg(r.device, bufs[5]),
+    preg(r.status, bufs[6]), suffix);
+}
+
 static void prettyprint(const unsigned char *p, const char *name){
   pout("\n===== [%s] DATA START (BASE-16) =====\n", name);
   for (int i=0; i<512; i+=16, p+=16)
@@ -563,14 +386,11 @@ static void prettyprint(const unsigned char *p, const char *name){
   pout("===== [%s] DATA END (512 Bytes) =====\n\n", name);
 }
 
-static int parsedev_command_interface(int fd, smart_command_set command, int select, char * data);
-
 // 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;
-
+int smartcommandhandler(ata_device * device, smart_command_set command, int select, char *data){
+  // TODO: Rework old stuff below
   // This conditional is true for commands that return data
   int getsdata=(command==PIDENTIFY || 
                 command==IDENTIFY || 
@@ -590,7 +410,7 @@ int smartcommandhandler(int device, smart_command_set command, int select, char
                          command==IMMEDIATE_OFFLINE ||
                          command==WRITE_LOG);
                   
-    pout("\nREPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]);
+    pout("\nREPORT-IOCTL: Device=%s Command=%s", device->get_dev_name(), commandstrings[command]);
     if (usesparam)
       pout(" InputParameter=%d\n", select);
     else
@@ -615,34 +435,135 @@ int smartcommandhandler(int device, smart_command_set command, int select, char
 
   // if requested, pretty-print the input data structure
   if (con->reportataioctl>1 && sendsdata)
+    //pout("REPORT-IOCTL: Device=%s Command=%s\n", device->get_dev_name(), commandstrings[command]);
     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;
-  case CONTROLLER_PARSEDEV:
-    retval=parsedev_command_interface(device, command, select, data);
-    break;
-  default:
-    retval=ata_command_interface(device, command, select, data);
+  int retval = -1;
+  {
+    ata_cmd_in in;
+    // Set common register values
+    switch (command) {
+      default: // SMART commands
+        in.in_regs.command = ATA_SMART_CMD;
+        in.in_regs.lba_high = SMART_CYL_HI; in.in_regs.lba_mid = SMART_CYL_LOW;
+        break;
+      case IDENTIFY: case PIDENTIFY: case CHECK_POWER_MODE: // Non SMART commands
+        break;
+    }
+    // Set specific values
+    switch (command) {
+      case IDENTIFY:
+        in.in_regs.command = ATA_IDENTIFY_DEVICE;
+        in.set_data_in(data, 1);
+        break;
+      case PIDENTIFY:
+        in.in_regs.command = ATA_IDENTIFY_PACKET_DEVICE;
+        in.set_data_in(data, 1);
+        break;
+      case CHECK_POWER_MODE:
+        in.in_regs.command = ATA_CHECK_POWER_MODE;
+        in.out_needed.sector_count = true; // Powermode returned here
+        break;
+      case READ_VALUES:
+        in.in_regs.features = ATA_SMART_READ_VALUES;
+        in.set_data_in(data, 1);
+        break;
+      case READ_THRESHOLDS:
+        in.in_regs.features = ATA_SMART_READ_THRESHOLDS;
+        in.in_regs.lba_low = 1; // TODO: CORRECT ???
+        in.set_data_in(data, 1);
+        break;
+      case READ_LOG:
+        in.in_regs.features = ATA_SMART_READ_LOG_SECTOR;
+        in.in_regs.lba_low = select;
+        in.set_data_in(data, 1);
+        break;
+      case WRITE_LOG:
+        in.in_regs.features = ATA_SMART_WRITE_LOG_SECTOR;
+        in.in_regs.lba_low = select;
+        in.set_data_out(data, 1);
+        break;
+      case ENABLE:
+        in.in_regs.features = ATA_SMART_ENABLE;
+        in.in_regs.lba_low = 1; // TODO: CORRECT ???
+        break;
+      case DISABLE:
+        in.in_regs.features = ATA_SMART_DISABLE;
+        in.in_regs.lba_low = 1;  // TODO: CORRECT ???
+        break;
+      case STATUS_CHECK:
+        in.out_needed.lba_high = in.out_needed.lba_mid = true; // Status returned here
+      case STATUS:
+        in.in_regs.features = ATA_SMART_STATUS;
+        break;
+      case AUTO_OFFLINE:
+        in.in_regs.features = ATA_SMART_AUTO_OFFLINE;
+        in.in_regs.sector_count = select;  // Caution: Non-DATA command!
+        break;
+      case AUTOSAVE:
+        in.in_regs.features = ATA_SMART_AUTOSAVE;
+        in.in_regs.sector_count = select;  // Caution: Non-DATA command!
+        break;
+      case IMMEDIATE_OFFLINE:
+        in.in_regs.features = ATA_SMART_IMMEDIATE_OFFLINE;
+        in.in_regs.lba_low = select;
+        break;
+      default:
+        pout("Unrecognized command %d in smartcommandhandler()\n"
+             "Please contact " PACKAGE_BUGREPORT "\n", command);
+        device->set_err(ENOSYS);
+        errno = ENOSYS;
+        return -1;
+    }
+
+    if (con->reportataioctl)
+      print_regs(" Input:  ", in.in_regs,
+        (in.direction==ata_cmd_in::data_in ? " IN\n":
+         in.direction==ata_cmd_in::data_out ? " OUT\n":"\n"));
+
+    ata_cmd_out out;
+    bool ok = device->ata_pass_through(in, out);
+
+    if (con->reportataioctl && out.out_regs.is_set())
+      print_regs(" Output: ", out.out_regs);
+
+    if (ok) switch (command) {
+      default:
+        retval = 0;
+        break;
+      case CHECK_POWER_MODE:
+        data[0] = out.out_regs.sector_count;
+        retval = 0;
+        break;
+      case STATUS_CHECK:
+        // Cyl low and Cyl high unchanged means "Good SMART status"
+        if ((out.out_regs.lba_high == SMART_CYL_HI) &&
+            (out.out_regs.lba_mid == SMART_CYL_LOW))
+          retval = 0;
+        // These values mean "Bad SMART status"
+        else if ((out.out_regs.lba_high == SRET_STATUS_HI_EXCEEDED) &&
+                 (out.out_regs.lba_mid == SRET_STATUS_MID_EXCEEDED))
+          retval = 1;
+        else if (out.out_regs.lba_mid == SMART_CYL_LOW) {
+          retval = 0;
+          if (con->reportataioctl)
+            pout("SMART STATUS RETURN: half healthy response sequence, "
+                 "probable SAT/USB truncation\n");
+          } else if (out.out_regs.lba_mid == SRET_STATUS_MID_EXCEEDED) {
+          retval = 1;
+          if (con->reportataioctl)
+            pout("SMART STATUS RETURN: half unhealthy response sequence, "
+                 "probable SAT/USB truncation\n");
+        } else {
+          // We haven't gotten output that makes sense; print out some debugging info
+          pout("Error SMART Status command failed\n"
+               "Please get assistance from %s\n", PACKAGE_HOMEPAGE);
+          errno = EIO;
+          retval = -1;
+        }
+        break;
+    }
   }
 
   // If requested, invalidate serial number before any printing is done
@@ -651,12 +572,13 @@ int smartcommandhandler(int device, smart_command_set command, int select, char
 
   // 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));
+    if (device->get_errno())
+      pout("REPORT-IOCTL: Device=%s Command=%s returned %d errno=%d [%s]\n",
+           device->get_dev_name(), commandstrings[command], retval,
+           device->get_errno(), device->get_errmsg());
     else
-      pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d\n",
-           device, commandstrings[command], retval);
+      pout("REPORT-IOCTL: Device=%s Command=%s returned %d\n",
+           device->get_dev_name(), commandstrings[command], retval);
     
     // if requested, pretty-print the output data structure
     if (con->reportataioctl>1 && getsdata) {
@@ -666,21 +588,111 @@ int smartcommandhandler(int device, smart_command_set command, int select, char
        prettyprint((unsigned char *)data, commandstrings[command]);
     }
   }
+
+  errno = device->get_errno(); // TODO: Callers should not call syserror()
   return retval;
 }
 
+// Get number of sectors from IDENTIFY sector. If the drive doesn't
+// support LBA addressing or has no user writable sectors
+// (eg, CDROM or DVD) then routine returns zero.
+uint64_t get_num_sectors(const ata_identify_device * drive)
+{
+  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];
+
+  // LBA support?
+  if (!(capabilities_0 & 0x0200))
+    return 0; // No
+
+  // if drive supports LBA addressing, determine 32-bit LBA capacity
+  uint64_t lba32 = (unsigned int)sects_32 << 16 |
+                   (unsigned int)sects_16 << 0  ;
+
+  uint64_t lba64 = 0;
+  // if drive supports 48-bit addressing, determine THAT capacity
+  if ((command_set_2 & 0xc000) == 0x4000 && (command_set_2 & 0x0400))
+      lba64 = (uint64_t)lba_64 << 48 |
+              (uint64_t)lba_48 << 32 |
+              (uint64_t)lba_32 << 16 |
+              (uint64_t)lba_16 << 0  ;
+
+  // return the larger of the two possible capacities
+  return (lba32 > lba64 ? lba32 : lba64);
+}
 
 // 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;
+unsigned char checksum(const void * data)
+{
+  unsigned char sum = 0;
+  for (int i = 0; i < 512; i++)
+    sum += ((const unsigned char *)data)[i];
+  return sum;
+}
+
+// Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents
+// bytes.
+static void swapbytes(char * out, const char * in, size_t n)
+{
+  for (size_t 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.
+static void trim(char * out, const char * in)
+{
+  // Find the first non-space character (maybe none).
+  int first = -1;
   int i;
-  
-  for (i=0; i<512; i++)
-    sum+=buffer[i];
+  for (i = 0; in[i]; i++)
+    if (!isspace((int)in[i])) {
+      first = i;
+      break;
+    }
 
-  return sum;
+  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--)
+    ;
+  int last = i;
+
+  strncpy(out, in+first, last-first+1);
+  out[last-first+1] = '\0';
+}
+
+// Convenience function for formatting strings from ata_identify_device
+void format_ata_string(char * out, const char * in, int n, bool fix_swap)
+{
+  bool must_swap = !fix_swap;
+#ifdef __NetBSD__
+  /* NetBSD kernel delivers IDENTIFY data in host byte order (but all else is LE) */
+  if (isbigendian())
+    must_swap = !must_swap;
+#endif
+
+  char tmp[65];
+  n = n > 64 ? 64 : n;
+  if (!must_swap)
+    strncpy(tmp, in, n);
+  else
+    swapbytes(tmp, in, n);
+  tmp[n] = '\0';
+  trim(out, tmp);
 }
 
 // returns -1 if command fails or the device is in Sleep mode, else
@@ -689,7 +701,7 @@ unsigned char checksum(unsigned char *buffer){
 //   80h device is in Idle mode.
 //   FFh device is in Active mode or Idle mode.
 
-int ataCheckPowerMode(int device) {
+int ataCheckPowerMode(ata_device * device) {
   unsigned char result;
 
   if ((smartcommandhandler(device, CHECK_POWER_MODE, 0, (char *)&result)))
@@ -710,7 +722,7 @@ int ataCheckPowerMode(int device) {
 // 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){
+int ataReadHDIdentity (ata_device * device, struct ata_identify_device *buf){
   unsigned short *rawshort=(unsigned short *)buf;
   unsigned char  *rawbyte =(unsigned char  *)buf;
 
@@ -756,10 +768,8 @@ int ataReadHDIdentity (int device, struct ata_identify_device *buf){
 // 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;
-
+int ataVersionInfo(const char ** description, const ata_identify_device * drive, unsigned short * minor)
+{
   // check that arrays at the top of this file are defined
   // consistently
   if (sizeof(minor_str) != sizeof(char *)*(1+MINOR_MAX)){
@@ -778,7 +788,7 @@ int ataVersionInfo (const char** description, struct ata_identify_device *drive,
   }
 
   // get major and minor ATA revision numbers
-  major=drive->major_rev_num;
+  unsigned short major = drive->major_rev_num;
   *minor=drive->minor_rev_num;
   
   // First check if device has ANY ATA version information in it
@@ -796,11 +806,12 @@ int ataVersionInfo (const char** description, struct ata_identify_device *drive,
     }
   }
 
-  // Try new ATA-8 minor revision numbers (Table 32 of T13/1699-D Revision 4c)
+  // Try new ATA-8 minor revision numbers (Table 31 of T13/1699-D Revision 6)
   // (not in actual_ver/minor_str to avoid large sparse tables)
   const char *desc;
   switch (*minor) {
     case 0x0027: desc = "ATA-8-ACS revision 3c"; break;
+    case 0x0028: desc = "ATA-8-ACS revision 6"; break;
     case 0x0029: desc = "ATA-8-ACS revision 4"; break;
     case 0x0033: desc = "ATA-8-ACS revision 3e"; break;
     case 0x0039: desc = "ATA-8-ACS revision 4c"; break;
@@ -817,6 +828,7 @@ int ataVersionInfo (const char** description, struct ata_identify_device *drive,
   // 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.
+  int i;
   for (i=15; i>0; i--)
     if (major & (0x1<<i))
       break;
@@ -829,7 +841,8 @@ int ataVersionInfo (const char** description, struct ata_identify_device *drive,
 }
 
 // returns 1 if SMART supported, 0 if SMART unsupported, -1 if can't tell
-int ataSmartSupport(struct ata_identify_device *drive){
+int ataSmartSupport(const ata_identify_device * drive)
+{
   unsigned short word82=drive->command_set_1;
   unsigned short word83=drive->command_set_2;
   
@@ -843,7 +856,8 @@ int ataSmartSupport(struct ata_identify_device *drive){
 }
 
 // returns 1 if SMART enabled, 0 if SMART disabled, -1 if can't tell
-int ataIsSmartEnabled(struct ata_identify_device *drive){
+int ataIsSmartEnabled(const ata_identify_device * drive)
+{
   unsigned short word85=drive->cfs_enable_1;
   unsigned short word87=drive->csf_default;
   
@@ -858,7 +872,7 @@ int ataIsSmartEnabled(struct ata_identify_device *drive){
 
 
 // Reads SMART attributes into *data
-int ataReadSmartValues(int device, struct ata_smart_values *data){      
+int ataReadSmartValues(ata_device * device, struct ata_smart_values *data){
   
   if (smartcommandhandler(device, READ_VALUES, 0, (char *)data)){
     syserror("Error SMART Values Read failed");
@@ -866,7 +880,7 @@ int ataReadSmartValues(int device, struct ata_smart_values *data){
   }
 
   // compute checksum
-  if (checksum((unsigned char *)data))
+  if (checksum(data))
     checksumwarning("SMART Attribute Data Structure");
   
   // swap endian order if needed
@@ -887,9 +901,8 @@ int ataReadSmartValues(int device, struct ata_smart_values *data){
 
 // This corrects some quantities that are byte reversed in the SMART
 // SELF TEST LOG
-void fixsamsungselftestlog(struct ata_smart_selftestlog *data){
-  int i;
-  
+static void fixsamsungselftestlog(ata_smart_selftestlog * data)
+{
   // bytes 508/509 (numbered from 0) swapped (swap of self-test index
   // with one byte of reserved.
   swap2((char *)&(data->mostrecenttest));
@@ -898,14 +911,16 @@ void fixsamsungselftestlog(struct ata_smart_selftestlog *data){
   // 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++)
+  for (int 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){
+int ataReadSelfTestLog (ata_device * device, ata_smart_selftestlog * data,
+                        unsigned char fix_firmwarebug)
+{
 
   // get data from device
   if (smartcommandhandler(device, READ_LOG, 0x06, (char *)data)){
@@ -914,11 +929,11 @@ int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){
   }
 
   // compute its checksum, and issue a warning if needed
-  if (checksum((unsigned char *)data))
+  if (checksum(data))
     checksumwarning("SMART Self-Test Log Structure");
   
   // fix firmware bugs in self-test log
-  if (con->fixfirmwarebug == FIX_SAMSUNG)
+  if (fix_firmwarebug == FIX_SAMSUNG)
     fixsamsungselftestlog(data);
 
   // swap endian order if needed
@@ -935,26 +950,117 @@ int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){
   return 0;
 }
 
+// Print checksum warning for multi sector log
+static void check_multi_sector_sum(const void * data, unsigned nsectors, const char * msg)
+{
+  unsigned errs = 0;
+  for (unsigned i = 0; i < nsectors; i++) {
+    if (checksum((const unsigned char *)data + i*512))
+      errs++;
+  }
+  if (errs > 0) {
+    if (nsectors == 1)
+      checksumwarning(msg);
+    else
+      checksumwarning(strprintf("%s (%u/%u)", msg, errs, nsectors).c_str());
+  }
+}
 
-// 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;
+// Read SMART Extended Self-test Log
+bool ataReadExtSelfTestLog(ata_device * device, ata_smart_extselftestlog * log,
+                           unsigned nsectors)
+{
+  if (!ataReadLogExt(device, 0x07, 0x00, 0, log, nsectors))
+    return false;
+
+  check_multi_sector_sum(log, nsectors, "SMART Extended Self-test Log Structure");
+
+  if (isbigendian()) {
+    swapx(&log->log_desc_index);
+    for (unsigned i = 0; i < nsectors; i++) {
+      for (unsigned j = 0; j < 19; j++)
+        swapx(&log->log_descs[i].timestamp);
+    }
   }
+  return true;
+}
 
-  // swap endian order if needed
-  if (isbigendian()){
-    swap2((char *)&(data->logversion));
+
+// Read GP Log page(s)
+bool ataReadLogExt(ata_device * device, unsigned char logaddr,
+                   unsigned char features, unsigned page,
+                   void * data, unsigned nsectors)
+{
+  ata_cmd_in in;
+  in.in_regs.command      = ATA_READ_LOG_EXT;
+  in.in_regs.features     = features; // log specific
+  in.set_data_in_48bit(data, nsectors);
+  in.in_regs.lba_low      = logaddr;
+  in.in_regs.lba_mid_16   = page;
+
+  if (!device->ata_pass_through(in)) { // TODO: Debug output
+    if (nsectors <= 1) {
+      pout("ATA_READ_LOG_EXT (addr=0x%02x:0x%02x, page=%u, n=%u) failed: %s\n",
+           logaddr, features, page, nsectors, device->get_errmsg());
+      return false;
+    }
+
+    // Recurse to retry with single sectors,
+    // multi-sector reads may not be supported by ioctl.
+    for (unsigned i = 0; i < nsectors; i++) {
+      if (!ataReadLogExt(device, logaddr,
+                         features, page + i,
+                         (char *)data + 512*i, 1))
+        return false;
+    }
   }
-  
+
+  return true;
+}
+
+// Read SMART Log page(s)
+bool ataReadSmartLog(ata_device * device, unsigned char logaddr,
+                     void * data, unsigned nsectors)
+{
+  ata_cmd_in in;
+  in.in_regs.command  = ATA_SMART_CMD;
+  in.in_regs.features = ATA_SMART_READ_LOG_SECTOR;
+  in.set_data_in(data, nsectors);
+  in.in_regs.lba_high = SMART_CYL_HI;
+  in.in_regs.lba_mid  = SMART_CYL_LOW;
+  in.in_regs.lba_low  = logaddr;
+
+  if (!device->ata_pass_through(in)) { // TODO: Debug output
+    pout("ATA_SMART_READ_LOG failed: %s\n", device->get_errmsg());
+    return false;
+  }
+  return true;
+}
+
+
+
+// Reads the SMART or GPL Log Directory (log #0)
+int ataReadLogDirectory(ata_device * device, ata_smart_log_directory * data, bool gpl)
+{
+  if (!gpl) { // SMART Log directory
+    if (smartcommandhandler(device, READ_LOG, 0x00, (char *)data))
+      return -1;
+  }
+  else { // GP Log directory
+    if (!ataReadLogExt(device, 0x00, 0x00, 0, data, 1))
+      return -1;
+  }
+
+  // swap endian order if needed
+  if (isbigendian())
+    swapx(&data->logversion);
+
   return 0;
 }
 
 
 // Reads the selective self-test log (log #9)
-int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *data){  
+int ataReadSelectiveSelfTestLog(ata_device * device, struct ata_selective_self_test_log *data){
   
   // get data from device
   if (smartcommandhandler(device, READ_LOG, 0x09, (char *)data)){
@@ -963,7 +1069,7 @@ int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *
   }
    
   // compute its checksum, and issue a warning if needed
-  if (checksum((unsigned char *)data))
+  if (checksum(data))
     checksumwarning("SMART Selective Self-Test Log Structure");
   
   // swap endian order if needed
@@ -981,14 +1087,15 @@ int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *
   }
   
   if (data->logversion != 1)
-    pout("SMART Selective Self-Test Log Data Structure Revision Number (%d) should be 1\n", data->logversion);
+    pout("Note: selective self-test log revision number (%d) not 1 implies that no selective self-test has ever been run\n", data->logversion);
   
   return 0;
 }
 
 // Writes the selective self-test log (log #9)
-int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv, uint64_t num_sectors){   
-
+int ataWriteSelectiveSelfTestLog(ata_device * device, ata_selective_selftest_args & args,
+                                 const ata_smart_values * sv, uint64_t num_sectors)
+{
   // Disk size must be known
   if (!num_sectors) {
     pout("Disk size is unknown, unable to check selective self-test spans\n");
@@ -1003,19 +1110,8 @@ int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv, uint64
     return -1;
   }
   
-  // Fix logversion if needed
-  if (data->logversion !=1) {
-    if (!con->permissive) {
-      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"
-           "To fix revision number, add one '-T permissive' option.\n", data->logversion);
-      return -2;
-    }
-    con->permissive--;
-    pout("SMART Selective Self-Test Log Data Structure Revision should be 1 but is %d\n"
-         "'-T permissive' specified, now trying to fix it by WRITE LOG.\n", data->logversion);
-    data->logversion = 1;
-  }
+  // Set log version
+  data->logversion = 1;
 
   // Host is NOT allowed to write selective self-test log if a selective
   // self-test is in progress.
@@ -1026,10 +1122,10 @@ int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv, uint64
 
   // Set start/end values based on old spans for special -t select,... options
   int i;
-  for (i=0; i<con->smartselectivenumspans; i++) {
-    char mode = con->smartselectivemode[i];
-    uint64_t start = con->smartselectivespan[i][0];
-    uint64_t end   = con->smartselectivespan[i][1];
+  for (i = 0; i < args.num_spans; i++) {
+    int mode = args.span[i].mode;
+    uint64_t start = args.span[i].start;
+    uint64_t end   = args.span[i].end;
     if (mode == SEL_CONT) {// redo or next dependig on last test status
       switch (sv->self_test_exec_status >> 4) {
         case 1: case 2: // Aborted/Interrupted by host
@@ -1071,9 +1167,10 @@ int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv, uint64
             uint64_t spans = (num_sectors + oldsize-1) / oldsize;
             uint64_t newsize = (num_sectors + spans-1) / spans;
             uint64_t newstart = num_sectors - newsize, newend = num_sectors - 1;
-            pout("Span %d changed from %"PRIu64"-%"PRIu64" (%"PRIu64" sectors)\n"
-                 "                 to %"PRIu64"-%"PRIu64" (%"PRIu64" sectors) (%"PRIu64" spans)\n",
-                i, start, end, oldsize, newstart, newend, newsize, spans);
+            pout("Span %d changed from %"PRIu64"-%"PRIu64" (%"PRIu64" sectors)\n",
+                 i, start, end, oldsize);
+            pout("                 to %"PRIu64"-%"PRIu64" (%"PRIu64" sectors) (%"PRIu64" spans)\n",
+                 newstart, newend, newsize, spans);
             start = newstart; end = newend;
           }
         }
@@ -1093,9 +1190,10 @@ int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv, uint64
         i, start, end, num_sectors);
       return -1;
     }
-    // Write back to allow ataSmartTest() to print the actual values
-    con->smartselectivespan[i][0] = start;
-    con->smartselectivespan[i][1] = end;
+    // Return the actual mode and range to caller.
+    args.span[i].mode  = mode;
+    args.span[i].start = start;
+    args.span[i].end   = end;
   }
 
   // Clear spans
@@ -1103,9 +1201,9 @@ int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv, uint64
     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];
+  for (i = 0; i < args.num_spans; i++){
+    data->span[i].start = args.span[i].start;
+    data->span[i].end   = args.span[i].end;
   }
 
   // host must initialize to zero before initiating selective self-test
@@ -1113,10 +1211,10 @@ int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv, uint64
   data->currentspan=0;
   
   // Perform off-line scan after selective test?
-  if (1 == con->scanafterselect)
+  if (args.scan_after_select == 1)
     // NO
     data->flags &= ~SELECTIVE_FLAG_DOSCAN;
-  else if (2 == con->scanafterselect)
+  else if (args.scan_after_select == 2)
     // YES
     data->flags |= SELECTIVE_FLAG_DOSCAN;
   
@@ -1125,8 +1223,8 @@ int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv, uint64
   data->flags &= ~(SELECTIVE_FLAG_PENDING);
 
   // modify pending time?
-  if (con->pendingtime)
-    data->pendingtime=(unsigned short)(con->pendingtime-1);
+  if (args.pending_time)
+    data->pendingtime = (unsigned short)(args.pending_time-1);
 
   // Set checksum to zero, then compute checksum
   data->checksum=0;
@@ -1161,18 +1259,17 @@ int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv, uint64
 
 // This corrects some quantities that are byte reversed in the SMART
 // ATA ERROR LOG.
-void fixsamsungerrorlog(struct ata_smart_errorlog *data){
-  int i,j;
-  
+static void fixsamsungerrorlog(ata_smart_errorlog * data)
+{
   // 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++){
+  for (int i = 0; i < 5; i++){
     // step through 5 command data structures
-    for (j=0; j<5; j++)
+    for (int 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));
@@ -1184,7 +1281,8 @@ void fixsamsungerrorlog(struct ata_smart_errorlog *data){
 }
 
 // NEEDED ONLY FOR SAMSUNG -22 (some) -23 AND -24?? FIRMWARE
-void fixsamsungerrorlog2(struct ata_smart_errorlog *data){
+static void fixsamsungerrorlog2(ata_smart_errorlog * data)
+{
   // Device error count in bytes 452-3
   swap2((char *)&(data->ata_error_count));
   return;
@@ -1193,7 +1291,9 @@ void fixsamsungerrorlog2(struct ata_smart_errorlog *data){
 // 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){      
+int ataReadErrorLog (ata_device * device, ata_smart_errorlog *data,
+                     unsigned char fix_firmwarebug)
+{
   
   // get data from device
   if (smartcommandhandler(device, READ_LOG, 0x01, (char *)data)){
@@ -1202,14 +1302,14 @@ int ataReadErrorLog (int device, struct ata_smart_errorlog *data){
   }
   
   // compute its checksum, and issue a warning if needed
-  if (checksum((unsigned char *)data))
+  if (checksum(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)
+  if (fix_firmwarebug == FIX_SAMSUNG)
     fixsamsungerrorlog(data);
-  else if (con->fixfirmwarebug == FIX_SAMSUNG2)
+  else if (fix_firmwarebug == FIX_SAMSUNG2)
     fixsamsungerrorlog2(data);
 
   // swap endian order if needed
@@ -1233,7 +1333,31 @@ int ataReadErrorLog (int device, struct ata_smart_errorlog *data){
   return 0;
 }
 
-int ataReadSmartThresholds (int device, struct ata_smart_thresholds_pvt *data){
+// Read Extended Comprehensive Error Log
+bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log,
+                        unsigned nsectors)
+{
+  if (!ataReadLogExt(device, 0x03, 0x00, 0, log, nsectors))
+    return false;
+
+  check_multi_sector_sum(log, nsectors, "SMART Extended Comprehensive Error Log Structure");
+
+  if (isbigendian()) {
+    swapx(&log->device_error_count);
+    swapx(&log->error_log_index);
+
+    for (unsigned i = 0; i < nsectors; i++) {
+      for (unsigned j = 0; j < 4; j++)
+        swapx(&log->error_logs[i].commands[j].timestamp);
+      swapx(&log->error_logs[i].error.timestamp);
+    }
+  }
+
+  return true;
+}
+
+
+int ataReadSmartThresholds (ata_device * device, struct ata_smart_thresholds_pvt *data){
   
   // get data from device
   if (smartcommandhandler(device, READ_THRESHOLDS, 0, (char *)data)){
@@ -1242,7 +1366,7 @@ int ataReadSmartThresholds (int device, struct ata_smart_thresholds_pvt *data){
   }
   
   // compute its checksum, and issue a warning if needed
-  if (checksum((unsigned char *)data))
+  if (checksum(data))
     checksumwarning("SMART Attribute Thresholds Structure");
   
   // swap endian order if needed
@@ -1252,7 +1376,7 @@ int ataReadSmartThresholds (int device, struct ata_smart_thresholds_pvt *data){
   return 0;
 }
 
-int ataEnableSmart (int device ){       
+int ataEnableSmart (ata_device * device ){
   if (smartcommandhandler(device, ENABLE, 0, NULL)){
     syserror("Error SMART Enable failed");
     return -1;
@@ -1260,7 +1384,7 @@ int ataEnableSmart (int device ){
   return 0;
 }
 
-int ataDisableSmart (int device ){      
+int ataDisableSmart (ata_device * device ){
   
   if (smartcommandhandler(device, DISABLE, 0, NULL)){
     syserror("Error SMART Disable failed");
@@ -1269,7 +1393,7 @@ int ataDisableSmart (int device ){
   return 0;
 }
 
-int ataEnableAutoSave(int device){  
+int ataEnableAutoSave(ata_device * device){
   if (smartcommandhandler(device, AUTOSAVE, 241, NULL)){
     syserror("Error SMART Enable Auto-save failed");
     return -1;
@@ -1277,7 +1401,7 @@ int ataEnableAutoSave(int device){
   return 0;
 }
 
-int ataDisableAutoSave(int device){
+int ataDisableAutoSave(ata_device * device){
   
   if (smartcommandhandler(device, AUTOSAVE, 0, NULL)){
     syserror("Error SMART Disable Auto-save failed");
@@ -1290,7 +1414,7 @@ int ataDisableAutoSave(int device){
 // 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 ){ 
+int ataEnableAutoOffline (ata_device * device){
   
   /* timer hard coded to 4 hours */  
   if (smartcommandhandler(device, AUTO_OFFLINE, 248, NULL)){
@@ -1302,7 +1426,7 @@ int ataEnableAutoOffline (int device ){
 
 // Another Obsolete Command.  See comments directly above, associated
 // with the corresponding Enable command.
-int ataDisableAutoOffline (int device ){        
+int ataDisableAutoOffline (ata_device * device){
   
   if (smartcommandhandler(device, AUTO_OFFLINE, 0, NULL)){
     syserror("Error SMART Disable Automatic Offline failed");
@@ -1315,7 +1439,7 @@ int ataDisableAutoOffline (int device ){
 // 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 ataDoesSmartWork(ata_device * device){
   int retval=smartcommandhandler(device, STATUS, 0, NULL);
 
   if (-1 == retval)
@@ -1326,15 +1450,17 @@ int ataDoesSmartWork(int device){
 
 // This function uses a different interface (DRIVE_TASK) than the
 // other commands in this file.
-int ataSmartStatus2(int device){
+int ataSmartStatus2(ata_device * 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, uint64_t num_sectors)
+// TODO: Move to ataprint.cpp ?
+int ataSmartTest(ata_device * device, int testtype, const ata_selective_selftest_args & selargs,
+                 const ata_smart_values * sv, uint64_t num_sectors)
 {
-  char cmdmsg[128],*type,*captive;
+  char cmdmsg[128]; const char *type, *captive;
   int errornum, cap, retval, select=0;
 
   // Boolean, if set, says test is captive
@@ -1361,7 +1487,8 @@ int ataSmartTest(int device, int testtype, struct ata_smart_values *sv, uint64_t
   
   // If doing a selective self-test, first use WRITE_LOG to write the
   // selective self-test log.
-  if (select && (retval=ataWriteSelectiveSelfTestLog(device, sv, num_sectors))) {
+  ata_selective_selftest_args selargs_io = selargs; // filled with info about actual spans
+  if (select && (retval = ataWriteSelectiveSelfTestLog(device, selargs_io, sv, num_sectors))) {
     if (retval==-4)
       pout("Can't start selective self-test without aborting current test: use '-X' option to smartctl.\n");
     return retval;
@@ -1377,10 +1504,10 @@ int ataSmartTest(int device, int testtype, struct ata_smart_values *sv, uint64_t
   if (select) {
     int i;
     pout("SPAN         STARTING_LBA           ENDING_LBA\n");
-    for (i = 0; i < con->smartselectivenumspans; i++)
+    for (i = 0; i < selargs_io.num_spans; i++)
       pout("   %d %20"PRId64" %20"PRId64"\n", i,
-           con->smartselectivespan[i][0],
-           con->smartselectivespan[i][1]);
+           selargs_io.span[i].start,
+           selargs_io.span[i].end);
   }
   
   // Now send the command to test
@@ -1403,7 +1530,8 @@ int ataSmartTest(int device, int testtype, struct ata_smart_values *sv, uint64_t
 }
 
 /* Test Time Functions */
-int TestTime(struct ata_smart_values *data,int testtype){
+int TestTime(const ata_smart_values *data, int testtype)
+{
   switch (testtype){
   case OFFLINE_FULL_SCAN:
     return (int) data->total_time_to_complete_off_line;
@@ -1428,8 +1556,8 @@ int TestTime(struct ata_smart_values *data,int testtype){
 // 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){
-
+int isSmartErrorLogCapable (const ata_smart_values * data, const 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);
@@ -1447,8 +1575,8 @@ int isSmartErrorLogCapable (struct ata_smart_values *data, struct ata_identify_d
 
 // 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){
-
+int isSmartTestLogCapable (const ata_smart_values * data, const 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);
@@ -1462,11 +1590,12 @@ int isSmartTestLogCapable (struct ata_smart_values *data, struct ata_identify_de
 
 
   // otherwise we'll use the poorly documented capability bit
-    return data->errorlog_capability & 0x01;
+  return data->errorlog_capability & 0x01;
 }
 
 
-int isGeneralPurposeLoggingCapable(struct ata_identify_device *identity){
+int isGeneralPurposeLoggingCapable(const ata_identify_device *identity)
+{
   unsigned short word84=identity->command_set_extension;
   unsigned short word87=identity->csf_default;
 
@@ -1496,30 +1625,38 @@ int isGeneralPurposeLoggingCapable(struct ata_identify_device *identity){
 // 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;
+int isSupportExecuteOfflineImmediate(const 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 isSupportAutomaticTimer(const 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 isSupportOfflineAbort(const ata_smart_values *data)
+{
+  return data->offline_data_collection_capability & 0x04;
 }
-int isSupportOfflineSurfaceScan(struct ata_smart_values *data){
+int isSupportOfflineSurfaceScan(const ata_smart_values * data)
+{
    return data->offline_data_collection_capability & 0x08;
 }
-int isSupportSelfTest (struct ata_smart_values *data){
+int isSupportSelfTest (const ata_smart_values * data)
+{
    return data->offline_data_collection_capability & 0x10;
 }
-int isSupportConveyanceSelfTest(struct ata_smart_values *data){
+int isSupportConveyanceSelfTest(const ata_smart_values * data)
+{
    return data->offline_data_collection_capability & 0x20;
 }
-int isSupportSelectiveSelfTest(struct ata_smart_values *data){
+int isSupportSelectiveSelfTest(const ata_smart_values * data)
+{
    return data->offline_data_collection_capability & 0x40;
 }
 
@@ -1540,17 +1677,16 @@ int isSupportSelectiveSelfTest(struct ata_smart_values *data){
 
 // 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;
-  
+int ataCheckSmart(const ata_smart_values * data,
+                  const ata_smart_thresholds_pvt * thresholds,
+                  int onlyfailed)
+{
   // loop over all attributes
-  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
+  for (int 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;
+    const ata_smart_attribute * disk = data->vendor_attributes+i;
+    const ata_smart_threshold_entry * thre = thresholds->thres_entries+i;
  
     // consider only valid attributes
     if (disk->id && thre->id){
@@ -1577,18 +1713,16 @@ int ataCheckSmart(struct ata_smart_values *data,
 // <= 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;
-  
+int ataCheckAttribute(const ata_smart_values * data,
+                      const ata_smart_thresholds_pvt * thresholds,
+                      int n)
+{
   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;
+  const ata_smart_attribute * disk = data->vendor_attributes+n;
+  const ata_smart_threshold_entry * thre = thresholds->thres_entries+n;
 
   if (!disk || !thre)
     return 0;
@@ -1637,8 +1771,8 @@ static void ataPrintTemperatureValue(char *out, const unsigned char *raw, const
 // non-default interpretations.
 
 int64_t ataPrintSmartAttribRawValue(char *out, 
-                                    struct ata_smart_attribute *attribute,
-                                    unsigned char *defs){
+                                    const ata_smart_attribute * attribute,
+                                    const unsigned char * defs){
   int64_t rawvalue;
   unsigned word[3];
   int j;
@@ -1701,13 +1835,22 @@ int64_t ataPrintSmartAttribRawValue(char *out,
     if (word[1])
       out+=sprintf(out, " (Average %d)", word[1]);
     break;
+    // reallocated sector count
+  case 5:
+    out+=sprintf(out, "%u", word[0]);
+    if (word[1] || word[2])
+      out+=sprintf(out, " (%u, %u)", word[2], word[1]);
+    break;
     // Power on time
   case 9:
     if (select==1){
       // minutes
-      int64_t tmp1=rawvalue/60;
-      int64_t tmp2=rawvalue%60;
+      int64_t temp=word[0]+(word[1]<<16);
+      int64_t tmp1=temp/60;
+      int64_t tmp2=temp%60;
       out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m", tmp1, tmp2);
+      if (word[2])
+        out+=sprintf(out, " (%u)", word[2]);
     }
     else if (select==3){
       // seconds
@@ -1756,6 +1899,12 @@ int64_t ataPrintSmartAttribRawValue(char *out,
     else
       ataPrintTemperatureValue(out, attribute->raw, word);
     break;
+    // reallocated event count
+  case 196:
+    out+=sprintf(out, "%u", word[0]);
+    if (word[1] || word[2])
+      out+=sprintf(out, " (%u, %u)", word[2], word[1]);
+    break;
   default:
     out+=sprintf(out, "%"PRIu64, rawvalue);
   }
@@ -1769,8 +1918,8 @@ int64_t ataPrintSmartAttribRawValue(char *out,
 // 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;
+void ataPrintSmartAttribName(char * out, unsigned char id, const unsigned char * definitions){
+  const char *name;
   unsigned char val;
 
   // If no data array, use default interpretations
@@ -1836,6 +1985,18 @@ void ataPrintSmartAttribName(char *out, unsigned char id, unsigned char *definit
   case 13:
     name="Read_Soft_Error_Rate";
     break;
+  case 178:
+    name="Used_Rsvd_Blk_Cnt_Chip";
+    break;
+  case 179:
+    name="Used_Rsvd_Blk_Cnt_Tot";
+    break;
+  case 180:
+    name="Unused_Rsvd_Blk_Cnt_Tot";
+    break;
+  case 183:
+    name="Runtime_Bad_Block";
+    break;
   case 187:
     name="Reported_Uncorrect";
     break;
@@ -1892,17 +2053,29 @@ void ataPrintSmartAttribName(char *out, unsigned char id, unsigned char *definit
     name="Reallocated_Event_Count";
     break;
   case 197:
-    name="Current_Pending_Sector";
+    switch (val) {
+    default:
+      name="Current_Pending_Sector";
+      break;
+    case 1:
+      // Not reset after sector reallocation
+      name="Total_Pending_Sectors";
+      break;
+    }
     break;
   case 198:
     switch (val){
+    default:
+      name="Offline_Uncorrectable";
+      break;
     case 1:
+      // Not reset after sector reallocation
+      name="Total_Offl_Uncorrectabl"/*e*/;
+      break;
+    case 2:
       // Fujitsu
       name="Off-line_Scan_UNC_Sector_Ct";
       break;
-    default:
-      name="Offline_Uncorrectable";
-      break;
     }
     break;
   case 199:
@@ -2025,16 +2198,15 @@ void ataPrintSmartAttribName(char *out, unsigned char id, unsigned char *definit
 // 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;
-
+int64_t ATAReturnAttributeRawValue(unsigned char id, const ata_smart_values * data)
+{
   // 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;
+  for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+    const 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;
@@ -2060,9 +2232,9 @@ int64_t ATAReturnAttributeRawValue(unsigned char id, struct ata_smart_values *da
 
 // 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++) {
+unsigned char ATAReturnTemperatureValue(const ata_smart_values * data, const unsigned char * defs)
+{
+  for (int 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);
@@ -2085,8 +2257,9 @@ unsigned char ATAReturnTemperatureValue(/*const*/ struct ata_smart_values *data,
   return 0;
 }
 
+
 // Read SCT Status
-int ataReadSCTStatus(int device, ata_sct_status_response * sts)
+int ataReadSCTStatus(ata_device * device, ata_sct_status_response * sts)
 {
   // read SCT status via SMART log 0xe0
   memset(sts, 0, sizeof(*sts));
@@ -2116,7 +2289,7 @@ int ataReadSCTStatus(int device, ata_sct_status_response * sts)
 }
 
 // Read SCT Temperature History Table and Status
-int ataReadSCTTempHist(int device, ata_sct_temperature_history_table * tmh,
+int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * tmh,
                        ata_sct_status_response * sts)
 {
   // Check initial status
@@ -2176,7 +2349,7 @@ int ataReadSCTTempHist(int device, ata_sct_temperature_history_table * tmh,
 }
 
 // Set SCT Temperature Logging Interval
-int ataSetSCTTempInterval(int device, unsigned interval, bool persistent)
+int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persistent)
 {
   // Check initial status
   ata_sct_status_response sts;
@@ -2217,26 +2390,176 @@ int ataSetSCTTempInterval(int device, unsigned interval, bool persistent)
   return 0;
 }
 
+// Print one self-test log entry.
+// Returns true if self-test showed an error.
+bool ataPrintSmartSelfTestEntry(unsigned testnum, unsigned char test_type,
+                                unsigned char test_status,
+                                unsigned short timestamp,
+                                uint64_t failing_lba,
+                                bool print_error_only, bool & print_header)
+{
+  const char * msgtest;
+  switch (test_type) {
+    case 0x00: msgtest = "Offline";            break;
+    case 0x01: msgtest = "Short offline";      break;
+    case 0x02: msgtest = "Extended offline";   break;
+    case 0x03: msgtest = "Conveyance offline"; break;
+    case 0x04: msgtest = "Selective offline";  break;
+    case 0x7f: msgtest = "Abort offline test"; break;
+    case 0x81: msgtest = "Short captive";      break;
+    case 0x82: msgtest = "Extended captive";   break;
+    case 0x83: msgtest = "Conveyance captive"; break;
+    case 0x84: msgtest = "Selective captive";  break;
+    default:
+      if ((0x40 <= test_type && test_type <= 0x7e) || 0x90 <= test_type)
+        msgtest = "Vendor offline";
+      else
+        msgtest = "Reserved offline";
+  }
+
+  bool is_error = false;
+  const char * msgstat;
+  switch (test_status >> 4) {
+    case 0x0: msgstat = "Completed without error";       break;
+    case 0x1: msgstat = "Aborted by host";               break;
+    case 0x2: msgstat = "Interrupted (host reset)";      break;
+    case 0x3: msgstat = "Fatal or unknown error";        is_error = true; break;
+    case 0x4: msgstat = "Completed: unknown failure";    is_error = true; break;
+    case 0x5: msgstat = "Completed: electrical failure"; is_error = true; break;
+    case 0x6: msgstat = "Completed: servo/seek failure"; is_error = true; break;
+    case 0x7: msgstat = "Completed: read failure";       is_error = true; break;
+    case 0x8: msgstat = "Completed: handling damage??";  is_error = true; break;
+    case 0xf: msgstat = "Self-test routine in progress"; break;
+    default:  msgstat = "Unknown/reserved test status";
+  }
+
+  if (!is_error && print_error_only)
+    return false;
+
+  // Print header once
+  if (print_header) {
+    print_header = false;
+    pout("Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error\n");
+  }
+
+  char msglba[32];
+  if (is_error && failing_lba < 0xffffffffffffULL)
+    snprintf(msglba, sizeof(msglba), "%"PRIu64, failing_lba);
+  else
+    strcpy(msglba, "-");
+
+  pout("#%2u  %-19s %-29s %1d0%%  %8u         %s\n", testnum, msgtest, msgstat,
+       test_status & 0x0f, timestamp, msglba);
+
+  return is_error;
+}
+
+// Print Smart self-test log, used by smartctl and smartd.
+// 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(const ata_smart_selftestlog * data, bool allentries,
+                             unsigned char fix_firmwarebug)
+{
+  if (allentries)
+    pout("SMART Self-test log structure revision number %d\n",(int)data->revnumber);
+  if ((data->revnumber!=0x0001) && allentries && fix_firmwarebug != 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;
+  }
+
+  bool noheaderprinted = true;
+  int retval=0, hours=0, testno=0;
+
+  // print log
+  for (int i = 20; i >= 0; i--) {
+    // log is a circular buffer
+    int j = (i+data->mostrecenttest)%21;
+    const ata_smart_selftestlog_struct * log = data->selftest_struct+j;
+
+    if (nonempty(log, sizeof(*log))) {
+      // count entry based on non-empty structures -- needed for
+      // Seagate only -- other vendors don't have blank entries 'in
+      // the middle'
+      testno++;
+
+      // 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
+      uint64_t lba48 = (log->lbafirstfailure < 0xffffffff ? log->lbafirstfailure : 0xffffffffffffULL);
+
+      // Print entry
+      bool errorfound = ataPrintSmartSelfTestEntry(testno,
+        log->selftestnumber, log->selfteststatus, log->timestamp,
+        lba48, !allentries, noheaderprinted);
+
+      // 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);
+}
+
 
 /////////////////////////////////////////////////////////////////////////////
 // Pseudo-device to parse "smartctl -r ataioctl,2 ..." output and simulate
 // an ATA device with same behaviour
 
-// Table of parsed commands, return value, data
-struct parsed_ata_command
-{ 
-  smart_command_set command;
-  int select;
-  int retval, errval;
-  char * data;
-};
+namespace {
+
+class parsed_ata_device
+: public /*implements*/ ata_device_with_command_set
+{
+public:
+  parsed_ata_device(smart_interface * intf, const char * dev_name);
+
+  virtual ~parsed_ata_device() throw();
+
+  virtual bool is_open() const;
+
+  virtual bool open();
 
-const int max_num_parsed_commands = 32;
-static parsed_ata_command parsed_command_table[max_num_parsed_commands];
-static int num_parsed_commands;
-static int next_replay_command;
-static bool replay_out_of_sync;
+  virtual bool close();
 
+  virtual bool ata_identify_is_cached() const;
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  // Table of parsed commands, return value, data
+  struct parsed_ata_command
+  {
+    smart_command_set command;
+    int select;
+    int retval, errval;
+    char * data;
+  };
+
+  enum { max_num_commands = 32 };
+  parsed_ata_command m_command_table[max_num_commands];
+
+  int m_num_commands;
+  int m_next_replay_command;
+  bool m_replay_out_of_sync;
+  bool m_ata_identify_is_cached;
+};
 
 static const char * nextline(const char * s, int & lineno)
 {
@@ -2276,13 +2599,32 @@ static inline int matchtoi(const char * src, const regmatch_t & srcmatch, int de
   return atoi(src + srcmatch.rm_so);
 }
 
+parsed_ata_device::parsed_ata_device(smart_interface * intf, const char * dev_name)
+: smart_device(intf, dev_name, "ata", ""),
+  m_num_commands(0),
+  m_next_replay_command(0),
+  m_replay_out_of_sync(false),
+  m_ata_identify_is_cached(false)
+{
+  memset(m_command_table, 0, sizeof(m_command_table));
+}
+
+parsed_ata_device::~parsed_ata_device() throw()
+{
+  close();
+}
+
+bool parsed_ata_device::is_open() const
+{
+  return (m_num_commands > 0);
+}
 
 // Parse stdin and build command table
-int parsedev_open(const char * pathname)
+bool parsed_ata_device::open()
 {
-  if (strcmp(pathname, "-")) {
-    errno = EINVAL; return -1;
-  }
+  const char * pathname = get_dev_name();
+  if (strcmp(pathname, "-"))
+    return set_err(EINVAL);
   pathname = "<stdin>";
   // Fill buffer
   char buffer[64*1024];
@@ -2293,20 +2635,16 @@ int parsedev_open(const char * pathname)
       break;
     size += nr;
   }
-  if (size <= 0) {
-    pout("%s: Unexpected EOF\n", pathname);
-    errno = ENOENT; return -1;
-  }
-  if (size >= (int)sizeof(buffer)) {
-    pout("%s: Buffer overflow\n", pathname);
-    errno = EIO; return -1;
-  }
+  if (size <= 0)
+    return set_err(ENOENT, "%s: Unexpected EOF", pathname);
+  if (size >= (int)sizeof(buffer))
+    return set_err(EIO, "%s: Buffer overflow", pathname);
   buffer[size] = 0;
 
   // Regex to match output from "-r ataioctl,2"
   static const char pattern[] = "^"
   "(" // (1
-    "REPORT-IOCTL: DeviceFD=[0-9]+ Command=([A-Z ]*[A-Z])" // (2)
+    "REPORT-IOCTL: DeviceF?D?=[^ ]+ Command=([A-Z ]*[A-Z])" // (2)
     "(" // (3
       "( InputParameter=([0-9]+))?" // (4 (5))
     "|"
@@ -2315,24 +2653,25 @@ int parsedev_open(const char * pathname)
     "[\r\n]" // EOL match necessary to match optional parts above
   "|"
     "===== \\[([A-Z ]*[A-Z])\\] DATA START " // (10)
+  "|"
+    "    *(En|Dis)abled status cached by OS, " // (11)
   ")"; // )
 
   // Compile regex
-  regex_t rex;
-  if (compileregex(&rex, pattern, REG_EXTENDED)) {
-    errno = EIO; return -1;
-  }
+  regular_expression regex;
+  if (!regex.compile(pattern, REG_EXTENDED))
+    return set_err(EIO, "invalid regex");
 
   // Parse buffer
   const char * errmsg = 0;
   int i = -1, state = 0, lineno = 1;
   for (const char * line = buffer; *line; line = nextline(line, lineno)) {
     // Match line
-    if (!(line[0] == 'R' || line[0] == '='))
+    if (!(line[0] == 'R' || line[0] == '=' || line[0] == ' '))
       continue;
-    const int nmatch = 1+10;
+    const int nmatch = 1+11;
     regmatch_t match[nmatch];
-    if (regexec(&rex, line, nmatch, match, 0))
+    if (!regex.execute(line, nmatch, match))
       continue;
 
     char cmdname[40];
@@ -2346,27 +2685,27 @@ int parsedev_open(const char * pathname)
         if (!(state == 0 || state == 2)) {
           errmsg = "Missing REPORT-IOCTL result"; break;
         }
-        if (++i >= max_num_parsed_commands) {
+        if (++i >= max_num_commands) {
           errmsg = "Too many ATA commands"; break;
         }
-        parsed_command_table[i].command = (smart_command_set)nc;
-        parsed_command_table[i].select = matchtoi(line, match[5], 0); // "InputParameter=%d"
+        m_command_table[i].command = (smart_command_set)nc;
+        m_command_table[i].select = matchtoi(line, match[5], 0); // "InputParameter=%d"
         state = 1;
       }
       else {
         // End of command
-        if (!(state == 1 && (int)parsed_command_table[i].command == nc)) {
+        if (!(state == 1 && (int)m_command_table[i].command == nc)) {
           errmsg = "Missing REPORT-IOCTL start"; break;
         }
-        parsed_command_table[i].retval = matchtoi(line, match[7], -1); // "returned %d"
-        parsed_command_table[i].errval = matchtoi(line, match[9], 0); // "errno=%d"
+        m_command_table[i].retval = matchtoi(line, match[7], -1); // "returned %d"
+        m_command_table[i].errval = matchtoi(line, match[9], 0); // "errno=%d"
         state = 2;
       }
     }
     else if (matchcpy(cmdname, sizeof(cmdname), line, match[10])) { // "===== [%s] DATA START "
       // Start of sector hexdump
       int nc = name2command(cmdname);
-      if (!(state == (nc == WRITE_LOG ? 1 : 2) && (int)parsed_command_table[i].command == nc)) {
+      if (!(state == (nc == WRITE_LOG ? 1 : 2) && (int)m_command_table[i].command == nc)) {
           errmsg = "Unexpected DATA START"; break;
       }
       line = nextline(line, lineno);
@@ -2391,10 +2730,13 @@ int parsedev_open(const char * pathname)
         free(data);
         errmsg = "Incomplete sector hex dump"; break;
       }
-      parsed_command_table[i].data = data;
+      m_command_table[i].data = data;
       if (nc != WRITE_LOG)
         state = 0;
     }
+    else if (match[11].rm_so > 0) { // "(En|Dis)abled status cached by OS"
+      m_ata_identify_is_cached = true;
+    }
   }
 
   if (!(state == 0 || state == 2))
@@ -2403,58 +2745,66 @@ int parsedev_open(const char * pathname)
   if (!errmsg && i < 0)
     errmsg = "No information found";
 
-  num_parsed_commands = i+1;
-  next_replay_command = 0;
-  replay_out_of_sync = false;
+  m_num_commands = i+1;
+  m_next_replay_command = 0;
+  m_replay_out_of_sync = false;
 
   if (errmsg) {
-    pout("%s(%d): Syntax error: %s\n", pathname, lineno, errmsg);
-    errno = EIO;
-    parsedev_close(0);
-    return -1;
+    close();
+    return set_err(EIO, "%s(%d): Syntax error: %s", pathname, lineno, errmsg);
   }
-  return 0;
+  return true;
 }
 
 // Report warnings and free command table 
-void parsedev_close(int /*fd*/)
+bool parsed_ata_device::close()
 {
-  if (replay_out_of_sync)
+  if (m_replay_out_of_sync)
       pout("REPLAY-IOCTL: Warning: commands replayed out of sync\n");
-  else if (next_replay_command != 0)
-      pout("REPLAY-IOCTL: Warning: %d command(s) not replayed\n", num_parsed_commands-next_replay_command);
+  else if (m_next_replay_command != 0)
+      pout("REPLAY-IOCTL: Warning: %d command(s) not replayed\n", m_num_commands-m_next_replay_command);
 
-  for (int i = 0; i < num_parsed_commands; i++) {
-    if (parsed_command_table[i].data) {
-      free(parsed_command_table[i].data); parsed_command_table[i].data = 0;
+  for (int i = 0; i < m_num_commands; i++) {
+    if (m_command_table[i].data) {
+      free(m_command_table[i].data); m_command_table[i].data = 0;
     }
   }
-  num_parsed_commands = 0;
+  m_num_commands = 0;
+  m_next_replay_command = 0;
+  m_replay_out_of_sync = false;
+  return true;
+}
+
+
+bool parsed_ata_device::ata_identify_is_cached() const
+{
+  return m_ata_identify_is_cached;
 }
 
+
 // Simulate ATA command from command table
-static int parsedev_command_interface(int /*fd*/, smart_command_set command, int select, char * data)
+int parsed_ata_device::ata_command_interface(smart_command_set command, int select, char * data)
 {
-  // Find command, try round-robin of out of sync
-  int i = next_replay_command;
+  // Find command, try round-robin if out of sync
+  int i = m_next_replay_command;
   for (int j = 0; ; j++) {
-    if (j >= num_parsed_commands) {
+    if (j >= m_num_commands) {
       pout("REPLAY-IOCTL: Warning: Command not found\n");
       errno = ENOSYS;
       return -1;
     }
-    if (parsed_command_table[i].command == command && parsed_command_table[i].select == select)
+    if (m_command_table[i].command == command && m_command_table[i].select == select)
       break;
-    if (!replay_out_of_sync) {
-      replay_out_of_sync = true;
+    if (!m_replay_out_of_sync) {
+      m_replay_out_of_sync = true;
       pout("REPLAY-IOCTL: Warning: Command #%d is out of sync\n", i+1);
     }
-    if (++i >= num_parsed_commands)
+    if (++i >= m_num_commands)
       i = 0;
   }
-  next_replay_command = i;
-  if (++next_replay_command >= num_parsed_commands)
-    next_replay_command = 0;
+  m_next_replay_command = i;
+  if (++m_next_replay_command >= m_num_commands)
+    m_next_replay_command = 0;
 
   // Return command data
   switch (command) {
@@ -2463,11 +2813,11 @@ static int parsedev_command_interface(int /*fd*/, smart_command_set command, int
     case READ_VALUES:
     case READ_THRESHOLDS:
     case READ_LOG:
-      if (parsed_command_table[i].data)
-        memcpy(data, parsed_command_table[i].data, 512);
+      if (m_command_table[i].data)
+        memcpy(data, m_command_table[i].data, 512);
       break;
     case WRITE_LOG:
-      if (!(parsed_command_table[i].data && !memcmp(data, parsed_command_table[i].data, 512)))
+      if (!(m_command_table[i].data && !memcmp(data, m_command_table[i].data, 512)))
         pout("REPLAY-IOCTL: Warning: WRITE LOG data does not match\n");
       break;
     case CHECK_POWER_MODE:
@@ -2476,7 +2826,14 @@ static int parsedev_command_interface(int /*fd*/, smart_command_set command, int
       break;
   }
 
-  if (parsed_command_table[i].errval)
-    errno = parsed_command_table[i].errval;
-  return parsed_command_table[i].retval;
+  if (m_command_table[i].errval)
+    errno = m_command_table[i].errval;
+  return m_command_table[i].retval;
+}
+
+} // namespace
+
+ata_device * get_parsed_ata_device(smart_interface * intf, const char * dev_name)
+{
+  return new parsed_ata_device(intf, dev_name);
 }
index 615e49544c5a6e40fa137b76a902265e19055cd2..c5f77e4254654b5332d9cd2ce226e82f1e8a4194 100644 (file)
--- a/atacmds.h
+++ b/atacmds.h
@@ -3,7 +3,8 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-9 Christian Franke <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
@@ -25,7 +26,9 @@
 #ifndef ATACMDS_H_
 #define ATACMDS_H_
 
-#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.90 2008/03/04 22:09:47 ballen4705 Exp $\n"
+#define ATACMDS_H_CVSID "$Id: atacmds.h 2859 2009-07-23 18:55:06Z chrfranke $"
+
+#include "dev_interface.h" // ata_device
 
 // Macro to check expected size of struct at compile time using a
 // dummy typedef.  On size mismatch, compiler reports a negative array
@@ -64,11 +67,22 @@ typedef enum {
   WRITE_LOG
 } smart_command_set;
 
+// Possible values for fix_firmwarebug.
+enum {
+  FIX_NOTSPECIFIED = 0,
+  FIX_NONE,
+  FIX_SAMSUNG,
+  FIX_SAMSUNG2,
+  FIX_SAMSUNG3
+};
+
 // ATA Specification Command Register Values (Commands)
-#define ATA_IDENTIFY_DEVICE             0xec                                              
+#define ATA_IDENTIFY_DEVICE             0xec
 #define ATA_IDENTIFY_PACKET_DEVICE      0xa1
 #define ATA_SMART_CMD                   0xb0
 #define ATA_CHECK_POWER_MODE            0xe5
+// 48-bit commands
+#define ATA_READ_LOG_EXT                0x2F
 
 // ATA Specification Feature Register Values (SMART Subcommands).
 // Note that some are obsolete as of ATA-7.
@@ -298,6 +312,90 @@ struct ata_smart_errorlog {
 #pragma pack()
 ASSERT_SIZEOF_STRUCT(ata_smart_errorlog, 512);
 
+
+// Extended Comprehensive SMART Error Log data structures
+// See Section A.7 of
+//   AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS)
+//   T13/1699-D Revision 6a (Working Draft), September 6, 2008.
+
+// Command data structure
+// Table A.9 of T13/1699-D Revision 6a
+#pragma pack(1)
+struct ata_smart_exterrlog_command
+{
+  unsigned char device_control_register;
+  unsigned char features_register;
+  unsigned char features_register_hi;
+  unsigned char count_register;
+  unsigned char count_register_hi;
+  unsigned char lba_low_register;
+  unsigned char lba_low_register_hi;
+  unsigned char lba_mid_register;
+  unsigned char lba_mid_register_hi;
+  unsigned char lba_high_register;
+  unsigned char lba_high_register_hi;
+  unsigned char device_register;
+  unsigned char command_register;
+
+  unsigned char reserved;
+  unsigned int timestamp;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog_command, 18);
+
+// Error data structure
+// Table A.10 T13/1699-D Revision 6a
+#pragma pack(1)
+struct ata_smart_exterrlog_error
+{
+  unsigned char device_control_register;
+  unsigned char error_register;
+  unsigned char count_register;
+  unsigned char count_register_hi;
+  unsigned char lba_low_register;
+  unsigned char lba_low_register_hi;
+  unsigned char lba_mid_register;
+  unsigned char lba_mid_register_hi;
+  unsigned char lba_high_register;
+  unsigned char lba_high_register_hi;
+  unsigned char device_register;
+  unsigned char status_register;
+
+  unsigned char extended_error[19];
+  unsigned char state;
+  unsigned short timestamp;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog_error, 34);
+
+// Error log data structure
+// Table A.8 of T13/1699-D Revision 6a
+#pragma pack(1)
+struct ata_smart_exterrlog_error_log
+{
+  ata_smart_exterrlog_command commands[5];
+  ata_smart_exterrlog_error error;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog_error_log, 124);
+
+// Ext. Comprehensive SMART error log
+// Table A.7 of T13/1699-D Revision 6a
+#pragma pack(1)
+struct ata_smart_exterrlog
+{
+  unsigned char version;
+  unsigned char reserved1;
+  unsigned short error_log_index;
+  ata_smart_exterrlog_error_log error_logs[4];
+  unsigned short device_error_count;
+  unsigned char reserved2[9];
+  unsigned char checksum;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_exterrlog, 512);
+
+
 // Table 45 of T13/1321D Rev 1 spec (Self-test log descriptor entry)
 #pragma pack(1)
 struct ata_smart_selftestlog_struct {
@@ -324,6 +422,42 @@ struct ata_smart_selftestlog {
 #pragma pack()
 ASSERT_SIZEOF_STRUCT(ata_smart_selftestlog, 512);
 
+// Extended SMART Self-test log data structures
+// See Section A.8 of
+//   AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS)
+//   T13/1699-D Revision 6a (Working Draft), September 6, 2008.
+
+// Extended Self-test log descriptor entry
+// Table A.13 of T13/1699-D Revision 6a
+#pragma pack(1)
+struct ata_smart_extselftestlog_desc
+{
+  unsigned char self_test_type;
+  unsigned char self_test_status;
+  unsigned short timestamp;
+  unsigned char checkpoint;
+  unsigned char failing_lba[6];
+  unsigned char vendorspecific[15];
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_extselftestlog_desc, 26);
+
+// Extended Self-test log data structure
+// Table A.12 of T13/1699-D Revision 6a
+#pragma pack(1)
+struct ata_smart_extselftestlog
+{
+  unsigned char version;
+  unsigned char reserved1;
+  unsigned short log_desc_index;
+  struct ata_smart_extselftestlog_desc log_descs[19];
+  unsigned char vendor_specifc[2];
+  unsigned char reserved2[11];
+  unsigned char chksum;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_extselftestlog, 512);
+
 // SMART LOG DIRECTORY Table 52 of T13/1532D Vol 1 Rev 1a
 #pragma pack(1)
 struct ata_smart_log_entry {
@@ -457,97 +591,141 @@ struct ata_sct_temperature_history_table
 #pragma pack()
 ASSERT_SIZEOF_STRUCT(ata_sct_temperature_history_table, 512);
 
+// Possible values for span_args.mode
+enum {
+  SEL_RANGE, // MIN-MAX
+  SEL_REDO,  // redo this
+  SEL_NEXT,  // do next range
+  SEL_CONT   // redo or next depending of last test status
+};
+
+// Arguments for selective self-test
+struct ata_selective_selftest_args
+{
+  // Arguments for each span
+  struct span_args
+  {
+    uint64_t start;   // First block
+    uint64_t end;     // Last block
+    int mode;         // SEL_*, see above
+
+    span_args()
+      : start(0), end(0), mode(SEL_RANGE) { }
+  };
+
+  span_args span[5];  // Range and mode for 5 spans
+  int num_spans;      // Number of spans
+  int pending_time;   // One plus time in minutes to wait after powerup before restarting
+                      // interrupted offline scan after selective self-test.
+  int scan_after_select; // Run offline scan after selective self-test:
+                      // 0: don't change,
+                      // 1: turn off scan after selective self-test,
+                      // 2: turn on scan after selective self-test.
+
+  ata_selective_selftest_args()
+    : num_spans(0), pending_time(0), scan_after_select(0) { }
+};
+
 
 // Get information from drive
-int ataReadHDIdentity(int device, struct ata_identify_device *buf);
-int ataCheckPowerMode(int device);
+int ataReadHDIdentity(ata_device * device, struct ata_identify_device *buf);
+int ataCheckPowerMode(ata_device * device);
 
 /* Read S.M.A.R.T information from drive */
-int ataReadSmartValues(int device,struct ata_smart_values *);
-int ataReadSmartThresholds(int device, struct ata_smart_thresholds_pvt *);
-int ataReadErrorLog(int device, struct ata_smart_errorlog *);
-int ataReadSelfTestLog(int device, struct ata_smart_selftestlog *);
-int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *data);
-int ataSmartStatus(int device);
-int ataSetSmartThresholds(int device, struct ata_smart_thresholds_pvt *);
-int ataReadLogDirectory(int device, struct ata_smart_log_directory *);
+int ataReadSmartValues(ata_device * device,struct ata_smart_values *);
+int ataReadSmartThresholds(ata_device * device, struct ata_smart_thresholds_pvt *);
+int ataReadErrorLog (ata_device * device, ata_smart_errorlog *data,
+                     unsigned char fix_firmwarebug);
+int ataReadSelfTestLog(ata_device * device, ata_smart_selftestlog * data,
+                       unsigned char fix_firmwarebug);
+int ataReadSelectiveSelfTestLog(ata_device * device, struct ata_selective_self_test_log *data);
+int ataSetSmartThresholds(ata_device * device, struct ata_smart_thresholds_pvt *);
+int ataReadLogDirectory(ata_device * device, ata_smart_log_directory *, bool gpl);
+
+// Read GP Log page(s)
+bool ataReadLogExt(ata_device * device, unsigned char logaddr,
+                   unsigned char features, unsigned page,
+                   void * data, unsigned nsectors);
+// Read SMART Log page(s)
+bool ataReadSmartLog(ata_device * device, unsigned char logaddr,
+                     void * data, unsigned nsectors);
+// Read SMART Extended Comprehensive Error Log
+bool ataReadExtErrorLog(ata_device * device, ata_smart_exterrlog * log,
+                        unsigned nsectors);
+// Read SMART Extended Self-test Log
+bool ataReadExtSelfTestLog(ata_device * device, ata_smart_extselftestlog * log,
+                           unsigned nsectors);
 
 // Read SCT information
-int ataReadSCTStatus(int device, ata_sct_status_response * sts);
-int ataReadSCTTempHist(int device, ata_sct_temperature_history_table * tmh,
+int ataReadSCTStatus(ata_device * device, ata_sct_status_response * sts);
+int ataReadSCTTempHist(ata_device * device, ata_sct_temperature_history_table * tmh,
                        ata_sct_status_response * sts);
 // Set SCT temperature logging interval
-int ataSetSCTTempInterval(int device, unsigned interval, bool persistent);
+int ataSetSCTTempInterval(ata_device * device, unsigned interval, bool persistent);
 
 
 /* Enable/Disable SMART on device */
-int ataEnableSmart ( int device );
-int ataDisableSmart (int device );
-int ataEnableAutoSave(int device);
-int ataDisableAutoSave(int device);
+int ataEnableSmart (ata_device * device);
+int ataDisableSmart (ata_device * device);
+int ataEnableAutoSave(ata_device * device);
+int ataDisableAutoSave(ata_device * device);
 
 /* Automatic Offline Testing */
-int ataEnableAutoOffline ( int device );
-int ataDisableAutoOffline (int device );
+int ataEnableAutoOffline (ata_device * device);
+int ataDisableAutoOffline (ata_device * device);
 
 /* S.M.A.R.T. test commands */
-int ataSmartOfflineTest (int device);
-int ataSmartExtendSelfTest (int device);
-int ataSmartShortSelfTest (int device);
-int ataSmartShortCapSelfTest (int device);
-int ataSmartExtendCapSelfTest (int device);
-int ataSmartSelfTestAbort (int device);
+int ataSmartOfflineTest (ata_device * device);
+int ataSmartExtendSelfTest (ata_device * device);
+int ataSmartShortSelfTest (ata_device * device);
+int ataSmartShortCapSelfTest (ata_device * device);
+int ataSmartExtendCapSelfTest (ata_device * device);
+int ataSmartSelfTestAbort (ata_device * device);
+int ataWriteSelectiveSelfTestLog(ata_device * device, ata_selective_selftest_args & args,
+                                 const ata_smart_values * sv, uint64_t num_sectors);
 
 // Returns the latest compatibility of ATA/ATAPI Version the device
 // supports. Returns -1 if Version command is not supported
-int ataVersionInfo (const char **description, struct ata_identify_device *drive, unsigned short *minor);
+int ataVersionInfo(const char ** description, const ata_identify_device * drive, unsigned short * minor);
 
 // If SMART supported, this is guaranteed to return 1 if SMART is enabled, else 0.
-int ataDoesSmartWork(int device);
+int ataDoesSmartWork(ata_device * device);
 
 // returns 1 if SMART supported, 0 if not supported or can't tell
-int ataSmartSupport ( struct ata_identify_device *drive);
+int ataSmartSupport(const ata_identify_device * drive);
 
 // Return values:
 //  1: SMART enabled
 //  0: SMART disabled
 // -1: can't tell if SMART is enabled -- try issuing ataDoesSmartWork command to see
-int ataIsSmartEnabled(struct ata_identify_device *drive);
+int ataIsSmartEnabled(const ata_identify_device * drive);
 
 /* Check SMART for Threshold failure */
 // 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 ataSmartStatus2(int device);
-
-// int isOfflineTestTime ( struct ata_smart_values data)
-//  returns S.M.A.R.T. Offline Test Time in seconds
-int isOfflineTestTime ( struct ata_smart_values *data);
-
-int isShortSelfTestTime ( struct ata_smart_values *data);
+int ataCheckSmart(const ata_smart_values * data, const ata_smart_thresholds_pvt * thresholds, int onlyfailed);
 
-int isExtendedSelfTestTime ( struct ata_smart_values *data);
+int ataSmartStatus2(ata_device * device);
 
-int isSmartErrorLogCapable(struct ata_smart_values *data, struct ata_identify_device *identity);
+int isSmartErrorLogCapable(const ata_smart_values * data, const ata_identify_device * identity);
 
-int isSmartTestLogCapable(struct ata_smart_values *data, struct ata_identify_device *identity);
+int isSmartTestLogCapable(const ata_smart_values * data, const ata_identify_device * identity);
 
-int isGeneralPurposeLoggingCapable(struct ata_identify_device *identity);
+int isGeneralPurposeLoggingCapable(const ata_identify_device * identity);
 
-int isSupportExecuteOfflineImmediate ( struct ata_smart_values *data);
+int isSupportExecuteOfflineImmediate(const ata_smart_values * data);
 
-int isSupportAutomaticTimer ( struct ata_smart_values *data);
+int isSupportAutomaticTimer(const ata_smart_values * data);
 
-int isSupportOfflineAbort ( struct ata_smart_values *data);
+int isSupportOfflineAbort(const ata_smart_values * data);
 
-int isSupportOfflineSurfaceScan ( struct ata_smart_values *data);
+int isSupportOfflineSurfaceScan(const ata_smart_values * data);
 
-int isSupportSelfTest (struct ata_smart_values *data);
+int isSupportSelfTest(const ata_smart_values * data);
 
-int isSupportConveyanceSelfTest(struct ata_smart_values *data);
+int isSupportConveyanceSelfTest(const ata_smart_values * data);
 
-int isSupportSelectiveSelfTest(struct ata_smart_values *data);
+int isSupportSelectiveSelfTest(const ata_smart_values * data);
 
 inline bool isSCTCapable(const ata_identify_device *drive)
   { return !!(drive->words088_255[206-88] & 0x01); } // 0x01 = SCT support
@@ -558,19 +736,20 @@ inline bool isSCTFeatureControlCapable(const ata_identify_device *drive)
 inline bool isSCTDataTableCapable(const ata_identify_device *drive)
   { return ((drive->words088_255[206-88] & 0x21) == 0x21); } // 0x20 = SCT Data Table support
 
-int ataSmartTest(int device, int testtype, struct ata_smart_values *data, uint64_t num_sectors);
+int ataSmartTest(ata_device * device, int testtype, const ata_selective_selftest_args & args,
+                 const ata_smart_values * sv, uint64_t num_sectors);
 
-int TestTime(struct ata_smart_values *data,int testtype);
+int TestTime(const ata_smart_values * data, int testtype);
 
 // Prints the raw value (with appropriate formatting) into the
 // character string out.
 int64_t ataPrintSmartAttribRawValue(char *out, 
-                                    struct ata_smart_attribute *attribute,
-                                    unsigned char *defs);
+                                    const ata_smart_attribute * attribute,
+                                    const unsigned char * defs);
 
 // Prints Attribute Name for standard SMART attributes. Writes a
 // 30 byte string with attribute name into output
-void ataPrintSmartAttribName(char *output, unsigned char id, unsigned char *definitions);
+void ataPrintSmartAttribName(char * out, unsigned char id, const unsigned char * definitions);
 
 // This checks the n'th attribute in the attribute list, NOT the
 // attribute with id==n.  If the attribute does not exist, or the
@@ -578,8 +757,8 @@ void ataPrintSmartAttribName(char *output, unsigned char id, unsigned char *defi
 // <= 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 ataCheckAttribute(const ata_smart_values * data,
+                      const ata_smart_thresholds_pvt * thresholds,
                       int n);
 
 // External handler function, for when a checksum is not correct.  Can
@@ -591,11 +770,11 @@ void checksumwarning(const char *string);
 // 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);
+int64_t ATAReturnAttributeRawValue(unsigned char id, const 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);
+unsigned char ATAReturnTemperatureValue(const ata_smart_values * data, const unsigned char * defs);
 
 
 // This are the meanings of the Self-test failure checkpoint byte.
@@ -609,41 +788,62 @@ const char *SelfTestFailureCodeName(unsigned char which);
 
 #define MAX_ATTRIBUTE_NUM 256
 
-extern const char *vendorattributeargs[];
-
 // function to parse pairs like "9,minutes" or "220,temp".  See end of
 // extern.h for definition of defs[].  Returns 0 if pair recognized,
-// else 1 if there is a problem.  Allocates memory for array if the
-// array address is *defs==NULL.
-int parse_attribute_def(char *pair, unsigned char **defs);
+// else 1 if there is a problem.
+int parse_attribute_def(const char * pair, unsigned char * defs);
+
+// Get ID and increase flag of current pending or offline
+// uncorrectable attribute.
+unsigned char get_unc_attr_id(bool offline, const unsigned char * defs,
+                              bool & increase);
 
-// Function to return a string containing a list of the arguments in
-// vendorattributeargs[].  Returns NULL if the required memory can't
-// be allocated.
-char *create_vendor_attribute_arg_list(void);
+// Return a multiline string containing a list of valid arguments for
+// parse_attribute_def().
+std::string create_vendor_attribute_arg_list();
 
 
 // These are two of the functions that are defined in os_*.c and need
 // to be ported to get smartmontools onto another OS.
-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);
+// Moved to C++ interface
+//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);
+//int areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data);
 
-// "smartctl -r ataioctl,2 ..." output parser pseudo-device
-int parsedev_open(const char * name);
-void parsedev_close(int fd);
 
 // Optional functions of os_*.c
 #ifdef HAVE_ATA_IDENTIFY_IS_CACHED
 // Return true if OS caches the ATA identify sector
-int ata_identify_is_cached(int fd);
+//int ata_identify_is_cached(int fd);
 #endif
 
 // This function is exported to give low-level capability
-int smartcommandhandler(int device, smart_command_set command, int select, char *data);
+int smartcommandhandler(ata_device * device, smart_command_set command, int select, char *data);
+
+// Print one self-test log entry.
+bool ataPrintSmartSelfTestEntry(unsigned testnum, unsigned char test_type,
+                                unsigned char test_status,
+                                unsigned short timestamp,
+                                uint64_t failing_lba,
+                                bool print_error_only, bool & print_header);
+
+// Print Smart self-test log, used by smartctl and smartd.
+int ataPrintSmartSelfTestlog(const ata_smart_selftestlog * data, bool allentries,
+                             unsigned char fix_firmwarebug);
+
+// Get number of sectors from IDENTIFY sector.
+uint64_t get_num_sectors(const ata_identify_device * drive);
+
+// Convenience function for formatting strings from ata_identify_device.
+void format_ata_string(char * out, const char * in, int n, bool fix_swap);
+inline void format_ata_string(char * out, const unsigned char * in, int n, bool fix_swap)
+  { format_ata_string(out, (const char *)in, n, fix_swap); }
 
 // Utility routines.
+unsigned char checksum(const void * data);
+
 void swap2(char *location);
 void swap4(char *location);
 void swap8(char *location);
@@ -655,4 +855,8 @@ inline void swapx(unsigned int * p)
 inline void swapx(uint64_t * p)
   { swap8((char*)p); }
 
+// Return pseudo-device to parse "smartctl -r ataioctl,2 ..." output
+// and simulate an ATA device with same behaviour
+ata_device * get_parsed_ata_device(smart_interface * intf, const char * dev_name);
+
 #endif /* ATACMDS_H_ */
index 3a8675b82ec3802fadc3beb6c3560151228c8d4c..ed5f286c67c5e2e6ae8c9ada4a53f3b03e8d9052 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-9 Christian Franke <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
@@ -27,6 +28,7 @@
 #include <ctype.h>
 #include <errno.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
 #include "int64.h"
 #include "atacmdnames.h"
 #include "atacmds.h"
+#include "dev_interface.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.185 2008/03/04 22:09:47 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;
+const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 2860 2009-07-23 20:27:28Z chrfranke $"
+                                  ATAPRINT_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 format_ata_string(char *out, const char *in, int n)
-{
-  bool must_swap = !con->fixswappedid;
-#ifdef __NetBSD__
-  /* NetBSD kernel delivers IDENTIFY data in host byte order (but all else is LE) */
-  if (isbigendian())
-    must_swap = !must_swap;
-#endif
-
-  char tmp[65];
-  n = n > 64 ? 64 : n;
-  if (!must_swap)
-    strncpy(tmp, in, n);
-  else
-    swapbytes(tmp, in, n);
-  tmp[n] = '\0';
-  trim(out, tmp);
-}
-
 static const char * infofound(const char *output) {
   return (*output ? output : "[No Information Found]");
 }
@@ -117,9 +57,8 @@ static const char * infofound(const char *output) {
 
 /* 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
+ * Register (ST) and Error Register (ER).  The caller passes the string
+ * buffer and the return value is a pointer to this string.  If the
  * meanings of the flags of the error register are not known for the given
  * command then it returns NULL.
  *
@@ -129,12 +68,16 @@ static const char * infofound(const char *output) {
  * 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;
+
+static const char * construct_st_er_desc(
+  char * s,
+  unsigned char CR, unsigned char FR,
+  unsigned char ST, unsigned char ER,
+  unsigned short SC,
+  const ata_smart_errorlog_error_struct * lba28_regs,
+  const ata_smart_exterrlog_error * lba48_regs
+)
+{
   const char *error_flag[8];
   int i, print_lba=0, print_sector=0;
 
@@ -203,7 +146,7 @@ char *construct_st_er_desc(struct ata_smart_errorlog_struct *data) {
     error_flag[1] = nm;
     error_flag[0] = ccto;
     print_lba=1;
-    print_sector=(int)data->error_struct.sector_count;
+    print_sector=SC;
     break;
   case 0x3A:  // WRITE STREAM DMA
   case 0x3B:  // WRITE STREAM PIO
@@ -217,7 +160,7 @@ char *construct_st_er_desc(struct ata_smart_errorlog_struct *data) {
     error_flag[1] = nm;
     error_flag[0] = ccto;
     print_lba=1;
-    print_sector=(int)data->error_struct.sector_count;
+    print_sector=SC;
     break;
   case 0x25:  /* READ DMA EXT */
   case 0x26:  // READ DMA QUEUED EXT
@@ -234,7 +177,7 @@ char *construct_st_er_desc(struct ata_smart_errorlog_struct *data) {
     error_flag[0] = amnf;
     print_lba=1;
     if (CR==0x25 || CR==0xC8)
-      print_sector=(int)data->error_struct.sector_count;
+      print_sector=SC;
     break;
   case 0x30:  /* WRITE SECTOR(S) */
   case 0x31:  // WRITE SECTOR(S)
@@ -365,7 +308,7 @@ char *construct_st_er_desc(struct ata_smart_errorlog_struct *data) {
     error_flag[0] = amnf;
     print_lba=1;
     if (CR==0x35)
-      print_sector=(int)data->error_struct.sector_count;
+      print_sector=SC;
     break;
   case 0xE4: // READ BUFFER
   case 0xE8: // WRITE BUFFER
@@ -375,10 +318,6 @@ char *construct_st_er_desc(struct ata_smart_errorlog_struct *data) {
     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 */
@@ -404,67 +343,74 @@ char *construct_st_er_desc(struct ata_smart_errorlog_struct *data) {
   // 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);
+    if (lba28_regs) {
+      unsigned lba;
+      // bits 24-27: bits 0-3 of DH
+      lba   = 0xf & lba28_regs->drive_head;
+      lba <<= 8;
+      // bits 16-23: CH
+      lba  |= lba28_regs->cylinder_high;
+      lba <<= 8;
+      // bits 8-15:  CL
+      lba  |= lba28_regs->cylinder_low;
+      lba <<= 8;
+      // bits 0-7:   SN
+      lba  |= lba28_regs->sector_number;
+      snprintf(tmp, 128, " at LBA = 0x%08x = %u", lba, lba);
+      strcat(s, tmp);
+    }
+    else if (lba48_regs) {
+      // This assumes that upper LBA registers are 0 for 28-bit commands
+      // (TODO: detect 48-bit commands above)
+      uint64_t lba48;
+      lba48   = lba48_regs->lba_high_register_hi;
+      lba48 <<= 8;
+      lba48  |= lba48_regs->lba_mid_register_hi;
+      lba48 <<= 8;
+      lba48  |= lba48_regs->lba_low_register_hi;
+      lba48  |= lba48_regs->device_register & 0xf;
+      lba48 <<= 8;
+      lba48  |= lba48_regs->lba_high_register;
+      lba48 <<= 8;
+      lba48  |= lba48_regs->lba_mid_register;
+      lba48 <<= 8;
+      lba48  |= lba48_regs->lba_low_register;
+      snprintf(tmp, 128, " at LBA = 0x%08"PRIx64" = %"PRIu64, lba48, lba48);
+      strcat(s, tmp);
+    }
   }
 
   return s;
 }
 
+static inline const char * construct_st_er_desc(char * s,
+  const ata_smart_errorlog_struct * data)
+{
+  return construct_st_er_desc(s,
+    data->commands[4].commandreg,
+    data->commands[4].featuresreg,
+    data->error_struct.status,
+    data->error_struct.error_register,
+    data->error_struct.sector_count,
+    &data->error_struct, (const ata_smart_exterrlog_error *)0);
+}
 
-// Get number of sectors from IDENTIFY sector. If the drive doesn't
-// support LBA addressing or has no user writable sectors
-// (eg, CDROM or DVD) then routine returns zero.
-static uint64_t get_num_sectors(const ata_identify_device *drive)
+static inline const char * construct_st_er_desc(char * s,
+  const ata_smart_exterrlog_error_log * data)
 {
-  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];
-
-  // LBA support?
-  if (!(capabilities_0 & 0x0200))
-    return 0; // No
-
-  // if drive supports LBA addressing, determine 32-bit LBA capacity
-  uint64_t lba32 = (unsigned int)sects_32 << 16 | 
-                   (unsigned int)sects_16 << 0  ;
-
-  uint64_t lba64 = 0;
-  // if drive supports 48-bit addressing, determine THAT capacity
-  if ((command_set_2 & 0xc000) == 0x4000 && (command_set_2 & 0x0400))
-      lba64 = (uint64_t)lba_64 << 48 | 
-              (uint64_t)lba_48 << 32 |
-              (uint64_t)lba_32 << 16 | 
-              (uint64_t)lba_16 << 0  ;
-
-  // return the larger of the two possible capacities
-  return (lba32 > lba64 ? lba32 : lba64);
+  return construct_st_er_desc(s,
+    data->commands[4].command_register,
+    data->commands[4].features_register,
+    data->error.status_register,
+    data->error.error_register,
+    data->error.count_register_hi << 8 | data->error.count_register,
+    (const ata_smart_errorlog_error_struct *)0, &data->error);
 }
 
 
@@ -472,10 +418,10 @@ static uint64_t get_num_sectors(const ata_identify_device *drive)
 // 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){
-
+static uint64_t determine_capacity(const ata_identify_device * drive, char * pstring)
+{
   // get correct character to use as thousands separator
-  char *separator=",";
+  const char *separator = ",";
 #ifdef HAVE_LOCALE_H
   struct lconv *currentlocale=NULL;
   setlocale (LC_ALL, "");
@@ -511,42 +457,41 @@ uint64_t determine_capacity(struct ata_identify_device *drive, char *pstring){
   return retval;
 }
 
-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];
-
+static bool PrintDriveInfo(const ata_identify_device * drive, bool fix_swapped_id)
+{
   // format drive information (with byte swapping as needed)
-  format_ata_string(model, (char *)drive->model,40);
-  format_ata_string(serial, (char *)drive->serial_no,20);
-  format_ata_string(firm, (char *)drive->fw_rev,8);
+  char model[64], serial[64], firm[64];
+  format_ata_string(model, drive->model, 40, fix_swapped_id);
+  format_ata_string(serial, drive->serial_no, 20, fix_swapped_id);
+  format_ata_string(firm, drive->fw_rev, 8, fix_swapped_id);
 
   // print out model, serial # and firmware versions  (byte-swap ASCI strings)
-  drivetype=lookupdrive(model, firm);
+  const drive_settings * dbentry = lookup_drive(model, firm);
 
   // Print model family if known
-  if (drivetype>=0 && knowndrives[drivetype].modelfamily)
-    pout("Model Family:     %s\n", knowndrives[drivetype].modelfamily);
+  if (dbentry && *dbentry->modelfamily)
+    pout("Model Family:     %s\n", dbentry->modelfamily);
 
   pout("Device Model:     %s\n", infofound(model));
   if (!con->dont_print_serial)
     pout("Serial Number:    %s\n", infofound(serial));
   pout("Firmware Version: %s\n", infofound(firm));
 
+  char capacity[64];
   if (determine_capacity(drive, capacity))
     pout("User Capacity:    %s bytes\n", capacity);
   
   // See if drive is recognized
-  pout("Device is:        %s\n", drivetype<0?
+  pout("Device is:        %s\n", !dbentry ?
        "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);
+  const char *description; unsigned short minorrev;
+  int version = ataVersionInfo(&description, drive, &minorrev);
 
   // unrecognized minor revision code
+  char unknown[64];
   if (!description){
     if (!minorrev)
       sprintf(unknown, "Exact ATA specification draft version not indicated");
@@ -566,23 +511,23 @@ int ataPrintDriveInfo (struct ata_identify_device *drive){
   pout("ATA Standard is:  %s\n",description);
   
   // print current time and date and timezone
-  dateandtimezone(timedatetz);
+  char timedatetz[DATEANDEPOCHLEN]; 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 (dbentry && *dbentry->warningmsg)
+    pout("\n==> WARNING: %s\n\n", dbentry->warningmsg);
 
   if (version>=3)
-    return drivetype;
+    return !!dbentry;
   
   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;
+  return !!dbentry;
 }
 
-
-const char *OfflineDataCollectionStatus(unsigned char status_byte){
+static const char *OfflineDataCollectionStatus(unsigned char status_byte)
+{
   unsigned char stat=status_byte & 0x7f;
   
   switch(stat){
@@ -603,16 +548,16 @@ const char *OfflineDataCollectionStatus(unsigned char status_byte){
     return "was aborted by the device with a fatal error";
   default:
     if (stat >= 0x40)
-      return "is in a Vendor Specific state\n";
+      return "is in a Vendor Specific state";
     else
-      return "is in a Reserved state\n";
+      return "is in a Reserved state";
   }
 }
   
   
-  /*  prints verbose value Off-line data collection status byte */
-  void PrintSmartOfflineStatus(struct ata_smart_values *data){
-  
+//  prints verbose value Off-line data collection status byte
+static void PrintSmartOfflineStatus(const ata_smart_values * data)
+{
   pout("Offline data collection status:  (0x%02x)\t",
        (int)data->offline_data_collection_status);
     
@@ -631,7 +576,8 @@ const char *OfflineDataCollectionStatus(unsigned char status_byte){
   return;
 }
 
-void PrintSmartSelfExecStatus(struct ata_smart_values *data)
+static void PrintSmartSelfExecStatus(const ata_smart_values * data,
+                                     unsigned char fix_firmwarebug)
 {
    pout("Self-test execution status:      ");
    
@@ -691,7 +637,7 @@ void PrintSmartSelfExecStatus(struct ata_smart_values *data)
           pout("damage.\n");
           break;
        case 15:
-          if (con->fixfirmwarebug == FIX_SAMSUNG3 && data->self_test_exec_status == 0xf0) {
+          if (fix_firmwarebug == FIX_SAMSUNG3 && data->self_test_exec_status == 0xf0) {
             pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t",
                     (int)data->self_test_exec_status);
             pout("with unknown result or self-test in\n\t\t\t\t\t");
@@ -712,17 +658,15 @@ void PrintSmartSelfExecStatus(struct ata_smart_values *data)
         
 }
 
-
-
-void PrintSmartTotalTimeCompleteOffline ( struct ata_smart_values *data){
+static void PrintSmartTotalTimeCompleteOffline (const 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){
+static void PrintSmartOfflineCollectCap(const ata_smart_values *data)
+{
   pout("Offline data collection\n");
   pout("capabilities: \t\t\t (0x%02x) ",
        (int)data->offline_data_collection_capability);
@@ -761,9 +705,7 @@ void PrintSmartOfflineCollectCap(struct ata_smart_values *data){
   }
 }
 
-
-
-void PrintSmartCapability ( struct ata_smart_values *data)
+static void PrintSmartCapability(const ata_smart_values *data)
 {
    pout("SMART capabilities:            ");
    pout("(0x%04x)\t", (int)data->smart_capability);
@@ -786,9 +728,8 @@ void PrintSmartCapability ( struct ata_smart_values *data)
    }
 }
 
-void PrintSmartErrorLogCapability (struct ata_smart_values *data, struct ata_identify_device *identity)
+static void PrintSmartErrorLogCapability(const ata_smart_values * data, const ata_identify_device * identity)
 {
-
    pout("Error logging capability:       ");
     
    if ( isSmartErrorLogCapable(data, identity) )
@@ -802,7 +743,8 @@ void PrintSmartErrorLogCapability (struct ata_smart_values *data, struct ata_ide
    }
 }
 
-void PrintSmartShortSelfTestPollingTime(struct ata_smart_values *data){
+static void PrintSmartShortSelfTestPollingTime(const ata_smart_values * data)
+{
   pout("Short self-test routine \n");
   if (isSupportSelfTest(data))
     pout("recommended polling time: \t (%4d) minutes.\n", 
@@ -811,7 +753,8 @@ void PrintSmartShortSelfTestPollingTime(struct ata_smart_values *data){
     pout("recommended polling time: \t        Not Supported.\n");
 }
 
-void PrintSmartExtendedSelfTestPollingTime(struct ata_smart_values *data){
+static void PrintSmartExtendedSelfTestPollingTime(const ata_smart_values * data)
+{
   pout("Extended self-test routine\n");
   if (isSupportSelfTest(data))
     pout("recommended polling time: \t (%4d) minutes.\n", 
@@ -820,7 +763,8 @@ void PrintSmartExtendedSelfTestPollingTime(struct ata_smart_values *data){
     pout("recommended polling time: \t        Not Supported.\n");
 }
 
-void PrintSmartConveyanceSelfTestPollingTime(struct ata_smart_values *data){
+static void PrintSmartConveyanceSelfTestPollingTime(const ata_smart_values * data)
+{
   pout("Conveyance self-test routine\n");
   if (isSupportConveyanceSelfTest(data))
     pout("recommended polling time: \t (%4d) minutes.\n", 
@@ -832,29 +776,34 @@ void PrintSmartConveyanceSelfTestPollingTime(struct ata_smart_values *data){
 // 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;
+static void PrintSmartAttribWithThres(const ata_smart_values * data,
+                                      const ata_smart_thresholds_pvt * thresholds,
+                                      const unsigned char * attributedefs,
+                                      int onlyfailed)
+{
   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;
+  for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+    const char *status;
+    const ata_smart_attribute * disk = data->vendor_attributes+i;
+    const 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);
-      
+      // Don't report a failed attribute if its threshold is 0.
+      // ATA-3 (X3T13/2008D Revision 7b) declares 0x00 as the "always passing"
+      // threshold (Later ATA versions declare all thresholds as "obsolete").
+      // In practice, threshold value 0 is often used for usage attributes or
+      // appears if the thresholds cannot be read.
+      bool failednow  = (thre->threshold > 0 && disk->current <= thre->threshold);
+      bool failedever = (thre->threshold > 0 && 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;
@@ -881,7 +830,7 @@ void PrintSmartAttribWithThres (struct ata_smart_values *data,
         status="    -";
 
       // Print name of attribute
-      ataPrintSmartAttribName(attributename,disk->id, con->attributedefs);
+      ataPrintSmartAttribName(attributename, disk->id, attributedefs);
       pout("%-28s",attributename);
 
       // printing line for each valid attribute
@@ -893,14 +842,15 @@ void PrintSmartAttribWithThres (struct ata_smart_values *data,
              (int)thre->threshold, type, update, status);
 
       // print raw value of attribute
-      ataPrintSmartAttribRawValue(rawstring, disk, con->attributedefs);
+      ataPrintSmartAttribRawValue(rawstring, disk, attributedefs);
       pout("%s\n", rawstring);
       
-      // print a warning if there is inconsistency here!
-      if (disk->id != thre->id){
+      // Print a warning if there is inconsistency here and
+      // threshold info is not empty.
+      if (disk->id != thre->id && (thre->id || thre->threshold)) {
         char atdat[64],atthr[64];
-        ataPrintSmartAttribName(atdat, disk->id, con->attributedefs);
-        ataPrintSmartAttribName(atthr, thre->id, con->attributedefs);
+        ataPrintSmartAttribName(atdat, disk->id, attributedefs);
+        ataPrintSmartAttribName(atthr, thre->id, attributedefs);
         pout("%-28s<== Data Page      |  WARNING: PREVIOUS ATTRIBUTE HAS TWO\n",atdat);
         pout("%-28s<== Threshold Page |  INCONSISTENT IDENTITIES IN THE DATA\n",atthr);
       }
@@ -923,13 +873,15 @@ static void ataPrintSCTCapability(const ata_identify_device *drive)
 }
 
 
-void ataPrintGeneralSmartValues(struct ata_smart_values *data, struct ata_identify_device *drive){
+static void PrintGeneralSmartValues(const ata_smart_values *data, const ata_identify_device *drive,
+                                    unsigned char fix_firmwarebug)
+{
   pout("General SMART Values:\n");
   
   PrintSmartOfflineStatus(data); 
   
   if (isSupportSelfTest(data)){
-    PrintSmartSelfExecStatus (data);
+    PrintSmartSelfExecStatus(data, fix_firmwarebug);
   }
   
   PrintSmartTotalTimeCompleteOffline(data);
@@ -954,65 +906,197 @@ void ataPrintGeneralSmartValues(struct ata_smart_values *data, struct ata_identi
   pout("\n");
 }
 
-int ataPrintLogDirectory(struct ata_smart_log_directory *data){
-  int i;
-  char *name;
+// Get # sectors of a log addr, 0 if log does not exist.
+static unsigned GetNumLogSectors(const ata_smart_log_directory * logdir, unsigned logaddr, bool gpl)
+{
+  if (!logdir)
+    return 0;
+  if (logaddr > 0xff)
+    return 0;
+  if (logaddr == 0)
+    return 1;
+  unsigned n = logdir->entry[logaddr-1].numsectors;
+  if (gpl)
+    // GP logs may have >255 sectors
+    n |= logdir->entry[logaddr-1].reserved << 8;
+  return n;
+}
 
-  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;
+// Get name of log.
+// Table A.2 of T13/1699-D Revision 6
+static const char * GetLogName(unsigned logaddr)
+{
+    switch (logaddr) {
+      case 0x00: return "Log Directory";
+      case 0x01: return "Summary SMART error log";
+      case 0x02: return "Comprehensive SMART error log";
+      case 0x03: return "Ext. Comprehensive SMART error log";
+      case 0x04: return "Device Statistics";
+      case 0x06: return "SMART self-test log";
+      case 0x07: return "Extended self-test log";
+      case 0x09: return "Selective self-test log";
+      case 0x10: return "NCQ Command Error";
+      case 0x11: return "SATA Phy Event Counters";
+      case 0x20: return "Streaming performance log"; // Obsolete
+      case 0x21: return "Write stream error log";
+      case 0x22: return "Read stream error log";
+      case 0x23: return "Delayed sector log"; // Obsolete
+      case 0xe0: return "SCT Command/Status";
+      case 0xe1: return "SCT Data Transfer";
       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;
-      }
+        if (0xa0 <= logaddr && logaddr <= 0xdf)
+          return "Device vendor specific log";
+        if (0x80 <= logaddr && logaddr <= 0x9f)
+          return "Host vendor specific log";
+        if (0x12 <= logaddr && logaddr <= 0x17)
+          return "Reserved for Serial ATA";
+        return "Reserved";
+    }
+    /*NOTREACHED*/
+}
 
-      // print name and length of log
-      pout("Log at address 0x%02x has %03d sectors [%s]\n",
-           i, numsect, name);
+// Print SMART and/or GP Log Directory
+static void PrintLogDirectories(const ata_smart_log_directory * gplogdir,
+                                const ata_smart_log_directory * smartlogdir)
+{
+  if (gplogdir)
+    pout("General Purpose Log Directory Version %u\n", gplogdir->logversion);
+  if (smartlogdir)
+    pout("SMART %sLog Directory Version %u%s\n",
+         (gplogdir ? "          " : ""), smartlogdir->logversion,
+         (smartlogdir->logversion==1 ? " [multi-sector log support]" : ""));
+
+  for (unsigned i = 0; i <= 0xff; i++) {
+    // Get number of sectors
+    unsigned smart_numsect = GetNumLogSectors(smartlogdir, i, false);
+    unsigned gp_numsect    = GetNumLogSectors(gplogdir   , i, true );
+
+    if (!(smart_numsect || gp_numsect))
+      continue; // Log does not exist
+
+    const char * name = GetLogName(i);
+
+    // Print name and length of log.
+    // If both SMART and GP exist, print separate entries if length differ.
+    if (smart_numsect == gp_numsect)
+      pout(  "GP/S  Log at address 0x%02x has %4d sectors [%s]\n", i, smart_numsect, name);
+    else {
+      if (gp_numsect)
+        pout("GP %sLog at address 0x%02x has %4d sectors [%s]\n", (smartlogdir?"   ":""),
+             i, gp_numsect, name);
+      if (smart_numsect)
+        pout("SMART Log at address 0x%02x has %4d sectors [%s]\n", i, smart_numsect, name);
     }
   }
-  return 0;
+  pout("\n");
 }
 
-// returns number of errors
-int ataPrintSmartErrorlog(struct ata_smart_errorlog *data){
-  int k;
+// Print hexdump of log pages.
+// Format is compatible with 'xxd -r'.
+static void PrintLogPages(const char * type, const unsigned char * data,
+                          unsigned char logaddr, unsigned page,
+                          unsigned num_pages, unsigned max_pages)
+{
+  pout("%s Log 0x%02x [%s], Page %u-%u (of %u)\n",
+    type, logaddr, GetLogName(logaddr), page, page+num_pages-1, max_pages);
+  for (unsigned i = 0; i < num_pages * 512; i += 16) {
+    const unsigned char * p = data+i;
+    pout("%07x: %02x %02x %02x %02x %02x %02x %02x %02x "
+               "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+         (page * 512) + i,
+         p[ 0], p[ 1], p[ 2], p[ 3], p[ 4], p[ 5], p[ 6], p[ 7],
+         p[ 8], p[ 9], p[10], p[11], p[12], p[13], p[14], p[15]);
+    if ((i & 0x1ff) == 0x1f0)
+      pout("\n");
+  }
+}
+
+// Print log 0x11
+static void PrintSataPhyEventCounters(const unsigned char * data, bool reset)
+{
+  if (checksum(data))
+    checksumwarning("SATA Phy Event Counters");
+  pout("SATA Phy Event Counters (GP Log 0x11)\n");
+  if (data[0] || data[1] || data[2] || data[3])
+    pout("[Reserved: 0x%02x 0x%02x 0x%02x 0x%02x]\n",
+    data[0], data[1], data[2], data[3]);
+  pout("ID      Size     Value  Description\n");
+
+  for (unsigned i = 4; ; ) {
+    // Get counter id and size (bits 14:12)
+    unsigned id = data[i] | (data[i+1] << 8);
+    unsigned size = ((id >> 12) & 0x7) << 1;
+    id &= 0x8fff;
+
+    // End of counter table ?
+    if (!id)
+      break;
+    i += 2;
 
+    if (!(2 <= size && size <= 8 && i + size < 512)) {
+      pout("0x%04x  %u: Invalid entry\n", id, size);
+      break;
+    }
+
+    // Get value
+    uint64_t val = 0, max_val = 0;
+    for (unsigned j = 0; j < size; j+=2) {
+        val |= (uint64_t)(data[i+j] | (data[i+j+1] << 8)) << (j*8);
+        max_val |= (uint64_t)0xffffU << (j*8);
+    }
+    i += size;
+
+    // Get name
+    const char * name;
+    switch (id) {
+      case 0x001: name = "Command failed due to ICRC error"; break; // Mandatory
+      case 0x002: name = "R_ERR response for data FIS"; break;
+      case 0x003: name = "R_ERR response for device-to-host data FIS"; break;
+      case 0x004: name = "R_ERR response for host-to-device data FIS"; break;
+      case 0x005: name = "R_ERR response for non-data FIS"; break;
+      case 0x006: name = "R_ERR response for device-to-host non-data FIS"; break;
+      case 0x007: name = "R_ERR response for host-to-device non-data FIS"; break;
+      case 0x008: name = "Device-to-host non-data FIS retries"; break;
+      case 0x009: name = "Transition from drive PhyRdy to drive PhyNRdy"; break;
+      case 0x00A: name = "Device-to-host register FISes sent due to a COMRESET"; break; // Mandatory
+      case 0x00B: name = "CRC errors within host-to-device FIS"; break;
+      case 0x00D: name = "Non-CRC errors within host-to-device FIS"; break;
+      case 0x00F: name = "R_ERR response for host-to-device data FIS, CRC"; break;
+      case 0x010: name = "R_ERR response for host-to-device data FIS, non-CRC"; break;
+      case 0x012: name = "R_ERR response for host-to-device non-data FIS, CRC"; break;
+      case 0x013: name = "R_ERR response for host-to-device non-data FIS, non-CRC"; break;
+      default:    name = (id & 0x8000 ? "Vendor specific" : "Unknown"); break;
+    }
+
+    // Counters stop at max value, add '+' in this case
+    pout("0x%04x  %u %12"PRIu64"%c %s\n", id, size, val,
+      (val == max_val ? '+' : ' '), name);
+  }
+  if (reset)
+    pout("All counters reset\n");
+  pout("\n");
+}
+
+// Get description for 'state' value from SMART Error Logs
+static const char * get_error_log_state_desc(unsigned state)
+{
+  state &= 0x0f;
+  switch (state){
+    case 0x0: return "in an unknown state";
+    case 0x1: return "sleeping";
+    case 0x2: return "in standby mode";
+    case 0x3: return "active or idle";
+    case 0x4: return "doing SMART Offline or Self-test";
+  default:
+    return (state < 0xb ? "in a reserved state"
+                        : "in a vendor specific state");
+  }
+}
+
+// returns number of errors
+static int PrintSmartErrorlog(const ata_smart_errorlog *data,
+                              unsigned char fix_firmwarebug)
+{
   pout("SMART Error Log Version: %d\n", (int)data->revnumber);
   
   // if no errors logged, return
@@ -1030,7 +1114,7 @@ int ataPrintSmartErrorlog(struct ata_smart_errorlog *data){
   }
 
   // Some internal consistency checking of the data structures
-  if ((data->ata_error_count-data->error_log_pointer)%5 && con->fixfirmwarebug != FIX_SAMSUNG2) {
+  if ((data->ata_error_count-data->error_log_pointer)%5 && fix_firmwarebug != FIX_SAMSUNG2) {
     pout("Warning: ATA error count %d inconsistent with error log pointer %d\n\n",
          data->ata_error_count,data->error_log_pointer);
   }
@@ -1057,34 +1141,19 @@ int ataPrintSmartErrorlog(struct ata_smart_errorlog *data){
        "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;
+  for (int k = 4; k >= 0; k-- ) {
 
     // 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);
+    const ata_smart_errorlog_struct * elog = data->errorlog_struct+i;
+    const 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;
+      const char *msgstate = get_error_log_state_desc(summary->state);
       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",
@@ -1104,17 +1173,16 @@ int ataPrintSmartErrorlog(struct ata_smart_errorlog *data){
            (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) {
+      char descbuf[256];
+      const char * st_er_desc = construct_st_er_desc(descbuf, 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;
+        const 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))) {
@@ -1146,9 +1214,216 @@ int ataPrintSmartErrorlog(struct ata_smart_errorlog *data){
   return data->ata_error_count;  
 }
 
-void ataPrintSelectiveSelfTestLog(struct ata_selective_self_test_log *log, struct ata_smart_values *sv) {
+// Print SMART Extended Comprehensive Error Log (GP Log 0x03)
+static int PrintSmartExtErrorLog(const ata_smart_exterrlog * log,
+                                 unsigned nsectors, unsigned max_errors)
+{
+  pout("SMART Extended Comprehensive Error Log Version: %u (%u sectors)\n",
+       log->version, nsectors);
+
+  if (!log->device_error_count) {
+    pout("No Errors Logged\n\n");
+    return 0;
+  }
+  PRINT_ON(con);
+
+  // Check index
+  unsigned nentries = nsectors * 4;
+  unsigned erridx = log->error_log_index;
+  if (!(1 <= erridx && erridx <= nentries)){
+    // Some Samsung disks (at least SP1614C/SW100-25, HD300LJ/ZT100-12) use the
+    // former index from Summary Error Log (byte 1, now reserved) and set byte 2-3
+    // to 0.
+    if (!(erridx == 0 && 1 <= log->reserved1 && log->reserved1 <= nentries)) {
+      pout("Invalid Error Log index = 0x%04x (reserved = 0x%02x)\n", erridx, log->reserved1);
+      return 0;
+    }
+    pout("Invalid Error Log index = 0x%04x, trying reserved byte (0x%02x) instead\n", erridx, log->reserved1);
+    erridx = log->reserved1;
+  }
+
+  // Index base is not clearly specified by ATA8-ACS (T13/1699-D Revision 6a),
+  // it is 1-based in practice.
+  erridx--;
+
+  // Calculate #errors to print
+  unsigned errcnt = log->device_error_count;
+
+  if (errcnt <= nentries)
+    pout("Device Error Count: %u\n", log->device_error_count);
+  else {
+    errcnt = nentries;
+    pout("Device Error Count: %u (device log contains only the most recent %u errors)\n",
+         log->device_error_count, errcnt);
+  }
+
+  if (max_errors < errcnt)
+    errcnt = max_errors;
+
+  PRINT_OFF(con);
+  pout("\tCR     = Command Register\n"
+       "\tFEATR  = Features Register\n"
+       "\tCOUNT  = Count (was: Sector Count) Register\n"
+       "\tLBA_48 = Upper bytes of LBA High/Mid/Low Registers ]  ATA-8\n"
+       "\tLH     = LBA High (was: Cylinder High) Register    ]   LBA\n"
+       "\tLM     = LBA Mid (was: Cylinder Low) Register      ] Register\n"
+       "\tLL     = LBA Low (was: Sector Number) Register     ]\n"
+       "\tDV     = Device (was: Device/Head) Register\n"
+       "\tDC     = Device Control Register\n"
+       "\tER     = Error register\n"
+       "\tST     = Status register\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");
+
+  // Iterate through circular buffer in reverse direction
+  for (unsigned i = 0, errnum = log->device_error_count;
+       i < errcnt; i++, errnum--, erridx = (erridx > 0 ? erridx - 1 : nentries - 1)) {
+
+    const ata_smart_exterrlog_error_log & entry = log[erridx / 4].error_logs[erridx % 4];
+
+    // Skip unused entries
+    if (!nonempty(&entry, sizeof(entry))) {
+      pout("Error %u [%u] log entry is empty\n", errnum, erridx);
+      continue;
+    }
+
+    // Print error information
+    PRINT_ON(con);
+    const ata_smart_exterrlog_error & err = entry.error;
+    pout("Error %u [%u] occurred at disk power-on lifetime: %u hours (%u days + %u hours)\n",
+         errnum, erridx, err.timestamp, err.timestamp / 24, err.timestamp % 24);
+    PRINT_OFF(con);
+
+    pout("  When the command that caused the error occurred, the device was %s.\n\n",
+      get_error_log_state_desc(err.state));
+
+    // Print registers
+    pout("  After command completion occurred, registers were:\n"
+         "  ER -- ST COUNT  LBA_48  LH LM LL DV DC\n"
+         "  -- -- -- == -- == == == -- -- -- -- --\n"
+         "  %02x -- %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
+         err.error_register,
+         err.status_register,
+         err.count_register_hi,
+         err.count_register,
+         err.lba_high_register_hi,
+         err.lba_mid_register_hi,
+         err.lba_low_register_hi,
+         err.lba_high_register,
+         err.lba_mid_register,
+         err.lba_low_register,
+         err.device_register,
+         err.device_control_register);
+
+    // Add a description of the contents of the status and error registers
+    // if possible
+    char descbuf[256];
+    const char * st_er_desc = construct_st_er_desc(descbuf, &entry);
+    if (st_er_desc)
+      pout("  %s", st_er_desc);
+    pout("\n\n");
+
+    // Print command history
+    pout("  Commands leading to the command that caused the error were:\n"
+         "  CR FEATR COUNT  LBA_48  LH LM LL DV DC  Powered_Up_Time  Command/Feature_Name\n"
+         "  -- == -- == -- == == == -- -- -- -- --  ---------------  --------------------\n");
+    for (int ci = 4; ci >= 0; ci--) {
+      const ata_smart_exterrlog_command & cmd = entry.commands[ci];
+
+      // Skip unused entries
+      if (!nonempty(&cmd, sizeof(cmd)))
+        continue;
+
+      // Print registers, timestamp and ATA command name
+      char timestring[32];
+      MsecToText(cmd.timestamp, timestring);
+
+      pout("  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %16s  %s\n",
+           cmd.command_register,
+           cmd.features_register_hi,
+           cmd.features_register,
+           cmd.count_register_hi,
+           cmd.count_register,
+           cmd.lba_high_register_hi,
+           cmd.lba_mid_register_hi,
+           cmd.lba_low_register_hi,
+           cmd.lba_high_register,
+           cmd.lba_mid_register,
+           cmd.lba_low_register,
+           cmd.device_register,
+           cmd.device_control_register,
+           timestring,
+           look_up_ata_command(cmd.command_register, cmd.features_register));
+    }
+    pout("\n");
+  }
+
+  PRINT_ON(con);
+  if (con->printing_switchable)
+    pout("\n");
+  PRINT_OFF(con);
+  return log->device_error_count;
+}
+
+// Print SMART Extended Self-test Log (GP Log 0x07)
+static void PrintSmartExtSelfTestLog(const ata_smart_extselftestlog * log,
+                                     unsigned nsectors, unsigned max_entries)
+{
+  pout("SMART Extended Self-test Log Version: %u (%u sectors)\n",
+       log->version, nsectors);
+
+  if (!log->log_desc_index){
+    pout("No self-tests have been logged.  [To run self-tests, use: smartctl -t]\n\n");
+    return;
+  }
+
+  // Check index
+  unsigned nentries = nsectors * 19;
+  unsigned logidx = log->log_desc_index;
+  if (logidx > nentries) {
+    pout("Invalid Self-test Log index = 0x%04x (reserved = 0x%02x)\n", logidx, log->reserved1);
+    return;
+  }
+
+  // Index base is not clearly specified by ATA8-ACS (T13/1699-D Revision 6a),
+  // it is 1-based in practice.
+  logidx--;
+
+  bool print_header = true;
+
+  // Iterate through circular buffer in reverse direction
+  for (unsigned i = 0, testnum = 1;
+       i < nentries && testnum <= max_entries;
+       i++, logidx = (logidx > 0 ? logidx - 1 : nentries - 1)) {
+
+    const ata_smart_extselftestlog_desc & entry = log[logidx / 19].log_descs[logidx % 19];
+
+    // Skip unused entries
+    if (!nonempty(&entry, sizeof(entry)))
+      continue;
+
+    // Get LBA
+    const unsigned char * b = entry.failing_lba;
+    uint64_t lba48 = b[0]
+        | (          b[1] <<  8)
+        | (          b[2] << 16)
+        | ((uint64_t)b[3] << 24)
+        | ((uint64_t)b[4] << 32)
+        | ((uint64_t)b[5] << 40);
+
+    // Print entry
+    ataPrintSmartSelfTestEntry(testnum++, entry.self_test_type,
+      entry.self_test_status, entry.timestamp, lba48,
+      false /*!print_error_only*/, print_header);
+  }
+  pout("\n");
+}
+
+static void ataPrintSelectiveSelfTestLog(const ata_selective_self_test_log * log, const ata_smart_values * sv)
+{
   int i,field1,field2;
-  char *msg;
+  const char *msg;
   char tmp[64];
   uint64_t maxl=0,maxr=0;
   uint64_t current=log->currentlba;
@@ -1157,7 +1432,7 @@ void ataPrintSelectiveSelfTestLog(struct ata_selective_self_test_log *log, struc
   // 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");
+    pout("Note: revision number not 1 implies that no selective self-test has ever been run\n");
   
   switch((sv->self_test_exec_status)>>4){
   case  0:msg="Completed";
@@ -1263,139 +1538,6 @@ void ataPrintSelectiveSelfTestLog(struct ata_selective_self_test_log *log, struc
   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");
-}
-
-
 // Format SCT Temperature value
 static const char * sct_ptemp(signed char x, char * buf)
 {
@@ -1559,38 +1701,23 @@ void failuretest(int type, int returnvalue){
   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;
+static ata_identify_device drive;
+static ata_smart_values smartval;
+static ata_smart_thresholds_pvt smartthres;
+static ata_smart_errorlog smarterror;
+static ata_smart_selftestlog smartselftest;
 
-int ataPrintMain (int fd){
+int ataPrintMain (ata_device * device, const ata_print_options & options)
+{
   int timewait,code;
-  int returnval=0, retid=0, supported=0, needupdate=0, known=0;
+  int returnval=0, retid=0, supported=0, needupdate=0;
   const char * powername = 0; char powerchg = 0;
 
   // If requested, check power mode first
-  if (con->powermode) {
+  if (options.powermode) {
     unsigned char powerlimit = 0xff;
-    int powermode = ataCheckPowerMode(fd);
+    int powermode = ataCheckPowerMode(device);
     switch (powermode) {
       case -1:
         if (errno == ENOSYS) {
@@ -1609,7 +1736,7 @@ int ataPrintMain (int fd){
         break;
     }
     if (powername) {
-      if (con->powermode >= powerlimit) {
+      if (options.powermode >= powerlimit) {
         pout("Device is in %s mode, exit(%d)\n", powername, FAILPOWER);
         return FAILPOWER;
       }
@@ -1618,32 +1745,29 @@ int ataPrintMain (int fd){
   }
 
   // Start by getting Drive ID information.  We need this, to know if SMART is supported.
-  if ((retid=ataReadHDIdentity(fd,&drive))<0){
+  if ((retid=ataReadHDIdentity(device,&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);
+  if (options.show_presets) {
+    show_presets(&drive, options.fix_swapped_id);
     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);
-    }
-  }
+  unsigned char attributedefs[256];
+  memcpy(attributedefs, options.attributedefs, sizeof(attributedefs));
+  unsigned char fix_firmwarebug = options.fix_firmwarebug;
+  if (!options.ignore_presets)
+    apply_presets(&drive, attributedefs, fix_firmwarebug, options.fix_swapped_id);
 
   // Print most drive identity information if requested
-  if (con->driveinfo){
+  bool known = false;
+  if (options.drive_info) {
     pout("=== START OF INFORMATION SECTION ===\n");
-    known = ataPrintDriveInfo(&drive);
+    known = PrintDriveInfo(&drive, options.fix_swapped_id);
   }
 
   // Was this a packet device?
@@ -1666,7 +1790,7 @@ int ataPrintMain (int fd){
       pout("                  Checking for SMART support by trying SMART ENABLE command.\n");
     }
 
-    if (ataEnableSmart(fd)){
+    if (ataEnableSmart(device)){
       pout("                  SMART ENABLE failed - this establishes that this device lacks SMART functionality.\n");
       failuretest(MANDATORY_CMD, returnval|=FAILSMART);
       supported=0;
@@ -1675,11 +1799,12 @@ int ataPrintMain (int fd){
       pout("                  SMART ENABLE appeared to work!  Continuing.\n");
       supported=1;
     }
-    if (!con->driveinfo) pout("\n");
+    if (!options.drive_info)
+      pout("\n");
   }
   
   // Now print remaining drive info: is SMART enabled?    
-  if (con->driveinfo){
+  if (options.drive_info) {
     int ison=ataIsSmartEnabled(&drive),isenabled=ison;
     
     if (ison==-1) {
@@ -1687,17 +1812,15 @@ int ataPrintMain (int fd){
       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);
+      isenabled=ataDoesSmartWork(device);
     }
     else {
       pout("SMART support is: Available - device has SMART capability.\n");
-#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
-      if (ata_identify_is_cached(fd)) {
+      if (device->ata_identify_is_cached()) {
         pout("                  %sabled status cached by OS, trying SMART RETURN STATUS cmd.\n",
                     (isenabled?"En":"Dis"));
-        isenabled=ataDoesSmartWork(fd);
+        isenabled=ataDoesSmartWork(device);
       }
-#endif
     }
 
     if (isenabled)
@@ -1715,14 +1838,14 @@ int ataPrintMain (int fd){
   }
   
   // START OF THE ENABLE/DISABLE SECTION OF THE CODE
-  if (con->smartenable || con->smartdisable || 
-      con->smartautosaveenable || con->smartautosavedisable || 
-      con->smartautoofflineenable || con->smartautoofflinedisable)
+  if (   options.smart_disable           || options.smart_enable
+      || options.smart_auto_save_disable || options.smart_auto_save_enable
+      || options.smart_auto_offl_disable || options.smart_auto_offl_enable)
     pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
   
   // Enable/Disable SMART commands
-  if (con->smartenable){
-    if (ataEnableSmart(fd)) {
+  if (options.smart_enable) {
+    if (ataEnableSmart(device)) {
       pout("Smartctl: SMART Enable Failed.\n\n");
       failuretest(MANDATORY_CMD, returnval|=FAILSMART);
     }
@@ -1731,14 +1854,14 @@ int ataPrintMain (int fd){
   }
   
   // From here on, every command requires that SMART be enabled...
-  if (!ataDoesSmartWork(fd)) {
+  if (!ataDoesSmartWork(device)) {
     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)) {
+  if (options.smart_disable) {
+    if (ataDisableSmart(device)) {
       pout( "Smartctl: SMART Disable Failed.\n\n");
       failuretest(MANDATORY_CMD,returnval|=FAILSMART);
     }
@@ -1747,21 +1870,22 @@ int ataPrintMain (int fd){
   }
   
   // Let's ALWAYS issue this command to get the SMART status
-  code=ataSmartStatus2(fd);
+  code=ataSmartStatus2(device);
   if (code==-1)
     failuretest(MANDATORY_CMD, returnval|=FAILSMART);
 
   // Enable/Disable Auto-save attributes
-  if (con->smartautosaveenable){
-    if (ataEnableAutoSave(fd)){
+  if (options.smart_auto_save_enable) {
+    if (ataEnableAutoSave(device)){
       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)){
+
+  if (options.smart_auto_save_disable) {
+    if (ataDisableAutoSave(device)){
       pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n");
       failuretest(MANDATORY_CMD, returnval|=FAILSMART);
     }
@@ -1770,36 +1894,37 @@ int ataPrintMain (int fd){
   }
   
   // for everything else read values and thresholds are needed
-  if (ataReadSmartValues(fd, &smartval)){
+  if (ataReadSmartValues(device, &smartval)){
     pout("Smartctl: SMART Read Values failed.\n\n");
     failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
   }
-  if (ataReadSmartThresholds(fd, &smartthres)){
+  if (ataReadSmartThresholds(device, &smartthres)){
     pout("Smartctl: SMART Read Thresholds failed.\n\n");
     failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
   }
 
   // Enable/Disable Off-line testing
-  if (con->smartautoofflineenable){
+  if (options.smart_auto_offl_enable) {
     if (!isSupportAutomaticTimer(&smartval)){
       pout("Warning: device does not support SMART Automatic Timers.\n\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
     needupdate=1;
-    if (ataEnableAutoOffline(fd)){
+    if (ataEnableAutoOffline(device)){
       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 (options.smart_auto_offl_disable) {
     if (!isSupportAutomaticTimer(&smartval)){
       pout("Warning: device does not support SMART Automatic Timers.\n\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
     needupdate=1;
-    if (ataDisableAutoOffline(fd)){
+    if (ataDisableAutoOffline(device)){
       pout("Smartctl: SMART Disable Automatic Offline Failed.\n\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
@@ -1807,36 +1932,39 @@ int ataPrintMain (int fd){
       pout("SMART Automatic Offline Testing Disabled.\n");
   }
 
-  if (needupdate && ataReadSmartValues(fd, &smartval)){
+  if (needupdate && ataReadSmartValues(device, &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)
+  if (   options.smart_disable           || options.smart_enable
+      || options.smart_auto_save_disable || options.smart_auto_save_enable
+      || options.smart_auto_offl_disable || options.smart_auto_offl_enable)
     pout("\n");
 
   // START OF READ-ONLY OPTIONS APART FROM -V and -i
-  if (   con->checksmart || con->generalsmartvalues || con->smartvendorattrib || con->smarterrorlog
-      || con->smartselftestlog || con->selectivetestlog || con->scttempsts || con->scttemphist     )
+  if (   options.smart_check_status  || options.smart_general_values
+      || options.smart_vendor_attrib || options.smart_error_log
+      || options.smart_selftest_log  || options.smart_selective_selftest_log
+      || options.smart_ext_error_log || options.smart_ext_selftest_log
+      || options.sct_temp_sts        || options.sct_temp_hist               )
     pout("=== START OF READ SMART DATA SECTION ===\n");
   
   // Check SMART status (use previously returned value)
-  if (con->checksmart){
+  if (options.smart_check_status) {
     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)
+        if (options.smart_vendor_attrib)
           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);
+          PrintSmartAttribWithThres(&smartval, &smartthres, attributedefs, 2);
         } 
         returnval|=FAILAGE;
       }
@@ -1852,12 +1980,12 @@ int ataPrintMain (int fd){
       PRINT_OFF(con);
       if (ataCheckSmart(&smartval, &smartthres,1)){
         returnval|=FAILATTR;
-        if (con->smartvendorattrib)
+        if (options.smart_vendor_attrib)
           pout("See vendor-specific Attribute list for failed Attributes.\n\n");
         else {
           PRINT_ON(con);
           pout("Failed Attributes:\n");
-          PrintSmartAttribWithThres(&smartval, &smartthres,1);
+          PrintSmartAttribWithThres(&smartval, &smartthres, attributedefs, 1);
         }
       }
       else
@@ -1876,23 +2004,23 @@ int ataPrintMain (int fd){
         PRINT_OFF(con);
         returnval|=FAILATTR;
         returnval|=FAILSTATUS;
-        if (con->smartvendorattrib)
+        if (options.smart_vendor_attrib)
           pout("See vendor-specific Attribute list for failed Attributes.\n\n");
         else {
           PRINT_ON(con);
           pout("Failed Attributes:\n");
-          PrintSmartAttribWithThres(&smartval, &smartthres,1);
+          PrintSmartAttribWithThres(&smartval, &smartthres, attributedefs, 1);
         }
       }
       else {
         pout("SMART overall-health self-assessment test result: PASSED\n");
         if (ataCheckSmart(&smartval, &smartthres,0)){
-          if (con->smartvendorattrib)
+          if (options.smart_vendor_attrib)
             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);
+            PrintSmartAttribWithThres(&smartval, &smartthres, attributedefs, 2);
           } 
           returnval|=FAILAGE;
         }
@@ -1907,68 +2035,210 @@ int ataPrintMain (int fd){
   } // end of checking SMART Status
   
   // Print general SMART values
-  if (con->generalsmartvalues)
-    ataPrintGeneralSmartValues(&smartval, &drive); 
+  if (options.smart_general_values)
+    PrintGeneralSmartValues(&smartval, &drive, fix_firmwarebug);
 
   // Print vendor-specific attributes
-  if (con->smartvendorattrib){
+  if (options.smart_vendor_attrib) {
     PRINT_ON(con);
-    PrintSmartAttribWithThres(&smartval, &smartthres,con->printing_switchable?2:0);
+    PrintSmartAttribWithThres(&smartval, &smartthres, attributedefs,
+                              (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);
+  // Print SMART and/or GP log Directory and/or logs
+  // Get #pages for extended SMART logs
+  ata_smart_log_directory smartlogdir_buf, gplogdir_buf;
+  const ata_smart_log_directory * smartlogdir = 0, * gplogdir = 0;
+
+  if (   options.gp_logdir || options.smart_logdir
+      || options.sataphy || options.smart_ext_error_log
+      || options.smart_ext_selftest_log
+      || !options.log_requests.empty()                 ) {
+    PRINT_ON(con);
+    if (isGeneralPurposeLoggingCapable(&drive))
+      pout("General Purpose Logging (GPL) feature set supported\n");
+
+    // Detect directories needed
+    bool need_smart_logdir = options.smart_logdir;
+    bool need_gp_logdir    = (   options.gp_logdir
+                              || options.smart_ext_error_log
+                              || options.smart_ext_selftest_log);
+    unsigned i;
+    for (i = 0; i < options.log_requests.size(); i++) {
+      if (options.log_requests[i].gpl)
+        need_gp_logdir = true;
+      else
+        need_smart_logdir = true;
     }
-    else {
-      PRINT_ON(con);
-      pout("Log Directory Supported\n");
-      if (ataReadLogDirectory(fd, &smartlogdirectory)){
+
+    // Read SMART Log directory
+    if (need_smart_logdir) {
+      if (ataReadLogDirectory(device, &smartlogdir_buf, false)){
+        PRINT_OFF(con);
+        pout("Read SMART Log Directory failed.\n\n");
+        failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+      }
+      else
+        smartlogdir = &smartlogdir_buf;
+    }
+    PRINT_ON(con);
+
+    // Read GP Log directory
+    if (need_gp_logdir) {
+      if (ataReadLogDirectory(device, &gplogdir_buf, true)){
         PRINT_OFF(con);
-        pout("Read Log Directory failed.\n\n");
+        pout("Read GP Log Directory failed.\n\n");
         failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
       }
       else
-        ataPrintLogDirectory( &smartlogdirectory);
+        gplogdir = &gplogdir_buf;
     }
+    PRINT_ON(con);
+
+    // Print log directories
+    if ((options.gp_logdir && gplogdir) || (options.smart_logdir && smartlogdir))
+      PrintLogDirectories(gplogdir, smartlogdir);
     PRINT_OFF(con);
+
+    // Print log pages
+    for (i = 0; i < options.log_requests.size(); i++) {
+      const ata_log_request & req = options.log_requests[i];
+
+      const char * type;
+      unsigned max_nsectors;
+      if (req.gpl) {
+        type = "General Purpose";
+        max_nsectors = GetNumLogSectors(gplogdir, req.logaddr, true);
+      }
+      else {
+        type = "SMART";
+        max_nsectors = GetNumLogSectors(smartlogdir, req.logaddr, false);
+      }
+
+      if (!max_nsectors) {
+        if (!con->permissive) {
+          pout("%s Log 0x%02x does not exist (override with '-T permissive' option)\n", type, req.logaddr);
+          continue;
+        }
+        con->permissive--;
+        max_nsectors = req.page+1;
+      }
+      if (max_nsectors <= req.page) {
+        pout("%s Log 0x%02x has only %u sectors, output skipped\n", type, req.logaddr, max_nsectors);
+        continue;
+      }
+
+      unsigned ns = req.nsectors;
+      if (ns > max_nsectors - req.page) {
+        if (req.nsectors != ~0U) // "FIRST-max"
+          pout("%s Log 0x%02x has only %u sectors, output truncated\n", type, req.logaddr, max_nsectors);
+        ns = max_nsectors - req.page;
+      }
+
+      // SMART log don't support sector offset, start with first sector
+      unsigned offs = (req.gpl ? 0 : req.page);
+
+      raw_buffer log_buf((offs + ns) * 512);
+      bool ok;
+      if (req.gpl)
+        ok = ataReadLogExt(device, req.logaddr, 0x00, req.page, log_buf.data(), ns);
+      else
+        ok = ataReadSmartLog(device, req.logaddr, log_buf.data(), offs + ns);
+      if (!ok)
+        failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+      else
+        PrintLogPages(type, log_buf.data() + offs*512, req.logaddr, req.page, ns, max_nsectors);
+    }
   }
-  
+
+  // Print SMART Extendend Comprehensive Error Log
+  bool do_smart_error_log = options.smart_error_log;
+  if (options.smart_ext_error_log) {
+    bool ok = false;
+    unsigned nsectors = GetNumLogSectors(gplogdir, 0x03, true);
+    if (!nsectors)
+      pout("SMART Extended Comprehensive Error Log (GP Log 0x03) not supported\n");
+    else if (nsectors >= 256)
+      pout("SMART Extended Comprehensive Error Log size %u not supported\n", nsectors);
+    else {
+      raw_buffer log_03_buf(nsectors * 512);
+      ata_smart_exterrlog * log_03 = (ata_smart_exterrlog *)log_03_buf.data();
+      if (!ataReadExtErrorLog(device, log_03, nsectors))
+        failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+      else {
+        PrintSmartExtErrorLog(log_03, nsectors, options.smart_ext_error_log);
+        ok = true;
+      }
+    }
+
+    if (!ok) {
+      if (options.retry_error_log)
+        do_smart_error_log = true;
+      else if (!do_smart_error_log)
+        pout("Try '-l [xerror,]error' to read traditional SMART Error Log\n");
+    }
+  }
+
   // Print SMART error log
-  if (con->smarterrorlog){
+  if (do_smart_error_log) {
     if (!isSmartErrorLogCapable(&smartval, &drive)){
       pout("Warning: device does not support Error Logging\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
-    if (ataReadErrorLog(fd, &smarterror)){
+    if (ataReadErrorLog(device, &smarterror, fix_firmwarebug)){
       pout("Smartctl: SMART Error Log Read Failed\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
     else {
       // quiet mode is turned on inside ataPrintSmartErrorLog()
-      if (ataPrintSmartErrorlog(&smarterror))
+      if (PrintSmartErrorlog(&smarterror, fix_firmwarebug))
        returnval|=FAILERR;
       PRINT_OFF(con);
     }
   }
-  
+
+  // Print SMART Extendend Self-test Log
+  bool do_smart_selftest_log = options.smart_selftest_log;
+  if (options.smart_ext_selftest_log) {
+    bool ok = false;
+    unsigned nsectors = GetNumLogSectors(gplogdir, 0x07, true);
+    if (!nsectors)
+      pout("SMART Extended Self-test Log (GP Log 0x07) not supported\n");
+    else if (nsectors >= 256)
+      pout("SMART Extended Self-test Log size %u not supported\n", nsectors);
+    else {
+      raw_buffer log_07_buf(nsectors * 512);
+      ata_smart_extselftestlog * log_07 = (ata_smart_extselftestlog *)log_07_buf.data();
+      if (!ataReadExtSelfTestLog(device, log_07, nsectors))
+        failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+      else {
+        PrintSmartExtSelfTestLog(log_07, nsectors, options.smart_ext_selftest_log);
+        ok = true;
+      }
+    }
+
+    if (!ok) {
+      if (options.retry_selftest_log)
+        do_smart_selftest_log = true;
+      else if (!do_smart_selftest_log)
+        pout("Try '-l [xselftest,]selftest' to read traditional SMART Self Test Log\n");
+    }
+  }
+
   // Print SMART self-test log
-  if (con->smartselftestlog){
+  if (do_smart_selftest_log) {
     if (!isSmartTestLogCapable(&smartval, &drive)){
       pout("Warning: device does not support Self Test Logging\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }    
-    if(ataReadSelfTestLog(fd, &smartselftest)){
+    if(ataReadSelfTestLog(device, &smartselftest, fix_firmwarebug)){
       pout("Smartctl: SMART Self Test Log Read Failed\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
     else {
       PRINT_ON(con);
-      if (ataPrintSmartSelfTestlog(&smartselftest,!con->printing_switchable))
+      if (ataPrintSmartSelfTestlog(&smartselftest, !con->printing_switchable, fix_firmwarebug))
        returnval|=FAILLOG;
       PRINT_OFF(con);
       pout("\n");
@@ -1976,37 +2246,40 @@ int ataPrintMain (int fd){
   }
 
   // Print SMART selective self-test log
-  if (con->selectivetestlog){
-    struct ata_selective_self_test_log log;
-    
+  if (options.smart_selective_selftest_log) {
+    ata_selective_self_test_log log;
+
     if (!isSupportSelectiveSelfTest(&smartval))
       pout("Device does not support Selective Self Tests/Logging\n");
-    else if(ataReadSelectiveSelfTestLog(fd, &log)) {
+    else if(ataReadSelectiveSelfTestLog(device, &log)) {
       pout("Smartctl: SMART Selective Self Test Log Read Failed\n");
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
     }
     else {
       PRINT_ON(con);
-      ataPrintSelectiveSelfTestLog(&log, &smartval);
+      // If any errors were found, they are logged in the SMART Self-test log.
+      // So there is no need to print the Selective Self Test log in silent
+      // mode.
+      if (!con->printing_switchable) ataPrintSelectiveSelfTestLog(&log, &smartval);
       PRINT_OFF(con);
       pout("\n");
     }
   }
 
-  // Print SMART SCT status and temperature history table
-  if (con->scttempsts || con->scttemphist || con->scttempint) {
+  // Print SCT status and temperature history table
+  if (options.sct_temp_sts || options.sct_temp_hist || options.sct_temp_int) {
     for (;;) {
       if (!isSCTCapable(&drive)) {
         pout("Warning: device does not support SCT Commands\n");
         failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
         break;
       }
-      if (con->scttempsts || con->scttemphist) {
+      if (options.sct_temp_sts || options.sct_temp_hist) {
         ata_sct_status_response sts;
         ata_sct_temperature_history_table tmh;
-        if (!con->scttemphist) {
+        if (!options.sct_temp_hist) {
           // Read SCT status only
-          if (ataReadSCTStatus(fd, &sts)) {
+          if (ataReadSCTStatus(device, &sts)) {
             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
             break;
           }
@@ -2018,42 +2291,53 @@ int ataPrintMain (int fd){
             break;
           }
           // Read SCT status and temperature history
-          if (ataReadSCTTempHist(fd, &tmh, &sts)) {
+          if (ataReadSCTTempHist(device, &tmh, &sts)) {
             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
             break;
           }
         }
-        if (con->scttempsts)
+        if (options.sct_temp_sts)
           ataPrintSCTStatus(&sts);
-        if (con->scttemphist)
+        if (options.sct_temp_hist)
           ataPrintSCTTempHist(&tmh);
         pout("\n");
       }
-      if (con->scttempint) {
+      if (options.sct_temp_int) {
         // Set new temperature logging interval
         if (!isSCTFeatureControlCapable(&drive)) {
           pout("Warning: device does not support SCT Feature Control command\n");
           failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
           break;
         }
-        if (ataSetSCTTempInterval(fd, con->scttempint, !!con->scttempintp)) {
+        if (ataSetSCTTempInterval(device, options.sct_temp_int, options.sct_temp_int_pers)) {
           failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
           break;
         }
         pout("Temperature Logging Interval set to %u minute%s (%s)\n",
-          con->scttempint, (con->scttempint==1?"":"s"), (con->scttempintp?"persistent":"volatile"));
+          options.sct_temp_int, (options.sct_temp_int == 1 ? "" : "s"),
+          (options.sct_temp_int_pers ? "persistent" : "volatile"));
       }
       break;
     }
   }
 
+  // Print SATA Phy Event Counters
+  if (options.sataphy) {
+    unsigned char log_11[512] = {0, };
+    unsigned char features = (options.sataphy_reset ? 0x01 : 0x00);
+    if (!ataReadLogExt(device, 0x11, features, 0, log_11, 1))
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    else
+      PrintSataPhyEventCounters(log_11, options.sataphy_reset);
+  }
+
   // START OF THE TESTING SECTION OF THE CODE.  IF NO TESTING, RETURN
-  if (con->testcase==-1)
+  if (options.smart_selftest_type == -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){
+  switch (options.smart_selftest_type) {
   case OFFLINE_FULL_SCAN:
     if (!isSupportExecuteOfflineImmediate(&smartval)){
       pout("Warning: device does not support Execute Offline Immediate function.\n\n");
@@ -2085,14 +2369,15 @@ int ataPrintMain (int fd){
     }
     break;
   default:
-    pout("Internal error in smartctl: con->testcase==%d not recognized\n", (int)con->testcase);
+    pout("Internal error in smartctl: smart_test_type==%d not recognized\n", options.smart_selftest_type);
     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, get_num_sectors(&drive)))
+  if (ataSmartTest(device, options.smart_selftest_type, options.smart_selective_args,
+                   &smartval, get_num_sectors(&drive)                                ))
     failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
   else {  
     // Tell user how long test will take to complete.  This is tricky
@@ -2100,19 +2385,19 @@ int ataPrintMain (int fd){
     // 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 (options.smart_selftest_type == OFFLINE_FULL_SCAN) {
       if (isSupportOfflineAbort(&smartval))
        pout("Note: giving further SMART commands will abort Offline testing\n");
-      else if (ataReadSmartValues(fd, &smartval)){
+      else if (ataReadSmartValues(device, &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))){ 
+    if ((timewait = TestTime(&smartval, options.smart_selftest_type))) {
       time_t t=time(NULL);
-      if (con->testcase==OFFLINE_FULL_SCAN) {
+      if (options.smart_selftest_type == OFFLINE_FULL_SCAN) {
        t+=timewait;
        pout("Please wait %d seconds for test to complete.\n", (int)timewait);
       } else {
@@ -2121,11 +2406,11 @@ int ataPrintMain (int fd){
       }
       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"); 
+      if (   options.smart_selftest_type != SHORT_CAPTIVE_SELF_TEST
+          && options.smart_selftest_type != EXTEND_CAPTIVE_SELF_TEST
+          && options.smart_selftest_type != CONVEYANCE_CAPTIVE_SELF_TEST
+          && options.smart_selftest_type != SELECTIVE_CAPTIVE_SELF_TEST )
+        pout("Use smartctl -X to abort test.\n");
     }
   }
 
index 82b533de71e713bd9dedc37d6034d0347c1d5025..a493892175f14e6c27fdfd9fba92b3823b2efcb0 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-9 Christian Franke <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
 #ifndef ATAPRINT_H_
 #define ATAPRINT_H_
 
-#define ATAPRINT_H_CVSID "$Id: ataprint.h,v 1.31 2008/03/04 22:09:47 ballen4705 Exp $\n"
+#define ATAPRINT_H_CVSID "$Id: ataprint.h,v 1.43 2009/07/07 19:28:29 chrfranke Exp $\n"
 
-#include <stdio.h>
-#include <stdlib.h>
+#include <vector>
 
-/* Prints ATA Drive Information and S.M.A.R.T. Capability */
-int ataPrintDriveInfo(struct ata_identify_device *);
+// Request to dump a GP or SMART log
+struct ata_log_request
+{
+  bool gpl; // false: SMART, true: GP
+  unsigned char logaddr; // Log address
+  unsigned page; // First page (sector)
+  unsigned nsectors; // # Sectors
 
-void ataPrintGeneralSmartValues(struct ata_smart_values *, struct ata_identify_device *);
+  ata_log_request()
+    : gpl(false), logaddr(0), page(0), nsectors(0)
+    { }
+};
 
-void ataPrintSmartThresholds(struct ata_smart_thresholds_pvt *);
+// Options for ataPrintMain
+// TODO: Move remaining options from con->* to here.
+struct ata_print_options
+{
+  bool drive_info;
+  bool smart_check_status;
+  bool smart_general_values;
+  bool smart_vendor_attrib;
+  bool smart_error_log;
+  bool smart_selftest_log;
+  bool smart_selective_selftest_log;
 
-// returns number of errors in Errorlog
-int  ataPrintSmartErrorlog(struct ata_smart_errorlog *);
+  bool gp_logdir, smart_logdir;
+  unsigned smart_ext_error_log;
+  unsigned smart_ext_selftest_log;
+  bool retry_error_log, retry_selftest_log;
 
-int ataPrintLogDirectory(struct ata_smart_log_directory *);
+  std::vector<ata_log_request> log_requests;
 
-void PrintSmartAttributes(struct ata_smart_values *);
+  bool sct_temp_sts, sct_temp_hist;
+  bool sataphy, sataphy_reset;
 
-void PrintSmartAttribWithThres(struct ata_smart_values *,
-                                struct ata_smart_thresholds_pvt *,
-                                int onlyfailed);
+  bool smart_disable, smart_enable;
+  bool smart_auto_offl_disable, smart_auto_offl_enable;
+  bool smart_auto_save_disable, smart_auto_save_enable;
 
-// returns number of entries that had logged errors
-int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog *, int allentries);
+  int smart_selftest_type; // OFFLINE_FULL_SCAN, ..., see atacmds.h. -1 for no test
+  ata_selective_selftest_args smart_selective_args; // Extra args for selective self-test
 
-void ataPseudoCheckSmart(struct ata_smart_values *, struct ata_smart_thresholds_pvt *);
+  unsigned sct_temp_int;
+  bool sct_temp_int_pers;
 
-// Convenience function for formatting strings from ata_identify_device.
-void format_ata_string(char *out, const char *in, int n);
+  unsigned char fix_firmwarebug; // FIX_*, see atacmds.h
+  bool fix_swapped_id; // Fix swapped ID strings returned by some buggy drivers
 
-int ataPrintMain(int fd);
+  // The i'th entry in this array will modify the printed meaning of
+  // the i'th SMART attribute.  The default definitions of the
+  // Attributes are obtained by having the array be all zeros.  If
+  // attributedefs[i] is nonzero, it means that the i'th attribute has
+  // a non-default meaning.  See the ataPrintSmartAttribName and
+  // and parse_attribute_def functions.
+  unsigned char attributedefs[256];
+
+  bool ignore_presets; // Ignore presets from drive database
+  bool show_presets; // Show presets and exit
+  unsigned char powermode; // Skip check, if disk in idle or standby mode
+
+  ata_print_options()
+    : drive_info(false),
+      smart_check_status(false),
+      smart_general_values(false),
+      smart_vendor_attrib(false),
+      smart_error_log(false),
+      smart_selftest_log(false),
+      smart_selective_selftest_log(false),
+      gp_logdir(false), smart_logdir(false),
+      smart_ext_error_log(0),
+      smart_ext_selftest_log(0),
+      retry_error_log(false), retry_selftest_log(false),
+      sct_temp_sts(false), sct_temp_hist(false),
+      sataphy(false), sataphy_reset(false),
+      smart_disable(false), smart_enable(false),
+      smart_auto_offl_disable(false), smart_auto_offl_enable(false),
+      smart_auto_save_disable(false), smart_auto_save_enable(false),
+      smart_selftest_type(-1),
+      sct_temp_int(0), sct_temp_int_pers(false),
+      fix_firmwarebug(FIX_NOTSPECIFIED),
+      fix_swapped_id(false),
+      ignore_presets(false),
+      show_presets(false),
+      powermode(0)
+    { memset(attributedefs, 0, sizeof(attributedefs)); }
+};
+
+int ataPrintMain(ata_device * device, const ata_print_options & options);
 
 #endif
index 249f47e2ab834145908468e44ea4cc4903c29627..e90692f2142498fb4c3d3ec082ca57dcda21442b 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $Id: autogen.sh,v 1.18 2008/01/29 18:36:20 chrfranke Exp $
+# $Id: autogen.sh 2844 2009-07-18 12:59:21Z chrfranke $
 #
 # Generate ./configure from config.in and Makefile.in from Makefile.am.
 # This also adds files like missing,depcomp,install-sh to the source
 # Cygwin?
 test -x /usr/bin/uname && /usr/bin/uname | grep -i CYGWIN >/dev/null &&
 {
-    # Enable strict case checking
-    # (to avoid e.g. "DIST_COMMON = ... ChangeLog ..." in Makefile.in)
-    export CYGWIN="${CYGWIN}${CYGWIN:+ }check_case:strict"
-
     # Check for Unix text file type
     echo > dostest.tmp
     test "`wc -c < dostest.tmp`" -eq 1 ||
@@ -36,17 +32,18 @@ typep()
     return 1
 }
 
-test -x "$AUTOMAKE" || AUTOMAKE=`typep automake-1.10` || AUTOMAKE=`typep automake-1.9` ||
-    AUTOMAKE=`typep automake-1.8` || AUTOMAKE=`typep automake-1.7` || AUTOMAKE=`typep automake17` ||
+test -x "$AUTOMAKE" || AUTOMAKE=`typep automake-1.11` || AUTOMAKE=`typep automake-1.10` ||
+    AUTOMAKE=`typep automake-1.9` || AUTOMAKE=`typep automake-1.8` ||
+    AUTOMAKE=`typep automake-1.7` || AUTOMAKE=`typep automake17` ||
 {
 echo
-echo "You must have at least GNU Automake 1.7 (up to 1.9.x) installed"
-echo "in order to bootstrap smartmontools from CVS. Download the"
+echo "You must have at least GNU Automake 1.7 (up to 1.11) installed"
+echo "in order to bootstrap smartmontools from SVN. Download the"
 echo "appropriate package for your distribution, or the source tarball"
 echo "from ftp://ftp.gnu.org/gnu/automake/ ."
 echo
 echo "Also note that support for new Automake series (anything newer"
-echo "than 1.9.x) is only added after extensive tests. If you live in"
+echo "than 1.11) is only added after extensive tests. If you live in"
 echo "the bleeding edge, you should know what you're doing, mainly how"
 echo "to test it before the developers. Be patient."
 exit 1;
@@ -55,31 +52,45 @@ exit 1;
 test -x "$ACLOCAL" || ACLOCAL="aclocal`echo "$AUTOMAKE" | sed 's/.*automake//'`" && ACLOCAL=`typep "$ACLOCAL"` ||
 {
 echo
-echo "autogen.sh found automake-1.7, automake-1.8, or automake-1.9 in"
-echo "your PATH, but not the respective aclocal-1.7, aclocal-1.8, or"
-echo "aclocal-1.9. Your installation of GNU Automake is broken or"
-echo "incomplete."
+echo "autogen.sh found automake-1.X, but not the respective aclocal-1.X."
+echo "Your installation of GNU Automake is broken or incomplete."
 exit 2;
 }
 
-# Warn if Automake version is unknown
-ver=
+# Detect Automake version
 case "$AUTOMAKE" in
-  *automake-1.[78]|*automake17)
-    ;;
+  *automake-1.7|*automake17)
+    ver=1.7 ;;
+  *automake-1.8)
+    ver=1.8 ;;
   *)
     ver="`$AUTOMAKE --version | sed -n '1s,^.*\([12]\.[.0-9]*[-pl0-9]*\).*$,\1,p'`"
     ver="${ver:-?.?.?}"
-    case "$ver" in
-      1.[78]*|1.9.[1-6]|1.10) ver= ;;
-    esac ;;
 esac
 
-test -z "$ver" ||
-{
-echo "Note: GNU Automake version ${ver} was not tested by the developers."
-echo "Please report success/failure to the smartmontools-support mailing list."
-}
+# Warn if Automake version was not tested or does not support filesystem
+case "$ver" in
+  1.[78]|1.[78].*)
+    # Check for case sensitive filesystem
+    # (to avoid e.g. "DIST_COMMON = ... ChangeLog ..." in Makefile.in on Cygwin)
+    rm -f CASETEST.TMP
+    echo > casetest.tmp
+    test -f CASETEST.TMP &&
+    {
+      echo "Warning: GNU Automake version ${ver} does not properly handle case"
+      echo "insensitive filesystems. Some make targets may not work."
+    }
+    rm -f casetest.tmp
+    ;;
+
+  1.9.[1-6]|1.10|1.10.[12]|1.11)
+    # OK
+    ;;
+
+  *)
+    echo "Note: GNU Automake version ${ver} was not tested by the developers."
+    echo "Please report success/failure to the smartmontools-support mailing list."
+esac
 
 set -e # stops on error status
 
index d425f8e1a73b0c7e03d286355d250788a269c89f..3c32585659d3eb67b703694b2317f5872590b4e3 100644 (file)
--- a/cciss.cpp
+++ b/cciss.cpp
@@ -26,6 +26,9 @@
 #include "scsicmds.h"
 #include "utility.h"
 
+const char *cciss_c_cvsid="$Id: cciss.cpp,v 1.9 2008/07/30 20:42:53 chrfranke Exp $"
+CONFIG_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
 typedef struct _ReportLUNdata_struct
 {
   uint32_t LUNListLength;      /* always big-endian */
@@ -177,7 +180,6 @@ static int cciss_getlun(int device, int target, unsigned char *physlun, int repo
     unsigned char CDB[16]= {0};
     ReportLunData_struct *luns;
     int reportlunsize = sizeof(*luns) + CISS_MAX_PHYS_LUN * 8;
-    int i;
     int ret;
 
     luns = (ReportLunData_struct *)malloc(reportlunsize);
@@ -209,7 +211,7 @@ static int cciss_getlun(int device, int target, unsigned char *physlun, int repo
          pout("%02x ",*stuff++);
        pout("%02x\n",*stuff++);
       }
-      pout("===== [%s] DATA END (%d Bytes) =====\n\n", "LUN DATA", sizeof(_ReportLUNdata_struct));
+      pout("===== [%s] DATA END (%u Bytes) =====\n\n", "LUN DATA", (unsigned)sizeof(_ReportLUNdata_struct));
     }
 
     if (target >= 0 && target < (int) be32toh(luns->LUNListLength) / 8)
diff --git a/config.guess b/config.guess
deleted file mode 100755 (executable)
index 396482d..0000000
+++ /dev/null
@@ -1,1500 +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, 2005, 2006 Free Software Foundation,
-#   Inc.
-
-timestamp='2006-07-02'
-
-# 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., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, 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, 2005
-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 ;;
-    --version | -v )
-       echo "$version" ; exit ;;
-    --help | --h* | -h )
-       echo "$usage"; exit ;;
-    -- )     # 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 "$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 ; set_cc_for_build= ;'
-
-# 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 ;;
-    *:OpenBSD:*:*)
-       UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
-       echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
-       exit ;;
-    *:ekkoBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
-       exit ;;
-    *:SolidBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
-       exit ;;
-    macppc:MirBSD:*:*)
-       echo powerpc-unknown-mirbsd${UNAME_RELEASE}
-       exit ;;
-    *:MirBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
-       exit ;;
-    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 ;;
-    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 ;;
-    21064:Windows_NT:50:3)
-       echo alpha-dec-winnt3.5
-       exit ;;
-    Amiga*:UNIX_System_V:4.0:*)
-       echo m68k-unknown-sysv4
-       exit ;;
-    *:[Aa]miga[Oo][Ss]:*:*)
-       echo ${UNAME_MACHINE}-unknown-amigaos
-       exit ;;
-    *:[Mm]orph[Oo][Ss]:*:*)
-       echo ${UNAME_MACHINE}-unknown-morphos
-       exit ;;
-    *:OS/390:*:*)
-       echo i370-ibm-openedition
-       exit ;;
-    *:z/VM:*:*)
-       echo s390-ibm-zvmoe
-       exit ;;
-    *:OS400:*:*)
-        echo powerpc-ibm-os400
-       exit ;;
-    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
-       echo arm-acorn-riscix${UNAME_RELEASE}
-       exit ;;
-    arm:riscos:*:*|arm:RISCOS:*:*)
-       echo arm-unknown-riscos
-       exit ;;
-    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
-       echo hppa1.1-hitachi-hiuxmpp
-       exit ;;
-    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 ;;
-    NILE*:*:*:dcosx)
-       echo pyramid-pyramid-svr4
-       exit ;;
-    DRS?6000:unix:4.0:6*)
-       echo sparc-icl-nx6
-       exit ;;
-    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 ;;
-       esac ;;
-    sun4H:SunOS:5.*:*)
-       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
-       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    i86pc:SunOS:5.*:*)
-       echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    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 ;;
-    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 ;;
-    sun3*:SunOS:*:*)
-       echo m68k-sun-sunos${UNAME_RELEASE}
-       exit ;;
-    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 ;;
-    aushp:SunOS:*:*)
-       echo sparc-auspex-sunos${UNAME_RELEASE}
-       exit ;;
-    # 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 ;;
-    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
-       echo m68k-atari-mint${UNAME_RELEASE}
-        exit ;;
-    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
-        echo m68k-atari-mint${UNAME_RELEASE}
-       exit ;;
-    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
-        echo m68k-milan-mint${UNAME_RELEASE}
-        exit ;;
-    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
-        echo m68k-hades-mint${UNAME_RELEASE}
-        exit ;;
-    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
-        echo m68k-unknown-mint${UNAME_RELEASE}
-        exit ;;
-    m68k:machten:*:*)
-       echo m68k-apple-machten${UNAME_RELEASE}
-       exit ;;
-    powerpc:machten:*:*)
-       echo powerpc-apple-machten${UNAME_RELEASE}
-       exit ;;
-    RISC*:Mach:*:*)
-       echo mips-dec-mach_bsd4.3
-       exit ;;
-    RISC*:ULTRIX:*:*)
-       echo mips-dec-ultrix${UNAME_RELEASE}
-       exit ;;
-    VAX*:ULTRIX*:*:*)
-       echo vax-dec-ultrix${UNAME_RELEASE}
-       exit ;;
-    2020:CLIX:*:* | 2430:CLIX:*:*)
-       echo clipper-intergraph-clix${UNAME_RELEASE}
-       exit ;;
-    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 &&
-         dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
-         SYSTEM_NAME=`$dummy $dummyarg` &&
-           { echo "$SYSTEM_NAME"; exit; }
-       echo mips-mips-riscos${UNAME_RELEASE}
-       exit ;;
-    Motorola:PowerMAX_OS:*:*)
-       echo powerpc-motorola-powermax
-       exit ;;
-    Motorola:*:4.3:PL8-*)
-       echo powerpc-harris-powermax
-       exit ;;
-    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
-       echo powerpc-harris-powermax
-       exit ;;
-    Night_Hawk:Power_UNIX:*:*)
-       echo powerpc-harris-powerunix
-       exit ;;
-    m88k:CX/UX:7*:*)
-       echo m88k-harris-cxux7
-       exit ;;
-    m88k:*:4*:R4*)
-       echo m88k-motorola-sysv4
-       exit ;;
-    m88k:*:3*:R3*)
-       echo m88k-motorola-sysv3
-       exit ;;
-    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 ;;
-    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
-       echo m88k-dolphin-sysv3
-       exit ;;
-    M88*:*:R3*:*)
-       # Delta 88k system running SVR3
-       echo m88k-motorola-sysv3
-       exit ;;
-    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
-       echo m88k-tektronix-sysv3
-       exit ;;
-    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
-       echo m68k-tektronix-bsd
-       exit ;;
-    *:IRIX*:*:*)
-       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
-       exit ;;
-    ????????: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 ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
-    i*86:AIX:*:*)
-       echo i386-ibm-aix
-       exit ;;
-    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 ;;
-    *: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
-               if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
-               then
-                       echo "$SYSTEM_NAME"
-               else
-                       echo rs6000-ibm-aix3.2.5
-               fi
-       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 ;;
-    *: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 ;;
-    *:AIX:*:*)
-       echo rs6000-ibm-aix
-       exit ;;
-    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
-       echo romp-ibm-bsd4.4
-       exit ;;
-    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
-       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
-       exit ;;                             # report: romp-ibm BSD 4.3
-    *:BOSX:*:*)
-       echo rs6000-bull-bosx
-       exit ;;
-    DPX/2?00:B.O.S.:*:*)
-       echo m68k-bull-sysv3
-       exit ;;
-    9000/[34]??:4.3bsd:1.*:*)
-       echo m68k-hp-bsd
-       exit ;;
-    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
-       echo m68k-hp-bsd4.4
-       exit ;;
-    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
-           eval $set_cc_for_build
-
-           # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
-           # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
-           # generating 64-bit code.  GNU and HP use different nomenclature:
-           #
-           # $ CC_FOR_BUILD=cc ./config.guess
-           # => hppa2.0w-hp-hpux11.23
-           # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
-           # => hppa64-hp-hpux11.23
-
-           if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
-               grep __LP64__ >/dev/null
-           then
-               HP_ARCH="hppa2.0w"
-           else
-               HP_ARCH="hppa64"
-           fi
-       fi
-       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
-       exit ;;
-    ia64:HP-UX:*:*)
-       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
-       echo ia64-hp-hpux${HPUX_REV}
-       exit ;;
-    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 && SYSTEM_NAME=`$dummy` &&
-               { echo "$SYSTEM_NAME"; exit; }
-       echo unknown-hitachi-hiuxwe2
-       exit ;;
-    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
-       echo hppa1.1-hp-bsd
-       exit ;;
-    9000/8??:4.3bsd:*:*)
-       echo hppa1.0-hp-bsd
-       exit ;;
-    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
-       echo hppa1.0-hp-mpeix
-       exit ;;
-    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
-       echo hppa1.1-hp-osf
-       exit ;;
-    hp8??:OSF1:*:*)
-       echo hppa1.0-hp-osf
-       exit ;;
-    i*86:OSF1:*:*)
-       if [ -x /usr/sbin/sysversion ] ; then
-           echo ${UNAME_MACHINE}-unknown-osf1mk
-       else
-           echo ${UNAME_MACHINE}-unknown-osf1
-       fi
-       exit ;;
-    parisc*:Lites*:*:*)
-       echo hppa1.1-hp-lites
-       exit ;;
-    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
-       echo c1-convex-bsd
-        exit ;;
-    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
-       if getsysinfo -f scalar_acc
-       then echo c32-convex-bsd
-       else echo c2-convex-bsd
-       fi
-        exit ;;
-    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
-       echo c34-convex-bsd
-        exit ;;
-    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
-       echo c38-convex-bsd
-        exit ;;
-    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
-       echo c4-convex-bsd
-        exit ;;
-    CRAY*Y-MP:*:*:*)
-       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    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 ;;
-    CRAY*TS:*:*:*)
-       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    CRAY*T3E:*:*:*)
-       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    CRAY*SV1:*:*:*)
-       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    *:UNICOS/mp:*:*)
-       echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    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 ;;
-    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 ;;
-    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
-       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
-       exit ;;
-    sparc*:BSD/OS:*:*)
-       echo sparc-unknown-bsdi${UNAME_RELEASE}
-       exit ;;
-    *:BSD/OS:*:*)
-       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
-       exit ;;
-    *:FreeBSD:*:*)
-       case ${UNAME_MACHINE} in
-           pc98)
-               echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
-           amd64)
-               echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
-           *)
-               echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
-       esac
-       exit ;;
-    i*:CYGWIN*:*)
-       echo ${UNAME_MACHINE}-pc-cygwin
-       exit ;;
-    i*:MINGW*:*)
-       echo ${UNAME_MACHINE}-pc-mingw32
-       exit ;;
-    i*:windows32*:*)
-       # uname -m includes "-pc" on this system.
-       echo ${UNAME_MACHINE}-mingw32
-       exit ;;
-    i*:PW*:*)
-       echo ${UNAME_MACHINE}-pc-pw32
-       exit ;;
-    x86:Interix*:[3456]*)
-       echo i586-pc-interix${UNAME_RELEASE}
-       exit ;;
-    EM64T:Interix*:[3456]*)
-       echo x86_64-unknown-interix${UNAME_RELEASE}
-       exit ;;
-    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
-       echo i${UNAME_MACHINE}-pc-mks
-       exit ;;
-    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 ;;
-    i*:UWIN*:*)
-       echo ${UNAME_MACHINE}-pc-uwin
-       exit ;;
-    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
-       echo x86_64-unknown-cygwin
-       exit ;;
-    p*:CYGWIN*:*)
-       echo powerpcle-unknown-cygwin
-       exit ;;
-    prep*:SunOS:5.*:*)
-       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    *:GNU:*:*)
-       # the GNU system
-       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
-       exit ;;
-    *: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 ;;
-    i*86:Minix:*:*)
-       echo ${UNAME_MACHINE}-pc-minix
-       exit ;;
-    arm*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    avr32*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    cris:Linux:*:*)
-       echo cris-axis-linux-gnu
-       exit ;;
-    crisv32:Linux:*:*)
-       echo crisv32-axis-linux-gnu
-       exit ;;
-    frv:Linux:*:*)
-       echo frv-unknown-linux-gnu
-       exit ;;
-    ia64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    m32r*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    m68*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    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 | sed -n '
-           /^CPU/{
-               s: ::g
-               p
-           }'`"
-       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
-       ;;
-    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 | sed -n '
-           /^CPU/{
-               s: ::g
-               p
-           }'`"
-       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
-       ;;
-    or32:Linux:*:*)
-       echo or32-unknown-linux-gnu
-       exit ;;
-    ppc:Linux:*:*)
-       echo powerpc-unknown-linux-gnu
-       exit ;;
-    ppc64:Linux:*:*)
-       echo powerpc64-unknown-linux-gnu
-       exit ;;
-    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 ;;
-    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 ;;
-    parisc64:Linux:*:* | hppa64:Linux:*:*)
-       echo hppa64-unknown-linux-gnu
-       exit ;;
-    s390:Linux:*:* | s390x:Linux:*:*)
-       echo ${UNAME_MACHINE}-ibm-linux
-       exit ;;
-    sh64*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    sh*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    sparc:Linux:*:* | sparc64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    vax:Linux:*:*)
-       echo ${UNAME_MACHINE}-dec-linux-gnu
-       exit ;;
-    x86_64:Linux:*:*)
-       echo x86_64-unknown-linux-gnu
-       exit ;;
-    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 ;;
-         coff-i386)
-               echo "${UNAME_MACHINE}-pc-linux-gnucoff"
-               exit ;;
-         "")
-               # 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 ;;
-       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
-       #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-       LIBC=gnu
-       #else
-       LIBC=gnuaout
-       #endif
-       #endif
-       #ifdef __dietlibc__
-       LIBC=dietlibc
-       #endif
-EOF
-       eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
-           /^LIBC/{
-               s: ::g
-               p
-           }'`"
-       test x"${LIBC}" != x && {
-               echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
-               exit
-       }
-       test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
-       ;;
-    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 ;;
-    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 ;;
-    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 ;;
-    i*86:XTS-300:*:STOP)
-       echo ${UNAME_MACHINE}-unknown-stop
-       exit ;;
-    i*86:atheos:*:*)
-       echo ${UNAME_MACHINE}-unknown-atheos
-       exit ;;
-    i*86:syllable:*:*)
-       echo ${UNAME_MACHINE}-pc-syllable
-       exit ;;
-    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
-       echo i386-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    i*86:*DOS:*:*)
-       echo ${UNAME_MACHINE}-pc-msdosdjgpp
-       exit ;;
-    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 ;;
-    i*86:*:5:[678]*)
-       # UnixWare 7.x, OpenUNIX and OpenServer 6.
-       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 ;;
-    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 ;;
-    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 ;;
-    Intel:Mach:3*:*)
-       echo i386-pc-mach3
-       exit ;;
-    paragon:*:*:*)
-       echo i860-intel-osf1
-       exit ;;
-    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 ;;
-    mini*:CTIX:SYS*5:*)
-       # "miniframe"
-       echo m68010-convergent-sysv
-       exit ;;
-    mc68k:UNIX:SYSTEM5:3.51m)
-       echo m68k-convergent-sysv
-       exit ;;
-    M680?0:D-NIX:5.3:*)
-       echo m68k-diab-dnix
-       exit ;;
-    M68*:*:R3V[5678]*:*)
-       test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
-    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; }
-       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
-         && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
-    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
-        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
-          && { echo i486-ncr-sysv4; exit; } ;;
-    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
-       echo m68k-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    mc68030:UNIX_System_V:4.*:*)
-       echo m68k-atari-sysv4
-       exit ;;
-    TSUNAMI:LynxOS:2.*:*)
-       echo sparc-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    rs6000:LynxOS:2.*:*)
-       echo rs6000-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
-       echo powerpc-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    SM[BE]S:UNIX_SV:*:*)
-       echo mips-dde-sysv${UNAME_RELEASE}
-       exit ;;
-    RM*:ReliantUNIX-*:*:*)
-       echo mips-sni-sysv4
-       exit ;;
-    RM*:SINIX-*:*:*)
-       echo mips-sni-sysv4
-       exit ;;
-    *: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 ;;
-    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
-                      # says <Richard.M.Bartel@ccMail.Census.GOV>
-        echo i586-unisys-sysv4
-        exit ;;
-    *:UNIX_System_V:4*:FTX*)
-       # From Gerald Hewes <hewes@openmarket.com>.
-       # How about differentiating between stratus architectures? -djm
-       echo hppa1.1-stratus-sysv4
-       exit ;;
-    *:*:*:FTX*)
-       # From seanf@swdc.stratus.com.
-       echo i860-stratus-sysv4
-       exit ;;
-    i*86:VOS:*:*)
-       # From Paul.Green@stratus.com.
-       echo ${UNAME_MACHINE}-stratus-vos
-       exit ;;
-    *:VOS:*:*)
-       # From Paul.Green@stratus.com.
-       echo hppa1.1-stratus-vos
-       exit ;;
-    mc68*:A/UX:*:*)
-       echo m68k-apple-aux${UNAME_RELEASE}
-       exit ;;
-    news*:NEWS-OS:6*:*)
-       echo mips-sony-newsos6
-       exit ;;
-    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 ;;
-    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
-       echo powerpc-be-beos
-       exit ;;
-    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
-       echo powerpc-apple-beos
-       exit ;;
-    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
-       echo i586-pc-beos
-       exit ;;
-    SX-4:SUPER-UX:*:*)
-       echo sx4-nec-superux${UNAME_RELEASE}
-       exit ;;
-    SX-5:SUPER-UX:*:*)
-       echo sx5-nec-superux${UNAME_RELEASE}
-       exit ;;
-    SX-6:SUPER-UX:*:*)
-       echo sx6-nec-superux${UNAME_RELEASE}
-       exit ;;
-    Power*:Rhapsody:*:*)
-       echo powerpc-apple-rhapsody${UNAME_RELEASE}
-       exit ;;
-    *:Rhapsody:*:*)
-       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
-       exit ;;
-    *:Darwin:*:*)
-       UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
-       case $UNAME_PROCESSOR in
-           unknown) UNAME_PROCESSOR=powerpc ;;
-       esac
-       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
-       exit ;;
-    *: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 ;;
-    *:QNX:*:4*)
-       echo i386-pc-qnx
-       exit ;;
-    NSE-?:NONSTOP_KERNEL:*:*)
-       echo nse-tandem-nsk${UNAME_RELEASE}
-       exit ;;
-    NSR-?:NONSTOP_KERNEL:*:*)
-       echo nsr-tandem-nsk${UNAME_RELEASE}
-       exit ;;
-    *:NonStop-UX:*:*)
-       echo mips-compaq-nonstopux
-       exit ;;
-    BS2000:POSIX*:*:*)
-       echo bs2000-siemens-sysv
-       exit ;;
-    DS/*:UNIX_System_V:*:*)
-       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
-       exit ;;
-    *: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 ;;
-    *:TOPS-10:*:*)
-       echo pdp10-unknown-tops10
-       exit ;;
-    *:TENEX:*:*)
-       echo pdp10-unknown-tenex
-       exit ;;
-    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
-       echo pdp10-dec-tops20
-       exit ;;
-    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
-       echo pdp10-xkl-tops20
-       exit ;;
-    *:TOPS-20:*:*)
-       echo pdp10-unknown-tops20
-       exit ;;
-    *:ITS:*:*)
-       echo pdp10-unknown-its
-       exit ;;
-    SEI:*:*:SEIUX)
-        echo mips-sei-seiux${UNAME_RELEASE}
-       exit ;;
-    *:DragonFly:*:*)
-       echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
-       exit ;;
-    *:*VMS:*:*)
-       UNAME_MACHINE=`(uname -p) 2>/dev/null`
-       case "${UNAME_MACHINE}" in
-           A*) echo alpha-dec-vms ; exit ;;
-           I*) echo ia64-dec-vms ; exit ;;
-           V*) echo vax-dec-vms ; exit ;;
-       esac ;;
-    *:XENIX:*:SysV)
-       echo i386-pc-xenix
-       exit ;;
-    i*86:skyos:*:*)
-       echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
-       exit ;;
-    i*86:rdos:*:*)
-       echo ${UNAME_MACHINE}-pc-rdos
-       exit ;;
-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\n"); 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 && SYSTEM_NAME=`$dummy` &&
-       { echo "$SYSTEM_NAME"; exit; }
-
-# Apollos put the system type in the environment.
-
-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
-
-# 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 ;;
-    c2*)
-       if getsysinfo -f scalar_acc
-       then echo c32-convex-bsd
-       else echo c2-convex-bsd
-       fi
-       exit ;;
-    c34*)
-       echo c34-convex-bsd
-       exit ;;
-    c38*)
-       echo c38-convex-bsd
-       exit ;;
-    c4*)
-       echo c4-convex-bsd
-       exit ;;
-    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
-
-  http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
-and
-  http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
-
-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 3a63ea2..0000000
+++ /dev/null
@@ -1,157 +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 <dev/ciss/cissio.h> header file. */
-#undef HAVE_DEV_CISS_CISSIO_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 <linux/cciss_ioctl.h> header file. */
-#undef HAVE_LINUX_CCISS_IOCTL_H
-
-/* Define to 1 if you have the <linux/compiler.h> header file. */
-#undef HAVE_LINUX_COMPILER_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 fab0aa3..0000000
+++ /dev/null
@@ -1,1616 +0,0 @@
-#! /bin/sh
-# Configuration validation subroutine script.
-#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-#   2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
-#   Inc.
-
-timestamp='2006-09-20'
-
-# 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., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, 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, 2005
-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 ;;
-    --version | -v )
-       echo "$version" ; exit ;;
-    --help | --h* | -h )
-       echo "$usage"; exit ;;
-    -- )     # 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 ;;
-
-    * )
-       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-newlib* | 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
-               ;;
-       -sco6)
-               os=-sco5v6
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
-               ;;
-       -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/'`
-               ;;
-       -sco5v6*)
-               # 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 | avr32 \
-       | bfin \
-       | 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 \
-       | m32c | m32r | m32rle | m68000 | m68k | m88k \
-       | maxq | mb | microblaze | mcore \
-       | mips | mipsbe | mipseb | mipsel | mipsle \
-       | mips16 \
-       | mips64 | mips64el \
-       | mips64vr | mips64vrel \
-       | mips64orion | mips64orionel \
-       | mips64vr4100 | mips64vr4100el \
-       | mips64vr4300 | mips64vr4300el \
-       | mips64vr5000 | mips64vr5000el \
-       | mips64vr5900 | mips64vr5900el \
-       | mipsisa32 | mipsisa32el \
-       | mipsisa32r2 | mipsisa32r2el \
-       | mipsisa64 | mipsisa64el \
-       | mipsisa64r2 | mipsisa64r2el \
-       | mipsisa64sb1 | mipsisa64sb1el \
-       | mipsisa64sr71k | mipsisa64sr71kel \
-       | mipstx39 | mipstx39el \
-       | mn10200 | mn10300 \
-       | mt \
-       | msp430 \
-       | nios | nios2 \
-       | ns16k | ns32k \
-       | or32 \
-       | pdp10 | pdp11 | pj | pjl \
-       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
-       | pyramid \
-       | score \
-       | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
-       | sh64 | sh64le \
-       | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
-       | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
-       | spu | strongarm \
-       | tahoe | thumb | tic4x | tic80 | tron \
-       | v850 | v850e \
-       | we32k \
-       | x86 | xc16x | xscale | xscalee[bl] | 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)
-               ;;
-       ms1)
-               basic_machine=mt-unknown
-               ;;
-
-       # 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-* | avr32-* \
-       | bfin-* | 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-* \
-       | m32c-* | m32r-* | m32rle-* \
-       | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
-       | m88110-* | m88k-* | maxq-* | mcore-* \
-       | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
-       | mips16-* \
-       | mips64-* | mips64el-* \
-       | mips64vr-* | mips64vrel-* \
-       | mips64orion-* | mips64orionel-* \
-       | mips64vr4100-* | mips64vr4100el-* \
-       | mips64vr4300-* | mips64vr4300el-* \
-       | mips64vr5000-* | mips64vr5000el-* \
-       | mips64vr5900-* | mips64vr5900el-* \
-       | mipsisa32-* | mipsisa32el-* \
-       | mipsisa32r2-* | mipsisa32r2el-* \
-       | mipsisa64-* | mipsisa64el-* \
-       | mipsisa64r2-* | mipsisa64r2el-* \
-       | mipsisa64sb1-* | mipsisa64sb1el-* \
-       | mipsisa64sr71k-* | mipsisa64sr71kel-* \
-       | mipstx39-* | mipstx39el-* \
-       | mmix-* \
-       | mt-* \
-       | msp430-* \
-       | nios-* | nios2-* \
-       | none-* | np1-* | ns16k-* | ns32k-* \
-       | orion-* \
-       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
-       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
-       | pyramid-* \
-       | romp-* | rs6000-* \
-       | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
-       | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
-       | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
-       | sparclite-* \
-       | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
-       | tahoe-* | thumb-* \
-       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
-       | tron-* \
-       | v850-* | v850e-* | vax-* \
-       | we32k-* \
-       | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
-       | 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
-               ;;
-       djgpp)
-               basic_machine=i586-pc
-               os=-msdosdjgpp
-               ;;
-       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
-               ;;
-       ms1-*)
-               basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
-               ;;
-       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
-               ;;
-       openrisc | openrisc-*)
-               basic_machine=or32-unknown
-               ;;
-       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
-               ;;
-       pc98)
-               basic_machine=i386-pc
-               ;;
-       pc98-*)
-               basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       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
-               ;;
-       rdos)
-               basic_machine=i386-pc
-               os=-rdos
-               ;;
-       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
-               ;;
-       sde)
-               basic_machine=mipsisa32-sde
-               os=-elf
-               ;;
-       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
-               ;;
-       xbox)
-               basic_machine=i686-pc
-               os=-mingw32
-               ;;
-       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
-               ;;
-       sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
-               basic_machine=sh-unknown
-               ;;
-       sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
-               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* | -solidbsd* \
-             | -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-newlib* | -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* \
-             | -skyos* | -haiku* | -rdos* | -toppers*)
-       # 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* | -haiku* \
-             | -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
-               ;;
-       -zvmoe)
-               os=-zvmoe
-               ;;
-       -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
-        score-*)
-               os=-elf
-               ;;
-        spu-*)
-               os=-elf
-               ;;
-       *-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
-               ;;
-       *-haiku)
-               os=-haiku
-               ;;
-       *-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
-
-# 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 38e877a..0000000
--- a/configure
+++ /dev/null
@@ -1,9685 +0,0 @@
-#! /bin/sh
-# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.61 for smartmontools 5.38.
-#
-# Report bugs to <smartmontools-support@lists.sourceforge.net>.
-#
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-# 2002, 2003, 2004, 2005, 2006 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 more Bourne compatible
-DUALCASE=1; export DUALCASE # for MKS sh
-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+"$@"}'='"$@"'
-  setopt NO_GLOB_SUBST
-else
-  case `(set -o) 2>/dev/null` in
-  *posix*) set -o posix ;;
-esac
-
-fi
-
-
-
-
-# PATH needs CR
-# 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
-
-# Support unset when possible.
-if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
-  as_unset=unset
-else
-  as_unset=false
-fi
-
-
-# IFS
-# We need space, tab and new line, in precisely that order.  Quoting is
-# there to prevent editors from complaining about space-tab.
-# (If _AS_PATH_WALK were called with IFS unset, it would disable word
-# splitting by setting IFS to empty value.)
-as_nl='
-'
-IFS=" ""       $as_nl"
-
-# Find who we are.  Look in the path if we contain no directory separator.
-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
-IFS=$as_save_IFS
-
-     ;;
-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_myself: error: cannot find myself; rerun with an absolute file name" >&2
-  { (exit 1); exit 1; }
-fi
-
-# Work around bugs in pre-3.0 UWIN ksh.
-for as_var in ENV MAIL MAILPATH
-do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
-done
-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) >/dev/null 2>&1 && $as_unset $as_var
-  fi
-done
-
-# Required to use basename.
-if expr a : '\(a\)' >/dev/null 2>&1 &&
-   test "X`expr 00001 : '.*\(...\)'`" = X001; 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'`
-
-# CDPATH.
-$as_unset CDPATH
-
-
-if test "x$CONFIG_SHELL" = x; then
-  if (eval ":") 2>/dev/null; then
-  as_have_required=yes
-else
-  as_have_required=no
-fi
-
-  if test $as_have_required = yes &&    (eval ":
-(as_func_return () {
-  (exit \$1)
-}
-as_func_success () {
-  as_func_return 0
-}
-as_func_failure () {
-  as_func_return 1
-}
-as_func_ret_success () {
-  return 0
-}
-as_func_ret_failure () {
-  return 1
-}
-
-exitcode=0
-if as_func_success; then
-  :
-else
-  exitcode=1
-  echo as_func_success failed.
-fi
-
-if as_func_failure; then
-  exitcode=1
-  echo as_func_failure succeeded.
-fi
-
-if as_func_ret_success; then
-  :
-else
-  exitcode=1
-  echo as_func_ret_success failed.
-fi
-
-if as_func_ret_failure; then
-  exitcode=1
-  echo as_func_ret_failure succeeded.
-fi
-
-if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
-  :
-else
-  exitcode=1
-  echo positional parameters were not saved.
-fi
-
-test \$exitcode = 0) || { (exit 1); exit 1; }
-
-(
-  as_lineno_1=\$LINENO
-  as_lineno_2=\$LINENO
-  test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&
-  test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }
-") 2> /dev/null; then
-  :
-else
-  as_candidate_shells=
-    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=.
-  case $as_dir in
-        /*)
-          for as_base in sh bash ksh sh5; do
-            as_candidate_shells="$as_candidate_shells $as_dir/$as_base"
-          done;;
-       esac
-done
-IFS=$as_save_IFS
-
-
-      for as_shell in $as_candidate_shells $SHELL; do
-        # Try only shells that exist, to save several forks.
-        if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
-               { ("$as_shell") 2> /dev/null <<\_ASEOF
-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+"$@"}'='"$@"'
-  setopt NO_GLOB_SUBST
-else
-  case `(set -o) 2>/dev/null` in
-  *posix*) set -o posix ;;
-esac
-
-fi
-
-
-:
-_ASEOF
-}; then
-  CONFIG_SHELL=$as_shell
-              as_have_required=yes
-              if { "$as_shell" 2> /dev/null <<\_ASEOF
-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+"$@"}'='"$@"'
-  setopt NO_GLOB_SUBST
-else
-  case `(set -o) 2>/dev/null` in
-  *posix*) set -o posix ;;
-esac
-
-fi
-
-
-:
-(as_func_return () {
-  (exit $1)
-}
-as_func_success () {
-  as_func_return 0
-}
-as_func_failure () {
-  as_func_return 1
-}
-as_func_ret_success () {
-  return 0
-}
-as_func_ret_failure () {
-  return 1
-}
-
-exitcode=0
-if as_func_success; then
-  :
-else
-  exitcode=1
-  echo as_func_success failed.
-fi
-
-if as_func_failure; then
-  exitcode=1
-  echo as_func_failure succeeded.
-fi
-
-if as_func_ret_success; then
-  :
-else
-  exitcode=1
-  echo as_func_ret_success failed.
-fi
-
-if as_func_ret_failure; then
-  exitcode=1
-  echo as_func_ret_failure succeeded.
-fi
-
-if ( set x; as_func_ret_success y && test x = "$1" ); then
-  :
-else
-  exitcode=1
-  echo positional parameters were not saved.
-fi
-
-test $exitcode = 0) || { (exit 1); exit 1; }
-
-(
-  as_lineno_1=$LINENO
-  as_lineno_2=$LINENO
-  test "x$as_lineno_1" != "x$as_lineno_2" &&
-  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }
-
-_ASEOF
-}; then
-  break
-fi
-
-fi
-
-      done
-
-      if test "x$CONFIG_SHELL" != x; then
-  for as_var in BASH_ENV ENV
-        do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
-        done
-        export CONFIG_SHELL
-        exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
-fi
-
-
-    if test $as_have_required = no; then
-  echo This script requires a shell more modern than all the
-      echo shells that I found on your system.  Please install a
-      echo modern shell, or manually run the script under such a
-      echo shell if you do have one.
-      { (exit 1); exit 1; }
-fi
-
-
-fi
-
-fi
-
-
-
-(eval "as_func_return () {
-  (exit \$1)
-}
-as_func_success () {
-  as_func_return 0
-}
-as_func_failure () {
-  as_func_return 1
-}
-as_func_ret_success () {
-  return 0
-}
-as_func_ret_failure () {
-  return 1
-}
-
-exitcode=0
-if as_func_success; then
-  :
-else
-  exitcode=1
-  echo as_func_success failed.
-fi
-
-if as_func_failure; then
-  exitcode=1
-  echo as_func_failure succeeded.
-fi
-
-if as_func_ret_success; then
-  :
-else
-  exitcode=1
-  echo as_func_ret_success failed.
-fi
-
-if as_func_ret_failure; then
-  exitcode=1
-  echo as_func_ret_failure succeeded.
-fi
-
-if ( set x; as_func_ret_success y && test x = \"\$1\" ); then
-  :
-else
-  exitcode=1
-  echo positional parameters were not saved.
-fi
-
-test \$exitcode = 0") || {
-  echo No shell found that supports shell functions.
-  echo Please tell autoconf@gnu.org about your system,
-  echo including any error possibly output before this
-  echo message
-}
-
-
-
-  as_lineno_1=$LINENO
-  as_lineno_2=$LINENO
-  test "x$as_lineno_1" != "x$as_lineno_2" &&
-  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
-
-  # 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 after each line using $LINENO; the second 'sed'
-  # does the real work.  The second script uses 'N' to pair each
-  # line-number line with the line containing $LINENO, 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
-  # scripts with optimization help from Paolo Bonzini.  Blame Lee
-  # E. McMahon (1931-1989) for sed's syntax.  :-)
-  sed -n '
-    p
-    /[$]LINENO/=
-  ' <$as_myself |
-    sed '
-      s/[$]LINENO.*/&-/
-      t lineno
-      b
-      :lineno
-      N
-      :loop
-      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
-      t loop
-      s/-\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 sensitive to this).
-  . "./$as_me.lineno"
-  # Exit status is that of the last command.
-  exit
-}
-
-
-if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
-  as_dirname=dirname
-else
-  as_dirname=false
-fi
-
-ECHO_C= ECHO_N= ECHO_T=
-case `echo -n x` in
--n*)
-  case `echo 'x\c'` in
-  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
-  *)   ECHO_C='\c';;
-  esac;;
-*)
-  ECHO_N='-n';;
-esac
-
-if expr a : '\(a\)' >/dev/null 2>&1 &&
-   test "X`expr 00001 : '.*\(...\)'`" = X001; then
-  as_expr=expr
-else
-  as_expr=false
-fi
-
-rm -f conf$$ conf$$.exe conf$$.file
-if test -d conf$$.dir; then
-  rm -f conf$$.dir/conf$$.file
-else
-  rm -f conf$$.dir
-  mkdir conf$$.dir
-fi
-echo >conf$$.file
-if ln -s conf$$.file conf$$ 2>/dev/null; then
-  as_ln_s='ln -s'
-  # ... but there are two gotchas:
-  # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
-  # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
-  # In both cases, we have to default to `cp -p'.
-  ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
-    as_ln_s='cp -p'
-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$$.dir/conf$$.file conf$$.file
-rmdir conf$$.dir 2>/dev/null
-
-if mkdir -p . 2>/dev/null; then
-  as_mkdir_p=:
-else
-  test -d ./-p && rmdir ./-p
-  as_mkdir_p=false
-fi
-
-if test -x / >/dev/null 2>&1; then
-  as_test_x='test -x'
-else
-  if ls -dL / >/dev/null 2>&1; then
-    as_ls_L_option=L
-  else
-    as_ls_L_option=
-  fi
-  as_test_x='
-    eval sh -c '\''
-      if test -d "$1"; then
-        test -d "$1/.";
-      else
-       case $1 in
-        -*)set "./$1";;
-       esac;
-       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
-       ???[sx]*):;;*)false;;esac;fi
-    '\'' sh
-  '
-fi
-as_executable_p=$as_test_x
-
-# 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'"
-
-
-
-exec 7<&0 </dev/null 6>&1
-
-# 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`
-
-#
-# Initializations.
-#
-ac_default_prefix=/usr/local
-ac_clean_files=
-ac_config_libobj_dir=.
-LIBOBJS=
-cross_compiling=no
-subdirs=
-MFLAGS=
-MAKEFLAGS=
-SHELL=${CONFIG_SHELL-/bin/sh}
-
-# Identity of this package.
-PACKAGE_NAME='smartmontools'
-PACKAGE_TARNAME='smartmontools'
-PACKAGE_VERSION='5.38'
-PACKAGE_STRING='smartmontools 5.38'
-PACKAGE_BUGREPORT='smartmontools-support@lists.sourceforge.net'
-
-ac_unique_file="smartctl.cpp"
-# Factoring default headers for most tests.
-ac_includes_default="\
-#include <stdio.h>
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-# include <sys/stat.h>
-#endif
-#ifdef STDC_HEADERS
-# include <stdlib.h>
-# include <stddef.h>
-#else
-# ifdef HAVE_STDLIB_H
-#  include <stdlib.h>
-# endif
-#endif
-#ifdef HAVE_STRING_H
-# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
-#  include <memory.h>
-# endif
-# include <string.h>
-#endif
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif
-#ifdef HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-#ifdef HAVE_STDINT_H
-# include <stdint.h>
-#endif
-#ifdef 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
-datarootdir
-datadir
-sysconfdir
-sharedstatedir
-localstatedir
-includedir
-oldincludedir
-docdir
-infodir
-htmldir
-dvidir
-pdfdir
-psdir
-libdir
-localedir
-mandir
-DEFS
-ECHO_C
-ECHO_N
-ECHO_T
-LIBS
-build_alias
-host_alias
-target_alias
-INSTALL_PROGRAM
-INSTALL_SCRIPT
-INSTALL_DATA
-am__isrc
-CYGPATH_W
-PACKAGE
-VERSION
-ACLOCAL
-AUTOCONF
-AUTOMAKE
-AUTOHEADER
-MAKEINFO
-install_sh
-STRIP
-INSTALL_STRIP_PROGRAM
-mkdir_p
-AWK
-SET_MAKE
-am__leading_dot
-AMTAR
-am__tar
-am__untar
-MAINTAINER_MODE_TRUE
-MAINTAINER_MODE_FALSE
-MAINT
-CXX
-CXXFLAGS
-LDFLAGS
-CPPFLAGS
-ac_ct_CXX
-EXEEXT
-OBJEXT
-DEPDIR
-am__include
-am__quote
-AMDEP_TRUE
-AMDEP_FALSE
-AMDEPBACKSLASH
-CXXDEPMODE
-am__fastdepCXX_TRUE
-am__fastdepCXX_FALSE
-CC
-CFLAGS
-ac_ct_CC
-CCDEPMODE
-am__fastdepCC_TRUE
-am__fastdepCC_FALSE
-CCAS
-CCASFLAGS
-CCASDEPMODE
-am__fastdepCCAS_TRUE
-am__fastdepCCAS_FALSE
-build
-build_cpu
-build_vendor
-build_os
-host
-host_cpu
-host_vendor
-host_os
-CXXCPP
-GREP
-EGREP
-libc_have_working_snprintf
-gcc_have_attr_packed
-ASFLAGS
-exampledir
-initddir
-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=''
-      ac_precious_vars='build_alias
-host_alias
-target_alias
-CXX
-CXXFLAGS
-LDFLAGS
-LIBS
-CPPFLAGS
-CCC
-CC
-CFLAGS
-CCAS
-CCASFLAGS
-CXXCPP'
-
-
-# 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.
-# (The list follows the same order as the GNU Coding Standards.)
-bindir='${exec_prefix}/bin'
-sbindir='${exec_prefix}/sbin'
-libexecdir='${exec_prefix}/libexec'
-datarootdir='${prefix}/share'
-datadir='${datarootdir}'
-sysconfdir='${prefix}/etc'
-sharedstatedir='${prefix}/com'
-localstatedir='${prefix}/var'
-includedir='${prefix}/include'
-oldincludedir='/usr/include'
-docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
-infodir='${datarootdir}/info'
-htmldir='${docdir}'
-dvidir='${docdir}'
-pdfdir='${docdir}'
-psdir='${docdir}'
-libdir='${exec_prefix}/lib'
-localedir='${datarootdir}/locale'
-mandir='${datarootdir}/man'
-
-ac_prev=
-ac_dashdash=
-for ac_option
-do
-  # If the previous option needs an argument, assign it.
-  if test -n "$ac_prev"; then
-    eval $ac_prev=\$ac_option
-    ac_prev=
-    continue
-  fi
-
-  case $ac_option in
-  *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
-  *)   ac_optarg=yes ;;
-  esac
-
-  # Accept the important Cygnus configure options, so we can diagnose typos.
-
-  case $ac_dashdash$ac_option in
-  --)
-    ac_dashdash=yes ;;
-
-  -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)
-    ac_prev=datadir ;;
-  -datadir=* | --datadir=* | --datadi=* | --datad=*)
-    datadir=$ac_optarg ;;
-
-  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
-  | --dataroo | --dataro | --datar)
-    ac_prev=datarootdir ;;
-  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
-  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
-    datarootdir=$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 ;;
-
-  -docdir | --docdir | --docdi | --doc | --do)
-    ac_prev=docdir ;;
-  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
-    docdir=$ac_optarg ;;
-
-  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
-    ac_prev=dvidir ;;
-  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
-    dvidir=$ac_optarg ;;
-
-  -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'`
-    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 ;;
-
-  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
-    ac_prev=htmldir ;;
-  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
-  | --ht=*)
-    htmldir=$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 ;;
-
-  -localedir | --localedir | --localedi | --localed | --locale)
-    ac_prev=localedir ;;
-  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
-    localedir=$ac_optarg ;;
-
-  -localstatedir | --localstatedir | --localstatedi | --localstated \
-  | --localstate | --localstat | --localsta | --localst | --locals)
-    ac_prev=localstatedir ;;
-  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
-  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
-    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 ;;
-
-  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
-    ac_prev=pdfdir ;;
-  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
-    pdfdir=$ac_optarg ;;
-
-  -psdir | --psdir | --psdi | --psd | --ps)
-    ac_prev=psdir ;;
-  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
-    psdir=$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'`
-    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; }; }
-    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 directory names.
-for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
-               datadir sysconfdir sharedstatedir localstatedir includedir \
-               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-               libdir localedir mandir
-do
-  eval ac_val=\$$ac_var
-  case $ac_val in
-    [\\/$]* | ?:[\\/]* )  continue;;
-    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
-  esac
-  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
-   { (exit 1); exit 1; }; }
-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
-
-
-ac_pwd=`pwd` && test -n "$ac_pwd" &&
-ac_ls_di=`ls -di .` &&
-ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
-  { echo "$as_me: error: Working directory cannot be determined" >&2
-   { (exit 1); exit 1; }; }
-test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
-  { echo "$as_me: error: pwd does not report name of working directory" >&2
-   { (exit 1); exit 1; }; }
-
-
-# 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 the parent directory.
-  ac_confdir=`$as_dirname -- "$0" ||
-$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
-  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
-  { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
-   { (exit 1); exit 1; }; }
-fi
-ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
-ac_abs_confdir=`(
-       cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2
-   { (exit 1); exit 1; }; }
-       pwd)`
-# When building in place, set srcdir=.
-if test "$ac_abs_confdir" = "$ac_pwd"; then
-  srcdir=.
-fi
-# Remove unnecessary trailing slashes from srcdir.
-# Double slashes in file names in object file debugging info
-# mess up M-x gdb in Emacs.
-case $srcdir in
-*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
-esac
-for ac_var in $ac_precious_vars; do
-  eval ac_env_${ac_var}_set=\${${ac_var}+set}
-  eval ac_env_${ac_var}_value=\$${ac_var}
-  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
-  eval ac_cv_env_${ac_var}_value=\$${ac_var}
-done
-
-#
-# 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.38 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 \`..']
-
-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]
-  --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]
-  --datarootdir=DIR      read-only arch.-independent data root [PREFIX/share]
-  --datadir=DIR          read-only architecture-independent data [DATAROOTDIR]
-  --infodir=DIR          info documentation [DATAROOTDIR/info]
-  --localedir=DIR        locale-dependent data [DATAROOTDIR/locale]
-  --mandir=DIR           man documentation [DATAROOTDIR/man]
-  --docdir=DIR           documentation root [DATAROOTDIR/doc/smartmontools]
-  --htmldir=DIR          html documentation [DOCDIR]
-  --dvidir=DIR           dvi documentation [DOCDIR]
-  --pdfdir=DIR           pdf documentation [DOCDIR]
-  --psdir=DIR            ps documentation [DOCDIR]
-_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.38:";;
-   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:
-  CXX         C++ compiler command
-  CXXFLAGS    C++ compiler flags
-  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
-  LIBS        libraries to pass to the linker, e.g. -l<library>
-  CPPFLAGS    C/C++/Objective C preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
-  CC          C compiler command
-  CFLAGS      C compiler flags
-  CCAS        assembler compiler command (defaults to CC)
-  CCASFLAGS   assembler compiler flags (defaults to CFLAGS)
-  CXXCPP      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
-ac_status=$?
-fi
-
-if test "$ac_init_help" = "recursive"; then
-  # If there are subdirs, report their specific --help.
-  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
-    test -d "$ac_dir" || continue
-    ac_builddir=.
-
-case "$ac_dir" in
-.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
-*)
-  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
-  # A ".." for each directory in $ac_dir_suffix.
-  ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
-  case $ac_top_builddir_sub in
-  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
-  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
-  esac ;;
-esac
-ac_abs_top_builddir=$ac_pwd
-ac_abs_builddir=$ac_pwd$ac_dir_suffix
-# for backward compatibility:
-ac_top_builddir=$ac_top_build_prefix
-
-case $srcdir in
-  .)  # We are building in place.
-    ac_srcdir=.
-    ac_top_srcdir=$ac_top_builddir_sub
-    ac_abs_top_srcdir=$ac_pwd ;;
-  [\\/]* | ?:[\\/]* )  # Absolute name.
-    ac_srcdir=$srcdir$ac_dir_suffix;
-    ac_top_srcdir=$srcdir
-    ac_abs_top_srcdir=$srcdir ;;
-  *) # Relative name.
-    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
-    ac_top_srcdir=$ac_top_build_prefix$srcdir
-    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
-esac
-ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
-
-    cd "$ac_dir" || { ac_status=$?; continue; }
-    # Check for guested 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
-    else
-      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
-    fi || ac_status=$?
-    cd "$ac_pwd" || { ac_status=$?; break; }
-  done
-fi
-
-test -n "$ac_init_help" && exit $ac_status
-if $ac_init_version; then
-  cat <<\_ACEOF
-smartmontools configure 5.38
-generated by GNU Autoconf 2.61
-
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-2002, 2003, 2004, 2005, 2006 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
-fi
-cat >config.log <<_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.38, which was
-generated by GNU Autoconf 2.61.  Invocation command line was
-
-  $ $0 $@
-
-_ACEOF
-exec 5>>config.log
-{
-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`
-/usr/bin/hostinfo      = `(/usr/bin/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
-IFS=$as_save_IFS
-
-} >&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_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_arg'"
-      ;;
-    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: Use '\'' to represent an apostrophe within the trap.
-# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
-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,
-(
-  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
-    eval ac_val=\$$ac_var
-    case $ac_val in #(
-    *${as_nl}*)
-      case $ac_var in #(
-      *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
-echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
-      esac
-      case $ac_var in #(
-      _ | IFS | as_nl) ;; #(
-      *) $as_unset $ac_var ;;
-      esac ;;
-    esac
-  done
-  (set) 2>&1 |
-    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
-    *${as_nl}ac_space=\ *)
-      sed -n \
-       "s/'\''/'\''\\\\'\'''\''/g;
-         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
-      ;; #(
-    *)
-      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
-      ;;
-    esac |
-    sort
-)
-    echo
-
-    cat <<\_ASBOX
-## ----------------- ##
-## Output variables. ##
-## ----------------- ##
-_ASBOX
-    echo
-    for ac_var in $ac_subst_vars
-    do
-      eval ac_val=\$$ac_var
-      case $ac_val in
-      *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
-      esac
-      echo "$ac_var='\''$ac_val'\''"
-    done | sort
-    echo
-
-    if test -n "$ac_subst_files"; then
-      cat <<\_ASBOX
-## ------------------- ##
-## File substitutions. ##
-## ------------------- ##
-_ASBOX
-      echo
-      for ac_var in $ac_subst_files
-      do
-       eval ac_val=\$$ac_var
-       case $ac_val in
-       *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
-       esac
-       echo "$ac_var='\''$ac_val'\''"
-      done | sort
-      echo
-    fi
-
-    if test -s confdefs.h; then
-      cat <<\_ASBOX
-## ----------- ##
-## confdefs.h. ##
-## ----------- ##
-_ASBOX
-      echo
-      cat confdefs.h
-      echo
-    fi
-    test "$ac_signal" != 0 &&
-      echo "$as_me: caught signal $ac_signal"
-    echo "$as_me: exit $exit_status"
-  } >&5
-  rm -f core *.core core.conftest.* &&
-    rm -f -r 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 -f -r conftest* 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 -n "$CONFIG_SITE"; then
-  set x "$CONFIG_SITE"
-elif test "x$prefix" != xNONE; then
-  set x "$prefix/share/config.site" "$prefix/etc/config.site"
-else
-  set x "$ac_default_prefix/share/config.site" \
-       "$ac_default_prefix/etc/config.site"
-fi
-shift
-for ac_site_file
-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 $ac_precious_vars; 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.134 2008/03/09 13:41:23 card_captor Exp $'`
-smartmontools_release_date=2008/03/10
-smartmontools_release_time="10:44:07 GMT"
-
-
-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.10'
-
-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
-
-# These three variables are undocumented and unsupported,
-# and are intended to be withdrawn in a future Autoconf release.
-# They can cause serious problems if a builder's source tree is in a directory
-# whose full name contains unusual characters.
-ac_config_guess="$SHELL $ac_aux_dir/config.guess"  # Please don't use this var.
-ac_config_sub="$SHELL $ac_aux_dir/config.sub"  # Please don't use this var.
-ac_configure="$SHELL $ac_aux_dir/configure"  # Please don't use this var.
-
-
-# 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 { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$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
-IFS=$as_save_IFS
-
-
-fi
-  if test "${ac_cv_path_install+set}" = set; then
-    INSTALL=$ac_cv_path_install
-  else
-    # As a last resort, use the slow shell script.  Don't cache a
-    # value for INSTALL within a source directory, because that will
-    # break other packages using the cache if that directory is
-    # removed, or if the value is a relative name.
-    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 -f 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
-
-{ echo "$as_me:$LINENO: checking for a thread-safe mkdir -p" >&5
-echo $ECHO_N "checking for a thread-safe mkdir -p... $ECHO_C" >&6; }
-if test -z "$MKDIR_P"; then
-  if test "${ac_cv_path_mkdir+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_prog in mkdir gmkdir; do
-        for ac_exec_ext in '' $ac_executable_extensions; do
-          { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
-          case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
-            'mkdir (GNU coreutils) '* | \
-            'mkdir (coreutils) '* | \
-            'mkdir (fileutils) '4.1*)
-              ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext
-              break 3;;
-          esac
-        done
-       done
-done
-IFS=$as_save_IFS
-
-fi
-
-  if test "${ac_cv_path_mkdir+set}" = set; then
-    MKDIR_P="$ac_cv_path_mkdir -p"
-  else
-    # As a last resort, use the slow shell script.  Don't cache a
-    # value for MKDIR_P within a source directory, because that will
-    # break other packages using the cache if that directory is
-    # removed, or if the value is a relative name.
-    test -d ./--version && rmdir ./--version
-    MKDIR_P="$ac_install_sh -d"
-  fi
-fi
-{ echo "$as_me:$LINENO: result: $MKDIR_P" >&5
-echo "${ECHO_T}$MKDIR_P" >&6; }
-
-mkdir_p="$MKDIR_P"
-case $mkdir_p in
-  [\\/$]* | ?:[\\/]*) ;;
-  */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
-esac
-
-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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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
-IFS=$as_save_IFS
-
-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 x ${MAKE-make}; ac_make=`echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
-if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.make <<\_ACEOF
-SHELL = /bin/sh
-all:
-       @echo '@@@%%%=$(MAKE)=@@@%%%'
-_ACEOF
-# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
-case `${MAKE-make} -f conftest.make 2>/dev/null` in
-  *@@@%%%=?*=@@@%%%*)
-    eval ac_cv_prog_make_${ac_make}_set=yes;;
-  *)
-    eval ac_cv_prog_make_${ac_make}_set=no;;
-esac
-rm -f conftest.make
-fi
-if eval test \$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
-
-if test "`cd $srcdir && pwd`" != "`pwd`"; then
-  # Use -I$(srcdir) only when $(srcdir) != ., so that make's output
-  # is not polluted with repeated "-I."
-  am__isrc=' -I$(srcdir)'
-  # test to see if srcdir already configured
-  if 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
-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.38'
-
-
-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-"\$(SHELL) $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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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
-IFS=$as_save_IFS
-
-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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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
-IFS=$as_save_IFS
-
-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
-
-  if test "x$ac_ct_STRIP" = x; then
-    STRIP=":"
-  else
-    case $cross_compiling:$ac_tool_warned in
-yes:)
-{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
-whose name does not start with the host triplet.  If you think this
-configuration is useful to you, please write to autoconf@gnu.org." >&5
-echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
-whose name does not start with the host triplet.  If you think this
-configuration is useful to you, please write to autoconf@gnu.org." >&2;}
-ac_tool_warned=yes ;;
-esac
-    STRIP=$ac_ct_STRIP
-  fi
-else
-  STRIP="$ac_cv_prog_STRIP"
-fi
-
-fi
-INSTALL_STRIP_PROGRAM="\$(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 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=cpp
-ac_cpp='$CXXCPP $CPPFLAGS'
-ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
-
-ac_ext=cpp
-ac_cpp='$CXXCPP $CPPFLAGS'
-ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
-if test -z "$CXX"; then
-  if test -n "$CCC"; then
-    CXX=$CCC
-  else
-    if test -n "$ac_tool_prefix"; then
-  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
-  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_CXX+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$CXX"; then
-  ac_cv_prog_CXX="$CXX" # 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
-    ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-IFS=$as_save_IFS
-
-fi
-fi
-CXX=$ac_cv_prog_CXX
-if test -n "$CXX"; then
-  { echo "$as_me:$LINENO: result: $CXX" >&5
-echo "${ECHO_T}$CXX" >&6; }
-else
-  { echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6; }
-fi
-
-
-    test -n "$CXX" && break
-  done
-fi
-if test -z "$CXX"; then
-  ac_ct_CXX=$CXX
-  for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
-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_CXX+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  if test -n "$ac_ct_CXX"; then
-  ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # 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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
-    ac_cv_prog_ac_ct_CXX="$ac_prog"
-    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-done
-IFS=$as_save_IFS
-
-fi
-fi
-ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
-if test -n "$ac_ct_CXX"; then
-  { echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5
-echo "${ECHO_T}$ac_ct_CXX" >&6; }
-else
-  { echo "$as_me:$LINENO: result: no" >&5
-echo "${ECHO_T}no" >&6; }
-fi
-
-
-  test -n "$ac_ct_CXX" && break
-done
-
-  if test "x$ac_ct_CXX" = x; then
-    CXX="g++"
-  else
-    case $cross_compiling:$ac_tool_warned in
-yes:)
-{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
-whose name does not start with the host triplet.  If you think this
-configuration is useful to you, please write to autoconf@gnu.org." >&5
-echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
-whose name does not start with the host triplet.  If you think this
-configuration is useful to you, please write to autoconf@gnu.org." >&2;}
-ac_tool_warned=yes ;;
-esac
-    CXX=$ac_ct_CXX
-  fi
-fi
-
-  fi
-fi
-# Provide some information about the compiler.
-echo "$as_me:$LINENO: checking for C++ compiler version" >&5
-ac_compiler=`set X $ac_compile; echo $2`
-{ (ac_try="$ac_compiler --version >&5"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_compiler --version >&5") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }
-{ (ac_try="$ac_compiler -v >&5"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_compiler -v >&5") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }
-{ (ac_try="$ac_compiler -V >&5"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_compiler -V >&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[^ ]*//'`
-#
-# List of possible output files, starting from the most likely.
-# The algorithm is not robust to junk in `.', hence go to wildcards (a.*)
-# only as a last resort.  b.out is created by i960 compilers.
-ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out'
-#
-# The IRIX 6 linker writes into existing files which may not be
-# executable, retaining their permissions.  Remove them first so a
-# subsequent execution test works.
-ac_rmfiles=
-for ac_file in $ac_files
-do
-  case $ac_file in
-    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;
-    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
-  esac
-done
-rm -f $ac_rmfiles
-
-if { (ac_try="$ac_link_default"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_link_default") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; then
-  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
-# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
-# in a Makefile.  We should not override ac_cv_exeext if it was cached,
-# so that the user can short-circuit this test for compilers unknown to
-# Autoconf.
-for ac_file in $ac_files ''
-do
-  test -f "$ac_file" || continue
-  case $ac_file in
-    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj )
-       ;;
-    [ab].out )
-       # We found the default executable, but exeext='' is most
-       # certainly right.
-       break;;
-    *.* )
-        if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
-       then :; else
-          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
-       fi
-       # We set ac_cv_exeext here because the later test for it is not
-       # safe: cross compilers may not add the suffix if given an `-o'
-       # argument, so we may need to know it at that point already.
-       # Even if this section looks crufty: it has the advantage of
-       # actually working.
-       break;;
-    * )
-       break;;
-  esac
-done
-test "$ac_cv_exeext" = no && ac_cv_exeext=
-
-else
-  ac_file=''
-fi
-
-{ echo "$as_me:$LINENO: result: $ac_file" >&5
-echo "${ECHO_T}$ac_file" >&6; }
-if test -z "$ac_file"; then
-  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
-
-# Check that 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'
-  { (case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 that 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 { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 | *.map | *.inf | *.o | *.obj ) ;;
-    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
-         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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_compile") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }; then
-  for ac_file in conftest.o conftest.obj conftest.*; do
-  test -f "$ac_file" || continue;
-  case $ac_file in
-    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;;
-    *) 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_cxx_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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core conftest.err conftest.$ac_objext conftest.$ac_ext
-ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
-
-fi
-{ echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5
-echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6; }
-GXX=`test $ac_compiler_gnu = yes && echo yes`
-ac_test_CXXFLAGS=${CXXFLAGS+set}
-ac_save_CXXFLAGS=$CXXFLAGS
-{ echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5
-echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6; }
-if test "${ac_cv_prog_cxx_g+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  ac_save_cxx_werror_flag=$ac_cxx_werror_flag
-   ac_cxx_werror_flag=yes
-   ac_cv_prog_cxx_g=no
-   CXXFLAGS="-g"
-   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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; then
-  ac_cv_prog_cxx_g=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-       CXXFLAGS=""
-      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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; then
-  :
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-       ac_cxx_werror_flag=$ac_save_cxx_werror_flag
-        CXXFLAGS="-g"
-        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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; then
-  ac_cv_prog_cxx_g=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-
-fi
-
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-   ac_cxx_werror_flag=$ac_save_cxx_werror_flag
-fi
-{ echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5
-echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6; }
-if test "$ac_test_CXXFLAGS" = set; then
-  CXXFLAGS=$ac_save_CXXFLAGS
-elif test $ac_cv_prog_cxx_g = yes; then
-  if test "$GXX" = yes; then
-    CXXFLAGS="-g -O2"
-  else
-    CXXFLAGS="-g"
-  fi
-else
-  if test "$GXX" = yes; then
-    CXXFLAGS="-O2"
-  else
-    CXXFLAGS=
-  fi
-fi
-ac_ext=cpp
-ac_cpp='$CXXCPP $CPPFLAGS'
-ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_cxx_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 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="$CXX"  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_CXX_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_CXX_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/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
-       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_CXX_dependencies_compiler_type=$depmode
-        break
-      fi
-    fi
-  done
-
-  cd ..
-  rm -rf conftest.dir
-else
-  am_cv_CXX_dependencies_compiler_type=none
-fi
-
-fi
-{ echo "$as_me:$LINENO: result: $am_cv_CXX_dependencies_compiler_type" >&5
-echo "${ECHO_T}$am_cv_CXX_dependencies_compiler_type" >&6; }
-CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type
-
- if
-  test "x$enable_dependency_tracking" != xno \
-  && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then
-  am__fastdepCXX_TRUE=
-  am__fastdepCXX_FALSE='#'
-else
-  am__fastdepCXX_TRUE='#'
-  am__fastdepCXX_FALSE=
-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
-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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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
-IFS=$as_save_IFS
-
-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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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
-IFS=$as_save_IFS
-
-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
-
-  if test "x$ac_ct_CC" = x; then
-    CC=""
-  else
-    case $cross_compiling:$ac_tool_warned in
-yes:)
-{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
-whose name does not start with the host triplet.  If you think this
-configuration is useful to you, please write to autoconf@gnu.org." >&5
-echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
-whose name does not start with the host triplet.  If you think this
-configuration is useful to you, please write to autoconf@gnu.org." >&2;}
-ac_tool_warned=yes ;;
-esac
-    CC=$ac_ct_CC
-  fi
-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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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
-IFS=$as_save_IFS
-
-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
-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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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
-IFS=$as_save_IFS
-
-if test $ac_prog_rejected = yes; then
-  # We found a bogon in the path, so make sure we never use it.
-  set dummy $ac_cv_prog_CC
-  shift
-  if test $# != 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.exe
-  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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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
-IFS=$as_save_IFS
-
-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.exe
-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 { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$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
-IFS=$as_save_IFS
-
-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
-
-  if test "x$ac_ct_CC" = x; then
-    CC=""
-  else
-    case $cross_compiling:$ac_tool_warned in
-yes:)
-{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools
-whose name does not start with the host triplet.  If you think this
-configuration is useful to you, please write to autoconf@gnu.org." >&5
-echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools
-whose name does not start with the host triplet.  If you think this
-configuration is useful to you, please write to autoconf@gnu.org." >&2;}
-ac_tool_warned=yes ;;
-esac
-    CC=$ac_ct_CC
-  fi
-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`
-{ (ac_try="$ac_compiler --version >&5"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_compiler --version >&5") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }
-{ (ac_try="$ac_compiler -v >&5"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_compiler -v >&5") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }
-{ (ac_try="$ac_compiler -V >&5"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_compiler -V >&5") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); }
-
-{ 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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_c_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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
-{ 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
-  ac_save_c_werror_flag=$ac_c_werror_flag
-   ac_c_werror_flag=yes
-   ac_cv_prog_cc_g=no
-   CFLAGS="-g"
-   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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_c_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; then
-  ac_cv_prog_cc_g=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-       CFLAGS=""
-      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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_c_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; then
-  :
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-       ac_c_werror_flag=$ac_save_c_werror_flag
-        CFLAGS="-g"
-        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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_c_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; then
-  ac_cv_prog_cc_g=yes
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-
-fi
-
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-   ac_c_werror_flag=$ac_save_c_werror_flag
-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 ISO C89" >&5
-echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; }
-if test "${ac_cv_prog_cc_c89+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  ac_cv_prog_cc_c89=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 -std 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 -std.  */
-int osf4_cc_array ['\x00' == 0 ? 1 : -1];
-
-/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
-   inside strings and character constants.  */
-#define FOO(x) 'x'
-int xlc6_cc_array[FOO(a) == 'x' ? 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
-for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
-       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
-do
-  CC="$ac_save_CC $ac_arg"
-  rm -f conftest.$ac_objext
-if { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_c_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; then
-  ac_cv_prog_cc_c89=$ac_arg
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-
-fi
-
-rm -f core conftest.err conftest.$ac_objext
-  test "x$ac_cv_prog_cc_c89" != "xno" && break
-done
-rm -f conftest.$ac_ext
-CC=$ac_save_CC
-
-fi
-# AC_CACHE_VAL
-case "x$ac_cv_prog_cc_c89" in
-  x)
-    { echo "$as_me:$LINENO: result: none needed" >&5
-echo "${ECHO_T}none needed" >&6; } ;;
-  xno)
-    { echo "$as_me:$LINENO: result: unsupported" >&5
-echo "${ECHO_T}unsupported" >&6; } ;;
-  *)
-    CC="$CC $ac_cv_prog_cc_c89"
-    { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5
-echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;;
-esac
-
-
-ac_ext=cpp
-ac_cpp='$CXXCPP $CPPFLAGS'
-ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
-
-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/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
-       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
-
-
-
-depcc="$CCAS"   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_CCAS_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_CCAS_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/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
-       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_CCAS_dependencies_compiler_type=$depmode
-        break
-      fi
-    fi
-  done
-
-  cd ..
-  rm -rf conftest.dir
-else
-  am_cv_CCAS_dependencies_compiler_type=none
-fi
-
-fi
-{ echo "$as_me:$LINENO: result: $am_cv_CCAS_dependencies_compiler_type" >&5
-echo "${ECHO_T}$am_cv_CCAS_dependencies_compiler_type" >&6; }
-CCASDEPMODE=depmode=$am_cv_CCAS_dependencies_compiler_type
-
- if
-  test "x$enable_dependency_tracking" != xno \
-  && test "$am_cv_CCAS_dependencies_compiler_type" = gcc3; then
-  am__fastdepCCAS_TRUE=
-  am__fastdepCCAS_FALSE='#'
-else
-  am__fastdepCCAS_TRUE='#'
-  am__fastdepCCAS_FALSE=
-fi
-
-
-# Find a good install program.  We prefer a C program (faster),
-# so one script is as good as another.  But avoid the broken or
-# incompatible versions:
-# SysV /etc/install, /usr/sbin/install
-# SunOS /usr/etc/install
-# IRIX /sbin/install
-# AIX /bin/install
-# 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 { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$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
-IFS=$as_save_IFS
-
-
-fi
-  if test "${ac_cv_path_install+set}" = set; then
-    INSTALL=$ac_cv_path_install
-  else
-    # As a last resort, use the slow shell script.  Don't cache a
-    # value for INSTALL within a source directory, because that will
-    # break other packages using the cache if that directory is
-    # removed, or if the value is a relative name.
-    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.
-$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
-  { { echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5
-echo "$as_me: error: cannot run $SHELL $ac_aux_dir/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_build_alias=$build_alias
-test "x$ac_build_alias" = x &&
-  ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
-test "x$ac_build_alias" = x &&
-  { { 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=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
-  { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5
-echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_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; }
-case $ac_cv_build in
-*-*-*) ;;
-*) { { echo "$as_me:$LINENO: error: invalid value of canonical build" >&5
-echo "$as_me: error: invalid value of canonical build" >&2;}
-   { (exit 1); exit 1; }; };;
-esac
-build=$ac_cv_build
-ac_save_IFS=$IFS; IFS='-'
-set x $ac_cv_build
-shift
-build_cpu=$1
-build_vendor=$2
-shift; shift
-# Remember, the first character of IFS is used to create $*,
-# except with old shells:
-build_os=$*
-IFS=$ac_save_IFS
-case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
-
-
-{ 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
-  if test "x$host_alias" = x; then
-  ac_cv_host=$ac_cv_build
-else
-  ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
-    { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5
-echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;}
-   { (exit 1); exit 1; }; }
-fi
-
-fi
-{ echo "$as_me:$LINENO: result: $ac_cv_host" >&5
-echo "${ECHO_T}$ac_cv_host" >&6; }
-case $ac_cv_host in
-*-*-*) ;;
-*) { { echo "$as_me:$LINENO: error: invalid value of canonical host" >&5
-echo "$as_me: error: invalid value of canonical host" >&2;}
-   { (exit 1); exit 1; }; };;
-esac
-host=$ac_cv_host
-ac_save_IFS=$IFS; IFS='-'
-set x $ac_cv_host
-shift
-host_cpu=$1
-host_vendor=$2
-shift; shift
-# Remember, the first character of IFS is used to create $*,
-# except with old shells:
-host_os=$*
-IFS=$ac_save_IFS
-case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac
-
-
-case "${host}" in
-       *-*-mingw*)
-               CPPFLAGS="$CPPFLAGS -mno-cygwin"
-               LDFLAGS="$LDFLAGS -mno-cygwin"
-               CPPFLAGS="$CPPFLAGS -idirafter ${srcdir}/posix -idirafter ${srcdir}/os_win32"
-               ;;
-       *-*-freebsd*)
-               CPPFLAGS="$CPPFLAGS -I/usr/src/sys"
-               ;;
-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
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char gethostbyname ();
-int
-main ()
-{
-return gethostbyname ();
-  ;
-  return 0;
-}
-_ACEOF
-for ac_lib in '' nsl; do
-  if test -z "$ac_lib"; then
-    ac_res="none required"
-  else
-    ac_res=-l$ac_lib
-    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
-  fi
-  rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest$ac_exeext &&
-       $as_test_x conftest$ac_exeext; then
-  ac_cv_search_gethostbyname=$ac_res
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-
-fi
-
-rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
-      conftest$ac_exeext
-  if test "${ac_cv_search_gethostbyname+set}" = set; then
-  break
-fi
-done
-if test "${ac_cv_search_gethostbyname+set}" = set; then
-  :
-else
-  ac_cv_search_gethostbyname=no
-fi
-rm conftest.$ac_ext
-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; }
-ac_res=$ac_cv_search_gethostbyname
-if test "$ac_res" != no; then
-  test "$ac_res" = "none required" || LIBS="$ac_res $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
-cat >conftest.$ac_ext <<_ACEOF
-/* confdefs.h.  */
-_ACEOF
-cat confdefs.h >>conftest.$ac_ext
-cat >>conftest.$ac_ext <<_ACEOF
-/* end confdefs.h.  */
-
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char gethostbyname ();
-int
-main ()
-{
-return gethostbyname ();
-  ;
-  return 0;
-}
-_ACEOF
-for ac_lib in '' nsl; do
-  if test -z "$ac_lib"; then
-    ac_res="none required"
-  else
-    ac_res=-l$ac_lib
-    LIBS="-l$ac_lib -lsocket $ac_func_search_save_LIBS"
-  fi
-
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest$ac_exeext &&
-       $as_test_x conftest$ac_exeext; then
-  ac_cv_search_gethostbyname=$ac_res
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-
-fi
-
-rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
-      conftest$ac_exeext
-  if test "${ac_cv_search_gethostbyname+set}" = set; then
-  break
-fi
-done
-if test "${ac_cv_search_gethostbyname+set}" = set; then
-  :
-else
-  ac_cv_search_gethostbyname=no
-fi
-rm conftest.$ac_ext
-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; }
-ac_res=$ac_cv_search_gethostbyname
-if test "$ac_res" != no; then
-  test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
-
-fi
-
-fi
-
-
-ac_ext=cpp
-ac_cpp='$CXXCPP $CPPFLAGS'
-ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_cxx_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; }
-if test -z "$CXXCPP"; then
-  if test "${ac_cv_prog_CXXCPP+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-      # Double quotes because CXXCPP needs to be expanded
-    for CXXCPP in "$CXX -E" "/lib/cpp"
-    do
-      ac_preproc_ok=false
-for ac_cxx_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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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 nonexistent 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_CXXCPP=$CXXCPP
-
-fi
-  CXXCPP=$ac_cv_prog_CXXCPP
-else
-  ac_cv_prog_CXXCPP=$CXXCPP
-fi
-{ echo "$as_me:$LINENO: result: $CXXCPP" >&5
-echo "${ECHO_T}$CXXCPP" >&6; }
-ac_preproc_ok=false
-for ac_cxx_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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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 nonexistent 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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 \"$CXXCPP\" fails sanity check
-See \`config.log' for more details." >&5
-echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check
-See \`config.log' for more details." >&2;}
-   { (exit 1); exit 1; }; }
-fi
-
-ac_ext=cpp
-ac_cpp='$CXXCPP $CPPFLAGS'
-ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
-
-
-{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5
-echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; }
-if test "${ac_cv_path_GREP+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  # Extract the first word of "grep ggrep" to use in msg output
-if test -z "$GREP"; then
-set dummy grep ggrep; ac_prog_name=$2
-if test "${ac_cv_path_GREP+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  ac_path_GREP_found=false
-# Loop through the user's path and test for each of PROGNAME-LIST
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_prog in grep ggrep; do
-  for ac_exec_ext in '' $ac_executable_extensions; do
-    ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
-    { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
-    # Check for GNU ac_path_GREP and select it if it is found.
-  # Check for GNU $ac_path_GREP
-case `"$ac_path_GREP" --version 2>&1` in
-*GNU*)
-  ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
-*)
-  ac_count=0
-  echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"
-  while :
-  do
-    cat "conftest.in" "conftest.in" >"conftest.tmp"
-    mv "conftest.tmp" "conftest.in"
-    cp "conftest.in" "conftest.nl"
-    echo 'GREP' >> "conftest.nl"
-    "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break
-    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
-    ac_count=`expr $ac_count + 1`
-    if test $ac_count -gt ${ac_path_GREP_max-0}; then
-      # Best one so far, save it but keep looking for a better one
-      ac_cv_path_GREP="$ac_path_GREP"
-      ac_path_GREP_max=$ac_count
-    fi
-    # 10*(2^10) chars as input seems more than enough
-    test $ac_count -gt 10 && break
-  done
-  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
-esac
-
-
-    $ac_path_GREP_found && break 3
-  done
-done
-
-done
-IFS=$as_save_IFS
-
-
-fi
-
-GREP="$ac_cv_path_GREP"
-if test -z "$GREP"; then
-  { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
-echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
-   { (exit 1); exit 1; }; }
-fi
-
-else
-  ac_cv_path_GREP=$GREP
-fi
-
-
-fi
-{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5
-echo "${ECHO_T}$ac_cv_path_GREP" >&6; }
- GREP="$ac_cv_path_GREP"
-
-
-{ echo "$as_me:$LINENO: checking for egrep" >&5
-echo $ECHO_N "checking for egrep... $ECHO_C" >&6; }
-if test "${ac_cv_path_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_path_EGREP="$GREP -E"
-   else
-     # Extract the first word of "egrep" to use in msg output
-if test -z "$EGREP"; then
-set dummy egrep; ac_prog_name=$2
-if test "${ac_cv_path_EGREP+set}" = set; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  ac_path_EGREP_found=false
-# Loop through the user's path and test for each of PROGNAME-LIST
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-  for ac_prog in egrep; do
-  for ac_exec_ext in '' $ac_executable_extensions; do
-    ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
-    { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
-    # Check for GNU ac_path_EGREP and select it if it is found.
-  # Check for GNU $ac_path_EGREP
-case `"$ac_path_EGREP" --version 2>&1` in
-*GNU*)
-  ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
-*)
-  ac_count=0
-  echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"
-  while :
-  do
-    cat "conftest.in" "conftest.in" >"conftest.tmp"
-    mv "conftest.tmp" "conftest.in"
-    cp "conftest.in" "conftest.nl"
-    echo 'EGREP' >> "conftest.nl"
-    "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
-    diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
-    ac_count=`expr $ac_count + 1`
-    if test $ac_count -gt ${ac_path_EGREP_max-0}; then
-      # Best one so far, save it but keep looking for a better one
-      ac_cv_path_EGREP="$ac_path_EGREP"
-      ac_path_EGREP_max=$ac_count
-    fi
-    # 10*(2^10) chars as input seems more than enough
-    test $ac_count -gt 10 && break
-  done
-  rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
-esac
-
-
-    $ac_path_EGREP_found && break 3
-  done
-done
-
-done
-IFS=$as_save_IFS
-
-
-fi
-
-EGREP="$ac_cv_path_EGREP"
-if test -z "$EGREP"; then
-  { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5
-echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}
-   { (exit 1); exit 1; }; }
-fi
-
-else
-  ac_cv_path_EGREP=$EGREP
-fi
-
-
-   fi
-fi
-{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5
-echo "${ECHO_T}$ac_cv_path_EGREP" >&6; }
- EGREP="$ac_cv_path_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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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>
-#include <stdlib.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))
-      return 2;
-  return 0;
-}
-_ACEOF
-rm -f conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_link") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
-  { (case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 core.conftest.* 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 { as_var=$as_ac_Header; eval "test \"\${$as_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.  */
-$ac_includes_default
-
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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/ciss/cissio.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 linux/compiler.h
-do
-as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  { echo "$as_me:$LINENO: checking for $ac_header" >&5
-echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { (ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 && {
-        test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
-        test ! -s conftest.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_cxx_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 { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  eval "$as_ac_Header=\$ac_header_preproc"
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 linux/cciss_ioctl.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 { as_var=$as_ac_Header; eval "test \"\${$as_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.  */
-$ac_includes_default
-#ifdef HAVE_LINUX_COMPILER_H
-# include <linux/compiler.h>
-#endif
-
-
-#include <$ac_header>
-_ACEOF
-rm -f conftest.$ac_objext
-if { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-ac_res=`eval echo '${'$as_ac_Header'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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
-
-
-{ 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
-typedef int64_t ac__type_new_;
-int
-main ()
-{
-if ((ac__type_new_ *) 0)
-  return 0;
-if (sizeof (ac__type_new_))
-  return 0;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext
-if { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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
-typedef uint64_t ac__type_new_;
-int
-main ()
-{
-if ((ac__type_new_ *) 0)
-  return 0;
-if (sizeof (ac__type_new_))
-  return 0;
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext
-if { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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 { as_var=$as_ac_var; eval "test \"\${$as_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 GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-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
-#endif
-
-int
-main ()
-{
-return $ac_func ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest$ac_exeext &&
-       $as_test_x conftest$ac_exeext; 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 core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-ac_res=`eval echo '${'$as_ac_var'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_var; eval "test \"\${$as_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 GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-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
-#endif
-
-int
-main ()
-{
-return $ac_func ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest$ac_exeext &&
-       $as_test_x conftest$ac_exeext; 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 core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-ac_res=`eval echo '${'$as_ac_var'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_var; eval "test \"\${$as_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 GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-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
-#endif
-
-int
-main ()
-{
-return $ac_func ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest$ac_exeext &&
-       $as_test_x conftest$ac_exeext; 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 core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-ac_res=`eval echo '${'$as_ac_var'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_var; eval "test \"\${$as_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 GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-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
-#endif
-
-int
-main ()
-{
-return $ac_func ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest$ac_exeext &&
-       $as_test_x conftest$ac_exeext; 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 core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-ac_res=`eval echo '${'$as_ac_var'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_var; eval "test \"\${$as_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 GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-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
-#endif
-
-int
-main ()
-{
-return $ac_func ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest$ac_exeext &&
-       $as_test_x conftest$ac_exeext; 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 core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-ac_res=`eval echo '${'$as_ac_var'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_var; eval "test \"\${$as_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 GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-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
-#endif
-
-int
-main ()
-{
-return $ac_func ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest$ac_exeext &&
-       $as_test_x conftest$ac_exeext; 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 core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-ac_res=`eval echo '${'$as_ac_var'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_var; eval "test \"\${$as_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 GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-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
-#endif
-
-int
-main ()
-{
-return $ac_func ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest$ac_exeext &&
-       $as_test_x conftest$ac_exeext; 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 core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-ac_res=`eval echo '${'$as_ac_var'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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 { as_var=$as_ac_var; eval "test \"\${$as_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 GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-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
-#endif
-
-int
-main ()
-{
-return $ac_func ();
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext conftest$ac_exeext
-if { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest$ac_exeext &&
-       $as_test_x conftest$ac_exeext; 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 core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
-      conftest$ac_exeext conftest.$ac_ext
-fi
-ac_res=`eval echo '${'$as_ac_var'}'`
-              { echo "$as_me:$LINENO: result: $ac_res" >&5
-echo "${ECHO_T}$ac_res" >&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
-  libc_have_working_snprintf=no
-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 { (ac_try="$ac_link"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
-  (eval "$ac_link") 2>&5
-  ac_status=$?
-  echo "$as_me:$LINENO: \$? = $ac_status" >&5
-  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
-  { (case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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 core.conftest.* 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 { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; 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 core 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.  */
-
-int
-main ()
-{
-#if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-#error "Sun's compiler cannot handle __attribute__((packed))!"
-#endif
-  ;
-  return 0;
-}
-_ACEOF
-rm -f conftest.$ac_objext
-if { (ac_try="$ac_compile"
-case "(($ac_try" in
-  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
-  *) ac_try_echo=$ac_try;;
-esac
-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&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); } && {
-        test -z "$ac_cxx_werror_flag" ||
-        test ! -s conftest.err
-       } && test -s conftest.$ac_objext; then
-  true
-else
-  echo "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
-       gcc_have_attr_packed=no
-fi
-
-rm -f core 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 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 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 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*)
-               os_deps='os_linux.o cciss.o'
-
-               os_libs=''
- ;;
-       *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*)
-               os_deps='os_freebsd.o cciss.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'
- ;;
-       *-*-nto-qnx*)
-               os_deps='os_qnxnto.o'
-
-               os_libs=''
- ;;
-
-       *)
-               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 "$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 "$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
-      CXXFLAGS="$CXXFLAGS -Wno-format";;
-  esac
-else
-  case "${host}" in
-       sparc*-*-solaris*)
-                    if test -z "`echo "$CXXFLAGS" | grep "\-xmemalign" 2> /dev/null`" ; then
-                        CXXFLAGS="-xmemalign=1i $CXXFLAGS"
-          fi
- esac
- case "${host}" in
-       *-*-solaris*)
-          if test -z "`echo "$CXXFLAGS" | grep "\-xO" 2> /dev/null`" ; then
-                        CXXFLAGS="-xO2 $CXXFLAGS"
-          fi
-          if test -z "`echo "$CXXFLAGS" | grep "\-erroff" 2> /dev/null`" ; then
-                           CXXFLAGS="-erroff=%none,wbadinitl,wbadasgl,badargtypel2w $CXXFLAGS"
-         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, we kill variables containing newlines.
-# 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.
-(
-  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
-    eval ac_val=\$$ac_var
-    case $ac_val in #(
-    *${as_nl}*)
-      case $ac_var in #(
-      *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5
-echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;
-      esac
-      case $ac_var in #(
-      _ | IFS | as_nl) ;; #(
-      *) $as_unset $ac_var ;;
-      esac ;;
-    esac
-  done
-
-  (set) 2>&1 |
-    case $as_nl`(ac_space=' '; set) 2>&1` in #(
-    *${as_nl}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 "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
-      ;;
-    esac |
-    sort
-) |
-  sed '
-     /^ac_cv_env_/b end
-     t clear
-     :clear
-     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
-     t end
-     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 "$as_me:$LINENO: updating cache $cache_file" >&5
-echo "$as_me: updating cache $cache_file" >&6;}
-    cat confcache >$cache_file
-  else
-    { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5
-echo "$as_me: not updating unwritable cache $cache_file" >&6;}
-  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}'
-
-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_script='s/\$U\././;s/\.o$//;s/\.obj$//'
-  ac_i=`echo "$ac_i" | sed "$ac_script"`
-  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
-  #    will be set to the directory where LIBOBJS objects are built.
-  ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"
-  ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$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__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then
-  { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCXX\" was never defined.
-Usually this means the macro was only invoked conditionally." >&5
-echo "$as_me: error: conditional \"am__fastdepCXX\" 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 "${am__fastdepCCAS_TRUE}" && test -z "${am__fastdepCCAS_FALSE}"; then
-  { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCCAS\" was never defined.
-Usually this means the macro was only invoked conditionally." >&5
-echo "$as_me: error: conditional \"am__fastdepCCAS\" 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 more Bourne compatible
-DUALCASE=1; export DUALCASE # for MKS sh
-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+"$@"}'='"$@"'
-  setopt NO_GLOB_SUBST
-else
-  case `(set -o) 2>/dev/null` in
-  *posix*) set -o posix ;;
-esac
-
-fi
-
-
-
-
-# PATH needs CR
-# 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
-
-# Support unset when possible.
-if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
-  as_unset=unset
-else
-  as_unset=false
-fi
-
-
-# IFS
-# We need space, tab and new line, in precisely that order.  Quoting is
-# there to prevent editors from complaining about space-tab.
-# (If _AS_PATH_WALK were called with IFS unset, it would disable word
-# splitting by setting IFS to empty value.)
-as_nl='
-'
-IFS=" ""       $as_nl"
-
-# Find who we are.  Look in the path if we contain no directory separator.
-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
-IFS=$as_save_IFS
-
-     ;;
-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_myself: error: cannot find myself; rerun with an absolute file name" >&2
-  { (exit 1); exit 1; }
-fi
-
-# Work around bugs in pre-3.0 UWIN ksh.
-for as_var in ENV MAIL MAILPATH
-do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var
-done
-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) >/dev/null 2>&1 && $as_unset $as_var
-  fi
-done
-
-# Required to use basename.
-if expr a : '\(a\)' >/dev/null 2>&1 &&
-   test "X`expr 00001 : '.*\(...\)'`" = X001; 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'`
-
-# CDPATH.
-$as_unset CDPATH
-
-
-
-  as_lineno_1=$LINENO
-  as_lineno_2=$LINENO
-  test "x$as_lineno_1" != "x$as_lineno_2" &&
-  test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {
-
-  # 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 after each line using $LINENO; the second 'sed'
-  # does the real work.  The second script uses 'N' to pair each
-  # line-number line with the line containing $LINENO, 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
-  # scripts with optimization help from Paolo Bonzini.  Blame Lee
-  # E. McMahon (1931-1989) for sed's syntax.  :-)
-  sed -n '
-    p
-    /[$]LINENO/=
-  ' <$as_myself |
-    sed '
-      s/[$]LINENO.*/&-/
-      t lineno
-      b
-      :lineno
-      N
-      :loop
-      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
-      t loop
-      s/-\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 sensitive to this).
-  . "./$as_me.lineno"
-  # Exit status is that of the last command.
-  exit
-}
-
-
-if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
-  as_dirname=dirname
-else
-  as_dirname=false
-fi
-
-ECHO_C= ECHO_N= ECHO_T=
-case `echo -n x` in
--n*)
-  case `echo 'x\c'` in
-  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
-  *)   ECHO_C='\c';;
-  esac;;
-*)
-  ECHO_N='-n';;
-esac
-
-if expr a : '\(a\)' >/dev/null 2>&1 &&
-   test "X`expr 00001 : '.*\(...\)'`" = X001; then
-  as_expr=expr
-else
-  as_expr=false
-fi
-
-rm -f conf$$ conf$$.exe conf$$.file
-if test -d conf$$.dir; then
-  rm -f conf$$.dir/conf$$.file
-else
-  rm -f conf$$.dir
-  mkdir conf$$.dir
-fi
-echo >conf$$.file
-if ln -s conf$$.file conf$$ 2>/dev/null; then
-  as_ln_s='ln -s'
-  # ... but there are two gotchas:
-  # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
-  # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
-  # In both cases, we have to default to `cp -p'.
-  ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
-    as_ln_s='cp -p'
-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$$.dir/conf$$.file conf$$.file
-rmdir conf$$.dir 2>/dev/null
-
-if mkdir -p . 2>/dev/null; then
-  as_mkdir_p=:
-else
-  test -d ./-p && rmdir ./-p
-  as_mkdir_p=false
-fi
-
-if test -x / >/dev/null 2>&1; then
-  as_test_x='test -x'
-else
-  if ls -dL / >/dev/null 2>&1; then
-    as_ls_L_option=L
-  else
-    as_ls_L_option=
-  fi
-  as_test_x='
-    eval sh -c '\''
-      if test -d "$1"; then
-        test -d "$1/.";
-      else
-       case $1 in
-        -*)set "./$1";;
-       esac;
-       case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in
-       ???[sx]*):;;*)false;;esac;fi
-    '\'' sh
-  '
-fi
-as_executable_p=$as_test_x
-
-# 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'"
-
-
-exec 6>&1
-
-# Save the log message, to keep $[0] and so on meaningful, and to
-# report actual input values of CONFIG_FILES etc. instead of their
-# values after options handling.
-ac_log="
-This file was extended by smartmontools $as_me 5.38, which was
-generated by GNU Autoconf 2.61.  Invocation command line was
-
-  CONFIG_FILES    = $CONFIG_FILES
-  CONFIG_HEADERS  = $CONFIG_HEADERS
-  CONFIG_LINKS    = $CONFIG_LINKS
-  CONFIG_COMMANDS = $CONFIG_COMMANDS
-  $ $0 $@
-
-on `(hostname || uname -n) 2>/dev/null | sed 1q`
-"
-
-_ACEOF
-
-cat >>$CONFIG_STATUS <<_ACEOF
-# Files that config.status was made for.
-config_files="$ac_config_files"
-config_headers="$ac_config_headers"
-config_commands="$ac_config_commands"
-
-_ACEOF
-
-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 and configuration settings, 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.38
-configured by $0, generated by GNU Autoconf 2.61,
-  with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
-
-Copyright (C) 2006 Free Software Foundation, Inc.
-This config.status script is free software; the Free Software Foundation
-gives unlimited permission to copy, distribute and modify it."
-
-ac_pwd='$ac_pwd'
-srcdir='$srcdir'
-INSTALL='$INSTALL'
-MKDIR_P='$MKDIR_P'
-_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
-    ;;
-  esac
-
-  case $ac_option in
-  # Handling of the options.
-  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
-    ac_cs_recheck=: ;;
-  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
-    echo "$ac_cs_version"; exit ;;
-  --debug | --debu | --deb | --de | --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;;
-  --he | --h)
-    # Conflict between --help and --header
-    { 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 ;;
-  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
-  | -silent | --silent | --silen | --sile | --sil | --si | --s)
-    ac_cs_silent=: ;;
-
-  # This is an error.
-  -*) { 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"
-     ac_need_defaults=false ;;
-
-  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 CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
-  CONFIG_SHELL=$SHELL
-  export CONFIG_SHELL
-  exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
-fi
-
-_ACEOF
-cat >>$CONFIG_STATUS <<\_ACEOF
-exec 5>>config.log
-{
-  echo
-  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
-## Running $as_me. ##
-_ASBOX
-  echo "$ac_log"
-} >&5
-
-_ACEOF
-cat >>$CONFIG_STATUS <<_ACEOF
-#
-# INIT-COMMANDS
-#
-AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
-
-_ACEOF
-
-cat >>$CONFIG_STATUS <<\_ACEOF
-
-# Handling of arguments.
-for ac_config_target in $ac_config_targets
-do
-  case $ac_config_target in
-    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
-    "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
-    "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
-    "examplescripts/Makefile") CONFIG_FILES="$CONFIG_FILES examplescripts/Makefile" ;;
-
-  *) { { 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 against having it here, and in addition,
-# creating and moving files from /tmp can sometimes cause problems.
-# Hook for its removal unless debugging.
-# Note that there is a small window in which the directory will not be cleaned:
-# after its creation but before its name has been assigned to `$tmp'.
-$debug ||
-{
-  tmp=
-  trap 'exit_status=$?
-  { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$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 "./confXXXXXX") 2>/dev/null` &&
-  test -n "$tmp" && test -d "$tmp"
-}  ||
-{
-  tmp=./conf$$-$RANDOM
-  (umask 077 && mkdir "$tmp")
-} ||
-{
-   echo "$me: cannot create a temporary directory in ." >&2
-   { (exit 1); exit 1; }
-}
-
-#
-# Set up the sed scripts for 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
-
-_ACEOF
-
-
-
-ac_delim='%!_!# '
-for ac_last_try in false false false false false :; do
-  cat >conf$$subs.sed <<_ACEOF
-SHELL!$SHELL$ac_delim
-PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim
-PACKAGE_NAME!$PACKAGE_NAME$ac_delim
-PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim
-PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim
-PACKAGE_STRING!$PACKAGE_STRING$ac_delim
-PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim
-exec_prefix!$exec_prefix$ac_delim
-prefix!$prefix$ac_delim
-program_transform_name!$program_transform_name$ac_delim
-bindir!$bindir$ac_delim
-sbindir!$sbindir$ac_delim
-libexecdir!$libexecdir$ac_delim
-datarootdir!$datarootdir$ac_delim
-datadir!$datadir$ac_delim
-sysconfdir!$sysconfdir$ac_delim
-sharedstatedir!$sharedstatedir$ac_delim
-localstatedir!$localstatedir$ac_delim
-includedir!$includedir$ac_delim
-oldincludedir!$oldincludedir$ac_delim
-docdir!$docdir$ac_delim
-infodir!$infodir$ac_delim
-htmldir!$htmldir$ac_delim
-dvidir!$dvidir$ac_delim
-pdfdir!$pdfdir$ac_delim
-psdir!$psdir$ac_delim
-libdir!$libdir$ac_delim
-localedir!$localedir$ac_delim
-mandir!$mandir$ac_delim
-DEFS!$DEFS$ac_delim
-ECHO_C!$ECHO_C$ac_delim
-ECHO_N!$ECHO_N$ac_delim
-ECHO_T!$ECHO_T$ac_delim
-LIBS!$LIBS$ac_delim
-build_alias!$build_alias$ac_delim
-host_alias!$host_alias$ac_delim
-target_alias!$target_alias$ac_delim
-INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim
-INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim
-INSTALL_DATA!$INSTALL_DATA$ac_delim
-am__isrc!$am__isrc$ac_delim
-CYGPATH_W!$CYGPATH_W$ac_delim
-PACKAGE!$PACKAGE$ac_delim
-VERSION!$VERSION$ac_delim
-ACLOCAL!$ACLOCAL$ac_delim
-AUTOCONF!$AUTOCONF$ac_delim
-AUTOMAKE!$AUTOMAKE$ac_delim
-AUTOHEADER!$AUTOHEADER$ac_delim
-MAKEINFO!$MAKEINFO$ac_delim
-install_sh!$install_sh$ac_delim
-STRIP!$STRIP$ac_delim
-INSTALL_STRIP_PROGRAM!$INSTALL_STRIP_PROGRAM$ac_delim
-mkdir_p!$mkdir_p$ac_delim
-AWK!$AWK$ac_delim
-SET_MAKE!$SET_MAKE$ac_delim
-am__leading_dot!$am__leading_dot$ac_delim
-AMTAR!$AMTAR$ac_delim
-am__tar!$am__tar$ac_delim
-am__untar!$am__untar$ac_delim
-MAINTAINER_MODE_TRUE!$MAINTAINER_MODE_TRUE$ac_delim
-MAINTAINER_MODE_FALSE!$MAINTAINER_MODE_FALSE$ac_delim
-MAINT!$MAINT$ac_delim
-CXX!$CXX$ac_delim
-CXXFLAGS!$CXXFLAGS$ac_delim
-LDFLAGS!$LDFLAGS$ac_delim
-CPPFLAGS!$CPPFLAGS$ac_delim
-ac_ct_CXX!$ac_ct_CXX$ac_delim
-EXEEXT!$EXEEXT$ac_delim
-OBJEXT!$OBJEXT$ac_delim
-DEPDIR!$DEPDIR$ac_delim
-am__include!$am__include$ac_delim
-am__quote!$am__quote$ac_delim
-AMDEP_TRUE!$AMDEP_TRUE$ac_delim
-AMDEP_FALSE!$AMDEP_FALSE$ac_delim
-AMDEPBACKSLASH!$AMDEPBACKSLASH$ac_delim
-CXXDEPMODE!$CXXDEPMODE$ac_delim
-am__fastdepCXX_TRUE!$am__fastdepCXX_TRUE$ac_delim
-am__fastdepCXX_FALSE!$am__fastdepCXX_FALSE$ac_delim
-CC!$CC$ac_delim
-CFLAGS!$CFLAGS$ac_delim
-ac_ct_CC!$ac_ct_CC$ac_delim
-CCDEPMODE!$CCDEPMODE$ac_delim
-am__fastdepCC_TRUE!$am__fastdepCC_TRUE$ac_delim
-am__fastdepCC_FALSE!$am__fastdepCC_FALSE$ac_delim
-CCAS!$CCAS$ac_delim
-CCASFLAGS!$CCASFLAGS$ac_delim
-CCASDEPMODE!$CCASDEPMODE$ac_delim
-am__fastdepCCAS_TRUE!$am__fastdepCCAS_TRUE$ac_delim
-am__fastdepCCAS_FALSE!$am__fastdepCCAS_FALSE$ac_delim
-build!$build$ac_delim
-build_cpu!$build_cpu$ac_delim
-build_vendor!$build_vendor$ac_delim
-build_os!$build_os$ac_delim
-host!$host$ac_delim
-host_cpu!$host_cpu$ac_delim
-host_vendor!$host_vendor$ac_delim
-host_os!$host_os$ac_delim
-_ACEOF
-
-  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
-    break
-  elif $ac_last_try; then
-    { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
-echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
-   { (exit 1); exit 1; }; }
-  else
-    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
-  fi
-done
-
-ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`
-if test -n "$ac_eof"; then
-  ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`
-  ac_eof=`expr $ac_eof + 1`
-fi
-
-cat >>$CONFIG_STATUS <<_ACEOF
-cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof
-/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
-_ACEOF
-sed '
-s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g
-s/^/s,@/; s/!/@,|#_!!_#|/
-:n
-t n
-s/'"$ac_delim"'$/,g/; t
-s/$/\\/; p
-N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n
-' >>$CONFIG_STATUS <conf$$subs.sed
-rm -f conf$$subs.sed
-cat >>$CONFIG_STATUS <<_ACEOF
-CEOF$ac_eof
-_ACEOF
-
-
-ac_delim='%!_!# '
-for ac_last_try in false false false false false :; do
-  cat >conf$$subs.sed <<_ACEOF
-CXXCPP!$CXXCPP$ac_delim
-GREP!$GREP$ac_delim
-EGREP!$EGREP$ac_delim
-libc_have_working_snprintf!$libc_have_working_snprintf$ac_delim
-gcc_have_attr_packed!$gcc_have_attr_packed$ac_delim
-ASFLAGS!$ASFLAGS$ac_delim
-exampledir!$exampledir$ac_delim
-initddir!$initddir$ac_delim
-smartd_suffix!$smartd_suffix$ac_delim
-SMARTD_SUFFIX_TRUE!$SMARTD_SUFFIX_TRUE$ac_delim
-SMARTD_SUFFIX_FALSE!$SMARTD_SUFFIX_FALSE$ac_delim
-releaseversion!$releaseversion$ac_delim
-smartmontools_release_date!$smartmontools_release_date$ac_delim
-smartmontools_release_time!$smartmontools_release_time$ac_delim
-os_deps!$os_deps$ac_delim
-os_libs!$os_libs$ac_delim
-OS_DARWIN_TRUE!$OS_DARWIN_TRUE$ac_delim
-OS_DARWIN_FALSE!$OS_DARWIN_FALSE$ac_delim
-OS_SOLARIS_TRUE!$OS_SOLARIS_TRUE$ac_delim
-OS_SOLARIS_FALSE!$OS_SOLARIS_FALSE$ac_delim
-OS_WIN32_MINGW_TRUE!$OS_WIN32_MINGW_TRUE$ac_delim
-OS_WIN32_MINGW_FALSE!$OS_WIN32_MINGW_FALSE$ac_delim
-LIBOBJS!$LIBOBJS$ac_delim
-LTLIBOBJS!$LTLIBOBJS$ac_delim
-_ACEOF
-
-  if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 24; then
-    break
-  elif $ac_last_try; then
-    { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5
-echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}
-   { (exit 1); exit 1; }; }
-  else
-    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
-  fi
-done
-
-ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`
-if test -n "$ac_eof"; then
-  ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`
-  ac_eof=`expr $ac_eof + 1`
-fi
-
-cat >>$CONFIG_STATUS <<_ACEOF
-cat >"\$tmp/subs-2.sed" <<\CEOF$ac_eof
-/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end
-_ACEOF
-sed '
-s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g
-s/^/s,@/; s/!/@,|#_!!_#|/
-:n
-t n
-s/'"$ac_delim"'$/,g/; t
-s/$/\\/; p
-N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n
-' >>$CONFIG_STATUS <conf$$subs.sed
-rm -f conf$$subs.sed
-cat >>$CONFIG_STATUS <<_ACEOF
-:end
-s/|#_!!_#|//g
-CEOF$ac_eof
-_ACEOF
-
-
-# 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
-
-cat >>$CONFIG_STATUS <<\_ACEOF
-fi # test -n "$CONFIG_FILES"
-
-
-for ac_tag in  :F $CONFIG_FILES  :H $CONFIG_HEADERS    :C $CONFIG_COMMANDS
-do
-  case $ac_tag in
-  :[FHLC]) ac_mode=$ac_tag; continue;;
-  esac
-  case $ac_mode$ac_tag in
-  :[FHL]*:*);;
-  :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5
-echo "$as_me: error: Invalid tag $ac_tag." >&2;}
-   { (exit 1); exit 1; }; };;
-  :[FH]-) ac_tag=-:-;;
-  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
-  esac
-  ac_save_IFS=$IFS
-  IFS=:
-  set x $ac_tag
-  IFS=$ac_save_IFS
-  shift
-  ac_file=$1
-  shift
-
-  case $ac_mode in
-  :L) ac_source=$1;;
-  :[FH])
-    ac_file_inputs=
-    for ac_f
-    do
-      case $ac_f in
-      -) ac_f="$tmp/stdin";;
-      *) # Look for the file first in the build tree, then in the source tree
-        # (if the path is not absolute).  The absolute path cannot be DOS-style,
-        # because $ac_f cannot contain `:'.
-        test -f "$ac_f" ||
-          case $ac_f in
-          [\\/$]*) false;;
-          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
-          esac ||
-          { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5
-echo "$as_me: error: cannot find input file: $ac_f" >&2;}
-   { (exit 1); exit 1; }; };;
-      esac
-      ac_file_inputs="$ac_file_inputs $ac_f"
-    done
-
-    # 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.  */
-    configure_input="Generated from "`IFS=:
-         echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure."
-    if test x"$ac_file" != x-; then
-      configure_input="$ac_file.  $configure_input"
-      { echo "$as_me:$LINENO: creating $ac_file" >&5
-echo "$as_me: creating $ac_file" >&6;}
-    fi
-
-    case $ac_tag in
-    *:-:* | *:-) cat >"$tmp/stdin";;
-    esac
-    ;;
-  esac
-
-  ac_dir=`$as_dirname -- "$ac_file" ||
-$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'`
-  { as_dir="$ac_dir"
-  case $as_dir in #(
-  -*) as_dir=./$as_dir;;
-  esac
-  test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
-    as_dirs=
-    while :; do
-      case $as_dir in #(
-      *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #(
-      *) as_qdir=$as_dir;;
-      esac
-      as_dirs="'$as_qdir' $as_dirs"
-      as_dir=`$as_dirname -- "$as_dir" ||
-$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'`
-      test -d "$as_dir" && break
-    done
-    test -z "$as_dirs" || eval "mkdir $as_dirs"
-  } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
-echo "$as_me: error: cannot create directory $as_dir" >&2;}
-   { (exit 1); exit 1; }; }; }
-  ac_builddir=.
-
-case "$ac_dir" in
-.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
-*)
-  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
-  # A ".." for each directory in $ac_dir_suffix.
-  ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`
-  case $ac_top_builddir_sub in
-  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
-  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
-  esac ;;
-esac
-ac_abs_top_builddir=$ac_pwd
-ac_abs_builddir=$ac_pwd$ac_dir_suffix
-# for backward compatibility:
-ac_top_builddir=$ac_top_build_prefix
-
-case $srcdir in
-  .)  # We are building in place.
-    ac_srcdir=.
-    ac_top_srcdir=$ac_top_builddir_sub
-    ac_abs_top_srcdir=$ac_pwd ;;
-  [\\/]* | ?:[\\/]* )  # Absolute name.
-    ac_srcdir=$srcdir$ac_dir_suffix;
-    ac_top_srcdir=$srcdir
-    ac_abs_top_srcdir=$srcdir ;;
-  *) # Relative name.
-    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
-    ac_top_srcdir=$ac_top_build_prefix$srcdir
-    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
-esac
-ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
-
-
-  case $ac_mode in
-  :F)
-  #
-  # CONFIG_FILE
-  #
-
-  case $INSTALL in
-  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
-  *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;;
-  esac
-  ac_MKDIR_P=$MKDIR_P
-  case $MKDIR_P in
-  [\\/$]* | ?:[\\/]* ) ;;
-  */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;;
-  esac
-_ACEOF
-
-cat >>$CONFIG_STATUS <<\_ACEOF
-# If the template does not know about datarootdir, expand it.
-# FIXME: This hack should be removed a few years after 2.60.
-ac_datarootdir_hack=; ac_datarootdir_seen=
-
-case `sed -n '/datarootdir/ {
-  p
-  q
-}
-/@datadir@/p
-/@docdir@/p
-/@infodir@/p
-/@localedir@/p
-/@mandir@/p
-' $ac_file_inputs` in
-*datarootdir*) ac_datarootdir_seen=yes;;
-*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
-  { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
-echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
-_ACEOF
-cat >>$CONFIG_STATUS <<_ACEOF
-  ac_datarootdir_hack='
-  s&@datadir@&$datadir&g
-  s&@docdir@&$docdir&g
-  s&@infodir@&$infodir&g
-  s&@localedir@&$localedir&g
-  s&@mandir@&$mandir&g
-    s&\\\${datarootdir}&$datarootdir&g' ;;
-esac
-_ACEOF
-
-# Neutralize VPATH when `$srcdir' = `.'.
-# Shell code in configure.ac might set extrasub.
-# FIXME: do we really want to maintain this feature?
-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&@top_builddir@&$ac_top_builddir_sub&;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&@abs_top_builddir@&$ac_abs_top_builddir&;t t
-s&@INSTALL@&$ac_INSTALL&;t t
-s&@MKDIR_P@&$ac_MKDIR_P&;t t
-$ac_datarootdir_hack
-" $ac_file_inputs | sed -f "$tmp/subs-1.sed" | sed -f "$tmp/subs-2.sed" >$tmp/out
-
-test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
-  { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
-  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
-  { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'
-which seems to be undefined.  Please make sure it is defined." >&5
-echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
-which seems to be undefined.  Please make sure it is defined." >&2;}
-
-  rm -f "$tmp/stdin"
-  case $ac_file in
-  -) cat "$tmp/out"; rm -f "$tmp/out";;
-  *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;;
-  esac
- ;;
-  :H)
-  #
-  # CONFIG_HEADER
-  #
-_ACEOF
-
-# Transform confdefs.h into a sed script `conftest.defines', that
-# substitutes the proper values into config.h.in to produce config.h.
-rm -f conftest.defines conftest.tail
-# First, append a space to every undef/define line, to ease matching.
-echo 's/$/ /' >conftest.defines
-# Then, protect against being on the right side of a sed subst, or in
-# an unquoted here document, in config.status.  If some macros were
-# called several times there might be several #defines for the same
-# symbol, which is useless.  But do not sort them, since the last
-# AC_DEFINE must be honored.
-ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
-# These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where
-# NAME is the cpp macro being defined, VALUE is the value it is being given.
-# PARAMS is the parameter list in the macro definition--in most cases, it's
-# just an empty string.
-ac_dA='s,^\\([  #]*\\)[^        ]*\\([  ]*'
-ac_dB='\\)[     (].*,\\1define\\2'
-ac_dC=' '
-ac_dD=' ,'
-
-uniq confdefs.h |
-  sed -n '
-       t rset
-       :rset
-       s/^[     ]*#[    ]*define[       ][      ]*//
-       t ok
-       d
-       :ok
-       s/[\\&,]/\\&/g
-       s/^\('"$ac_word_re"'\)\(([^()]*)\)[      ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p
-       s/^\('"$ac_word_re"'\)[  ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p
-  ' >>conftest.defines
-
-# Remove the space that was appended to ease matching.
-# Then replace #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.
-# (The regexp can be short, since the line contains either #define or #undef.)
-echo 's/ $//
-s,^[    #]*u.*,/* & */,' >>conftest.defines
-
-# Break up conftest.defines:
-ac_max_sed_lines=50
-
-# First sed command is:         sed -f defines.sed $ac_file_inputs >"$tmp/out1"
-# Second one is:        sed -f defines.sed "$tmp/out1" >"$tmp/out2"
-# Third one will be:    sed -f defines.sed "$tmp/out2" >"$tmp/out1"
-# et cetera.
-ac_in='$ac_file_inputs'
-ac_out='"$tmp/out1"'
-ac_nxt='"$tmp/out2"'
-
-while :
-do
-  # Write a here document:
-    cat >>$CONFIG_STATUS <<_ACEOF
-    # First, check the format of the line:
-    cat >"\$tmp/defines.sed" <<\\CEOF
-/^[     ]*#[    ]*undef[        ][      ]*$ac_word_re[  ]*\$/b def
-/^[     ]*#[    ]*define[       ][      ]*$ac_word_re[(         ]/b def
-b
-:def
-_ACEOF
-  sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS
-  echo 'CEOF
-    sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS
-  ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in
-  sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail
-  grep . conftest.tail >/dev/null || break
-  rm -f conftest.defines
-  mv conftest.tail conftest.defines
-done
-rm -f conftest.defines conftest.tail
-
-echo "ac_result=$ac_in" >>$CONFIG_STATUS
-cat >>$CONFIG_STATUS <<\_ACEOF
-  if test x"$ac_file" != x-; then
-    echo "/* $configure_input  */" >"$tmp/config.h"
-    cat "$ac_result" >>"$tmp/config.h"
-    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
-      rm -f $ac_file
-      mv "$tmp/config.h" $ac_file
-    fi
-  else
-    echo "/* $configure_input  */"
-    cat "$ac_result"
-  fi
-  rm -f "$tmp/out12"
-# 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" >`$as_dirname -- $ac_file ||
-$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
- ;;
-
-  :C)  { echo "$as_me:$LINENO: executing $ac_file commands" >&5
-echo "$as_me: executing $ac_file commands" >&6;}
- ;;
-  esac
-
-
-  case $ac_file$ac_mode in
-    "depfiles":C) 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.
-  # Grep'ing the whole file is not good either: AIX grep has a line
-  # limit of 2048, but all sed's we know have understand at least 4000.
-  if sed 10q "$mf" | grep '^#.*generated by automake' > /dev/null 2>&1; then
-    dirpart=`$as_dirname -- "$mf" ||
-$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=`$as_dirname -- "$file" ||
-$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'`
-    { as_dir=$dirpart/$fdir
-  case $as_dir in #(
-  -*) as_dir=./$as_dir;;
-  esac
-  test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {
-    as_dirs=
-    while :; do
-      case $as_dir in #(
-      *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #(
-      *) as_qdir=$as_dir;;
-      esac
-      as_dirs="'$as_qdir' $as_dirs"
-      as_dir=`$as_dirname -- "$as_dir" ||
-$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'`
-      test -d "$as_dir" && break
-    done
-    test -z "$as_dirs" || eval "mkdir $as_dirs"
-  } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5
-echo "$as_me: error: cannot create directory $as_dir" >&2;}
-   { (exit 1); exit 1; }; }; }
-    # echo "creating $dirpart/$file"
-    echo '# dummy' > "$dirpart/$file"
-  done
-done
- ;;
-
-  esac
-done # for ac_tag
-
-
-{ (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 x ${MAKE-make}; ac_make=`echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
-if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then
-  echo $ECHO_N "(cached) $ECHO_C" >&6
-else
-  cat >conftest.make <<\_ACEOF
-SHELL = /bin/sh
-all:
-       @echo '@@@%%%=$(MAKE)=@@@%%%'
-_ACEOF
-# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
-case `${MAKE-make} -f conftest.make 2>/dev/null` in
-  *@@@%%%=?*=@@@%%%*)
-    eval ac_cv_prog_make_${ac_make}_set=yes;;
-  *)
-    eval ac_cv_prog_make_${ac_make}_set=no;;
-esac
-rm -f conftest.make
-fi
-if eval test \$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 60caaf1067171beef5379c9e709932b243a79f66..dd9ee6232ca6785a50bef2c92f4b0fb2a9d6973a 100644 (file)
@@ -1,13 +1,13 @@
 #
-# $Id: configure.in,v 1.135 2008/03/10 10:44:30 ballen4705 Exp $
+# $Id: configure.in 2870 2009-08-02 20:38:30Z manfred99 $
 #
 dnl Process this file with autoconf to produce a configure script.
 AC_PREREQ(2.50)
-AC_INIT(smartmontools, 5.38, smartmontools-support@lists.sourceforge.net)
+AC_INIT(smartmontools, 5.39, 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.135 2008/03/10 10:44:30 ballen4705 Exp $'`
+smartmontools_cvs_tag=`echo '$Id: configure.in 2870 2009-08-02 20:38:30Z manfred99 $'`
 smartmontools_release_date=2008/03/10
 smartmontools_release_time="10:44:07 GMT"
 
@@ -43,14 +43,28 @@ case "${host}" in
                ;;
 esac
 
-dnl Checks for libraries.needed for gethostbyname (Solaris needs
+# Check for SVN.
+AC_MSG_CHECKING([whether this is a build from SVN])
+is_svn_build=no
+if test -f "$srcdir/.svn/entries"; then
+  is_svn_build=unknown
+  if (cd "$srcdir" && svn --version && svnversion && svn info) >/dev/null 2>&1; then
+    is_svn_build=yes
+  fi
+fi
+AM_CONDITIONAL(IS_SVN_BUILD, [test "$is_svn_build" = "yes"])
+AC_MSG_RESULT([$is_svn_build])
+
+dnl Checks for libraries needed for name services (Solaris needs
 dnl libnsl, might in the future also need libsocket)
 #  AC_SEARCH_LIBS (FUNCTION, SEARCH-LIBS, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], [OTHER-LIBRARIES])
-AC_SEARCH_LIBS(gethostbyname, nsl, , AC_SEARCH_LIBS(gethostbyname, nsl, , , -lsocket), , )
+AC_SEARCH_LIBS(socket, socket)
+AC_SEARCH_LIBS(gethostbyname, nsl)
+AC_SEARCH_LIBS(getaddrinfo, nsl)
+AC_SEARCH_LIBS(getdomainname, nsl)
 
 dnl Checks for header files.
 AC_CHECK_HEADERS([locale.h])
-AC_CHECK_HEADERS([getopt.h])
 AC_CHECK_HEADERS([dev/ata/atavar.h])
 AC_CHECK_HEADERS([netdb.h])
 dnl we need [u]int64_t and friends.
@@ -78,10 +92,16 @@ dnl Checks for typedefs, structures, and compiler characteristics.
 AC_CHECK_TYPES([int64_t, uint64_t])
 
 dnl Checks for library functions.
-AC_CHECK_FUNCS([getopt])
-AC_CHECK_FUNCS([getopt_long])
+AC_CHECK_FUNCS([getopt_long], , [
+  AC_MSG_NOTICE([smartmontools does no longer support platforms without getopt_long().])
+  AC_MSG_NOTICE([Please inform ${PACKAGE_BUGREPORT},])
+  AC_MSG_NOTICE([including details about your build environment.])
+  AC_MSG_ERROR([function getopt_long() not found])
+])
+
 AC_CHECK_FUNCS([getdomainname])
 AC_CHECK_FUNCS([gethostname])
+AC_CHECK_FUNCS([getaddrinfo])
 AC_CHECK_FUNCS([gethostbyname])
 AC_CHECK_FUNCS([sigset])
 AC_CHECK_FUNCS([strtoull])
@@ -124,14 +144,66 @@ AC_SUBST([exampledir], ['${docdir}/examplescripts'])
 
 AC_ARG_WITH(initscriptdir,[AC_HELP_STRING([--with-initscriptdir=dir],[Location of init scripts (default is ${sysconfdir}/rc.d/init.d)])],[initddir="$withval"],[initddir='${sysconfdir}/rc.d/init.d'])
 AC_SUBST(initddir)
+AM_CONDITIONAL(INSTALL_INITSCRIPT, [test "$with_initscriptdir" != "no"])
 
 AC_ARG_WITH(docdir,[AC_HELP_STRING([--with-docdir=dir],[Location of documentation (default is ${prefix}/share/doc/smartmontools-5.X)])],[docdir="$withval"],[docdir='${prefix}/share/doc/${PACKAGE}-${VERSION}'])
 AC_SUBST(docdir)
 
+AC_ARG_ENABLE(drivedb, [AC_HELP_STRING([--enable-drivedb],[Enables drive database file])])
+
+AC_ARG_WITH(drivedbdir,
+  [AC_HELP_STRING([--with-drivedbdir=DIR],[Location of drive database file (implies --enable-drivedb) [DATADIR/smartmontools]])],
+  [drivedbdir="$withval"; enable_drivedb="yes"],
+  [drivedbdir=; test "$enable_drivedb" = "yes" && drivedbdir='${datadir}/${PACKAGE}'])
+AC_SUBST(drivedbdir)
+AM_CONDITIONAL(ENABLE_DRIVEDB, [test "$enable_drivedb" = "yes"])
+
+AC_ARG_ENABLE(savestates, [AC_HELP_STRING([--enable-savestates],[Enables default smartd state files])])
+
+AC_ARG_WITH(savestates,
+  [AC_HELP_STRING([--with-savestates=PREFIX],[Prefix for default smartd state files (implies --enable-savestates) [LOCALSTATEDIR/lib/smartmontools/smartd.]])],
+  [savestates="$withval"; enable_savestates="yes"],
+  [savestates=; test "$enable_savestates" = "yes" && savestates='${localstatedir}/lib/${PACKAGE}/smartd.'])
+savestatesdir="${savestates%/*}"
+AC_SUBST(savestates)
+AC_SUBST(savestatesdir)
+AM_CONDITIONAL(ENABLE_SAVESTATES, [test "$enable_savestates" = "yes"])
+
+AC_ARG_ENABLE(attributelog, [AC_HELP_STRING([--enable-attributelog],[Enables default smartd attribute log files])])
+
+AC_ARG_WITH(attributelog,
+  [AC_HELP_STRING([--with-attributelog=PREFIX],[Prefix for default smartd attribute log files (implies --enable-attributelog) [LOCALSTATEDIR/lib/smartmontools/attrlog.]])],
+  [attributelog="$withval"; enable_attributelog="yes"],
+  [attributelog=; test "$enable_attributelog" = "yes" && attributelog='${localstatedir}/lib/${PACKAGE}/attrlog.'])
+attributelogdir="${attributelog%/*}"
+AC_SUBST(attributelog)
+AC_SUBST(attributelogdir)
+AM_CONDITIONAL(ENABLE_ATTRIBUTELOG, [test "$enable_attributelog" = "yes"])
+
 AC_ARG_ENABLE(sample,[AC_HELP_STRING([--enable-sample],[Enables appending .sample to the installed smartd rc script and configuration file])],[smartd_suffix='.sample'],[smartd_suffix=''])
 AC_SUBST(smartd_suffix)
 AM_CONDITIONAL(SMARTD_SUFFIX, test $smartd_suffix)
 
+AC_ARG_WITH(os-deps,
+  [AC_HELP_STRING([--with-os-deps='os_module.o ...'],[Specify OS dependent module(s) [guessed]])],
+  [ for x in $with_os_deps; do
+      case $x in
+        *.o) ;;
+        *) AC_MSG_ERROR([non-object file specified by --with-os-deps]) ;;
+      esac
+    done
+  ],[])
+
+AC_ARG_WITH(selinux,[AC_HELP_STRING([--with-selinux],[Enables SELinux support])],
+       [
+               AC_CHECK_HEADERS([selinux/selinux.h], [], [echo "*** Error: Missing SELinux header files";exit 1])
+               AC_CHECK_LIB(selinux, matchpathcon, [with_selinux=yes], [echo "*** Error: Missing or incorrect SELinux library files"; exit 1],)
+       ],[])
+AC_SUBST(with_selinux)
+if test "$with_selinux" = "yes"; then
+       AC_DEFINE(WITH_SELINUX, [1], [Define to 1 if SELinux support is enabled])
+fi
+
 if test "$prefix" = "NONE"; then
     dnl no prefix and no mandir, so use ${prefix}/share/man as default
     if test "$mandir" = '${prefix}/man'; then
@@ -143,11 +215,16 @@ AC_SUBST(releaseversion,['${PACKAGE}-${VERSION}'])
 AC_SUBST(smartmontools_release_date)
 AC_SUBST(smartmontools_release_time)
 
+AC_MSG_CHECKING([for OS dependent modules and libraries])
 dnl if OS not recognized, then use the os_generic modules
 case "${host}" in
        *-*-linux*)
                AC_SUBST([os_deps], ['os_linux.o cciss.o']) 
-               AC_SUBST([os_libs], ['']) ;;
+               if test "$with_selinux" = "yes"; then
+                       AC_SUBST([os_libs], ['-lselinux'])
+               else
+                       AC_SUBST([os_libs], [''])
+               fi;;
        *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*)
                AC_SUBST([os_deps], ['os_freebsd.o cciss.o']) 
                AC_SUBST([os_libs], ['-lcam']);;
@@ -184,6 +261,10 @@ case "${host}" in
                AC_SUBST([os_libs], ['']) ;;
 esac
 
+# Replace if '--with-os-deps' was specified
+test -z "$with_os_deps" || os_deps="$with_os_deps"
+AC_MSG_RESULT([$os_deps $os_libs])
+
 # Define symbols for optional functions in OS specific module
 case "${os_deps}" in
   os_win32*)
@@ -194,10 +275,23 @@ case "${os_deps}" in
     AC_DEFINE(HAVE_GET_OS_VERSION_STR, 1, [Define to 1 if you have the `get_os_version_str' function in os_*.c.]) ;;
 esac
 
+# Check if we need adapter to old interface (dev_legacy.cpp)
+os_src=`echo "${os_deps}"|sed -n 's,^\([[^ .]]*\)\.o.*$,\1.cpp,p'`
+AC_MSG_CHECKING([whether ${os_src} uses new interface])
+if grep "smart_interface" "${srcdir}/${os_src}" >/dev/null 2>&1; then
+  os_new_interface=yes
+else
+  os_new_interface=no
+  os_deps="${os_deps} dev_legacy.o"
+  AC_DEFINE(OLD_INTERFACE, 1, [Define to 1 if os_*.cpp still uses the old interface])
+fi
+AC_MSG_RESULT([$os_new_interface])
+
 dnl Define platform-specific symbol.
 AM_CONDITIONAL(OS_DARWIN, [echo $host_os | grep '^darwin' > /dev/null])
 AM_CONDITIONAL(OS_SOLARIS, [echo $host_os | grep '^solaris' > /dev/null])
 AM_CONDITIONAL(OS_WIN32_MINGW, [echo $host_os | grep '^mingw' > /dev/null])
+AM_CONDITIONAL(OS_FREEBSD, [echo $host_os | grep '^freebsd' > /dev/null])
 
 dnl Add -Wall and -W if using gcc and its not already specified.
 if test "x$GCC" = "xyes"; then
@@ -230,9 +324,8 @@ else
             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"
+           dnl suppress trivial warnings
+           CXXFLAGS="-erroff=%none,wbadinitl,wbadasgl,badargtypel2w,badargtype2w $CXXFLAGS"
          fi
  esac
 fi
diff --git a/depcomp b/depcomp
deleted file mode 100755 (executable)
index ca5ea4e..0000000
--- a/depcomp
+++ /dev/null
@@ -1,584 +0,0 @@
-#! /bin/sh
-# depcomp - compile a program generating dependencies as side-effects
-
-scriptversion=2006-10-15.18
-
-# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, 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 $?
-    ;;
-  -v | --v*)
-    echo "depcomp $scriptversion"
-    exit $?
-    ;;
-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.
-## Unfortunately, FreeBSD c89 acceptance of flags depends upon
-## the command line argument order; so add the flags where they
-## appear in depend2.am.  Note that the slowdown incurred here
-## affects only configure: in makefiles, %FASTDEP% shortcuts this.
-  for arg
-  do
-    case $arg in
-    -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
-    *)  set fnord "$@" "$arg" ;;
-    esac
-    shift # fnord
-    shift # $arg
-  done
-  "$@"
-  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"
-  ;;
-
-hp2)
-  # The "hp" stanza above does not work with aCC (C++) and HP's ia64
-  # compilers, which have integrated preprocessors.  The correct option
-  # to use with these is +Maked; it writes dependencies to a file named
-  # 'foo.d', which lands next to the object file, wherever that
-  # happens to be.
-  # Much of this is similar to the tru64 case; see comments there.
-  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
-    tmpdepfile1=$dir$base.d
-    tmpdepfile2=$dir.libs/$base.d
-    "$@" -Wc,+Maked
-  else
-    tmpdepfile1=$dir$base.d
-    tmpdepfile2=$dir$base.d
-    "$@" +Maked
-  fi
-  stat=$?
-  if test $stat -eq 0; then :
-  else
-     rm -f "$tmpdepfile1" "$tmpdepfile2"
-     exit $stat
-  fi
-
-  for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
-  do
-    test -f "$tmpdepfile" && break
-  done
-  if test -f "$tmpdepfile"; then
-    sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
-    # Add `dependent.h:' lines.
-    sed -ne '2,${; s/^ *//; s/ \\*$//; s/$/:/; p;}' "$tmpdepfile" >> "$depfile"
-  else
-    echo "#dummy" > "$depfile"
-  fi
-  rm -f "$tmpdepfile" "$tmpdepfile2"
-  ;;
-
-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
-      # With Tru64 cc, shared objects can also be used to make a
-      # static library.  This mechanism is used in libtool 1.4 series to
-      # handle both shared and static libraries in a single compilation.
-      # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
-      #
-      # With libtool 1.5 this exception was removed, and libtool now
-      # generates 2 separate objects for the 2 libraries.  These two
-      # compilations output dependencies in $dir.libs/$base.o.d and
-      # in $dir$base.o.d.  We have to check for both files, because
-      # one of the two compilations can be disabled.  We should prefer
-      # $dir$base.o.d over $dir.libs/$base.o.d because the latter is
-      # automatically cleaned when .libs/ is deleted, while ignoring
-      # the former would cause a distcleancheck panic.
-      tmpdepfile1=$dir.libs/$base.lo.d   # libtool 1.4
-      tmpdepfile2=$dir$base.o.d          # libtool 1.5
-      tmpdepfile3=$dir.libs/$base.o.d    # libtool 1.5
-      tmpdepfile4=$dir.libs/$base.d      # Compaq CCC V6.2-504
-      "$@" -Wc,-MD
-   else
-      tmpdepfile1=$dir$base.o.d
-      tmpdepfile2=$dir$base.d
-      tmpdepfile3=$dir$base.d
-      tmpdepfile4=$dir$base.d
-      "$@" -MD
-   fi
-
-   stat=$?
-   if test $stat -eq 0; then :
-   else
-      rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
-      exit $stat
-   fi
-
-   for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
-   do
-     test -f "$tmpdepfile" && break
-   done
-   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 -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-       -e '/^#line [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/dev_ata_cmd_set.cpp b/dev_ata_cmd_set.cpp
new file mode 100644 (file)
index 0000000..41c6234
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * dev_ata_cmd_set.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "dev_ata_cmd_set.h"
+
+#include <errno.h>
+
+const char * dev_ata_cmd_set_cpp_cvsid = "$Id: dev_ata_cmd_set.cpp,v 1.4 2008/10/24 21:49:23 manfred99 Exp $"
+  DEV_ATA_CMD_SET_H_CVSID;
+
+
+/////////////////////////////////////////////////////////////////////////////
+// ata_device_with_command_set
+
+// Adapter routine to implement new ATA pass through with old interface
+
+bool ata_device_with_command_set::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+{
+  if (!ata_cmd_is_ok(in, true)) // data_out_support
+    return false;
+
+  smart_command_set command = (smart_command_set)-1;
+  int select = 0;
+  char * data = (char *)in.buffer;
+  char buffer[512];
+  switch (in.in_regs.command) {
+    case ATA_IDENTIFY_DEVICE:
+      command = IDENTIFY;
+      break;
+    case ATA_IDENTIFY_PACKET_DEVICE:
+      command = PIDENTIFY;
+      break;
+    case ATA_CHECK_POWER_MODE:
+      command = CHECK_POWER_MODE;
+      data = buffer; data[0] = 0;
+      break;
+    case ATA_SMART_CMD:
+      switch (in.in_regs.features) {
+        case ATA_SMART_ENABLE:
+          command = ENABLE;
+          break;
+        case ATA_SMART_READ_VALUES:
+          command = READ_VALUES;
+          break;
+        case ATA_SMART_READ_THRESHOLDS:
+          command = READ_THRESHOLDS;
+          break;
+        case ATA_SMART_READ_LOG_SECTOR:
+          command = READ_LOG;
+          select = in.in_regs.lba_low;
+          break;
+        case ATA_SMART_WRITE_LOG_SECTOR:
+          command = WRITE_LOG;
+          select = in.in_regs.lba_low;
+          break;
+        case ATA_SMART_DISABLE:
+          command = DISABLE;
+          break;
+        case ATA_SMART_STATUS:
+          command = (in.out_needed.lba_high ? STATUS_CHECK : STATUS);
+          break;
+        case ATA_SMART_AUTO_OFFLINE:
+          command = AUTO_OFFLINE;
+          select = in.in_regs.sector_count;
+          break;
+        case ATA_SMART_AUTOSAVE:
+          command = AUTOSAVE;
+          select = in.in_regs.sector_count;
+          break;
+        case ATA_SMART_IMMEDIATE_OFFLINE:
+          command = IMMEDIATE_OFFLINE;
+          select = in.in_regs.lba_low;
+          break;
+        default:
+          return set_err(ENOSYS, "Unknown SMART command");
+      }
+      break;
+    default:
+      return set_err(ENOSYS, "Non-SMART commands not implemented");
+  }
+
+  clear_err(); errno = 0;
+  int rc = ata_command_interface(command, select, data);
+  if (rc < 0) {
+    if (!get_errno())
+      set_err(errno);
+    return false;
+  }
+
+  switch (command) {
+    case CHECK_POWER_MODE:
+      out.out_regs.sector_count = data[0];
+      break;
+    case STATUS_CHECK:
+      switch (rc) {
+        case 0: // Good SMART status
+          out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
+          break;
+        case 1: // Bad SMART status
+          out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
+          break;
+      }
+      break;
+    default:
+      break;
+  }
+  return true;
+}
+
diff --git a/dev_ata_cmd_set.h b/dev_ata_cmd_set.h
new file mode 100644 (file)
index 0000000..ec1457b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * dev_ata_cmd_set.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef DEV_ATA_CMD_SET_H
+#define DEV_ATA_CMD_SET_H
+
+#define DEV_ATA_CMD_SET_H_CVSID "$Id: dev_ata_cmd_set.h,v 1.3 2008/08/23 21:32:12 chrfranke Exp $\n"
+
+#include "atacmds.h" // smart_command_set
+#include "dev_interface.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// ata_device_with_command_set
+
+/// Adapter class to implement new ATA pass through old interface.
+
+class ata_device_with_command_set
+: public /*implements*/ ata_device
+{
+public:
+  /// ATA pass through mapped to ata_command_interface().
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
+
+protected:
+  /// Old ATA interface called by ata_pass_through()
+  virtual int ata_command_interface(smart_command_set command, int select, char * data) = 0;
+
+  ata_device_with_command_set()
+    : smart_device(never_called) { }
+};
+
+#endif // DEV_ATA_CMD_SET_H
diff --git a/dev_interface.cpp b/dev_interface.cpp
new file mode 100644 (file)
index 0000000..f19aff7
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * dev_interface.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "dev_interface.h"
+#include "dev_tunnelled.h"
+#include "utility.h"
+
+#include <stdexcept>
+
+const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp,v 1.5 2009/01/30 18:34:55 chrfranke Exp $"
+  DEV_INTERFACE_H_CVSID;
+
+/////////////////////////////////////////////////////////////////////////////
+// smart_device
+
+smart_device::smart_device(smart_interface * intf, const char * dev_name,
+    const char * dev_type, const char * req_type)
+: m_intf(intf), m_info(dev_name, dev_type, req_type),
+  m_ata_ptr(0), m_scsi_ptr(0)
+{
+}
+
+smart_device::smart_device(do_not_use_in_implementation_classes)
+: m_intf(0), m_ata_ptr(0), m_scsi_ptr(0)
+{
+  throw std::logic_error("smart_device: wrong constructor called in implementation class");
+}
+
+smart_device::~smart_device() throw()
+{
+}
+
+bool smart_device::set_err(int no, const char * msg, ...)
+{
+  if (!msg)
+    return set_err(no);
+  m_err.no = no;
+  va_list ap; va_start(ap, msg);
+  m_err.msg = vstrprintf(msg, ap);
+  va_end(ap);
+  return false;
+}
+
+bool smart_device::set_err(int no)
+{
+  smi()->set_err_var(&m_err, no);
+  return false;
+}
+
+smart_device * smart_device::autodetect_open()
+{
+  open();
+  return this;
+}
+
+bool smart_device::owns(const smart_device * /*dev*/) const
+{
+  return false;
+}
+
+void smart_device::release(const smart_device * /*dev*/)
+{
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// ata_device
+
+ata_in_regs_48bit::ata_in_regs_48bit()
+: features_16(features, prev.features),
+  sector_count_16(sector_count, prev.sector_count),
+  lba_low_16(lba_low, prev.lba_low),
+  lba_mid_16(lba_mid, prev.lba_mid),
+  lba_high_16(lba_high, prev.lba_high)
+{
+}
+
+ata_out_regs_48bit::ata_out_regs_48bit()
+: sector_count_16(sector_count, prev.sector_count),
+  lba_low_16(lba_low, prev.lba_low),
+  lba_mid_16(lba_mid, prev.lba_mid),
+  lba_high_16(lba_high, prev.lba_high)
+{
+}
+
+ata_cmd_in::ata_cmd_in()
+: direction(no_data),
+  buffer(0),
+  size(0)
+{
+}
+
+ata_cmd_out::ata_cmd_out()
+{
+}
+
+bool ata_device::ata_pass_through(const ata_cmd_in & in)
+{
+  ata_cmd_out dummy;
+  return ata_pass_through(in, dummy);
+}
+
+bool ata_device::ata_cmd_is_ok(const ata_cmd_in & in,
+  bool data_out_support /*= false*/,
+  bool multi_sector_support /*= false*/,
+  bool ata_48bit_support /*= false*/)
+{
+  // Check DATA IN/OUT
+  switch (in.direction) {
+    case ata_cmd_in::no_data:  break;
+    case ata_cmd_in::data_in:  break;
+    case ata_cmd_in::data_out: break;
+    default:
+      return set_err(EINVAL, "Invalid data direction %d", (int)in.direction);
+  }
+
+  // Check buffer size
+  if (in.direction == ata_cmd_in::no_data) {
+    if (in.size)
+      return set_err(EINVAL, "Buffer size %u > 0 for NO DATA command", in.size);
+  }
+  else {
+    if (!in.buffer)
+      return set_err(EINVAL, "Buffer not set for DATA IN/OUT command");
+    unsigned count = (in.in_regs.prev.sector_count<<16)|in.in_regs.sector_count;
+    // TODO: Add check for sector count == 0
+    if (count * 512 != in.size)
+      return set_err(EINVAL, "Sector count %u does not match buffer size %u", count, in.size);
+  }
+
+  // Check features
+  if (in.direction == ata_cmd_in::data_out && !data_out_support)
+    return set_err(ENOSYS, "DATA OUT ATA commands not supported");
+  if (!(in.size == 0 || in.size == 512) && !multi_sector_support)
+    return set_err(ENOSYS, "Multi-sector ATA commands not supported");
+  if (in.in_regs.is_48bit_cmd() && !ata_48bit_support)
+    return set_err(ENOSYS, "48-bit ATA commands not supported");
+  return true;
+}
+
+bool ata_device::ata_identify_is_cached() const
+{
+  return false;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// tunnelled_device_base
+
+tunnelled_device_base::tunnelled_device_base(smart_device * tunnel_dev)
+: smart_device(never_called),
+  m_tunnel_base_dev(tunnel_dev)
+{
+}
+
+tunnelled_device_base::~tunnelled_device_base() throw()
+{
+  delete m_tunnel_base_dev;
+}
+
+bool tunnelled_device_base::is_open() const
+{
+  return (m_tunnel_base_dev && m_tunnel_base_dev->is_open());
+}
+
+bool tunnelled_device_base::open()
+{
+  if (!m_tunnel_base_dev)
+    return set_err(ENOSYS);
+  if (!m_tunnel_base_dev->open())
+    return set_err(m_tunnel_base_dev->get_err());
+  return true;
+}
+
+bool tunnelled_device_base::close()
+{
+  if (!m_tunnel_base_dev)
+    return true;
+  if (!m_tunnel_base_dev->close())
+    return set_err(m_tunnel_base_dev->get_err());
+  return true;
+}
+
+bool tunnelled_device_base::owns(const smart_device * dev) const
+{
+  return (m_tunnel_base_dev && (m_tunnel_base_dev == dev));
+}
+
+void tunnelled_device_base::release(const smart_device * dev)
+{
+  if (m_tunnel_base_dev == dev)
+    m_tunnel_base_dev = 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// smart_interface
+
+// Pointer to (usually singleton) interface object returned by ::smi()
+smart_interface * smart_interface::s_instance;
+
+const char * smart_interface::get_os_version_str()
+{
+  return SMARTMONTOOLS_BUILD_HOST;
+}
+
+const char * smart_interface::get_valid_dev_types_str()
+{
+  static std::string buf;
+  if (!buf.empty())
+    return buf.c_str();
+  // default
+  buf = "ata, scsi, sat[,N][+TYPE]";
+  // append custom
+  const char * add = get_valid_custom_dev_types_str();
+  if (!add || !*add)
+    return buf.c_str();
+  buf += ", "; buf += add;
+  return buf.c_str();
+}
+
+const char * smart_interface::get_app_examples(const char * /*appname*/)
+{
+  return 0;
+}
+
+void smart_interface::set_err(int no, const char * msg, ...)
+{
+  if (!msg) {
+    set_err(no); return;
+  }
+  m_err.no = no;
+  va_list ap; va_start(ap, msg);
+  m_err.msg = vstrprintf(msg, ap);
+  va_end(ap);
+}
+
+void smart_interface::set_err(int no)
+{
+  set_err_var(&m_err, no);
+}
+
+void smart_interface::set_err_var(smart_device::error_info * err, int no)
+{
+  err->no = no;
+  err->msg = get_msg_for_errno(no);
+  if (err->msg.empty() && no != 0)
+    err->msg = strprintf("Unknown error %d", no);
+}
+
+const char * smart_interface::get_msg_for_errno(int no)
+{
+  return strerror(no);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Default device factory
+
+smart_device * smart_interface::get_smart_device(const char * name, const char * type)
+{
+  clear_err();
+  if (!type || !*type) {
+    smart_device * dev = autodetect_smart_device(name);
+    if (!dev && !get_errno())
+      set_err(EINVAL, "Unable to detect device type");
+    return dev;
+  }
+
+  smart_device * dev = get_custom_smart_device(name, type);
+  if (dev || get_errno())
+    return dev;
+
+  if (!strcmp(type, "ata"))
+    dev = get_ata_device(name, type);
+  else if (!strcmp(type, "scsi"))
+    dev = get_scsi_device(name, type);
+
+  else if (  ((!strncmp(type, "sat", 3) && (!type[3] || strchr(",+", type[3])))
+           || (!strncmp(type, "usb", 3)))) {
+    // Split "sat...+base..." -> ("sat...", "base...")
+    unsigned satlen = strcspn(type, "+");
+    std::string sattype(type, satlen);
+    const char * basetype = (type[satlen] ? type+satlen+1 : "");
+    // Recurse to allocate base device, default is standard SCSI
+    if (!*basetype)
+      basetype = "scsi";
+    dev = get_smart_device(name, basetype);
+    if (!dev) {
+      set_err(EINVAL, "Type '%s+...': %s", sattype.c_str(), get_errmsg());
+      return 0;
+    }
+    // Result must be SCSI
+    if (!dev->is_scsi()) {
+      delete dev;
+      set_err(EINVAL, "Type '%s+...': Device type '%s' is not SCSI", sattype.c_str(), basetype);
+      return 0;
+    }
+    // Attach SAT tunnel
+    try {
+      ata_device * satdev = get_sat_device(sattype.c_str(), dev->to_scsi());
+      if (!satdev) {
+        delete dev;
+        return 0;
+      }
+      return satdev;
+    }
+    catch (...) {
+      delete dev; throw;
+    }
+  }
+
+  else {
+    set_err(EINVAL, "Unknown device type '%s'", type);
+    return 0;
+  }
+  if (!dev && !get_errno())
+    set_err(EINVAL, "Not a device of type '%s'", type);
+  return dev;
+}
+
+smart_device * smart_interface::get_custom_smart_device(const char * /*name*/, const char * /*type*/)
+{
+  return 0;
+}
+
+const char * smart_interface::get_valid_custom_dev_types_str()
+{
+  return 0;
+}
diff --git a/dev_interface.h b/dev_interface.h
new file mode 100644 (file)
index 0000000..7550c3a
--- /dev/null
@@ -0,0 +1,727 @@
+/*
+ * dev_interface.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008-9 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef DEV_INTERFACE_H
+#define DEV_INTERFACE_H
+
+#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h,v 1.9 2009/03/12 20:31:12 chrfranke Exp $\n"
+
+#include <stdarg.h>
+#include <string>
+#include <vector>
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)  /**/
+#endif
+
+#ifdef _MSC_VER // Disable MSVC warning
+#pragma warning(disable:4250) // 'class1' : inherits 'class2::member' via dominance
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+// Common functionality for all device types
+
+// Forward declarations
+class smart_interface;
+class ata_device;
+class scsi_device;
+
+/// Base class for all devices
+class smart_device
+{
+// Types
+public:
+  /// Device info strings
+  struct device_info {
+    device_info()
+      { }
+    device_info(const char * d_name, const char * d_type, const char * r_type)
+      : dev_name(d_name), info_name(d_name),
+        dev_type(d_type), req_type(r_type)
+      { }
+
+    std::string dev_name;  ///< Device (path)name
+    std::string info_name; ///< Informal name
+    std::string dev_type;  ///< Actual device type
+    std::string req_type;  ///< Device type requested by user, empty if none
+  };
+
+  /// Error (number,message) pair
+  struct error_info {
+    explicit error_info(int n = 0)
+      : no(n) { }
+    error_info(int n, const char * m)
+      : no(n), msg(m) { }
+    void clear()
+      { no = 0; msg.erase(); }
+
+    int no;          ///< Error number
+    std::string msg; ///< Error message
+  };
+
+// Construction
+protected:
+  /// Constructor to init interface and device info.
+  /// Must be called in implementation classes.
+  smart_device(smart_interface * intf, const char * dev_name,
+    const char * dev_type, const char * req_type);
+
+  /// Dummy enum for dummy constructor.
+  enum do_not_use_in_implementation_classes { never_called };
+  /// Dummy constructor for abstract classes.
+  /// Must never be called in implementation classes.
+  smart_device(do_not_use_in_implementation_classes);
+
+public:
+  virtual ~smart_device() throw();
+
+// Attributes
+public:
+  ///////////////////////////////////////////////
+  // Dynamic downcasts to actual device flavor
+
+  /// Return true if ATA device
+  bool is_ata() const
+    { return !!m_ata_ptr; }
+  /// Return true if SCSI device
+  bool is_scsi() const
+    { return !!m_scsi_ptr; }
+
+  /// Downcast to ATA device.
+  ata_device * to_ata()
+    { return m_ata_ptr; }
+  /// Downcast to ATA device (const).
+  const ata_device * to_ata() const
+    { return m_ata_ptr; }
+  /// Downcast to SCSI device.
+  scsi_device * to_scsi()
+    { return m_scsi_ptr; }
+  /// Downcast to ATA device (const).
+  const scsi_device * to_scsi() const
+    { return m_scsi_ptr; }
+
+  ///////////////////////////////////////////////
+  // Device information
+
+  /// Get device info struct.
+  const device_info & get_info() const
+    { return m_info; }
+
+  /// Get device (path)name.
+  const char * get_dev_name() const
+    { return m_info.dev_name.c_str(); }
+  /// Get informal name.
+  const char * get_info_name() const
+    { return m_info.info_name.c_str(); }
+  /// Get device type.
+  const char * get_dev_type() const
+    { return m_info.dev_type.c_str(); }
+  /// Get type requested by user, empty if none.
+  const char * get_req_type() const
+    { return m_info.req_type.c_str(); }
+
+protected:
+  /// R/W access to device info struct.
+  device_info & set_info()
+    { return m_info; }
+
+public:
+  ///////////////////////////////////////////////
+  // Last error information
+
+  /// Get last error info struct.
+  const error_info & get_err() const
+    { return m_err; }
+  /// Get last error number.
+  int get_errno() const
+    { return m_err.no; }
+  /// Get last error message.
+  const char * get_errmsg() const
+    { return m_err.msg.c_str(); }
+
+  /// Set last error number and message.
+  /// Printf()-like formatting is supported.
+  /// Returns false always to allow use as a return expression.
+  bool set_err(int no, const char * msg, ...)
+    __attribute__ ((format (printf, 3, 4)));
+
+  /// Set last error info struct.
+  bool set_err(const error_info & err)
+    { m_err = err; return false; }
+
+  /// Clear last error info.
+  void clear_err()
+    { m_err.clear(); }
+
+  /// Set last error number and default message.
+  /// Message is retrieved from interface's get_msg_for_errno(no).
+  bool set_err(int no);
+
+// Operations
+public:
+  ///////////////////////////////////////////////
+  // Device open/close
+  // Must be implemented in derived class
+
+  /// Return true if device is open.
+  virtual bool is_open() const = 0;
+
+  /// Open device, return false on error.
+  virtual bool open() = 0;
+
+  /// Close device, return false on error.
+  virtual bool close() = 0;
+
+  /// Open device with autodetection support.
+  /// May return another device for further access.
+  /// In this case, the original pointer is no longer valid.
+  /// Default Implementation calls 'open()' and returns 'this'.
+  virtual smart_device * autodetect_open();
+
+  ///////////////////////////////////////////////
+  // Support for tunnelled devices
+
+  /// Return true if other device is owned by this device.
+  /// Default implementation returns false.
+  virtual bool owns(const smart_device * dev) const;
+
+  /// Release ownership of other device.
+  /// Default implementation does nothing.
+  virtual void release(const smart_device * dev);
+
+protected:
+  /// Set dynamic downcast for ATA
+  void this_is_ata(ata_device * ata);
+    // {see below;}
+
+  /// Set dynamic downcast for SCSI
+  void this_is_scsi(scsi_device * scsi);
+    // {see below;}
+
+  /// Get interface which produced this object.
+  smart_interface * smi()
+    { return m_intf; }
+  /// Get interface which produced this object (const).
+  const smart_interface * smi() const
+    { return m_intf; }
+
+// Implementation
+private:
+  smart_interface * m_intf;
+  device_info m_info;
+  ata_device * m_ata_ptr;
+  scsi_device * m_scsi_ptr;
+  error_info m_err;
+
+  // Prevent copy/assigment
+  smart_device(const smart_device &);
+  void operator=(const smart_device &);
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// ATA specific interface
+
+/// ATA register value and info whether is has been ever set
+// (Automatically set by first assignment)
+class ata_register
+{
+public:
+  ata_register()
+    : m_val(0x00), m_is_set(false) { }
+
+  ata_register & operator=(unsigned char val)
+    { m_val = val; m_is_set = true; return * this; }
+
+  unsigned char val() const
+    { return m_val; }
+  operator unsigned char() const
+    { return m_val; }
+
+  bool is_set() const
+    { return m_is_set; }
+
+private:
+  unsigned char m_val; ///< Register value
+  bool m_is_set; ///< true if set
+};
+
+/// ATA Input registers (for 28-bit commands)
+struct ata_in_regs
+{
+  // ATA-6/7 register names  // ATA-3/4/5        // ATA-8
+  ata_register features;     // features         // features
+  ata_register sector_count; // sector count     // count
+  ata_register lba_low;      // sector number    // ]
+  ata_register lba_mid;      // cylinder low     // ] lba
+  ata_register lba_high;     // cylinder high    // ]
+  ata_register device;       // device/head      // device
+  ata_register command;      // command          // command
+
+  /// Return true if any register is set
+  bool is_set() const
+    { return (features.is_set() || sector_count.is_set()
+      || lba_low.is_set() || lba_mid.is_set() || lba_high.is_set()
+      || device.is_set() || command.is_set());                    }
+};
+
+/// ATA Output registers (for 28-bit commands)
+struct ata_out_regs
+{
+  ata_register error;
+  ata_register sector_count;
+  ata_register lba_low;
+  ata_register lba_mid;
+  ata_register lba_high;
+  ata_register device;
+  ata_register status;
+
+  /// Return true if any register is set
+  bool is_set() const
+    { return (error.is_set() || sector_count.is_set()
+      || lba_low.is_set() || lba_mid.is_set() || lba_high.is_set()
+      || device.is_set() || status.is_set());                      }
+};
+
+
+/// 16-bit alias to a 8-bit ATA register pair.
+class ata_reg_alias_16
+{
+public:
+  ata_reg_alias_16(ata_register & lo, ata_register & hi)
+    : m_lo(lo), m_hi(hi) { }
+
+  ata_reg_alias_16 & operator=(unsigned short val)
+    { m_lo = (unsigned char) val;
+      m_hi = (unsigned char)(val >> 8);
+      return * this;                   }
+
+  unsigned short val() const
+    { return m_lo | (m_hi << 8); }
+  operator unsigned short() const
+    { return m_lo | (m_hi << 8); }
+
+private:
+  ata_register & m_lo, & m_hi;
+
+  // References must not be copied.
+  ata_reg_alias_16(const ata_reg_alias_16 &);
+  void operator=(const ata_reg_alias_16 &);
+};
+
+
+/// ATA Input registers for 48-bit commands
+// See section 4.14 of T13/1532D Volume 1 Revision 4b
+//
+// Uses ATA-6/7 method to specify 16-bit registers as
+// recent (low byte) and previous (high byte) content of
+// 8-bit registers.
+//
+// (ATA-8 ACS does not longer follow this scheme, it uses
+// abstract registers with sufficient size and leaves the
+// actual mapping to the transport layer.)
+//
+struct ata_in_regs_48bit
+: public ata_in_regs   // "most recently written" registers
+{
+  ata_in_regs prev;  ///< "previous content"
+
+  // 16-bit aliases for above pair.
+  ata_reg_alias_16 features_16;
+  ata_reg_alias_16 sector_count_16;
+  ata_reg_alias_16 lba_low_16;
+  ata_reg_alias_16 lba_mid_16;
+  ata_reg_alias_16 lba_high_16;
+
+  /// Return true if 48-bit command
+  bool is_48bit_cmd() const
+    { return prev.is_set(); }
+
+  /// Return true if 48-bit command with any nonzero high byte
+  bool is_real_48bit_cmd() const
+    { return (   prev.features || prev.sector_count
+              || prev.lba_low || prev.lba_mid || prev.lba_high); }
+
+  ata_in_regs_48bit();
+};
+
+
+/// ATA Output registers for 48-bit commands
+struct ata_out_regs_48bit
+: public ata_out_regs   // read with HOB=0
+{
+  ata_out_regs prev;  ///< read with HOB=1
+
+  // 16-bit aliases for above pair.
+  ata_reg_alias_16 sector_count_16;
+  ata_reg_alias_16 lba_low_16;
+  ata_reg_alias_16 lba_mid_16;
+  ata_reg_alias_16 lba_high_16;
+
+  ata_out_regs_48bit();
+};
+
+
+/// Flags for each ATA output register
+struct ata_out_regs_flags
+{
+  bool error, sector_count, lba_low, lba_mid, lba_high, device, status;
+
+  /// Return true if any flag is set.
+  bool is_set() const
+    { return (   error || sector_count || lba_low
+              || lba_mid || lba_high || device || status); }
+
+  /// Default constructor clears all flags.
+  ata_out_regs_flags()
+    : error(false), sector_count(false), lba_low(false), lba_mid(false),
+      lba_high(false), device(false), status(false) { }
+};
+
+
+/// ATA pass through input parameters
+struct ata_cmd_in
+{
+  ata_in_regs_48bit in_regs;  ///< Input registers
+  ata_out_regs_flags out_needed; ///< True if output register value needed
+  enum { no_data = 0, data_in, data_out } direction; ///< I/O direction
+  void * buffer; ///< Pointer to data buffer
+  unsigned size; ///< Size of buffer
+
+  /// Prepare for 28-bit DATA IN command
+  void set_data_in(void * buf, unsigned nsectors)
+    {
+      buffer = buf;
+      in_regs.sector_count = nsectors;
+      direction = data_in;
+      size = nsectors * 512;
+    }
+
+  /// Prepare for 28-bit DATA OUT command
+  void set_data_out(const void * buf, unsigned nsectors)
+    {
+      buffer = const_cast<void *>(buf);
+      in_regs.sector_count = nsectors;
+      direction = data_out;
+      size = nsectors * 512;
+    }
+
+  /// Prepare for 48-bit DATA IN command
+  void set_data_in_48bit(void * buf, unsigned nsectors)
+    {
+      buffer = buf;
+      // Note: This also sets 'in_regs.is_48bit_cmd()'
+      in_regs.sector_count_16 = nsectors;
+      direction = data_in;
+      size = nsectors * 512;
+    }
+
+  ata_cmd_in();
+};
+
+/// ATA pass through output parameters
+struct ata_cmd_out
+{
+  ata_out_regs_48bit out_regs; ///< Output registers
+
+  ata_cmd_out();
+};
+
+/// ATA device access
+class ata_device
+: virtual public /*extends*/ smart_device
+{
+public:
+  /// ATA pass through.
+  /// Return false on error.
+  /// Must be implemented in derived class.
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) = 0;
+
+  /// ATA pass through without output registers.
+  /// Return false on error.
+  /// Calls ata_pass_through(in, dummy), cannot be reimplemented.
+  bool ata_pass_through(const ata_cmd_in & in);
+
+  /// Return true if OS caches ATA identify sector.
+  /// Default implementation returns false.
+  virtual bool ata_identify_is_cached() const;
+
+protected:
+  /// Check command input parameters.
+  /// Calls set_err(...) accordingly.
+  bool ata_cmd_is_ok(const ata_cmd_in & in,
+    bool data_out_support = false,
+    bool multi_sector_support = false,
+    bool ata_48bit_support = false);
+
+  /// Default constructor, registers device as ATA.
+  ata_device()
+    : smart_device(never_called)
+    { this_is_ata(this); }
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// SCSI specific interface
+
+struct scsi_cmnd_io;
+
+/// SCSI device access
+class scsi_device
+: virtual public /*extends*/ smart_device
+{
+public:
+  /// SCSI pass through.
+  /// Returns false on error.
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop) = 0;
+
+protected:
+  /// Default constructor, registers device as SCSI.
+  scsi_device()
+    : smart_device(never_called)
+    { this_is_scsi(this); }
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Set dynamic downcasts
+// Note that due to virtual inheritance,
+// (ata == this) does not imply ((void*)ata == (void*)this))
+
+inline void smart_device::this_is_ata(ata_device * ata)
+{
+  m_ata_ptr = (ata == this ? ata : 0);
+}
+
+inline void smart_device::this_is_scsi(scsi_device * scsi)
+{
+  m_scsi_ptr = (scsi == this ? scsi : 0);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// smart_device_list
+
+/// List of devices for DEVICESCAN
+class smart_device_list
+{
+// Construction
+public:
+  smart_device_list()
+    { }
+
+  ~smart_device_list() throw()
+    {
+      for (unsigned i = 0; i < m_list.size(); i++)
+        delete m_list[i];
+    }
+
+// Attributes
+  unsigned size() const
+    { return m_list.size(); }
+
+// Operations
+  void clear()
+    {
+      for (unsigned i = 0; i < m_list.size(); i++)
+        delete m_list[i];
+      m_list.clear();
+    }
+
+
+  void add(smart_device * dev)
+    { m_list.push_back(dev); }
+
+  void push_back(smart_device * dev)
+    { m_list.push_back(dev); }
+
+  smart_device * at(unsigned i)
+    { return m_list.at(i); }
+
+  const smart_device * at(unsigned i) const
+    { return m_list.at(i); }
+
+  smart_device * release(unsigned i)
+    {
+      smart_device * dev = m_list.at(i);
+      m_list[i] = 0;
+      return dev;
+    }
+
+// Implementation
+private:
+  std::vector<smart_device *> m_list;
+
+  // Prevent copy/assigment
+  smart_device_list(const smart_device_list &);
+  void operator=(const smart_device_list &);
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// smart_interface
+
+/// The platform interface abstraction
+class smart_interface
+{
+public:
+  /// Initialize platform interface and register with smi().
+  /// Must be implemented by platform module and register interface with set()
+  static void init();
+
+  smart_interface()
+    { }
+
+  virtual ~smart_interface() throw()
+    { }
+
+  /// Return build host and OS version as static string
+  virtual const char * get_os_version_str();
+
+  /// Return valid args for device type option/directive.
+  /// Default implementation returns "ata, scsi" concatenated
+  /// with result from get_valid_custom_dev_types_str() below.
+  virtual const char * get_valid_dev_types_str();
+
+  /// Return example string for program 'appname'.
+  /// Default implementation returns 0.
+  /// For the migration of print_smartctl_examples(),
+  /// function is allowed to print examples to stdout.
+  /// TODO: Remove this hack.
+  virtual const char * get_app_examples(const char * appname);
+
+  ///////////////////////////////////////////////
+  // Last error information
+
+  /// Get last error info struct.
+  const smart_device::error_info & get_err() const
+    { return m_err; }
+  /// Get last error number.
+  int get_errno() const
+    { return m_err.no; }
+  /// Get last error message.
+  const char * get_errmsg() const
+    { return m_err.msg.c_str(); }
+
+  /// Set last error number and message.
+  /// Printf()-like formatting is supported.
+  void set_err(int no, const char * msg, ...)
+    __attribute__ ((format (printf, 3, 4)));
+
+  /// Set last error info struct.
+  void set_err(const smart_device::error_info & err)
+    { m_err = err; }
+
+  /// Clear last error info.
+  void clear_err()
+    { m_err.clear(); }
+
+  /// Set last error number and default message.
+  /// Message is retrieved from get_msg_for_errno(no).
+  void set_err(int no);
+
+  /// Set last error number and default message to any error_info.
+  /// Used by set_err(no).
+  void set_err_var(smart_device::error_info * err, int no);
+
+  /// Convert error number into message, used by set_err(no).
+  /// Default implementation returns strerror(no).
+  virtual const char * get_msg_for_errno(int no);
+
+  ///////////////////////////////////////////////////////////////////////////
+  // Device factory:
+
+  /// Return device object for device 'name' with some 'type'.
+  /// 'type' is 0 if not specified by user.
+  /// Return 0 on error.
+  /// Default implementation selects between ata, scsi and custom device.
+  virtual smart_device * get_smart_device(const char * name, const char * type);
+
+  /// Fill 'devlist' with devices of some 'type' with devices names.
+  /// specified by some optional 'pattern'.
+  /// Return false on error.
+  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
+    const char * pattern = 0) = 0;
+
+protected:
+  /// Return standard ATA device.
+  virtual ata_device * get_ata_device(const char * name, const char * type) = 0;
+
+  /// Return standard SCSI device.
+  virtual scsi_device * get_scsi_device(const char * name, const char * type) = 0;
+
+  /// Autodetect device if no device type specified.
+  virtual smart_device * autodetect_smart_device(const char * name) = 0;
+
+  /// Return device for platform specific 'type'.
+  /// Default implementation returns 0.
+  virtual smart_device * get_custom_smart_device(const char * name, const char * type);
+
+  /// Return valid 'type' args accepted by above.
+  /// This is called in get_valid_dev_types_str().
+  /// Default implementation returns 0.
+  virtual const char * get_valid_custom_dev_types_str();
+
+  /// Return ATA->SCSI filter for SAT or USB.
+  /// Override only if platform needs special handling.
+  virtual ata_device * get_sat_device(const char * type, scsi_device * scsidev);
+  //{ implemented in scsiata.cpp }
+
+public:
+  /// Try to detect a SAT device behind a SCSI interface.
+  /// Inquiry data can be passed if available.
+  /// Return appropriate device if yes, otherwise 0.
+  /// Override only if platform needs special handling.
+  virtual ata_device * autodetect_sat_device(scsi_device * scsidev,
+    const unsigned char * inqdata, unsigned inqsize);
+  //{ implemented in scsiata.cpp }
+
+  /// Get type name for USB device with known VENDOR:PRODUCT ID.
+  /// Return name if device known and supported, otherwise 0.
+  virtual const char * get_usb_dev_type_by_id(int vendor_id, int product_id,
+                                              int version = -1);
+  //{ implemented in scsiata.cpp }
+
+protected:
+  /// Set interface to use, must be called from init().
+  static void set(smart_interface * intf)
+    { s_instance = intf; }
+
+// Implementation
+private:
+  smart_device::error_info m_err;
+
+  friend smart_interface * smi(); // below
+  static smart_interface * s_instance; ///< Pointer to the interface object.
+
+  // Prevent copy/assigment
+  smart_interface(const smart_interface &);
+  void operator=(const smart_interface &);
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// smi()
+
+/// Global access to the (usually singleton) smart_interface
+inline smart_interface * smi()
+  { return smart_interface::s_instance; }
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif // DEV_INTERFACE_H
diff --git a/dev_legacy.cpp b/dev_legacy.cpp
new file mode 100644 (file)
index 0000000..76ed947
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ * dev_legacy.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "extern.h"
+#include "utility.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "dev_interface.h"
+#include "dev_ata_cmd_set.h"
+
+const char * dev_legacy_cpp_cvsid = "$Id: dev_legacy.cpp 2879 2009-08-29 17:19:00Z chrfranke $"
+  DEV_INTERFACE_H_CVSID;
+
+extern smartmonctrl * con; // con->reportscsiioctl
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Legacy interface declarations (now commented out globally):
+
+// from utility.h:
+int guess_device_type(const char * dev_name);
+int make_device_names (char ***devlist, const char* name);
+int deviceopen(const char *pathname, char *type);
+int deviceclose(int fd);
+#ifdef HAVE_GET_OS_VERSION_STR
+const char * get_os_version_str(void);
+#endif
+
+// from atacmds.h:
+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);
+int areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data);
+#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
+int ata_identify_is_cached(int fd);
+#endif
+
+// from scsicmds.h:
+int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);
+
+// from smartctl.h:
+void print_smartctl_examples();
+
+/////////////////////////////////////////////////////////////////////////////
+
+namespace os { // No need to publish anything, name provided for Doxygen
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement shared open/close routines with old functions.
+
+class legacy_smart_device
+: virtual public /*implements*/ smart_device
+{
+public:
+  explicit legacy_smart_device(const char * mode)
+    : smart_device(never_called),
+      m_fd(-1), m_mode(mode) { }
+
+  virtual ~legacy_smart_device() throw();
+
+  virtual bool is_open() const;
+
+  virtual bool open();
+
+  virtual bool close();
+
+protected:
+  /// Return filedesc for derived classes.
+  int get_fd() const
+    { return m_fd; }
+
+private:
+  int m_fd; ///< filedesc, -1 if not open.
+  const char * m_mode; ///< Mode string for deviceopen().
+};
+
+
+legacy_smart_device::~legacy_smart_device() throw()
+{
+  if (m_fd >= 0)
+    ::deviceclose(m_fd);
+}
+
+bool legacy_smart_device::is_open() const
+{
+  return (m_fd >= 0);
+}
+
+bool legacy_smart_device::open()
+{
+  m_fd = ::deviceopen(get_dev_name(), (char*)m_mode);
+  if (m_fd < 0) {
+    set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno);
+    return false;
+  }
+  return true;
+}
+
+bool legacy_smart_device::close()
+{
+  int fd = m_fd; m_fd = -1;
+  if (::deviceclose(fd) < 0) {
+    set_err(errno);
+    return false;
+  }
+  return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement standard ATA support with old functions
+
+class legacy_ata_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
+  virtual bool ata_identify_is_cached() const;
+#endif
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+};
+
+legacy_ata_device::legacy_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "ata", req_type),
+  legacy_smart_device("ATA")
+{
+}
+
+int legacy_ata_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+  return ::ata_command_interface(get_fd(), command, select, data);
+}
+
+#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
+bool legacy_ata_device::ata_identify_is_cached() const
+{
+  return !!::ata_identify_is_cached(get_fd());
+}
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement AMCC/3ware RAID support with old functions
+
+class legacy_escalade_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_escalade_device(smart_interface * intf, const char * dev_name,
+    int escalade_type, int disknum);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  int m_escalade_type; ///< Type string for escalade_command_interface().
+  int m_disknum; ///< Disk number.
+};
+
+legacy_escalade_device::legacy_escalade_device(smart_interface * intf, const char * dev_name,
+    int escalade_type, int disknum)
+: smart_device(intf, dev_name, "3ware", "3ware"),
+  legacy_smart_device(
+    escalade_type==CONTROLLER_3WARE_9000_CHAR ? "ATA_3WARE_9000" :
+    escalade_type==CONTROLLER_3WARE_678K_CHAR ? "ATA_3WARE_678K" :
+    /*             CONTROLLER_3WARE_678K     */ "ATA"             ),
+  m_escalade_type(escalade_type), m_disknum(disknum)
+{
+  set_info().info_name = strprintf("%s [3ware_disk_%02d]", dev_name, disknum);
+}
+
+int legacy_escalade_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+  return ::escalade_command_interface(get_fd(), m_disknum, m_escalade_type, command, select, data);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement Areca RAID support with old functions
+
+class legacy_areca_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_areca_device(smart_interface * intf, const char * dev_name, int disknum);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  int m_disknum; ///< Disk number.
+};
+
+legacy_areca_device::legacy_areca_device(smart_interface * intf, const char * dev_name, int disknum)
+: smart_device(intf, dev_name, "areca", "areca"),
+  legacy_smart_device("ATA_ARECA"),
+  m_disknum(disknum)
+{
+  set_info().info_name = strprintf("%s [areca_%02d]", dev_name, disknum);
+}
+
+int legacy_areca_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+  return ::areca_command_interface(get_fd(), m_disknum, command, select, data);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement Marvell support with old functions
+
+class legacy_marvell_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_marvell_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+};
+
+
+legacy_marvell_device::legacy_marvell_device(smart_interface * intf,
+  const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "marvell", req_type),
+  legacy_smart_device("ATA")
+{
+}
+
+int legacy_marvell_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+  return ::marvell_command_interface(get_fd(), command, select, data);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement Highpoint RAID support with old functions
+
+class legacy_highpoint_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_highpoint_device(smart_interface * intf, const char * dev_name,
+    unsigned char controller, unsigned char channel, unsigned char port);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  unsigned char m_hpt_data[3]; ///< controller/channel/port
+};
+
+
+legacy_highpoint_device::legacy_highpoint_device(smart_interface * intf, const char * dev_name,
+  unsigned char controller, unsigned char channel, unsigned char port)
+: smart_device(intf, dev_name, "hpt", "hpt"),
+  legacy_smart_device("ATA")
+{
+  m_hpt_data[0] = controller; m_hpt_data[1] = channel; m_hpt_data[2] = port;
+  set_info().info_name = strprintf("%s [hpt_disk_%u/%u/%u]", dev_name, m_hpt_data[0], m_hpt_data[1], m_hpt_data[2]);
+}
+
+int legacy_highpoint_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+  unsigned char old_hpt_data[3];
+  memcpy(old_hpt_data, con->hpt_data, 3);
+  memcpy(con->hpt_data, m_hpt_data, 3);
+  int status = ::highpoint_command_interface(get_fd(), command, select, data);
+  memcpy(con->hpt_data, old_hpt_data, 3);
+  return status;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement standard SCSI support with old functions
+
+class legacy_scsi_device
+: public /*implements*/ scsi_device,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+  virtual smart_device * autodetect_open();
+
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+};
+
+legacy_scsi_device::legacy_scsi_device(smart_interface * intf,
+  const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "scsi", req_type),
+  legacy_smart_device("SCSI")
+{
+}
+
+bool legacy_scsi_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+  unsigned char oldtype = con->controller_type, oldport = con->controller_port;
+  con->controller_type = CONTROLLER_SCSI; con->controller_port = 0;
+  int status = ::do_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl);
+  con->controller_type = oldtype; con->controller_port = oldport;
+  if (status < 0) {
+      set_err(-status);
+      return false;
+  }
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement CCISS RAID support with old functions
+
+class legacy_cciss_device
+: public /*implements*/ scsi_device,
+  public /*extends*/ legacy_smart_device
+{
+public:
+  legacy_cciss_device(smart_interface * intf, const char * name, unsigned char disknum);
+
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+
+private:
+  unsigned char m_disknum; ///< Disk number.
+};
+
+
+legacy_cciss_device::legacy_cciss_device(smart_interface * intf,
+  const char * dev_name, unsigned char disknum)
+: smart_device(intf, dev_name, "cciss", "cciss"),
+  legacy_smart_device("SCSI"),
+  m_disknum(disknum)
+{
+  set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum);
+}
+
+bool legacy_cciss_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+  // See os_linux.cpp
+  unsigned char oldtype = con->controller_type, oldport = con->controller_port;
+  con->controller_type = CONTROLLER_CCISS; con->controller_port = m_disknum+1;
+  int status = ::do_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl);
+  con->controller_type = oldtype; con->controller_port = oldport;
+  if (status < 0) {
+      set_err(-status);
+      return false;
+  }
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// SCSI open with autodetection support
+
+smart_device * legacy_scsi_device::autodetect_open()
+{
+  // Open device
+  if (!open())
+    return this;
+
+  // No Autodetection if device type was specified by user
+  if (*get_req_type())
+    return this;
+
+  // The code below is based on smartd.cpp:SCSIFilterKnown()
+
+  // Get INQUIRY
+  unsigned char req_buff[64] = {0, };
+  int req_len = 36;
+  if (scsiStdInquiry(this, 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(this, req_buff, req_len)) {
+      // device doesn't like INQUIRY commands
+      close();
+      set_err(EIO, "INQUIRY failed");
+      return this;
+    }
+  }
+
+  int avail_len = req_buff[4] + 5;
+  int len = (avail_len < req_len ? avail_len : req_len);
+  if (len < 36)
+      return this;
+
+  // Use INQUIRY to detect type
+  smart_device * newdev = 0;
+  try {
+    // 3ware ?
+    if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) {
+      close();
+#if defined(_WIN32) || defined(__CYGWIN__)
+      set_err(EINVAL, "AMCC/3ware controller, please try changing device to %s,N", get_dev_name());
+#else
+      set_err(EINVAL, "AMCC/3ware controller, please try adding '-d 3ware,N',\n"
+                      "you may need to replace %s with /dev/twaN or /dev/tweN", get_dev_name());
+#endif
+      return this;
+    }
+
+    // Marvell ?
+    if (len >= 42 && !memcmp(req_buff + 36, "MVSATA", 6)) { // TODO: Linux-specific?
+      //pout("Device %s: using '-d marvell' for ATA disk with Marvell driver\n", get_dev_name());
+      close();
+      newdev = new legacy_marvell_device(smi(), get_dev_name(), get_req_type());
+      newdev->open(); // TODO: Can possibly pass open fd
+      delete this;
+      return newdev;
+    }
+
+    // SAT or USB ?
+    newdev = smi()->autodetect_sat_device(this, req_buff, len);
+    if (newdev)
+      // NOTE: 'this' is now owned by '*newdev'
+      return newdev;
+  }
+  catch (...) {
+    // Cleanup if exception occurs after newdev was allocated
+    delete newdev;
+    throw;
+  }
+
+  // Nothing special found
+  return this;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement platform interface with old functions.
+
+class legacy_smart_interface
+: public /*implements*/ smart_interface
+{
+public:
+#ifdef HAVE_GET_OS_VERSION_STR
+  virtual const char * get_os_version_str();
+#endif
+
+  virtual const char * get_app_examples(const char * appname);
+
+  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
+    const char * pattern = 0);
+
+protected:
+  virtual ata_device * get_ata_device(const char * name, const char * type);
+
+  virtual scsi_device * get_scsi_device(const char * name, const char * type);
+
+  virtual smart_device * autodetect_smart_device(const char * name);
+
+  virtual smart_device * get_custom_smart_device(const char * name, const char * type);
+
+  virtual const char * get_valid_custom_dev_types_str();
+};
+
+
+//////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_GET_OS_VERSION_STR
+const char * legacy_smart_interface::get_os_version_str()
+{
+  return ::get_os_version_str();
+}
+#endif
+
+const char * legacy_smart_interface::get_app_examples(const char * appname)
+{
+  if (!strcmp(appname, "smartctl"))
+    ::print_smartctl_examples(); // this prints to stdout ...
+  return 0; // ... so don't print again.
+}
+
+ata_device * legacy_smart_interface::get_ata_device(const char * name, const char * type)
+{
+  return new legacy_ata_device(this, name, type);
+}
+
+scsi_device * legacy_smart_interface::get_scsi_device(const char * name, const char * type)
+{
+  return new legacy_scsi_device(this, name, type);
+}
+
+
+smart_device * legacy_smart_interface::autodetect_smart_device(const char * name)
+{
+  switch (::guess_device_type(name)) {
+    case CONTROLLER_ATA : return new legacy_ata_device(this, name, "");
+    case CONTROLLER_SCSI: return new legacy_scsi_device(this, name, "");
+  }
+  // TODO: Test autodetect device here
+  return 0;
+}
+
+
+static void free_devnames(char * * devnames, int numdevs)
+{
+  static const char version[] = "$Id: dev_legacy.cpp 2879 2009-08-29 17:19:00Z chrfranke $";
+  for (int i = 0; i < numdevs; i++)
+    FreeNonZero(devnames[i], -1,__LINE__, version);
+  FreeNonZero(devnames, (sizeof (char*) * numdevs),__LINE__, version);
+}
+
+bool legacy_smart_interface::scan_smart_devices(smart_device_list & devlist,
+  const char * type, const char * pattern /*= 0*/)
+{
+  if (pattern) {
+    set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
+    return false;
+  }
+
+  // Make namelists
+  char * * atanames = 0; int numata = 0;
+  if (!type || !strcmp(type, "ata")) {
+    numata = ::make_device_names(&atanames, "ATA");
+    if (numata < 0) {
+      set_err(ENOMEM);
+      return false;
+    }
+  }
+
+  char * * scsinames = 0; int numscsi = 0;
+  if (!type || !strcmp(type, "scsi")) {
+    numscsi = ::make_device_names(&scsinames, "SCSI");
+    if (numscsi < 0) {
+      free_devnames(atanames, numata);
+      set_err(ENOMEM);
+      return false;
+    }
+  }
+
+  // Add to devlist
+  int i;
+  if (type==NULL)
+    type="";
+  for (i = 0; i < numata; i++) {
+    ata_device * atadev = get_ata_device(atanames[i], type);
+    if (atadev)
+      devlist.add(atadev);
+  }
+  free_devnames(atanames, numata);
+
+  for (i = 0; i < numscsi; i++) {
+    scsi_device * scsidev = get_scsi_device(scsinames[i], type);
+    if (scsidev)
+      devlist.add(scsidev);
+  }
+  free_devnames(scsinames, numscsi);
+  return true;
+}
+
+
+smart_device * legacy_smart_interface::get_custom_smart_device(const char * name, const char * type)
+{
+  // Marvell ?
+  if (!strcmp(type, "marvell"))
+    return new legacy_marvell_device(this, name, type);
+
+  // 3Ware ?
+  int disknum = -1, n1 = -1, n2 = -1;
+  if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    if (n2 != (int)strlen(type)) {
+      set_err(EINVAL, "Option -d 3ware,N requires N to be a non-negative integer");
+      return 0;
+    }
+    if (!(0 <= disknum && disknum <= 127)) {
+      set_err(EINVAL, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127", disknum);
+      return 0;
+    }
+    int contr = ::guess_device_type(name);
+    if (contr != CONTROLLER_3WARE_9000_CHAR && contr != CONTROLLER_3WARE_678K_CHAR)
+      contr = CONTROLLER_3WARE_678K;
+    return new legacy_escalade_device(this, name, contr, disknum);
+  }
+
+  // Areca?
+  disknum = n1 = n2 = -1;
+  if (sscanf(type, "areca,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    if (n2 != (int)strlen(type)) {
+      set_err(EINVAL, "Option -d areca,N requires N to be a non-negative integer");
+      return 0;
+    }
+    if (!(1 <= disknum && disknum <= 24)) {
+      set_err(EINVAL, "Option -d areca,N (N=%d) must have 1 <= N <= 24", disknum);
+      return 0;
+    }
+    return new legacy_areca_device(this, name, disknum);
+  }
+
+  // Highpoint ?
+  int controller = -1, channel = -1; disknum = 1;
+  n1 = n2 = -1; int n3 = -1;
+  if (sscanf(type, "hpt,%n%d/%d%n/%d%n", &n1, &controller, &channel, &n2, &disknum, &n3) >= 2 || n1 == 4) {
+    int len = strlen(type);
+    if (!(n2 == len || n3 == len)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' supports 2-3 items");
+      return 0;
+    }
+    if (!(1 <= controller && controller <= 8)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied");
+      return 0;
+    }
+    if (!(1 <= channel && channel <= 8)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied");
+      return 0;
+    }
+    if (!(1 <= disknum && disknum <= 15)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid pmport number N supplied");
+      return 0;
+    }
+    return new legacy_highpoint_device(this, name, controller, channel, disknum);
+  }
+
+  // CCISS ?
+  disknum = n1 = n2 = -1;
+  if (sscanf(type, "cciss,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    if (n2 != (int)strlen(type)) {
+      set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer");
+      return 0;
+    }
+    if (!(0 <= disknum && disknum <= 15)) {
+      set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 15", disknum);
+      return 0;
+    }
+    return new legacy_cciss_device(this, name, disknum);
+  }
+
+  return 0;
+}
+
+const char * legacy_smart_interface::get_valid_custom_dev_types_str()
+{
+  return "marvell, areca,N, 3ware,N, hpt,L/M/N, cciss,N";
+}
+
+} // namespace
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Initialize platform interface and register with smi()
+
+void smart_interface::init()
+{
+  static os::legacy_smart_interface the_interface;
+  smart_interface::set(&the_interface);
+}
diff --git a/dev_tunnelled.h b/dev_tunnelled.h
new file mode 100644 (file)
index 0000000..173c6f4
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * dev_tunnelled.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef DEV_TUNNELLED_H
+#define DEV_TUNNELLED_H
+
+#define DEV_TUNNELLED_H_CVSID "$Id: dev_tunnelled.h,v 1.1 2008/07/25 21:16:00 chrfranke Exp $\n"
+
+#include "dev_interface.h"
+
+/////////////////////////////////////////////////////////////////////////////
+// tunnelled_device_base
+
+/// Common functionality for all tunnelled_device classes.
+
+class tunnelled_device_base
+: virtual public /*implements*/ smart_device
+{
+protected:
+  explicit tunnelled_device_base(smart_device * tunnel_dev);
+
+public:
+  virtual ~tunnelled_device_base() throw();
+
+  virtual bool is_open() const;
+
+  virtual bool open();
+
+  virtual bool close();
+
+  virtual bool owns(const smart_device * dev) const;
+
+  virtual void release(const smart_device * dev);
+
+private:
+  smart_device * m_tunnel_base_dev;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+// tunnelled_device
+
+/// Implement a device by tunneling through another device
+
+template <class BaseDev, class TunnelDev>
+class tunnelled_device
+: public BaseDev,
+  public tunnelled_device_base
+{
+public:
+  typedef TunnelDev tunnel_device_type;
+
+protected:
+  explicit tunnelled_device(tunnel_device_type * tunnel_dev)
+    : smart_device(smart_device::never_called),
+      tunnelled_device_base(tunnel_dev),
+      m_tunnel_dev(tunnel_dev)
+    { }
+
+public:
+  virtual void release(const smart_device * dev)
+    {
+      if (m_tunnel_dev == dev)
+        m_tunnel_dev = 0;
+      tunnelled_device_base::release(dev);
+    }
+
+  tunnel_device_type * get_tunnel_dev()
+    { return m_tunnel_dev; }
+
+  const tunnel_device_type * get_tunnel_dev() const
+    { return m_tunnel_dev; }
+
+private:
+  tunnel_device_type * m_tunnel_dev;
+};
+
+#endif // DEV_TUNNELLED_H
diff --git a/do_release b/do_release
new file mode 100755 (executable)
index 0000000..67b957a
--- /dev/null
@@ -0,0 +1,99 @@
+#!/bin/bash -ev
+#
+# do a smartmontools release
+# (C) 2003-6 Bruce Allen <ballen4705@users.sourceforge.net>, 
+#          Guido Guenther <agx@sigxcpu.org>
+# $Id: do_release 2845 2009-07-18 13:25:18Z chrfranke $
+
+echo 'TODO: Rework this script for SVN.'
+exit 1
+
+# Notes on generating releases:
+# (1) update NEWS
+# (2) update CHANGELOG -- put in release number
+# (3) update release number in configure.in 
+# (4) to test, set USECVS below to 0
+# (5) when satisfied, set USECVS below to 1
+
+USECVS=1
+
+KEYID=0x841ABAE8
+
+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 ..
+
+# increase release number:
+inc_release
+if [ $USECVS -ne 0 ] ; then
+    perl -p -i.bak -e "s/$PERL_OLD/$PERL_NEW/" configure.in
+fi
+
+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 config.guess config.sub \
+       aclocal.m4 missing *.bak
index 2d1d187b5bdc84200ecc891fd125936fe083f590..1667f25d16202ebd1d5b29eead5d88e09a793de6 100644 (file)
@@ -1,4 +1,8 @@
 ## Process this file with automake to produce Makefile.in
+#
+# $Id: Makefile.am 2846 2009-07-18 14:18:51Z chrfranke $
+#
+
 examplesdir=$(exampledir)
 
 examples_DATA = README
@@ -9,3 +13,5 @@ examples_SCRIPTS = Example1     \
                    Example4
 
 EXTRA_DIST = $(examples_SCRIPTS)
+
+MAINTAINERCLEANFILES = $(srcdir)/Makefile.in
diff --git a/examplescripts/Makefile.in b/examplescripts/Makefile.in
deleted file mode 100644 (file)
index adfb803..0000000
+++ /dev/null
@@ -1,381 +0,0 @@
-# Makefile.in generated by automake 1.10 from Makefile.am.
-# @configure_input@
-
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006  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@
-
-
-VPATH = @srcdir@
-pkgdatadir = $(datadir)/@PACKAGE@
-pkglibdir = $(libdir)/@PACKAGE@
-pkgincludedir = $(includedir)/@PACKAGE@
-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
-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@
-AMTAR = @AMTAR@
-ASFLAGS = @ASFLAGS@
-AUTOCONF = @AUTOCONF@
-AUTOHEADER = @AUTOHEADER@
-AUTOMAKE = @AUTOMAKE@
-AWK = @AWK@
-CC = @CC@
-CCAS = @CCAS@
-CCASDEPMODE = @CCASDEPMODE@
-CCASFLAGS = @CCASFLAGS@
-CCDEPMODE = @CCDEPMODE@
-CFLAGS = @CFLAGS@
-CPPFLAGS = @CPPFLAGS@
-CXX = @CXX@
-CXXCPP = @CXXCPP@
-CXXDEPMODE = @CXXDEPMODE@
-CXXFLAGS = @CXXFLAGS@
-CYGPATH_W = @CYGPATH_W@
-DEFS = @DEFS@
-DEPDIR = @DEPDIR@
-ECHO_C = @ECHO_C@
-ECHO_N = @ECHO_N@
-ECHO_T = @ECHO_T@
-EGREP = @EGREP@
-EXEEXT = @EXEEXT@
-GREP = @GREP@
-INSTALL = @INSTALL@
-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@
-MAKEINFO = @MAKEINFO@
-MKDIR_P = @MKDIR_P@
-OBJEXT = @OBJEXT@
-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@
-STRIP = @STRIP@
-VERSION = @VERSION@
-abs_builddir = @abs_builddir@
-abs_srcdir = @abs_srcdir@
-abs_top_builddir = @abs_top_builddir@
-abs_top_srcdir = @abs_top_srcdir@
-ac_ct_CC = @ac_ct_CC@
-ac_ct_CXX = @ac_ct_CXX@
-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@
-builddir = @builddir@
-datadir = @datadir@
-datarootdir = @datarootdir@
-docdir = @docdir@
-dvidir = @dvidir@
-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@
-htmldir = @htmldir@
-includedir = @includedir@
-infodir = @infodir@
-initddir = @initddir@
-install_sh = @install_sh@
-libc_have_working_snprintf = @libc_have_working_snprintf@
-libdir = @libdir@
-libexecdir = @libexecdir@
-localedir = @localedir@
-localstatedir = @localstatedir@
-mandir = @mandir@
-mkdir_p = @mkdir_p@
-oldincludedir = @oldincludedir@
-os_deps = @os_deps@
-os_libs = @os_libs@
-pdfdir = @pdfdir@
-prefix = @prefix@
-program_transform_name = @program_transform_name@
-psdir = @psdir@
-releaseversion = @releaseversion@
-sbindir = @sbindir@
-sharedstatedir = @sharedstatedir@
-smartd_suffix = @smartd_suffix@
-smartmontools_release_date = @smartmontools_release_date@
-smartmontools_release_time = @smartmontools_release_time@
-srcdir = @srcdir@
-sysconfdir = @sysconfdir@
-target_alias = @target_alias@
-top_builddir = @top_builddir@
-top_srcdir = @top_srcdir@
-examplesdir = $(exampledir)
-examples_DATA = README
-examples_SCRIPTS = Example1     \
-                   Example2     \
-                   Example3     \
-                   Example4
-
-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
-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)'; \
-         dist_files=`for file in $$list; do echo $$file; done | \
-         sed -e "s|^$$srcdirstrip/||;t" \
-             -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
-       case $$dist_files in \
-         */*) $(MKDIR_P) `echo "$$dist_files" | \
-                          sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
-                          sort -u` ;; \
-       esac; \
-       for file in $$dist_files; do \
-         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
-         if test -d $$d/$$file; then \
-           dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
-           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-dvi: install-dvi-am
-
-install-exec-am:
-
-install-html: install-html-am
-
-install-info: install-info-am
-
-install-man:
-
-install-pdf: install-pdf-am
-
-install-ps: install-ps-am
-
-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
-
-.MAKE: install-am install-strip
-
-.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-dvi \
-       install-dvi-am install-examplesDATA install-examplesSCRIPTS \
-       install-exec install-exec-am install-html install-html-am \
-       install-info install-info-am install-man install-pdf \
-       install-pdf-am install-ps install-ps-am 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
-
-# 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 44988d5d68c5d7c402507a0d05da0dc4c1b686a9..a1b067e791b161c271308d68ad05813270422437 100644 (file)
--- a/extern.h
+++ b/extern.h
@@ -3,7 +3,7 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 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
 #ifndef EXTERN_H_
 #define EXTERN_H_
 
-#define EXTERN_H_CVSID "$Id: extern.h,v 1.54 2008/03/04 22:09:47 ballen4705 Exp $\n"
-
-// Possible values for fixfirmwarebug.  If user has NOT specified -F at
-// all, then value is 0.
-#define FIX_NOTSPECIFIED     0
-#define FIX_NONE             1
-#define FIX_SAMSUNG          2
-#define FIX_SAMSUNG2         3
-#define FIX_SAMSUNG3         4
+#define EXTERN_H_CVSID "$Id: extern.h,v 1.63 2009/07/07 19:28:29 chrfranke Exp $\n"
 
 // Block used for global control/communications.  If you need more
 // global variables, this should be the only place that you need to
 // add them.
 typedef struct smartmonctrl_s {
-  // spans for selective self-test
-  uint64_t smartselectivespan[5][2];
-  // mode for each span, see SEL_* in utility.h
-  char smartselectivemode[5];
-  // number of spans
-  int smartselectivenumspans;
-  int           testcase;
-  unsigned      scttempint;
-  // one plus time in minutes to wait after powerup before restarting
-  // interrupted offline scan after selective self-test.
-  int  pendingtime;
-  // run offline scan after selective self-test.  0: don't change, 1:
-  // 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;
-  unsigned char generalsmartvalues;
-  unsigned char smartlogdirectory;
-  unsigned char smartselftestlog;
-  unsigned char selectivetestlog;
-  unsigned char smarterrorlog;
-  unsigned char smartbackgroundlog;
-  unsigned char scttempsts;
-  unsigned char scttemphist;
-  unsigned char scttempintp;
-  unsigned char smartdisable;
-  unsigned char smartenable; 
-  unsigned char smartstatus;
-  unsigned char smartexeoffimmediate;
-  unsigned char smartshortselftest;
-  unsigned char smartextendselftest;
-  unsigned char smartconveyanceselftest;
-  unsigned char smartselectiveselftest;
-  unsigned char smartshortcapselftest;
-  unsigned char smartextendcapselftest;
-  unsigned char smartconveyancecapselftest;
-  unsigned char smartselectivecapselftest;
-  unsigned char smartselftestabort;
-  unsigned char smartautoofflineenable;
-  unsigned char smartautoofflinedisable;
-  unsigned char smartautosaveenable;
-  unsigned char smartautosavedisable;
-  unsigned char printing_switchable;
-  unsigned char dont_print;
-  unsigned char dont_print_serial;
+  bool printing_switchable;
+  bool dont_print;
+  bool dont_print_serial;
   unsigned char permissive;
-  unsigned char conservative;
-  unsigned char checksumfail;
-  unsigned char checksumignore;
+  bool conservative;
   unsigned char reportataioctl;
   unsigned char reportscsiioctl;
-  unsigned char fixfirmwarebug;
-  unsigned char fixswappedid;
-  unsigned char satpassthrulen;
-  // Controller type (device type) has been specified explicitly
-  unsigned char controller_explicit;
+#ifdef OLD_INTERFACE
   // 3Ware controller type, but also extensible to other contoller types
-  unsigned char controller_type;
+  unsigned char controller_type; // TODO: Only needed for os_linux.cpp
   // For 3Ware controllers, nonzero value is 1 plus the disk number
-  unsigned char controller_port;
+  unsigned char controller_port;  // TODO: Only needed for os_linux.cpp
   // 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
-  // the i'th SMART attribute.  The default definitions of the
-  // Attributes are obtained by having the array be all zeros.  If
-  // attributedefs[i] is nonzero, it means that the i'th attribute has
-  // a non-default meaning.  See the ataPrintSmartAttribName and
-  // and parse_attribute_def functions.
-  unsigned char attributedefs[256];
+  unsigned char hpt_data[3]; // TODO: Only needed for os_linux.cpp
+#endif
 } smartmonctrl;
 
 #endif
diff --git a/install-sh b/install-sh
deleted file mode 100755 (executable)
index 4fbbae7..0000000
+++ /dev/null
@@ -1,507 +0,0 @@
-#!/bin/sh
-# install - install a program, script, or datafile
-
-scriptversion=2006-10-14.15
-
-# 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.
-
-nl='
-'
-IFS=" ""       $nl"
-
-# set DOITPROG to echo to test this script
-
-# Don't use :- since 4.3BSD and earlier shells don't like it.
-doit="${DOITPROG-}"
-if test -z "$doit"; then
-  doit_exec=exec
-else
-  doit_exec=$doit
-fi
-
-# Put in absolute file names if you don't have them in your path;
-# or use environment 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}"
-
-posix_glob=
-posix_mkdir=
-
-# Desired mode of installed file.
-mode=0755
-
-chmodcmd=$chmodprog
-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 $# -ne 0; do
-  case $1 in
-    -c) shift
-        continue;;
-
-    -d) dir_arg=true
-        shift
-        continue;;
-
-    -g) chgrpcmd="$chgrpprog $2"
-        shift
-        shift
-        continue;;
-
-    --help) echo "$usage"; exit $?;;
-
-    -m) mode=$2
-        shift
-        shift
-       case $mode in
-         *' '* | *'    '* | *'
-'*       | *'*'* | *'?'* | *'['*)
-           echo "$0: invalid mode: $mode" >&2
-           exit 1;;
-       esac
-        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 $?;;
-
-    --)        shift
-       break;;
-
-    -*)        echo "$0: invalid option: $1" >&2
-       exit 1;;
-
-    *)  break;;
-  esac
-done
-
-if test $# -ne 0 && test -z "$dir_arg$dstarg"; then
-  # When -d is used, all remaining arguments are directories to create.
-  # When -t is used, the destination is already specified.
-  # 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
-fi
-
-if test $# -eq 0; 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
-
-if test -z "$dir_arg"; then
-  trap '(exit $?); exit' 1 2 13 15
-
-  # Set umask so as not to create temps with too-generous modes.
-  # However, 'strip' requires both read and write access to temps.
-  case $mode in
-    # Optimize common cases.
-    *644) cp_umask=133;;
-    *755) cp_umask=22;;
-
-    *[0-7])
-      if test -z "$stripcmd"; then
-       u_plus_rw=
-      else
-       u_plus_rw='% 200'
-      fi
-      cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
-    *)
-      if test -z "$stripcmd"; then
-       u_plus_rw=
-      else
-       u_plus_rw=,u+rw
-      fi
-      cp_umask=$mode$u_plus_rw;;
-  esac
-fi
-
-for src
-do
-  # Protect names starting with `-'.
-  case $src in
-    -*) src=./$src ;;
-  esac
-
-  if test -n "$dir_arg"; then
-    dst=$src
-    dstdir=$dst
-    test -d "$dstdir"
-    dstdir_status=$?
-  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
-      dstdir=$dst
-      dst=$dstdir/`basename "$src"`
-      dstdir_status=0
-    else
-      # Prefer dirname, but fall back on a substitute if dirname fails.
-      dstdir=`
-       (dirname "$dst") 2>/dev/null ||
-       expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
-            X"$dst" : 'X\(//\)[^/]' \| \
-            X"$dst" : 'X\(//\)$' \| \
-            X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
-       echo X"$dst" |
-           sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
-                  s//\1/
-                  q
-                }
-                /^X\(\/\/\)[^/].*/{
-                  s//\1/
-                  q
-                }
-                /^X\(\/\/\)$/{
-                  s//\1/
-                  q
-                }
-                /^X\(\/\).*/{
-                  s//\1/
-                  q
-                }
-                s/.*/./; q'
-      `
-
-      test -d "$dstdir"
-      dstdir_status=$?
-    fi
-  fi
-
-  obsolete_mkdir_used=false
-
-  if test $dstdir_status != 0; then
-    case $posix_mkdir in
-      '')
-       # Create intermediate dirs using mode 755 as modified by the umask.
-       # This is like FreeBSD 'install' as of 1997-10-28.
-       umask=`umask`
-       case $stripcmd.$umask in
-         # Optimize common cases.
-         *[2367][2367]) mkdir_umask=$umask;;
-         .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
-
-         *[0-7])
-           mkdir_umask=`expr $umask + 22 \
-             - $umask % 100 % 40 + $umask % 20 \
-             - $umask % 10 % 4 + $umask % 2
-           `;;
-         *) mkdir_umask=$umask,go-w;;
-       esac
-
-       # With -d, create the new directory with the user-specified mode.
-       # Otherwise, rely on $mkdir_umask.
-       if test -n "$dir_arg"; then
-         mkdir_mode=-m$mode
-       else
-         mkdir_mode=
-       fi
-
-       posix_mkdir=false
-       case $umask in
-         *[123567][0-7][0-7])
-           # POSIX mkdir -p sets u+wx bits regardless of umask, which
-           # is incompatible with FreeBSD 'install' when (umask & 300) != 0.
-           ;;
-         *)
-           tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
-           trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
-
-           if (umask $mkdir_umask &&
-               exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
-           then
-             if test -z "$dir_arg" || {
-                  # Check for POSIX incompatibilities with -m.
-                  # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
-                  # other-writeable bit of parent directory when it shouldn't.
-                  # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
-                  ls_ld_tmpdir=`ls -ld "$tmpdir"`
-                  case $ls_ld_tmpdir in
-                    d????-?r-*) different_mode=700;;
-                    d????-?--*) different_mode=755;;
-                    *) false;;
-                  esac &&
-                  $mkdirprog -m$different_mode -p -- "$tmpdir" && {
-                    ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
-                    test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
-                  }
-                }
-             then posix_mkdir=:
-             fi
-             rmdir "$tmpdir/d" "$tmpdir"
-           else
-             # Remove any dirs left behind by ancient mkdir implementations.
-             rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
-           fi
-           trap '' 0;;
-       esac;;
-    esac
-
-    if
-      $posix_mkdir && (
-       umask $mkdir_umask &&
-       $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
-      )
-    then :
-    else
-
-      # The umask is ridiculous, or mkdir does not conform to POSIX,
-      # or it failed possibly due to a race condition.  Create the
-      # directory the slow way, step by step, checking for races as we go.
-
-      case $dstdir in
-       /*) prefix=/ ;;
-       -*) prefix=./ ;;
-       *)  prefix= ;;
-      esac
-
-      case $posix_glob in
-        '')
-         if (set -f) 2>/dev/null; then
-           posix_glob=true
-         else
-           posix_glob=false
-         fi ;;
-      esac
-
-      oIFS=$IFS
-      IFS=/
-      $posix_glob && set -f
-      set fnord $dstdir
-      shift
-      $posix_glob && set +f
-      IFS=$oIFS
-
-      prefixes=
-
-      for d
-      do
-       test -z "$d" && continue
-
-       prefix=$prefix$d
-       if test -d "$prefix"; then
-         prefixes=
-       else
-         if $posix_mkdir; then
-           (umask=$mkdir_umask &&
-            $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
-           # Don't fail if two instances are running concurrently.
-           test -d "$prefix" || exit 1
-         else
-           case $prefix in
-             *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
-             *) qprefix=$prefix;;
-           esac
-           prefixes="$prefixes '$qprefix'"
-         fi
-       fi
-       prefix=$prefix/
-      done
-
-      if test -n "$prefixes"; then
-       # Don't fail if two instances are running concurrently.
-       (umask $mkdir_umask &&
-        eval "\$doit_exec \$mkdirprog $prefixes") ||
-         test -d "$dstdir" || exit 1
-       obsolete_mkdir_used=true
-      fi
-    fi
-  fi
-
-  if test -n "$dir_arg"; then
-    { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
-    { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
-    { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
-      test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
-  else
-
-    # 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
-
-    # Copy the file name to the temp name.
-    (umask $cp_umask && $doit_exec $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 $mode "$dsttmp"; } &&
-
-    # Now rename the file to the real destination.
-    { $doit $mvcmd -f "$dsttmp" "$dst" 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 "$dst"; then
-              $doit $rmcmd -f "$dst" 2>/dev/null \
-              || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \
-                    && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\
-              || {
-                echo "$0: cannot unlink or rename $dst" >&2
-                (exit 1); exit 1
-              }
-            else
-              :
-            fi
-          } &&
-
-          # Now rename the file to the real destination.
-          $doit $mvcmd "$dsttmp" "$dst"
-        }
-    } || exit 1
-
-    trap '' 0
-  fi
-done
-
-# 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:
index d2b77e791663a7968ed38577c7f9a5dbbc3ea1f5..4ed09c84b6582fd1c495e4fb955bee47e1e56a61 100644 (file)
@@ -4,7 +4,8 @@
  * Home page of code is: http://smartmontools.sourceforge.net
  * Address of support mailing list: smartmontools-support@lists.sourceforge.net
  *
- * Copyright (C) 2003-8 Philip Williams, Bruce Allen
+ * Copyright (C) 2003-9 Philip Williams, Bruce Allen
+ * Copyright (C) 2008-9 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
 #include "int64.h"
 #include <stdio.h>
 #include "atacmds.h"
-#include "ataprint.h"
 #include "extern.h"
 #include "knowndrives.h"
-#include "utility.h" // includes <regex.h>
+#include "utility.h"
 
-const char *knowndrives_c_cvsid="$Id: knowndrives.cpp,v 1.166 2008/02/02 18:10:48 chrfranke Exp $"
-ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID UTILITY_H_CVSID;
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef _WIN32
+#include <io.h> // access()
+#endif
+
+#include <stdexcept>
+
+const char *knowndrives_c_cvsid="$Id: knowndrives.cpp,v 1.207 2009/07/04 23:24:37 manfred99 Exp $"
+ATACMDS_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 same_as_minus_F3[]="Fixes completed self-test reported as in progress (same as -F samsung3)";
-
-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.";
-const char may_need_minus_F3_enabled[] ="May need -F samsung3 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;
-}
-void specialpurpose_fix_samsung3(smartmonctrl *con)
-{
-  if (con->fixfirmwarebug==FIX_NOTSPECIFIED)
-    con->fixfirmwarebug = FIX_SAMSUNG3;
-}
 
 /* Table of settings for known drives terminated by an element containing all
  * zeros.  The drivesettings structure is described in knowndrives.h.  Note
@@ -146,941 +53,1040 @@ void specialpurpose_fix_samsung3(smartmonctrl *con)
 // 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[] = {
+// Starting with CVS version 1.179 of this file, the regular expressions
+// for drive model and firmware must match the full string.  The effect of
+// "^FULLSTRING$" is identical to "FULLSTRING".  The special characters '^'
+// and '$' are no longer required, but still allowed. The form ".*SUBSTRING.*"
+// can be used if substring match is desired.
+
+static const drive_settings builtin_knowndrives[] = {
+// BEGIN drivedb.h (DO NOT DELETE - used by Makefile)
+  { "Apple SSD SM128",
+    "APPLE SSD SM128",
+    "", "", ""
+  },
+  { "Asus-Phison SSD",
+    "ASUS-PHISON SSD",
+    "", "", ""
+  },
+  { "OCZ Vertex SSD",
+    "OCZ-VERTEX.*",
+    "", "", ""
+  },
+  { "Transcend Solid-State Drive",
+    "TS(8|16|32|64|128)GSSD25-(M|S)",
+    "", "", ""
+  },
+  { "Transcend Solid-State Drive V series",
+    "TS(8|16|32|64|128|192)GSSD25S-(M|S)",
+    "", "", ""
+  },
+  { "Marvell SSD SD88SA024BA0 (SUN branded)",
+    "MARVELL SD88SA024BA0 SUN24G 0902M0054V",
+    "", "", ""
+  },
+  { "HP 1TB SATA disk GB1000EAFJL",
+    "GB1000EAFJL",
+    "", "", ""
+  },
   { "IBM Deskstar 60GXP series",  // ER60A46A firmware
-    "(IBM-|Hitachi )?IC35L0[12346]0AVER07",
-    "^ER60A46A$",
-    NULL, NULL, NULL, NULL
+    "(IBM-|Hitachi )?IC35L0[12346]0AVER07.*",
+    "ER60A46A",
+    "", ""
   },
   { "IBM Deskstar 60GXP series",  // All other firmware
-    "(IBM-|Hitachi )?IC35L0[12346]0AVER07",
-    ".*",
+    "(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-)?DTLA-30[57]0[123467][05].*",
+    "T[WX][123468AG][OF]A[56]AA",
+    "", ""
   },
   { "IBM Deskstar 40GV & 75GXP series (all other firmware)",
-    "(IBM-)?DTLA-30[57]0[123467][05]",
-    ".*",
+    "(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
+  { "", // ExcelStor J240, J340, J360, J680, and J880
+    "ExcelStor Technology J(24|34|36|68|88)0",
+    "", "", ""
   },
-  { NULL, // Fujitsu M1623TAU
-    "^FUJITSU M1623TAU$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
+  { "", // Fujitsu M1623TAU
+    "FUJITSU M1623TAU",
+    "",
+    "",
+    "-v 9,seconds"
   },
   { "Fujitsu MHG series",
-    "^FUJITSU MHG2...ATU?",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
+    "FUJITSU MHG2...ATU?.*",
+    "",
+    "",
+    "-v 9,seconds"
   },
   { "Fujitsu MHH series",
-    "^FUJITSU MHH2...ATU?",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
+    "FUJITSU MHH2...ATU?.*",
+    "",
+    "",
+    "-v 9,seconds"
   },
   { "Fujitsu MHJ series",
-    "^FUJITSU MHJ2...ATU?",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
+    "FUJITSU MHJ2...ATU?.*",
+    "",
+    "",
+    "-v 9,seconds"
   },
   { "Fujitsu MHK series",
-    "^FUJITSU MHK2...ATU?",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { NULL,  // Fujitsu MHL2300AT
-    "^FUJITSU MHL2300AT$",
-    ".*",
+    "FUJITSU MHK2...ATU?.*",
+    "",
+    "",
+    "-v 9,seconds"
+  },
+  { "",  // Fujitsu MHL2300AT
+    "FUJITSU MHL2300AT",
+    "",
     "This drive's firmware has a harmless Drive Identity Structure\n"
       "checksum error bug.",
-    vendoropts_9_seconds,
-    NULL, NULL
+    "-v 9,seconds"
   },
-  { NULL,  // MHM2200AT, MHM2150AT, MHM2100AT, MHM2060AT
-    "^FUJITSU MHM2(20|15|10|06)0AT$",
-    ".*",
+  { "",  // MHM2200AT, MHM2150AT, MHM2100AT, MHM2060AT
+    "FUJITSU MHM2(20|15|10|06)0AT",
+    "",
     "This drive's firmware has a harmless Drive Identity Structure\n"
       "checksum error bug.",
-    vendoropts_9_seconds,
-    NULL, NULL
+    "-v 9,seconds"
   },
   { "Fujitsu MHN series",
-    "^FUJITSU MHN2...AT$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { NULL, // Fujitsu MHR2020AT
-    "^FUJITSU MHR2020AT$",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { NULL, // Fujitsu MHR2040AT
-    "^FUJITSU MHR2040AT$",
-    ".*",    // Tested on 40BA
-    NULL,
-    vendoropts_Fujitsu_MHR2040AT,
-    NULL, NULL
+    "FUJITSU MHN2...AT",
+    "",
+    "",
+    "-v 9,seconds"
+  },
+  { "", // Fujitsu MHR2020AT
+    "FUJITSU MHR2020AT",
+    "",
+    "",
+    "-v 9,seconds"
+  },
+  { "", // Fujitsu MHR2040AT
+    "FUJITSU MHR2040AT",
+    "",    // Tested on 40BA
+    "",
+    "-v 9,seconds -v 192,emergencyretractcyclect "
+    "-v 198,offlinescanuncsectorct -v 200,writeerrorcount"
   },
   { "Fujitsu MHSxxxxAT family",
-    "^FUJITSU MHS20[6432]0AT(  .)?$",
-    ".*",
-    NULL,
-    vendoropts_Fujitsu_MHS2020AT,
-    NULL, NULL
+    "FUJITSU MHS20[6432]0AT(  .)?",
+    "",
+    "",
+    "-v 9,seconds -v 192,emergencyretractcyclect "
+    "-v 198,offlinescanuncsectorct -v 200,writeerrorcount "
+    "-v 201,detectedtacount"
   },
   { "Fujitsu MHT series",
-    "^FUJITSU MHT2...(AH|AS|AT|BH)U?",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
+    "FUJITSU MHT2...(AH|AS|AT|BH)U?.*",
+    "",
+    "",
+    "-v 9,seconds"
   },
   { "Fujitsu MHU series",
-    "^FUJITSU MHU2...ATU?",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
+    "FUJITSU MHU2...ATU?.*",
+    "",
+    "",
+    "-v 9,seconds"
   },
   { "Fujitsu MHV series",
-    "^FUJITSU MHV2...(AH|AS|AT|BH|BS|BT)",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
+    "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*",
+    "",
+    "",
+    "-v 9,seconds"
   },
   { "Fujitsu MPA..MPG series",
-    "^FUJITSU MP[A-G]3...A[HTEV]U?",
-    ".*",
-    NULL,
-    vendoropts_9_seconds,
-    NULL, NULL
-  },
-  { "Fujitsu MHW2 BH",
-    "^FUJITSU MHW2(04|06|08|10|12|16)0BH$",
-    ".*",
-    NULL, NULL, 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, // SAMSUNG SP8004H with QW100-61 firmware
-    "^SAMSUNG SP8004H$",
-    "^QW100-61$",
-    NULL,
-    vendoropts_9_halfminutes,
-    specialpurpose_reverse_samsung,
-    same_as_minus_F
+    "FUJITSU MP[A-G]3...A[HTEV]U?.*",
+    "",
+    "",
+    "-v 9,seconds"
+  },
+  { "Fujitsu MHY2 BH series",
+    "FUJITSU MHY2(04|06|08|10|12|16|20|25)0BH.*",
+    "", "", ""
+  },
+  { "Fujitsu MHW2 BH series",
+    "FUJITSU MHW2(04|06|08|10|12|16)0BH.*",
+    "", "", ""
+  },
+  { "Fujitsu MHW2 BJ series",
+    "FUJITSU MHW2(08|12|16)0BJ.*",
+    "", "", ""
+  },
+  { "Fujitsu MHZ2 BH series",
+    "FUJITSU MHZ2(04|08|12|16|20|25|32)0BH.*",
+    "", "", ""
+  },
+  { "Fujitsu MHZ2 BJ series",
+    "FUJITSU MHZ2(08|12|16|20|25|32)0BJ.*",
+    "",
+    "",
+    "-v 9,minutes"
+  },
+  { "Fujitsu MHZ2 BS series",
+    "FUJITSU MHZ2(12|25)0BS.*",
+    "", "", ""
+  },
+  { "", // Samsung SV4012H (known firmware)
+    "SAMSUNG SV4012H",
+    "RM100-08",
+    "",
+    "-v 9,halfminutes -F samsung"
+  },
+  { "", // Samsung SV4012H (all other firmware)
+    "SAMSUNG SV4012H",
+    "",
+    "May need -F samsung disabled; see manual for details.",
+    "-v 9,halfminutes -F samsung"
+  },
+  { "", // Samsung SV0412H (known firmware)
+    "SAMSUNG SV0412H",
+    "SK100-01",
+    "",
+    "-v 9,halfminutes -v 194,10xCelsius -F samsung"
+  },
+  { "", // Samsung SV0412H (all other firmware)
+    "SAMSUNG SV0412H",
+    "",
+    "May need -F samsung disabled; see manual for details.",
+    "-v 9,halfminutes -v 194,10xCelsius -F samsung"
+  },
+  { "", // Samsung SV1204H (known firmware)
+    "SAMSUNG SV1204H",
+    "RK100-1[3-5]",
+    "",
+    "-v 9,halfminutes -v 194,10xCelsius -F samsung"
+  },
+  { "", // Samsung SV1204H (all other firmware)
+    "SAMSUNG SV1204H",
+    "",
+    "May need -F samsung disabled; see manual for details.",
+    "-v 9,halfminutes -v 194,10xCelsius -F samsung"
+  },
+  { "", // SAMSUNG SV0322A tested with FW JK200-35
+    "SAMSUNG SV0322A",
+    "", "", ""
+  },
+  { "", // SAMSUNG SP40A2H with RR100-07 firmware
+    "SAMSUNG SP40A2H",
+    "RR100-07",
+    "",
+    "-v 9,halfminutes -F samsung"
+  },
+  { "", // SAMSUNG SP80A4H with RT100-06 firmware
+    "SAMSUNG SP80A4H",
+    "RT100-06",
+    "",
+    "-v 9,halfminutes -F samsung"
+  },
+  { "", // SAMSUNG SP8004H with QW100-61 firmware
+    "SAMSUNG SP8004H",
+    "QW100-61",
+    "",
+    "-v 9,halfminutes -F samsung"
+  },
+  { "SAMSUNG SpinPoint F1 DT series", // tested with HD103UJ/1AA01113
+    "SAMSUNG HD(083G|16[12]G|25[12]H|32[12]H|50[12]I|642J|75[23]L|10[23]U)J",
+    "", "", ""
+  },
+  { "SAMSUNG SpinPoint F1 RE series", // tested with HE103UJ/1AA01113
+    "SAMSUNG HE(252H|322H|502I|642J|753L|103U)J",
+    "", "", ""
+  },
+  { "SAMSUNG SpinPoint S250 series", // tested with HD200HJ/KF100-06
+    "SAMSUNG HD(162|200|250)HJ",
+    "", "", ""
   },
   { "SAMSUNG SpinPoint T133 series", // tested with HD300LJ/ZT100-12, HD400LJ/ZZ100-14, HD401LJ/ZZ100-15
-    "^SAMSUNG HD(250KD|(30[01]|320|40[01])L[DJ])$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "SAMSUNG HD(250KD|(30[01]|320|40[01])L[DJ])",
+    "", "", ""
   },
   { "SAMSUNG SpinPoint T166 series", // tested with HD501LJ/CR100-10
-    "^SAMSUNG HD(080G|160H|32[01]K|403L|50[01]L)J$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "SAMSUNG HD(080G|160H|32[01]K|403L|50[01]L)J",
+    "", "", ""
   },
   { "SAMSUNG SpinPoint P120 series", // VF100-37 firmware, tested with SP2514N/VF100-37
-    "^SAMSUNG SP(16[01]3|2[05][01]4)[CN]$",
-    "^VF100-37$",
-    NULL, NULL,
-    specialpurpose_fix_samsung3,
-    same_as_minus_F3
+    "SAMSUNG SP(16[01]3|2[05][01]4)[CN]",
+    "VF100-37",
+    "",
+    "-F samsung3"
   },
   { "SAMSUNG SpinPoint P120 series", // other firmware, tested with SP2504C/VT100-33
-    "^SAMSUNG SP(16[01]3|2[05][01]4)[CN]$",
-    ".*",
-    may_need_minus_F3_enabled,
-    NULL, NULL, NULL
+    "SAMSUNG SP(16[01]3|2[05][01]4)[CN]",
+    "",
+    "May need -F samsung3 enabled; see manual for details.",
+    ""
   },
   { "SAMSUNG SpinPoint P80 SD series", // tested with HD160JJ/ZM100-33
-    "^SAMSUNG HD(080H|120I|160J)J$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "SAMSUNG HD(080H|120I|160J)J",
+    "", "", ""
   },
   { "SAMSUNG SpinPoint P80 series", // BH100-35 firmware, tested with SP0842N/BH100-35
-    "^SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]$",
-    "^BH100-35$",
-    NULL, NULL,
-    specialpurpose_fix_samsung3,
-    same_as_minus_F3
+    "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]",
+    "BH100-35",
+    "",
+    "-F samsung3"
   },
   { "SAMSUNG SpinPoint P80 series", // firmware *-35 or later
-    "^SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]$",
-    ".*-3[5-9]$",
-    may_need_minus_F3_enabled,
-    NULL, NULL, NULL
+    "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]",
+    ".*-3[5-9]",
+    "May need -F samsung3 enabled; see manual for details.",
+    ""
   },
   { "SAMSUNG SpinPoint P80 series", // firmware *-25...34, tested with SP1614C/SW100-25 and -34
-    "^SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]$",
-    ".*-(2[5-9]|3[0-4])$",
-    NULL,
-    vendoropts_9_halfminutes,
-    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
+    "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]",
+    ".*-(2[5-9]|3[0-4])",
+    "",
+    "-v 9,halfminutes -v 198,increasing"
+  },
+  { "SAMSUNG SpinPoint P80 series", // firmware *-23...24, tested with
+    // SP0802N/TK100-23,
+    // SP1213N/TL100-23,
+    // SP1604N/TM100-23 and -24
+    "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]",
+    ".*-2[34]",
+    "",
+    "-v 9,halfminutes -F samsung2"
+  },
+  { "SAMSUNG SpinPoint P80 series", // unknown firmware
+    "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]",
+    "",
+    "May need -F samsung2 or -F samsung3 enabled; see manual for details.",
+    ""
   },
+/*
+  // TODO: Make the entries below more specific.
+  // These entries produce misleading results, because newer
+  // Samsung disks reuse the version numbers *-NN.
+  { "", // All Samsung drives with '.*-25' firmware
+    "SAMSUNG.*",
+    ".*-25",
+    "May need -F samsung2 disabled; see manual for details.",
+    "-v 9,halfminutes -F samsung2"
+  },
+  { "", // All Samsung drives with '.*-26 or later (currently to -39)' firmware
+    "SAMSUNG.*",
+    ".*-(2[6789]|3[0-9])",
+    "",
+    "-v 9,halfminutes"
+  },
+  { "", // Samsung ALL OTHER DRIVES
+    "SAMSUNG.*",
+    "",
+    "May need -F samsung or -F samsung2 enabled; see manual for details.",
+    ""
+  },
+*/
   { "Maxtor Fireball 541DX family",
-    "^Maxtor 2B0(0[468]|1[05]|20)H1$",
-    ".*",
-    NULL,
-    vendoropts_Maxtor_4D080H4,
-    NULL, NULL
+    "Maxtor 2B0(0[468]|1[05]|20)H1",
+    "",
+    "",
+    "-v 9,minutes -v 194,unknown"
   },
   { "Maxtor Fireball 3 family",
-    "^Maxtor 2F0[234]0[JL]0$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 2F0[234]0[JL]0",
+    "",
+    "",
+    "-v 9,minutes"
+  },
+  { "Maxtor DiamondMax 1280 ATA family",  // no self-test log, ATA2-Fast
+    "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 2160 Ultra ATA family",
-    "^Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "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 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "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 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax D540X-4G family",
-    "^Maxtor 4G(120J6|160J[68])$",
-    ".*",
-    NULL,
-    vendoropts_Maxtor_4D080H4,
-    NULL, NULL
+    "Maxtor 4G(120J6|160J[68])",
+    "",
+    "",
+    "-v 9,minutes -v 194,unknown"
   },
   { "Maxtor DiamondMax D540X-4K family",
-    "^MAXTOR 4K(020H1|040H2|060H3|080H4)$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "MAXTOR 4K(020H1|040H2|060H3|080H4)",
+    "", "", ""
   },
   { "Maxtor DiamondMax Plus D740X family",
-    "^MAXTOR 6L0(20[JL]1|40[JL]2|60[JL]3|80[JL]4)$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "MAXTOR 6L0(20[JL]1|40[JL]2|60[JL]3|80[JL]4)",
+    "", "", ""
   },
   { "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 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "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 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax D540X-4D",
-    "^Maxtor 4D0(20H1|40H2|60H3|80H4)$",
-    ".*",
-    NULL,
-    vendoropts_Maxtor_4D080H4,
-    NULL, NULL
+    "Maxtor 4D0(20H1|40H2|60H3|80H4)",
+    "",
+    "",
+    "-v 9,minutes -v 194,unknown"
   },
   { "Maxtor DiamondMax 16 family",
-    "^Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 4320 Ultra ATA family",
-    "^Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 17 VL family",
-    "^Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "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 (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)",
+    "",
+    "",
+    "-v 9,minutes"
+  },
+  { "Maxtor DiamondMax VL 30 family",  // U: ATA66, H: ATA100
+    "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 36 family",
-    "^Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 40 ATA 66 series",
-    "^Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "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 (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 40 VL Ultra ATA 100 series",
-    "^Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax Plus 45 Ulta ATA 100 family",
-    "^Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 60 ATA 66 family",
-    "^Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 60 ATA 100 family",
-    "^Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4610H6|6147H8)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax Plus 60 family",
-    "^Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 80 family",
-    "^Maxtor (98196H8|96147H6)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor (98196H8|96147H6)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 536DX family",
-    "^Maxtor 4W(100H6|080H6|060H4|040H3|030H2)$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax Plus 8 family",
-    "^Maxtor 6(E0[234]|K04)0L0$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 6(E0[234]|K04)0L0",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 10 family (ATA/133 and SATA/150)",
-    "^Maxtor 6(B(30|25|20|16|12|08)0[MPRS]|L(080[MLP]|(100|120)[MP]|160[MP]|200[MPRS]|250[RS]|300[RS]))0$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 6(B(30|25|20|16|12|10|08)0[MPRS]|L(080[MLP]|(100|120)[MP]|160[MP]|200[MPRS]|250[RS]|300[RS]))0",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 10 family (SATA/300)",
-    "^Maxtor 6V(080E|160E|200E|250F|300F|320F)0$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "Maxtor 6V(080E|160E|200E|250F|300F|320F)0",
+    "", "", ""
   },
   { "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 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor DiamondMax 11 family",
-    "^Maxtor 6H[45]00[FR]0$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "Maxtor 6H[45]00[FR]0",
+    "", "", ""
   },
   { "Maxtor DiamondMax 17",
-    "^Maxtor 6G(080L|160[PE])0$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "Maxtor 6G(080L|160[PE])0",
+    "", "", ""
   },
   { "Seagate Maxtor DiamondMax 20",
-    "^MAXTOR STM3(40|80|160)[28]1[12]0?AS?$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "MAXTOR STM3(40|80|160)[28]1[12]0?AS?",
+    "", "", ""
   },
   { "Seagate Maxtor DiamondMax 21",
-    "^MAXTOR STM3(250|320)820AS?$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "MAXTOR STM3(160215|(250|320)820|320620|500630)AS?",
+    "", "", ""
+  },
+  { "Seagate Maxtor DiamondMax 22", // fixed firmware
+    "(MAXTOR )?STM3(500320|750330|1000340)AS?",
+    "MX1A", // http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207969
+    "", ""
+  },
+  { "Seagate Maxtor DiamondMax 22", // fixed firmware
+    "(MAXTOR )?STM3(160813|320614|640323|1000334)AS?",
+    "MX1B", // http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207975
+    "", ""
+  },
+  { "Seagate Maxtor DiamondMax 22", // buggy firmware
+    "(MAXTOR )?STM3(500320|750330|1000340)AS?",
+    "MX15",
+    "There are known problems with these drives,\n"
+    "AND THIS FIRMWARE VERSION IS AFFECTED,\n"
+    "see the following Seagate web pages:\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207931\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207969",
+    ""
+  },
+  { "Seagate Maxtor DiamondMax 22", // unknown firmware
+    "(MAXTOR )?STM3(160813|32061[34]|500320|640323|750330|10003(34|40))AS?",
+    "",
+    "There are known problems with these drives,\n"
+    "see the following Seagate web pages:\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207931\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207969\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207975",
+    ""
+  },
+  { "Seagate Maxtor DiamondMax 23",
+    "STM3((160|250)31|(320|500)41|(750|1000)52)8AS?",
+    "", "", ""
   },
   { "Maxtor MaXLine Plus II",
-    "^Maxtor 7Y250[PM]0$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 7Y250[PM]0",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor MaXLine II family",
-    "^Maxtor [45]A(25|30|32)0[JN]0$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor [45]A(25|30|32)0[JN]0",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor MaXLine III family (ATA/133 and SATA/150)",
-    "^Maxtor 7L(25|30)0[SR]0$",
-    ".*",
-    NULL,
-    vendoropts_9_minutes,
-    NULL, NULL
+    "Maxtor 7L(25|30)0[SR]0",
+    "",
+    "",
+    "-v 9,minutes"
   },
   { "Maxtor MaXLine III family (SATA/300)",
-    "^Maxtor 7V(25|30)0F0$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "Maxtor 7V(25|30)0F0",
+    "", "", ""
   },
   { "Maxtor MaXLine Pro 500 family",  // There is also a 7H500R0 model, but I
-    "^Maxtor 7H500F0$",               // haven't added it because I suspect
-    ".*",                             // it might need vendoropts_9_minutes
-    NULL, NULL, NULL, NULL            // and nobody has submitted a report yet
+    "Maxtor 7H500F0",               // haven't added it because I suspect
+    "",                               // it might need vendoropts_9_minutes
+    "", ""                            // and nobody has submitted a report yet
   },
-  { NULL, // HITACHI_DK14FA-20B
-    "^HITACHI_DK14FA-20B$",
-    ".*",
-    NULL,
-    vendoropts_Hitachi_DK23XX,
-    NULL, NULL
+  { "", // HITACHI_DK14FA-20B
+    "HITACHI_DK14FA-20B",
+    "",
+    "",
+    "-v 9,minutes -v 193,loadunload"
   },
   { "HITACHI Travelstar DK23XX/DK23XXB series",
-    "^HITACHI_DK23..-..B?$",
-    ".*",
-    NULL,
-    vendoropts_Hitachi_DK23XX,
-    NULL, NULL
+    "HITACHI_DK23..-..B?",
+    "",
+    "",
+    "-v 9,minutes -v 193,loadunload"
   },
   { "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
+    "(HITACHI_DK23FA-20J|HTA422020F9AT[JN]0)",
+    "",
+    "",
+    "-v 9,minutes -v 193,loadunload"
+  },
+  { "Hitachi Endurastar J4K30/N4K30",
+    "HE[JN]4230[23]0F9AT00",
+    "",
+    "",
+    "-v 9,minutes -v 193,loadunload"
+  },
+  { "Hitachi Travelstar C4K60 family",  // 1.8" slim drive
+    "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00",
+    "",
+    "",
+    "-v 9,minutes -v 193,loadunload"
   },
   { "IBM Travelstar 4GT family",
-    "^IBM-DTCA-2(324|409)0$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "IBM-DTCA-2(324|409)0",
+    "", "", ""
+  },
+  { "IBM Travelstar 6GN family",
+    "IBM-DBCA-20(324|486|648)0",
+    "", "", ""
   },
   { "IBM Travelstar 25GS, 18GT, and 12GN family",
-    "^IBM-DARA-2(25|18|15|12|09|06)000$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "IBM-DARA-2(25|18|15|12|09|06)000",
+    "", "", ""
+  },
+  { "IBM Travelstar 14GS",
+    "IBM-DCYA-214000",
+    "", "", ""
+  },
+  { "IBM Travelstar 4LP",
+    "IBM-DTNA-2(180|216)0",
+    "", "", ""
   },
   { "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-|Hitachi )?IC25(T048ATDA05|N0(30|20|15|12|10|07|06|05)ATDA04)-.",
+    "", "", ""
   },
   { "IBM Travelstar 32GH, 30GT, and 20GN family",
-    "^IBM-DJSA-2(32|30|20|10|05)$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "IBM-DJSA-2(32|30|20|10|05)",
+    "", "", ""
   },
   { "IBM Travelstar 4GN family",
-    "^IBM-DKLA-2(216|324|432)0$",
-    ".*",
-    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-DKLA-2(216|324|432)0",
+    "", "", ""
   },
   { "IBM/Hitachi Travelstar 60GH and 40GN family",
-    "^(IBM-|Hitachi )?IC25(T060ATC[SX]05|N0[4321]0ATC[SX]04)-.$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(IBM-|Hitachi )?IC25(T060ATC[SX]05|N0[4321]0ATC[SX]04)-.",
+    "", "", ""
   },
   { "IBM/Hitachi Travelstar 40GNX family",
-    "^(IBM-|Hitachi )?IC25N0[42]0ATC[SX]05-.$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(IBM-|Hitachi )?IC25N0[42]0ATC[SX]05-.",
+    "", "", ""
   },
   { "Hitachi Travelstar 80GN family",
-    "^(Hitachi )?IC25N0[23468]0ATMR04-.$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "(Hitachi )?IC25N0[23468]0ATMR04-.",
+    "", "", ""
   },
   { "Hitachi Travelstar 4K40",
-    "^(Hitachi )?HTS4240[234]0M9AT00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(Hitachi )?HTS4240[234]0M9AT00",
+    "", "", ""
+  },
+  { "Hitachi Travelstar 4K120",
+    "(Hitachi )?(HTS4212(60|80|10|12)H9AT00|HTS421260G9AT00)",
+    "", "", ""
   },
   { "Hitachi Travelstar 5K80 family",
-    "^(Hitachi )?HTS5480[8642]0M9AT00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(Hitachi )?HTS5480[8642]0M9AT00",
+    "", "", ""
   },
   { "Hitachi Travelstar 5K100 series",
-    "^(Hitachi )?HTS5410[1864]0G9(AT|SA)00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(Hitachi )?HTS5410[1864]0G9(AT|SA)00",
+    "", "", ""
   },
   { "Hitachi Travelstar E5K100 series",
-    "^(Hitachi )?HTE541040G9(AT|SA)00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(Hitachi )?HTE541040G9(AT|SA)00",
+    "", "", ""
   },
   { "Hitachi Travelstar 5K120",
-    "^(Hitachi )?HTS5412(60|80|10|12)H9(AT|SA)00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(Hitachi )?HTS5412(60|80|10|12)H9(AT|SA)00",
+    "", "", ""
   },
   { "Hitachi Travelstar 5K160 series",
-    "^(Hitachi )?HTS5416([468]0|1[26])J9(AT|SA)00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(Hitachi |HITACHI )?HTS5416([468]0|1[26])J9(AT|SA)00",
+    "", "", ""
+  },
+  { "Hitachi Travelstar E5K160 series",
+    "(Hitachi )?HTE5416(12|16|60|80)J9(AT|SA)00",
+    "", "", ""
+  },
+  { "Hitachi Travelstar 5K250 series",
+    "(Hitachi |HITACHI )?HTS5425(80|12|16|20|25)K9(A3|SA)00",
+    "", "", ""
   },
   { "Hitachi Travelstar 7K60",
-    "^(Hitachi )?HTS726060M9AT00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(Hitachi )?HTS726060M9AT00",
+    "", "", ""
   },
   { "Hitachi Travelstar E7K60",
-    "^(Hitachi )?HTE7260[46]0M9AT00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(Hitachi )?HTE7260[46]0M9AT00",
+    "", "", ""
   },
   { "Hitachi Travelstar 7K100",
-    "^(Hitachi )?HTS7210[168]0G9(AT|SA)00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(Hitachi )?HTS7210[168]0G9(AT|SA)00",
+    "", "", ""
   },
   { "Hitachi Travelstar E7K100",
-    "^(Hitachi )?HTE7210[168]0G9(AT|SA)00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(Hitachi )?HTE7210[168]0G9(AT|SA)00",
+    "", "", ""
   },
   { "Hitachi Travelstar 7K200",
-    "^(Hitachi )?HTS7220(80|10|12|16|20)K9(A3|SA)00$",
-    ".*",
-    NULL, NULL, NULL, NULL 
+    "(Hitachi )?HTS7220(80|10|12|16|20)K9(A3|SA)00",
+    "", "", ""
   },
-  { "IBM/Hitachi Deskstar 120GXP family",
-    "^(IBM-)?IC35L((020|040|060|080|120)AVVA|0[24]0AVVN)07-[01]$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "IBM Deskstar 14GXP and 16GP series",
+    "IBM-DTTA-3(7101|7129|7144|5032|5043|5064|5084|5101|5129|5168)0",
+    "", "", ""
   },
-  { "IBM/Hitachi Deskstar GXP-180 family",
-    "^(IBM-)?IC35L(030|060|090|120|180)AVV207-[01]$",
-    ".*", 
-    NULL, NULL, NULL, NULL 
+  { "IBM Deskstar 25GP and 22GXP family",
+    "IBM-DJNA-3(5(101|152|203|250)|7(091|135|180|220))0",
+    "", "", ""
   },
-  { "IBM Travelstar 14GS",
-    "^IBM-DCYA-214000$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "IBM Deskstar 37GP and 34GXP family",
+    "IBM-DPTA-3(5(375|300|225|150)|7(342|273|205|136))0",
+    "", "", ""
   },
-  { "IBM Travelstar 4LP",
-    "^IBM-DTNA-2(180|216)0$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "IBM/Hitachi Deskstar 120GXP family",
+    "(IBM-)?IC35L((020|040|060|080|120)AVVA|0[24]0AVVN)07-[01]",
+    "", "", ""
+  },
+  { "IBM/Hitachi Deskstar GXP-180 family",
+    "(IBM-)?IC35L(030|060|090|120|180)AVV207-[01]",
+    "", "", ""
   },
   { "Hitachi Deskstar 7K80 series",
-    "^(Hitachi )?HDS7280([48]0PLAT20|(40)?PLA320|80PLA380)$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "(Hitachi )?HDS7280([48]0PLAT20|(40)?PLA320|80PLA380).*",
+    "", "", ""
   },
   { "Hitachi Deskstar 7K160",
-    "^(Hitachi )?HDS7216(80|16)PLA[3T]80$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "(Hitachi )?HDS7216(80|16)PLA[3T]80.*",
+    "", "", ""
   },
   { "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 )?HDS7225((40|80|12|16)VLAT20|(12|16|25)VLAT80|(80|12|16|25)VLSA80)",
+    "", "", ""
+  },
+  { "Hitachi Deskstar 7K250 (SUN branded)",
+    "HITACHI HDS7225SBSUN250G.*",
+    "", "", ""
   },
   { "Hitachi Deskstar T7K250 series",
-    "^(Hitachi )?HDT7225((25|20|16)DLA(T80|380))$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "(Hitachi )?HDT7225((25|20|16)DLA(T80|380))",
+    "", "", ""
   },
   { "Hitachi Deskstar 7K400 series",
-    "^(Hitachi )?HDS724040KL(AT|SA)80$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "(Hitachi )?HDS724040KL(AT|SA)80",
+    "", "", ""
   },
   { "Hitachi Deskstar 7K500 series",
-    "^(Hitachi )?HDS725050KLA(360|T80)$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "(Hitachi )?HDS725050KLA(360|T80)",
+    "", "", ""
+  },
+  { "Hitachi Deskstar P7K500 series",
+    "(Hitachi )?HDP7250(16|25|32|40|50)GLA(36|38|T8)0",
+    "", "", ""
   },
   { "Hitachi Deskstar T7K500",
-    "^(Hitachi )?HDT7250(25|32|40|50)VLA(360|380|T80)$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "(Hitachi )?HDT7250(25|32|40|50)VLA(360|380|T80)",
+    "", "", ""
   },
   { "Hitachi Deskstar 7K1000",
-    "^(Hitachi )?HDS7210(75|10)KLA330$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "(Hitachi )?HDS7210(50|75|10)KLA330",
+    "", "", ""
+  },
+  { "Hitachi Deskstar 7K1000.B",
+    "(Hitachi )?HDT7210((16|25)SLA380|(32|50|64|75|10)SLA360)",
+    "", "", ""
   },
   { "Hitachi Ultrastar 7K1000",
-    "^(Hitachi )?HUA7210(50|75|10)KLA330$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "(Hitachi )?HUA7210(50|75|10)KLA330",
+    "", "", ""
+  },
+  { "Toshiba 2.5\" HDD series (10-20 GB)",
+    "TOSHIBA MK(101[67]GAP|15[67]GAP|20(1[678]GAP|(18|23)GAS))",
+    "", "", ""
   },
   { "Toshiba 2.5\" HDD series (30-60 GB)",
-    "^TOSHIBA MK((6034|4032)GSX|(6034|4032)GAX|(6026|4026|4019|3019)GAXB?|(6025|6021|4025|4021|4018|3025|3021|3018)GAS|(4036|3029)GACE?|(4018|3017)GAP)$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "TOSHIBA MK((6034|4032)GSX|(6034|4032)GAX|(6026|4026|4019|3019)GAXB?|(6025|6021|4025|4021|4018|3025|3021|3018)GAS|(4036|3029)GACE?|(4018|3017)GAP)",
+    "", "", ""
   },
   { "Toshiba 2.5\" HDD series (80 GB and above)",
-    "^TOSHIBA MK(80(25GAS|26GAX|32GAX|32GSX)|10(31GAS|32GAX)|12(33GAS|34G[AS]X)|2035GSS)$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "TOSHIBA MK(80(25GAS|26GAX|32GAX|32GSX)|10(31GAS|32GAX)|12(33GAS|34G[AS]X)|2035GSS)",
+    "", "", ""
+  },
+  { "Toshiba 2.5\" HDD MK..52GSX series",
+    "TOSHIBA MK(80|12|16|25|32)52GSX",
+    "", "", ""
   },
   { "Toshiba 1.8\" HDD series",
-    "^TOSHIBA MK[23468]00[4-9]GA[HL]$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // TOSHIBA MK6022GAX
-    "^TOSHIBA MK6022GAX$",
-    ".*",
-    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
+    "TOSHIBA MK[23468]00[4-9]GA[HL]",
+    "", "", ""
+  },
+  { "", // TOSHIBA MK6022GAX
+    "TOSHIBA MK6022GAX",
+    "", "", ""
+  },
+  { "", // TOSHIBA MK6409MAV
+    "TOSHIBA MK6409MAV",
+    "", "", ""
+  },
+  { "Toshiba MKx019GAXB (SUN branded)",
+    "TOS MK[34]019GAXB SUN[34]0G",
+    "", "", ""
   },
   { "Seagate Momentus family",
-    "^ST9(20|28|40|48)11A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST9(20|28|40|48)11A",
+    "", "", ""
   },
   { "Seagate Momentus 42 family",
-    "^ST9(2014|3015|4019)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST9(2014|3015|4019)A",
+    "", "", ""
   },
-  { "Seagate Momentus 4200.2 Series",
-    "^ST9(100822|808210|60821|50212|402113|30219)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "Seagate Momentus 4200.2 series",
+    "ST9(100822|808210|60821|50212|402113|30219)A",
+    "", "", ""
   },
   { "Seagate Momentus 5400.2 series",
-    "^ST9(808211|60822|408114|308110|120821|10082[34]|8823|6812|4813|3811)AS?$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST9(808211|60822|408114|308110|120821|10082[34]|8823|6812|4813|3811)AS?",
+    "", "", ""
+  },
+  { "Seagate Momentus 5400.3 series",
+    "ST9(4081[45]|6081[35]|8081[15]|100828|120822|160821)AS?",
+    "", "", ""
+  },
+  { "Seagate Momentus 5400.3 ED series",
+    "ST9(4081[45]|6081[35]|8081[15]|100828|120822|160821)AB",
+    "", "", ""
   },
-  { "Seagate Momentus 5400.3",
-    "^ST9(4081[45]|6081[35]|8081[15]|100828|120822|160821)AS?$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "Seagate Momentus 5400.4 series",
+    "ST9(120817|(160|200|250)827)AS",
+    "", "", ""
   },
-  { "Seagate Momentus 5400.3 ED",
-    "^ST9(4081[45]|6081[35]|8081[15]|100828|120822|160821)AB$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "Seagate Momentus 5400.5 series",
+    "ST9((80|120|160)310|(250|320)320)AS",
+    "", "", ""
+  },
+  { "Seagate Momentus 5400.6 series",
+    "ST9((12|25)0315AS|500325)ASG?",
+    "", "", ""
+  },
+  { "Seagate Momentus 5400 PSD series", // Hybrid drives
+    "ST9(808212|(120|160)8220)AS",
+    "", "", ""
   },
   { "Seagate Momentus 7200.1 series",
-    "^ST9(10021|80825|6023|4015)AS?$", 
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST9(10021|80825|6023|4015)AS?",
+    "", "", ""
+  },
+  { "Seagate Momentus 7200.2 series",
+    "ST9(80813|100821|120823|160823|200420)ASG?",
+    "", "", ""
   },
-  { "Seagate Momentus 7200.2",
-    "^ST9(80813|100821|120823|160823|200420)ASG?$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "Seagate Momentus 7200.3 series",
+    "ST9((80|120|160)411|(250|320)421)ASG?",
+    "", "", ""
+  },
+  { "Seagate Medalist 1010, 1721, 2120, 3230 and 4340",  // ATA2, with -t permissive
+    "ST3(1010|1721|2120|3230|4340)A",
+    "", "", ""
   },
   { "Seagate Medalist 2110, 3221, 4321, 6531, and 8641",
-    "^ST3(2110|3221|4321|6531|8641)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(2110|3221|4321|6531|8641)A",
+    "", "", ""
   },
   { "Seagate U Series X family",
-    "^ST3(10014A(CE)?|20014A)$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(10014A(CE)?|20014A)",
+    "", "", ""
+  },
+  { "Seagate U8 family",
+    "ST3(4313|6811|8410|13021|17221)A",
+    "", "", ""
   },
   { "Seagate U7 family",
-    "^ST3(30012|40012|60012|80022|120020)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(30012|40012|60012|80022|120020)A",
+    "", "", ""
   },
   { "Seagate U Series 6 family",
-    "^ST3(8002|6002|4081|3061|2041)0A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(8002|6002|4081|3061|2041)0A",
+    "", "", ""
   },
   { "Seagate U Series 5 family",
-    "^ST3(40823|30621|20413|15311|10211)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(40823|30621|20413|15311|10211)A",
+    "", "", ""
   },
   { "Seagate U4 family",
-    "^ST3(2112|4311|6421|8421)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(2112|4311|6421|8421)A",
+    "", "", ""
   },
   { "Seagate U8 family",
-    "^ST3(8410|4313|17221|13021)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(8410|4313|17221|13021)A",
+    "", "", ""
   },
   { "Seagate U10 family",
-    "^ST3(20423|15323|10212)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(20423|15323|10212)A",
+    "", "", ""
+  },
+  { "Seagate Barracuda ATA family",
+    "ST3(2804|2724|2043|1362|1022|681)0A",
+    "", "", ""
   },
   { "Seagate Barracuda ATA II family",
-    "^ST3(3063|2042|1532|1021)0A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(3063|2042|1532|1021)0A",
+    "", "", ""
   },
   { "Seagate Barracuda ATA III family",
-    "^ST3(40824|30620|20414|15310|10215)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(40824|30620|20414|15310|10215)A",
+    "", "", ""
   },
   { "Seagate Barracuda ATA IV family",
-    "^ST3(20011|30011|40016|60021|80021)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(20011|30011|40016|60021|80021)A",
+    "", "", ""
   },
   { "Seagate Barracuda ATA V family",
-    "^ST3(12002(3A|4A|9A|3AS)|800(23A|15A|23AS)|60(015A|210A)|40017A)$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(12002(3A|4A|9A|3AS)|800(23A|15A|23AS)|60(015A|210A)|40017A)",
+    "", "", ""
   },
   { "Seagate Barracuda 5400.1",
-    "^ST340015A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST340015A",
+    "", "", ""
   },
   { "Seagate Barracuda 7200.7 and 7200.7 Plus family",
-    "^ST3(200021A|200822AS?|16002[13]AS?|12002[26]AS?|1[26]082[78]AS|8001[13]AS?|80817AS|60014A|40111AS|40014AS?)$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(200021A|200822AS?|16002[13]AS?|12002[26]AS?|1[26]082[78]AS|8001[13]AS?|8081[79]AS|60014A|40111AS|40014AS?)",
+    "", "", ""
   },
   { "Seagate Barracuda 7200.8 family",
-    "^ST3(400[68]32|300[68]31|250[68]23|200826)AS?$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(400[68]32|300[68]31|250[68]23|200826)AS?",
+    "", "", ""
   },
   { "Seagate Barracuda 7200.9 family",
-    "^ST3(402111?|80[28]110?|120[28]1[0134]|160[28]1[012]|200827|250[68]24|300[68]22|(320|400)[68]33|500[68](32|41))AS?$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(402111?|80[28]110?|120[28]1[0134]|160[28]1[012]|200827|250[68]24|300[68]22|(320|400)[68]33|500[68](32|41))AS?.*",
+    "", "", ""
   },
   { "Seagate Barracuda 7200.10 family",
-    "^ST3((80|160)[28]15|200820|250[34]10|(250|300|320|400)[68]20|500[68]30|750[68]40)AS?$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Seagate Barracuda 7200.11",
-    "^ST3(500[368]2|750[36]3|1000[36]4)0AS?$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3((80|160)[28]15|200820|250[34]10|(250|300|320|400)[68]20|500[68]30|750[68]40)AS?",
+    "", "", ""
+  },
+  { "Seagate Barracuda 7200.11 family", // unaffected firmware
+    "ST3(160813|320[68]13|500[368]20|640[36]23|640[35]30|750[36]30|1000(333|[36]40)|1500341)AS?",
+    "CC.?.?", // http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207957
+    "", ""
+  },
+  { "Seagate Barracuda 7200.11 family", // fixed firmware
+    "ST3(500[368]20|750[36]30|1000340)AS?",
+    "SD1A", // http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207951
+    "", ""
+  },
+  { "Seagate Barracuda 7200.11 family", // fixed firmware
+    "ST3(160813|320[68]13|640[36]23|1000333|1500341)AS?",
+    "SD1B", // http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207957
+    "", ""
+  },
+  { "Seagate Barracuda 7200.11 family", // buggy firmware
+    "ST3(500[368]20|640[35]30|750[36]30|1000340)AS?",
+    "(AD14|SD1[5-9])",
+    "There are known problems with these drives,\n"
+    "AND THIS FIRMWARE VERSION IS AFFECTED,\n"
+    "see the following Seagate web pages:\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207931\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207951",
+    ""
+  },
+  { "Seagate Barracuda 7200.11 family", // unknown firmware
+    "ST3(160813|320[68]13|500[368]20|640[36]23|640[35]30|750[36]30|1000(333|[36]40)|1500341)AS?",
+    "",
+    "There are known problems with these drives,\n"
+    "see the following Seagate web pages:\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207931\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207951\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207957",
+    ""
+  },
+  { "Seagate Barracuda 7200.12 family",
+    "ST3((160|250)318|(320|500)418|500410|(750|1000)528)AS",
+    "", "", ""
   },
   { "Seagate Barracuda ES",
-    "^ST3(250[68]2|32062|40062|50063|75064)0NS$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(250[68]2|32062|40062|50063|75064)0NS",
+    "", "", ""
+  },
+  { "Seagate Barracuda ES.2", // fixed firmware
+    "ST3(25031|50032|75033|100034)0NS",
+    "SN[01]6", // http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207963
+    "", ""
+  },
+  { "Seagate Barracuda ES.2", // unknown firmware
+    "ST3(25031|50032|75033|100034)0NS",
+    "",
+    "There are known problems with these drives,\n"
+    "see the following Seagate web pages:\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207931\n"
+    "http://seagate.custkb.com/seagate/crm/selfservice/search.jsp?DocId=207963",
+    ""
   },
   { "Seagate Medalist 17240, 13030, 10231, 8420, and 4310",
-    "^ST3(17240|13030|10231|8420|4310)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(17240|13030|10231|8420|4310)A",
+    "", "", ""
   },
   { "Seagate Medalist 17242, 13032, 10232, 8422, and 4312",
-    "^ST3(1724|1303|1023|842|431)2A$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(1724|1303|1023|842|431)2A",
+    "", "", ""
   },
   { "Seagate NL35 family",
-    "^ST3(250623|250823|400632|400832|250824|250624|400633|400833|500641|500841)NS$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "ST3(250623|250823|400632|400832|250824|250624|400633|400833|500641|500841)NS",
+    "", "", ""
+  },
+  { "Seagate SV35.2 Series",
+    "ST3(160815|250820|320620|500630|750640)(A|S)V",
+    "", "", ""
+  },
+  { "Seagate DB35.3 Series",
+    "ST3(750640SCE|((80|160)215|(250|320|400)820|500830|750840)(A|S)CE)",
+    "", "", ""
   },
   { "Western Digital Protege",
   /* Western Digital drives with this comment all appear to use Attribute 9 in
@@ -1088,9 +1094,8 @@ const drivesettings knowndrives[] = {
    * 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
+    "WDC WD([2468]00E|1[26]00A)B-.*",
+    "", "", ""
   },
   { "Western Digital Caviar family",
   /* Western Digital drives with this comment all appear to use Attribute 9 in
@@ -1098,9 +1103,8 @@ const drivesettings knowndrives[] = {
    * 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
+    "WDC WD(2|3|4|6|8|10|12|16|18|20|25)00BB-.*",
+    "", "", ""
   },
   { "Western Digital Caviar WDxxxAB series",
   /* Western Digital drives with this comment all appear to use Attribute 9 in
@@ -1108,9 +1112,8 @@ const drivesettings knowndrives[] = {
    * 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
+    "WDC WD(3|4|6|8|25)00AB-.*",
+    "", "", ""
   },
   { "Western Digital Caviar WDxxxAA series",
   /* Western Digital drives with this comment all appear to use Attribute 9 in
@@ -1118,9 +1121,8 @@ const drivesettings knowndrives[] = {
    * 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
+    "WDC WD...?AA(-.*)?",
+    "", "", ""
   },
   { "Western Digital Caviar WDxxxBA series",
   /* Western Digital drives with this comment all appear to use Attribute 9 in
@@ -1128,20 +1130,12 @@ const drivesettings knowndrives[] = {
    * 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
+    "WDC WD...BA",
+    "", "", ""
   },
-  { "Western Digital Caviar Serial ATA family",
-    "^WDC WD(4|8|20|32)00BD-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "Western Digital Caviar AC series", // add only 5400rpm/7200rpm (ata33 and faster)
+    "WDC AC((116|121|125|225|132|232)|([1-4][4-9][0-9])|([1-4][0-9][0-9][0-9]))00[A-Z]?.*",
+    "", "", ""
   },
   { "Western Digital Caviar SE family",
   /* Western Digital drives with this comment all appear to use Attribute 9 in
@@ -1150,266 +1144,455 @@ const drivesettings knowndrives[] = {
    * UPDATE: this is probably explained by the WD firmware bug described in the
    * smartmontools FAQ 
    * UPDATE 2: this does not apply to more recent models, at least WD3200AAJB */
-    "^WDC WD((4|6|8|10|12|16|18|20|25|30|32|40|50)00(JB|PB|AAJB|AAKB))-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "WDC WD(4|6|8|10|12|16|18|20|25|30|32|40|50)00(JB|PB)-.*",
+    "", "", ""
+  },
+  { "Western Digital Caviar Blue EIDE family",  // WD Caviar SE EIDE family
+    /* not completely accurate: at least also WD800JB, WD(4|8|20|25)00BB sold as Caviar Blue */
+    "WDC WD(16|25|32|40|50)00AAJB-.*",
+    "", "", ""
+  },
+  { "Western Digital Caviar Blue EIDE family",  // WD Caviar SE16 EIDE family
+    "WDC WD(25|32|40|50)00AAKB-.*",
+    "", "", ""
+  },
+  { "Western Digital RE EIDE family",
+    "WDC WD(12|16|25|32)00SB-.*",
+    "", "", ""
+  },
+  { "Western Digital Caviar Serial ATA family",
+    "WDC WD(4|8|20|32)00BD-.*",
+    "", "", ""
+  },
+  { "Western Digital Caviar SE Serial ATA family",
+    "WDC WD(4|8|12|16|20|25|32|40)00(JD|KD|PD)-.*",
+    "", "", ""
   },
   { "Western Digital Caviar SE Serial ATA family",
-    "^WDC WD((4|8|12|16|20|25|32|40)00(JD|KD))-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
+    "WDC WD(8|12|16|20|25|30|32|40|50)00JS-.*",
+    "", "", ""
+  },
+  { "Western Digital Caviar SE16 Serial ATA family",
+    "WDC WD(16|20|25|32|40|50|75)00KS-.*",
+    "", "", ""
+  },
+  { "Western Digital Caviar Blue Serial ATA family",  // WD Caviar SE Serial ATA family
+    /* not completely accurate: at least also WD800BD, (4|8)00JD sold as Caviar Blue */
+    "WDC WD((8|12|16|25|32)00AABS|(12|16|25|32|40|50)00AAJS)-.*",
+    "", "", ""
+  },
+  { "Western Digital Caviar Blue Serial ATA family",  // WD Caviar SE16 Serial ATA family
+    "WDC WD(16|20|25|32|40|50|64|75)00AAKS-.*",
+    "", "", ""
+  },
+  { "Western Digital RE Serial ATA family",
+    "WDC WD(12|16|25|32)00(SD|YD|YS)-.*",
+    "", "", ""
+  },
+  { "Western Digital RE2 Serial ATA family",
+    "WDC WD((40|50|75)00(YR|YS|AYYS)|(16|32|40|50)0[01]ABYS)-.*",
+    "", "", ""
+  },
+  { "Western Digital RE2-GP family",
+    "WDC WD(5000AB|7500AY|1000FY)PS-.*",
+    "", "", ""
+  },
+  { "Western Digital RE3 Serial ATA family",
+    "WDC WD((25|32|50)02A|(75|10)02F)BYS-.*",
+    "", "", ""
+  },
+  { "Western Digital Caviar Green family",
+    "WDC WD((50|64|75)00AA(C|V)S|(50|75)00AADS|10EA(C|V)S|(10|15|20)EADS)-.*",
+    "", "", ""
   },
-  { "Western Digital Caviar Second Generation Serial ATA family",
-    "^WDC WD((8|12|16|20|25|30|32|40|50|75)00(JS|KS|AABS|AAJS|AAKS))-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "Western Digital Caviar Black family",
+    "WDC WD((500|640|750)1AA|1001FA)LS-.*",
+    "", "", ""
   },
-  { "Western Digital Caviar RE Serial ATA family",
-    "^WDC WD((12|16|25|32|40|50|75)00(SD|YD|YR|YS|ABYS|AYYS))-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "Western Digital AV ATA family",
+    "WDC WD(8|16|50)00AV(B|J)B-.*",
+    "", "", ""
   },
-  { "Western Digital Caviar RE EIDE family",
-    "^WDC WD((12|16|25|32)00SB)-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "Western Digital AV-GP family",
+    "WDC WD((16|25|32|50|64|75)00AVVS|(50|75)00AVCS|10EVVS|(10|20)EVCS|WD(10|15|20)EVDS)-.*",
+    "", "", ""
   },
   { "Western Digital Raptor family",
-    "^WDC WD((360|740|800)GD|(360|740|1500)ADFD)-.*$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { "Western Digital Scorpio family",
-    "^WDC WD((4|6|8|10|12|16|20|25)00(UE|VE|BEAS|BEVE|BEVS))-.*$",
-    ".*",
-    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 [123]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
+    "WDC WD((360|740|800)GD|(360|740|1500)ADF[DS])-.*",
+    "", "", ""
   },
-  { "Quantum Fireball Plus LM series",
-    "^QUANTUM FIREBALLP LM(10.2|15|20.5|30)$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "Western Digital VelociRaptor family",
+    "WDC WD((1500|3000)B|3000G)LFS-.*",
+    "", "", ""
+  },
+  { "Western Digital Scorpio EIDE family",
+    "WDC WD(4|6|8|10|12|16)00(UE|VE)-.*",
+    "", "", ""
+  },
+  { "Western Digital Scorpio Blue EIDE family",
+    "WDC WD(4|6|8|10|12|16|25)00BEVE-.*",
+    "", "", ""
+  },
+  { "Western Digital Scorpio Serial ATA family",
+    "WDC WD(4|6|8|10|12|16|25)00BEAS-.*",
+    "", "", ""
+  },
+  { "Western Digital Scorpio Blue Serial ATA family",
+    "WDC WD((4|6|8|10|12|16|25)00BEVS|(8|12|16|25|32|40|50)00BEVT)-.*",
+    "", "", ""
+  },
+  { "Western Digital Scorpio Black Serial ATA family",
+    "WDC WD(8|12|16|25|32)00B[EJ]KT-.*",
+    "", "", ""
+  },
+  { "Quantum Bigfoot series",
+    "QUANTUM BIGFOOT TS10.0A",
+    "", "", ""
+  },
+  { "Quantum Fireball lct15 series",
+    "QUANTUM FIREBALLlct15 ([123]0|22)",
+    "", "", ""
+  },
+  { "Quantum Fireball lct20 series",
+    "QUANTUM FIREBALLlct20 [234]0",
+    "", "", ""
+  },
+  { "Quantum Fireball CX series", 
+    "QUANTUM FIREBALL CX10.2A",
+    "", "", ""
   },
   { "Quantum Fireball CR series",
-    "^QUANTUM FIREBALL CR(4.3|8.4)A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  { NULL, // QUANTUM FIREBALLP AS10.2, AS20.5, AS30.0, and AS40.0
-    "^QUANTUM FIREBALLP AS(10.2|20.5|30.0|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 ST(3.2|4.3)A$",
-    ".*",
-    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
+    "QUANTUM FIREBALL CR(4.3|6.4|8.4|13.0)A",
+    "", "", ""
   },
-  { "Quantum Fireball Plus KA series",
-    "^QUANTUM FIREBALLP KA(9|10).1$",
-    ".*",
-    NULL, NULL, NULL, NULL
+  { "Quantum Fireball EX series",
+    "QUANTUM FIREBALL EX(3.2|6.4)A",
+    "", "", ""
+  },
+  { "Quantum Fireball ST series",
+    "QUANTUM FIREBALL ST(3.2|4.3|4300)A",
+    "", "", ""
   },
   { "Quantum Fireball SE series",
-    "^QUANTUM FIREBALL SE4.3A$",
-    ".*",
-    NULL, NULL, NULL, NULL
-  },
-  /*------------------------------------------------------------
-   *  End of table.  Do not add entries below this marker.
-   *------------------------------------------------------------ */
-  {NULL, NULL, NULL, NULL, NULL, NULL, NULL}
+    "QUANTUM FIREBALL SE4.3A",
+    "", "", ""
+  },
+  { "Quantum Fireball Plus LM series",
+    "QUANTUM FIREBALLP LM(10.2|15|20.[45]|30)",
+    "", "", ""
+  },
+  { "Quantum Fireball Plus AS series",
+    "QUANTUM FIREBALLP AS(10.2|20.5|30.0|40.0)",
+    "", "", ""
+  },
+  { "Quantum Fireball Plus KX series",
+    "QUANTUM FIREBALLP KX27.3",
+    "", "", ""
+  },
+  { "Quantum Fireball Plus KA series",
+    "QUANTUM FIREBALLP KA(9|10).1",
+    "", "", ""
+  },
+// END drivedb.h (DO NOT DELETE - used by Makefile)
 };
 
+
+/// Drive database class. Stores custom entries read from file.
+/// Provides transparent access to concatenation of custom and
+/// default table.
+class drive_database
+{
+public:
+  drive_database();
+
+  ~drive_database();
+
+  /// Get total number of entries.
+  unsigned size() const
+    { return m_custom_tab.size() + m_builtin_size; }
+
+  /// Get number of custom entries.
+  unsigned custom_size() const
+    { return m_custom_tab.size(); }
+
+  /// Array access.
+  const drive_settings & operator[](unsigned i);
+
+  /// Append new custom entry.
+  void push_back(const drive_settings & src);
+
+  /// Append builtin table.
+  void append(const drive_settings * builtin_tab, unsigned builtin_size)
+    { m_builtin_tab = builtin_tab; m_builtin_size = builtin_size; }
+
+private:
+  const drive_settings * m_builtin_tab;
+  unsigned m_builtin_size;
+
+  std::vector<drive_settings> m_custom_tab;
+  std::vector<char *> m_custom_strings;
+
+  const char * copy_string(const char * str);
+
+  drive_database(const drive_database &);
+  void operator=(const drive_database &);
+};
+
+drive_database::drive_database()
+: m_builtin_tab(0), m_builtin_size(0)
+{
+}
+
+drive_database::~drive_database()
+{
+  for (unsigned i = 0; i < m_custom_strings.size(); i++)
+    delete [] m_custom_strings[i];
+}
+
+const drive_settings & drive_database::operator[](unsigned i)
+{
+  return (i < m_custom_tab.size() ? m_custom_tab[i]
+          : m_builtin_tab[i - m_custom_tab.size()] );
+}
+
+void drive_database::push_back(const drive_settings & src)
+{
+  drive_settings dest;
+  dest.modelfamily    = copy_string(src.modelfamily);
+  dest.modelregexp    = copy_string(src.modelregexp);
+  dest.firmwareregexp = copy_string(src.firmwareregexp);
+  dest.warningmsg     = copy_string(src.warningmsg);
+  dest.presets        = copy_string(src.presets);
+  m_custom_tab.push_back(dest);
+}
+
+const char * drive_database::copy_string(const char * src)
+{
+  char * dest = new char[strlen(src)+1];
+  try {
+    m_custom_strings.push_back(dest);
+  }
+  catch (...) {
+    delete [] dest; throw;
+  }
+  return strcpy(dest, src);
+}
+
+
+/// The drive database.
+static drive_database knowndrives;
+
+
+// Compile regular expression, print message on failure.
+static bool compile(regular_expression & regex, const char *pattern)
+{
+  if (!regex.compile(pattern, REG_EXTENDED)) {
+    pout("Internal error: unable to compile regular expression \"%s\": %s\n"
+         "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n",
+      pattern, regex.get_errmsg());
+    return false;
+  }
+  return true;
+}
+
+// Compile & match a regular expression.
+static bool match(const char * pattern, const char * str)
+{
+  regular_expression regex;
+  if (!compile(regex, pattern))
+    return false;
+  return regex.full_match(str);
+}
+
 // 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)
+// manufacturer then values of NULL may be used.  Returns the entry of the
+// first match in knowndrives[] or 0 if no match if found.
+const drive_settings * lookup_drive(const char * model, const char * firmware)
 {
-  regex_t regex;
-  int i, index;
-  const char *empty = "";
+  if (!model)
+    model = "";
+  if (!firmware)
+    firmware = "";
 
-  model = model ? model : empty;
-  firmware = firmware ? firmware : empty;
+  for (unsigned i = 0; i < knowndrives.size(); i++) {
+    // Check whether model matches the regular expression in knowndrives[i].
+    if (!match(knowndrives[i].modelregexp, model))
+      continue;
 
-  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;
+    // Model matches, now check firmware. "" matches always.
+    if (!(  !*knowndrives[i].firmwareregexp
+          || match(knowndrives[i].firmwareregexp, firmware)))
+      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);
+    // Found
+    return &knowndrives[i];
   }
 
-  return index;
+  // Not found
+  return 0;
 }
 
+// Parse '-v' and '-F' options in preset string, return false on error.
+static bool parse_presets(const char * presets, unsigned char * opts, unsigned char & fix_firmwarebug)
+{
+  for (int i = 0; ; ) {
+    i += strspn(presets+i, " \t");
+    if (!presets[i])
+      break;
+    char opt, arg[40+1+13]; int len = -1;
+    if (!(sscanf(presets+i, "-%c %40[^ ]%n", &opt, arg, &len) >= 2 && len > 0))
+      return false;
+    if (opt == 'v') {
+      // Parse "-v N,option"
+      unsigned char newopts[MAX_ATTRIBUTE_NUM] = {0, };
+      if (parse_attribute_def(arg, newopts))
+        return false;
+      // Set only if not set by user
+      for (int j = 0; j < MAX_ATTRIBUTE_NUM; j++)
+        if (newopts[j] && !opts[j])
+          opts[j] = newopts[j];
+    }
+    else if (opt == 'F') {
+      unsigned char fix;
+      if (!strcmp(arg, "samsung"))
+        fix = FIX_SAMSUNG;
+      else if (!strcmp(arg, "samsung2"))
+        fix = FIX_SAMSUNG2;
+      else if (!strcmp(arg, "samsung3"))
+        fix = FIX_SAMSUNG3;
+      else
+        return false;
+      // Set only if not set by user
+      if (fix_firmwarebug == FIX_NOTSPECIFIED)
+        fix_firmwarebug = fix;
+    }
+    else
+      return false;
 
-// Shows all presets for drives in knowndrives[].
-void showonepreset(const drivesettings *drivetable){
-  
-  const unsigned char (* presets)[2] = drivetable->vendoropts;
-  int first_preset = 1;
-  
+    i += len;
+  }
+  return true;
+}
+
+// Shows one entry of knowndrives[], returns #errors.
+static int showonepreset(const drive_settings * dbentry)
+{
   // Basic error check
-  if (!drivetable || !drivetable->modelregexp){
-    pout("Null known drive table pointer. Please report\n"
+  if (!(   dbentry
+        && dbentry->modelfamily
+        && dbentry->modelregexp && *dbentry->modelregexp
+        && dbentry->firmwareregexp
+        && dbentry->warningmsg
+        && dbentry->presets                             )) {
+    pout("Invalid drive database entry. Please report\n"
          "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n");
-    return;
+    return 1;
   }
   
-  // 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];
+  // print and check model and firmware regular expressions
+  int errcnt = 0;
+  regular_expression regex;
+  pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL REGEXP:", dbentry->modelregexp);
+  if (!compile(regex, dbentry->modelregexp))
+    errcnt++;
 
-    // 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++;
+  pout("%-*s %s\n", TABLEPRINTWIDTH, "FIRMWARE REGEXP:", *dbentry->firmwareregexp ?
+    dbentry->firmwareregexp : ".*"); // preserve old output (TODO: Change)
+  if (*dbentry->firmwareregexp && !compile(regex, dbentry->firmwareregexp))
+    errcnt++;
+
+  pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", dbentry->modelfamily);
+
+  // if there are any presets, then show them
+  unsigned char fix_firmwarebug = 0;
+  bool first_preset = true;
+  if (*dbentry->presets) {
+    unsigned char opts[MAX_ATTRIBUTE_NUM] = {0,};
+    if (!parse_presets(dbentry->presets, opts, fix_firmwarebug)) {
+      pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
+      errcnt++;
+    }
+    for (int i = 0; i < MAX_ATTRIBUTE_NUM; i++) {
+      char out[256];
+      if (opts[i]) {
+        ataPrintSmartAttribName(out, i, opts);
+        // 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 = false;
+      }
+    }
   }
-  else
+  if (first_preset)
     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"); 
+  // describe firmwarefix
+  if (fix_firmwarebug) {
+    const char * fixdesc;
+    switch (fix_firmwarebug) {
+      case FIX_SAMSUNG:
+        fixdesc = "Fixes byte order in some SMART data (same as -F samsung)";
+        break;
+      case FIX_SAMSUNG2:
+        fixdesc = "Fixes byte order in some SMART data (same as -F samsung2)";
+        break;
+      case FIX_SAMSUNG3:
+        fixdesc = "Fixes completed self-test reported as in progress (same as -F samsung3)";
+        break;
+      default:
+        fixdesc = "UNKNOWN"; errcnt++;
+        break;
+    }
+    pout("%-*s %s\n", TABLEPRINTWIDTH, "OTHER PRESETS:", fixdesc);
   }
-  
+
   // Print any special warnings
-  if (drivetable->warningmsg){
-    pout("%-*s ", TABLEPRINTWIDTH, "WARNINGS:");
-    pout("%s\n", drivetable->warningmsg);
-  }
-  
-  return;
+  if (*dbentry->warningmsg)
+    pout("%-*s %s\n", TABLEPRINTWIDTH, "WARNINGS:", dbentry->warningmsg);
+  return errcnt;
 }
 
 // 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;
-
+// Returns #syntax errors.
+int showallpresets()
+{
   // 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]);
+  int errcnt = 0;
+  for (unsigned i = 0; i < knowndrives.size(); i++) {
+    errcnt += 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("Total number of entries  :%5u\n"
+       "Entries read from file(s):%5u\n\n",
+    knowndrives.size(), knowndrives.custom_size());
+
   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;
+
+  if (errcnt > 0)
+    pout("\nFound %d syntax error(s) in database.\n"
+         "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n", errcnt);
+  return errcnt;
 }
 
 // Shows all matching presets for a drive in knowndrives[].
 // Returns # matching entries.
-int showmatchingpresets(const char *model, const char *firmware){
-  int i;
+int showmatchingpresets(const char *model, const char *firmware)
+{
   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))
+  for (unsigned i = 0; i < knowndrives.size(); i++) {
+    if (!match(knowndrives[i].modelregexp, model))
       continue;
-    if (firmware && knowndrives[i].firmwareregexp) {
-      regfree(&regex);
-      if (compileregex(&regex, knowndrives[i].firmwareregexp, REG_EXTENDED))
+    if (   firmware && *knowndrives[i].firmwareregexp
+        && !match(knowndrives[i].firmwareregexp, firmware))
         continue;
-      if (regexec(&regex, firmware, 0, NULL, 0))
-        continue;
-    }
+    // Found
     if (++cnt == 1)
       pout("Drive found in smartmontools Database.  Drive identity strings:\n"
            "%-*s %s\n"
@@ -1421,7 +1604,6 @@ int showmatchingpresets(const char *model, const char *firmware){
     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"
@@ -1432,16 +1614,17 @@ int showmatchingpresets(const char *model, const char *firmware){
 }
 
 // Shows the presets (if any) that are available for the given drive.
-void showpresets(const struct ata_identify_device *drive){
-  int i;
+void show_presets(const ata_identify_device * drive, bool fix_swapped_id)
+{
   char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
 
   // get the drive's model/firmware strings
-  format_ata_string(model, (char *)drive->model, MODEL_STRING_LENGTH);
-  format_ata_string(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH);
+  format_ata_string(model, drive->model, MODEL_STRING_LENGTH, fix_swapped_id);
+  format_ata_string(firmware, drive->fw_rev, FIRMWARE_STRING_LENGTH, fix_swapped_id);
   
   // and search to see if they match values in the table
-  if ((i = lookupdrive(model, firmware)) < 0) {
+  const drive_settings * dbentry = lookup_drive(model, firmware);
+  if (!dbentry) {
     // no matches found
     pout("No presets are defined for this drive.  Its identity strings:\n"
          "MODEL:    %s\n"
@@ -1458,67 +1641,326 @@ void showpresets(const struct ata_identify_device *drive){
        "%-*s %s\n"
        "match smartmontools Drive Database entry:\n",
        TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmware);
-  showonepreset(&knowndrives[i]);
-  return;
+  showonepreset(dbentry);
 }
 
 // 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;
-  
+// already been set in opts will not be changed.  Returns false if drive
+// not recognized.
+bool apply_presets(const ata_identify_device *drive, unsigned char * opts,
+                   unsigned char & fix_firmwarebug, bool fix_swapped_id)
+{
   // get the drive's model/firmware strings
-  format_ata_string(model, (char *)drive->model, MODEL_STRING_LENGTH);
-  format_ata_string(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH);
+  char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
+  format_ata_string(model, drive->model, MODEL_STRING_LENGTH, fix_swapped_id);
+  format_ata_string(firmware, drive->fw_rev, FIRMWARE_STRING_LENGTH, fix_swapped_id);
   
   // 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++;
+  const drive_settings * dbentry = lookup_drive(model, firmware);
+  if (!dbentry)
+    return false;
+
+  if (*dbentry->presets) {
+    // Apply presets
+    if (!parse_presets(dbentry->presets, opts, fix_firmwarebug))
+      pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
+  }
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Parser for drive database files
+
+// Abstract pointer to read file input.
+// Operations supported: c = *p; c = p[1]; ++p;
+class stdin_iterator
+{
+public:
+  explicit stdin_iterator(FILE * f)
+    : m_f(f) { get(); get(); }
+
+  stdin_iterator & operator++()
+    { get(); return *this; }
+
+  char operator*() const
+    { return m_c; }
+
+  char operator[](int i) const
+    {
+      if (i != 1)
+        fail();
+      return m_next;
+    }
+
+private:
+  FILE * m_f;
+  char m_c, m_next;
+  void get();
+  void fail() const;
+};
+
+void stdin_iterator::get()
+{
+  m_c = m_next;
+  int ch = getc(m_f);
+  m_next = (ch != EOF ? ch : 0);
+}
+
+void stdin_iterator::fail() const
+{
+  throw std::runtime_error("stdin_iterator: wrong usage");
+}
+
+
+// Use above as parser input 'pointer'. Can easily be changed later
+// to e.g. 'const char *' if above is too slow.
+typedef stdin_iterator parse_ptr;
+
+// Skip whitespace and comments.
+static parse_ptr skip_white(parse_ptr src, const char * path, int & line)
+{
+  for ( ; ; ++src) switch (*src) {
+    case ' ': case '\t':
+      continue;
+
+    case '\n':
+      ++line;
+      continue;
+
+    case '/':
+      switch (src[1]) {
+        case '/':
+          // skip '// comment'
+          ++src; ++src;
+          while (*src && *src != '\n')
+            ++src;
+          if (*src)
+            ++line;
+          break;
+        case '*':
+          // skip '/* comment */'
+          ++src; ++src;
+          for (;;) {
+            if (!*src) {
+              pout("%s(%d): Missing '*/'\n", path, line);
+              return src;
+            }
+            char c = *src; ++src;
+            if (c == '\n')
+              ++line;
+            else if (c == '*' && *src == '/')
+              break;
+          }
+          break;
+        default:
+          return src;
       }
+      continue;
+
+    default:
+      return src;
+  }
+}
+
+// Info about a token.
+struct token_info
+{
+  char type;
+  int line;
+  std::string value;
+
+  token_info() : type(0), line(0) { }
+};
+
+// Get next token.
+static parse_ptr get_token(parse_ptr src, token_info & token, const char * path, int & line)
+{
+  src = skip_white(src, path, line);
+  switch (*src) {
+    case '{': case '}': case ',':
+      // Simple token
+      token.type = *src; token.line = line;
+      ++src;
+      break;
+
+    case '"':
+      // String constant
+      token.type = '"'; token.line = line;
+      token.value = "";
+      do {
+        for (++src; *src != '"'; ++src) {
+          char c = *src;
+          if (!c || c == '\n' || (c == '\\' && !src[1])) {
+            pout("%s(%d): Missing terminating '\"'\n", path, line);
+            token.type = '?'; token.line = line;
+            return src;
+          }
+          if (c == '\\') {
+            c = *++src;
+            switch (c) {
+              case 'n' : c = '\n'; break;
+              case '\n': ++line; break;
+              case '\\': case '"': break;
+              default:
+                pout("%s(%d): Unknown escape sequence '\\%c'\n", path, line, c);
+                token.type = '?'; token.line = line;
+                continue;
+            }
+          }
+          token.value += c;
+        }
+        // Lookahead to detect string constant concatentation
+        src = skip_white(++src, path, line);
+      } while (*src == '"');
+      break;
+
+    case 0:
+      // EOF
+      token.type = 0; token.line = line;
+      break;
+
+    default:
+      pout("%s(%d): Syntax error, invalid char '%c'\n", path, line, *src);
+      token.type = '?'; token.line = line;
+      while (*src && *src != '\n')
+        ++src;
+      break;
+  }
+
+  return src;
+}
+
+// Parse drive database from abstract input pointer.
+static bool parse_drive_database(parse_ptr src, drive_database & db, const char * path)
+{
+  int state = 0, field = 0;
+  std::string values[5];
+  bool ok = true;
+
+  token_info token; int line = 1;
+  src = get_token(src, token, path, line);
+  for (;;) {
+    // EOF is ok after '}', trailing ',' is also allowed.
+    if (!token.type && (state == 0 || state == 4))
+      break;
+
+    // Check expected token
+    const char expect[] = "{\",},";
+    if (token.type != expect[state]) {
+      if (token.type != '?')
+        pout("%s(%d): Syntax error, '%c' expected\n", path, token.line, expect[state]);
+      ok = false;
+      // Skip to next entry
+      while (token.type && token.type != '{')
+        src = get_token(src, token, path, line);
+      state = 0;
+      if (token.type)
+        continue;
+      break;
     }
-    
-    // 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);
+
+    // Interpret parser state
+    switch (state) {
+      case 0: // ... ^{...}
+        state = 1; field = 0;
+        break;
+      case 1: // {... ^"..." ...}
+        switch (field) {
+          case 1: case 2:
+            if (!token.value.empty()) {
+              regular_expression regex;
+              if (!regex.compile(token.value.c_str(), REG_EXTENDED)) {
+                pout("%s(%d): Error in regular expression: %s\n", path, token.line, regex.get_errmsg());
+                ok = false;
+              }
+            }
+            else if (field == 1) {
+              pout("%s(%d): Missing regular expression for drive model\n", path, token.line);
+              ok = false;
+            }
+            break;
+          case 4:
+            if (!token.value.empty()) {
+              unsigned char opts[MAX_ATTRIBUTE_NUM] = {0, }; unsigned char fix = 0;
+              if (!parse_presets(token.value.c_str(), opts, fix)) {
+                pout("%s(%d): Syntax error in preset option string\n", path, token.line);
+                ok = false;
+              }
+            }
+            break;
+        }
+        values[field] = token.value;
+        state = (++field < 5 ? 2 : 3);
+        break;
+      case 2: // {... "..."^, ...}
+        state = 1;
+        break;
+      case 3: // {...^}, ...
+        {
+          drive_settings entry;
+          entry.modelfamily    = values[0].c_str();
+          entry.modelregexp    = values[1].c_str();
+          entry.firmwareregexp = values[2].c_str();
+          entry.warningmsg     = values[3].c_str();
+          entry.presets        = values[4].c_str();
+          db.push_back(entry);
+        }
+        state = 4;
+        break;
+      case 4: // {...}^, ...
+        state = 0;
+        break;
+      default:
+        pout("Bad state %d\n", state);
+        return false;
+    }
+    src = get_token(src, token, path, line);
   }
-  
-  // return <0 if drive wasn't recognized, or index>=0 into database
-  // if it was
-  return i;
+  return ok;
+}
+
+// Read drive database from file.
+bool read_drive_database(const char * path)
+{
+  stdio_file f(path, "r");
+  if (!f) {
+    pout("%s: cannot open drive database file\n", path);
+    return false;
+  }
+
+  return parse_drive_database(parse_ptr(f), knowndrives, path);
+}
+
+// Read drive databases from standard places.
+bool read_default_drive_databases()
+{
+#ifndef _WIN32
+  // Read file for local additions: /{,usr/local/}etc/smart_drivedb.h
+  static const char db1[] = SMARTMONTOOLS_SYSCONFDIR"/smart_drivedb.h";
+#else
+  static const char db1[] = "./smart_drivedb.h";
+#endif
+  if (!access(db1, 0)) {
+    if (!read_drive_database(db1))
+      return false;
+  }
+
+#ifdef SMARTMONTOOLS_DRIVEDBDIR
+  // Read file from package: // /usr/{,local/}share/smartmontools/drivedb.h
+  static const char db2[] = SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h";
+  if (!access(db2, 0)) {
+    if (!read_drive_database(db2))
+      return false;
+  }
+  else
+#endif
+  {
+    // Append builtin table.
+    knowndrives.append(builtin_knowndrives,
+      sizeof(builtin_knowndrives)/sizeof(builtin_knowndrives[0]));
+  }
+
+  return true;
 }
index 8c7447e9b6d2d44324f079d421a05ae23d5f1aea..3908e40f918eafbd54e46eef96f586af44456c5c 100644 (file)
@@ -5,6 +5,7 @@
  * Address of support mailing list: smartmontools-support@lists.sourceforge.net
  *
  * Copyright (C) 2003-8 Philip Williams, Bruce Allen
+ * Copyright (C) 2008   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
 #ifndef KNOWNDRIVES_H_
 #define KNOWNDRIVES_H_
 
-#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h,v 1.18 2008/03/04 22:09:47 ballen4705 Exp $\n"
+#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h,v 1.23 2009/04/16 21:24:08 chrfranke Exp $\n"
 
 /* Structure used to store settings for specific drives in knowndrives[]. The
  * elements are used in the following ways:
  *
  *  modelfamily     Informal string about the model family/series of a 
- *                  device. Set to NULL if no info (apart from device id)
+ *                  device. Set to "" if no info (apart from device id)
  *                  known.
  *  modelregexp     POSIX regular expression to match the model of a device.
- *                  This should never be NULL (except to terminate the
- *                  knowndrives array).
+ *                  This should never be "".
  *  firmwareregexp  POSIX regular expression to match a devices's firmware
- *                  version.  This is optional and should be NULL if it is not
- *                  to be used.  If it is non-NULL then it will be used to
+ *                  version.  This is optional and should be "" if it is not
+ *                  to be used.  If it is nonempty then it will be used to
  *                  narrow the set of devices matched by modelregexp.
  *  warningmsg      A message that may be displayed for matching drives.  For
  *                  example, to inform the user that they may need to apply a
  *                  firmware patch.
- *  vendoropts      Pointer to first element of an array of vendor-specific
- *                  option attribute/value pairs that should be set for a
- *                  matching device unless the user has requested otherwise.
- *                  The user's own settings override these.  The array should
- *                  be terminated with the entry {0,0}.
- *  specialpurpose  Pointer to a function that defines some additional action
- *                  that may be taken for matching devices.
- *  functiondesc    A description of the effect of the specialpurpose
- *                  function.  Used by showpresets() and showallpresets() to
- *                  make the output more informative.
+ *  presets         String with vendor-specific attribute ('-v') and firmware
+ *                  bug fix ('-F') options.  Same syntax as in smartctl command
+ *                  line.  The user's own settings override these.
  */
-typedef struct drivesettings_s {
+struct drive_settings {
   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. */
-extern const drivesettings knowndrives[];
+  const char * presets;
+};
 
 // Searches knowndrives[] for a drive with the given model number and firmware
 // string.
-int lookupdrive(const char *model, const char *firmware);
+const drive_settings * lookup_drive(const char * model, const char * firmware);
 
 // Shows the presets (if any) that are available for the given drive.
-void showpresets(const struct ata_identify_device *drive);
+void show_presets(const ata_identify_device * drive, bool fix_swapped_id);
 
 // Shows all presets for drives in knowndrives[].
-// Returns <0 on syntax error in regular expressions.
-int showallpresets(void);
+// Returns #syntax errors.
+int showallpresets();
 
 // Shows all matching presets for a drive in knowndrives[].
 // Returns # matching entries.
@@ -80,9 +68,14 @@ int showmatchingpresets(const char *model, const char *firmware);
 // 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.  Also sets options in
-// con.  Returns <0 if drive not recognized else index of drive in
-// database.
-int applypresets(const struct ata_identify_device *drive, unsigned char **opts,
-                  smartmonctrl *con);
+// con.  Returns false if drive not recognized.
+bool apply_presets(const ata_identify_device * drive, unsigned char * opts,
+                   unsigned char & fix_firmwarebug, bool fix_swapped_id);
+
+// Read drive database from file.
+bool read_drive_database(const char * path);
+
+// Read drive databases from standard places.
+bool read_default_drive_databases();
 
 #endif
diff --git a/megaraid.h b/megaraid.h
new file mode 100644 (file)
index 0000000..fee8ddd
--- /dev/null
@@ -0,0 +1,225 @@
+int megaraid_io_interface(int device, int target, struct scsi_cmnd_io *, int);
+
+#undef u32
+
+#define u8  uint8_t
+#define u16 uint16_t
+#define u32 uint32_t
+#define u64 uint64_t
+
+/*======================================================
+ * PERC2/3/4 Passthrough SCSI Command Interface
+ *
+ * Contents from:
+ *  drivers/scsi/megaraid/megaraid_ioctl.h
+ *  drivers/scsi/megaraid/mbox_defs.h
+ *======================================================*/
+#define MEGAIOC_MAGIC          'm'
+#define MEGAIOCCMD             _IOWR(MEGAIOC_MAGIC, 0, struct uioctl_t)
+
+/* Following subopcode work for opcode == 0x82 */
+#define MKADAP(adapno)   (MEGAIOC_MAGIC << 8 | adapno)
+#define MEGAIOC_QNADAP     'm'
+#define MEGAIOC_QDRVRVER   'e'
+#define MEGAIOC_QADAPINFO  'g'
+
+#define MEGA_MBOXCMD_PASSTHRU 0x03
+
+#define MAX_REQ_SENSE_LEN  0x20
+#define MAX_CDB_LEN 10
+
+typedef struct
+{
+       uint8_t  timeout : 3;
+       uint8_t  ars : 1;
+       uint8_t  reserved : 3;
+       uint8_t  islogical : 1;
+       uint8_t  logdrv;
+       uint8_t  channel;
+       uint8_t  target;
+       uint8_t  queuetag;
+       uint8_t  queueaction;
+       uint8_t  cdb[MAX_CDB_LEN];
+       uint8_t  cdblen;
+       uint8_t  reqsenselen;
+       uint8_t  reqsensearea[MAX_REQ_SENSE_LEN];
+       uint8_t  numsgelements;
+       uint8_t  scsistatus;
+       uint32_t dataxferaddr;
+       uint32_t dataxferlen;
+} __attribute__((packed)) mega_passthru;
+
+typedef struct
+{
+       uint8_t   cmd;
+       uint8_t   cmdid;
+       uint8_t   opcode;
+       uint8_t   subopcode;
+       uint32_t  lba;
+       uint32_t  xferaddr;
+       uint8_t   logdrv;
+       uint8_t   resvd[3];
+       uint8_t   numstatus;
+       uint8_t   status;
+} __attribute__((packed)) megacmd_t;
+
+typedef struct {
+       uint8_t   *pointer;
+#if BITS_PER_LONG == 32
+       uint8_t    pad[4];
+#endif
+} ptr_t;
+
+struct uioctl_t
+{
+       uint32_t       inlen;
+       uint32_t       outlen;
+       union {
+               uint8_t      fca[16];
+               struct {
+                       uint8_t  opcode;
+                       uint8_t  subopcode;
+                       uint16_t adapno;
+                       ptr_t    buffer;
+                       uint32_t length;
+               } __attribute__((packed)) fcs;
+       } __attribute__((packed)) ui;
+
+       megacmd_t     mbox;
+       mega_passthru pthru;
+       ptr_t         data;
+} __attribute__((packed));
+
+/*===================================================
+ * PERC5/6 Passthrough SCSI Command Interface
+ *
+ * Contents from:
+ *  drivers/scsi/megaraid/megaraid_sas.h
+ *===================================================*/
+#define MEGASAS_MAGIC          'M'
+#define MEGASAS_IOC_FIRMWARE   _IOWR(MEGASAS_MAGIC, 1, struct megasas_iocpacket)
+
+#define MFI_CMD_PD_SCSI_IO        0x04
+#define MFI_FRAME_SGL64           0x02
+#define MFI_FRAME_DIR_READ        0x10 
+
+#define MAX_IOCTL_SGE                  16
+
+struct megasas_sge32 {
+
+       u32 phys_addr;
+       u32 length;
+
+} __attribute__ ((packed));
+
+struct megasas_sge64 {
+
+       u64 phys_addr;
+       u32 length;
+
+} __attribute__ ((packed));
+
+union megasas_sgl {
+
+       struct megasas_sge32 sge32[1];
+       struct megasas_sge64 sge64[1];
+
+} __attribute__ ((packed));
+
+struct megasas_header {
+
+       u8 cmd;                 /*00h */
+       u8 sense_len;           /*01h */
+       u8 cmd_status;          /*02h */
+       u8 scsi_status;         /*03h */
+
+       u8 target_id;           /*04h */
+       u8 lun;                 /*05h */
+       u8 cdb_len;             /*06h */
+       u8 sge_count;           /*07h */
+
+       u32 context;            /*08h */
+       u32 pad_0;              /*0Ch */
+
+       u16 flags;              /*10h */
+       u16 timeout;            /*12h */
+       u32 data_xferlen;       /*14h */
+
+} __attribute__ ((packed));
+
+struct megasas_pthru_frame {
+
+       u8 cmd;                 /*00h */
+       u8 sense_len;           /*01h */
+       u8 cmd_status;          /*02h */
+       u8 scsi_status;         /*03h */
+
+       u8 target_id;           /*04h */
+       u8 lun;                 /*05h */
+       u8 cdb_len;             /*06h */
+       u8 sge_count;           /*07h */
+
+       u32 context;            /*08h */
+       u32 pad_0;              /*0Ch */
+
+       u16 flags;              /*10h */
+       u16 timeout;            /*12h */
+       u32 data_xfer_len;      /*14h */
+
+       u32 sense_buf_phys_addr_lo;     /*18h */
+       u32 sense_buf_phys_addr_hi;     /*1Ch */
+
+       u8 cdb[16];             /*20h */
+       union megasas_sgl sgl;  /*30h */
+
+} __attribute__ ((packed));
+
+struct megasas_dcmd_frame {
+
+       u8 cmd;                 /*00h */
+       u8 reserved_0;          /*01h */
+       u8 cmd_status;          /*02h */
+       u8 reserved_1[4];       /*03h */
+       u8 sge_count;           /*07h */
+
+       u32 context;            /*08h */
+       u32 pad_0;              /*0Ch */
+
+       u16 flags;              /*10h */
+       u16 timeout;            /*12h */
+
+       u32 data_xfer_len;      /*14h */
+       u32 opcode;             /*18h */
+
+       union {                 /*1Ch */
+               u8 b[12];
+               u16 s[6];
+               u32 w[3];
+       } mbox;
+
+       union megasas_sgl sgl;  /*28h */
+
+} __attribute__ ((packed));
+
+struct megasas_iocpacket {
+
+       u16 host_no;
+       u16 __pad1;
+       u32 sgl_off;
+       u32 sge_count;
+       u32 sense_off;
+       u32 sense_len;
+       union {
+               u8 raw[128];
+               struct megasas_header hdr;
+       } frame;
+
+       struct iovec sgl[MAX_IOCTL_SGE];
+
+} __attribute__ ((packed));
+
+#undef u8
+#undef u16
+#undef u32
+#undef u64
+
diff --git a/missing b/missing
deleted file mode 100755 (executable)
index 1c8ff70..0000000
--- a/missing
+++ /dev/null
@@ -1,367 +0,0 @@
-#! /bin/sh
-# Common stub for a few missing GNU programs while installing.
-
-scriptversion=2006-05-10.23
-
-# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006
-#   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., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, 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=:
-sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
-sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
-
-# 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'
-  autom4te     touch the output file, or create a stub one
-  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 $?
-    ;;
-
-  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
-    echo "missing $scriptversion (GNU Automake)"
-    exit $?
-    ;;
-
-  -*)
-    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 "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    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 test $# -ne 1; then
-        eval LASTARG="\${$#}"
-       case $LASTARG in
-       *.y)
-           SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
-           if test -f "$SRCFILE"; then
-                cp "$SRCFILE" y.tab.c
-           fi
-           SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
-           if test -f "$SRCFILE"; then
-                cp "$SRCFILE" y.tab.h
-           fi
-         ;;
-       esac
-    fi
-    if test ! -f y.tab.h; then
-       echo >y.tab.h
-    fi
-    if test ! -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 test $# -ne 1; then
-        eval LASTARG="\${$#}"
-       case $LASTARG in
-       *.l)
-           SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
-           if test -f "$SRCFILE"; then
-                cp "$SRCFILE" lex.yy.c
-           fi
-         ;;
-       esac
-    fi
-    if test ! -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 "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    if test -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."
-    # The file to touch is that specified with -o ...
-    file=`echo "$*" | sed -n "$sed_output"`
-    test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
-    if test -z "$file"; then
-      # ... or it is the one specified with @setfilename ...
-      infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
-      file=`sed -n '
-       /^@setfilename/{
-         s/.* \([^ ]*\) *$/\1/
-         p
-         q
-       }' $infile`
-      # ... or it is derived from the source name (dir/f.texi becomes f.info)
-      test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
-    fi
-    # If the file does not exist, the user really needs makeinfo;
-    # let's fail without touching anything.
-    test -f $file || exit 1
-    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:
index 387a2fc53b4a76ce2871dd69a3a4ab9fc617e4cf..61c3425c7de3addd8e62b16c8edfa3a54fd30658 100644 (file)
@@ -44,7 +44,7 @@
 #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.20 2008/03/04 22:09:47 ballen4705 Exp $" \
+const char *os_XXXX_c_cvsid="$Id: os_darwin.cpp,v 1.21 2008/06/12 21:46:31 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.
@@ -435,6 +435,21 @@ int escalade_command_interface(int fd, int escalade_port, int escalade_type,
   return -1;
 }
 
+int areca_command_interface(int fd, int escalade_port, 
+                              smart_command_set command, int select,
+                              char *data)
+{
+  fd = fd;
+  escalade_port = escalade_port;
+  command = command;
+  select = select;
+  data = data;
+  return -1;
+}
+
+
+
+
 int marvell_command_interface(int fd, smart_command_set command,
                      int select, char *data)
 { 
index 338202d042e57d2cf657eae0276724de8a38db71..445238ee78830219677eb70925a03d0362008bf2 100644 (file)
@@ -22,6 +22,8 @@
 #include <err.h>
 #include <camlib.h>
 #include <cam/scsi/scsi_message.h>
+#include <cam/scsi/scsi_pass.h>
+#include <dev/usb/usb.h>
 #if defined(__DragonFly__)
 #include <sys/nata.h>
 #else
 #endif
 #include <sys/stat.h>
 #include <unistd.h>
-#include <fcntl.h>
 #include <glob.h>
-#include <fcntl.h>
 #include <stddef.h>
-
+#include <paths.h>
+#include <sys/utsname.h>
 
 #include "config.h"
 #include "int64.h"
 #include "extern.h"
 #include "os_freebsd.h"
 
-static const char *filenameandversion="$Id: os_freebsd.cpp,v 1.58 2008/03/04 22:09:47 ballen4705 Exp $";
+#include "dev_interface.h"
+#include "dev_ata_cmd_set.h"
+
+#define USBDEV "/dev/usb"
 
-const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp,v 1.58 2008/03/04 22:09:47 ballen4705 Exp $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+#define CONTROLLER_UNKNOWN              0x00
+#define CONTROLLER_ATA                  0x01
+#define CONTROLLER_SCSI                 0x02
+#define CONTROLLER_3WARE                0x03  // set by -d option, but converted to one of three types below
+#define CONTROLLER_3WARE_678K           0x04  // NOT set by guess_device_type()
+#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
+#define CONTROLLER_CCISS               0x10  // CCISS controller 
+#define CONTROLLER_PARSEDEV             0x11  // "smartctl -r ataioctl,2 ..." output parser pseudo-device
+#define CONTROLLER_USBCYPRESS          0x12  // ATA device behind Cypress USB bridge
+#define CONTROLLER_ARECA                0x13  // Areca controller
 
-// to hold onto exit code for atexit routine
-extern int exitstatus;
+static __unused const char *filenameandversion="$Id: os_freebsd.cpp 2879 2009-08-29 17:19:00Z chrfranke $";
+
+const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 2879 2009-08-29 17:19:00Z chrfranke $" \
+ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 extern smartmonctrl * con;
 
@@ -59,130 +77,8 @@ extern smartmonctrl * con;
 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, __unused char* mode) {
-  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_CCISS) {
-    if ((fdchan->device = open(dev,O_RDWR))<0) {
-      int myerror = errno; // preserver across free call
-      free(fdchan);
-      errno=myerror;
-      return -1;
-    }
-  }
+// static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *ch);
 
-  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) {
@@ -198,43 +94,6 @@ static int isnotopen(int *fd, struct freebsd_dev_channel** fdchan) {
   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
@@ -265,865 +124,2102 @@ void printwarning(int msgNo, const char* extra) {
   return;
 }
 
-// Interface to ATA devices.  See os_linux.c
 
-int marvell_command_interface(__unused int fd, __unused smart_command_set command, __unused int select, __unused char *data) {
-  return -1;
-}
+// Interface to ATA devices behind 3ware escalade RAID controller cards.  See os_linux.c
 
-int highpoint_command_interface(__unused int fd, __unused smart_command_set command, __unused int select, __unused char *data) {
-{
-  return -1;
-}
+#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 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=0xc24f<<8;                                                                                                                                         
-    request.u.ata.count=select;                                                                                                                                          
-    request.flags=ATA_CMD_CONTROL;
-    break;
-  case AUTOSAVE:
-    request.u.ata.feature=ATA_SMART_AUTOSAVE;
-    request.u.ata.lba=0xc24f<<8;
-    request.u.ata.count=select;
-    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)
+#ifndef ATA_DEVICE
+#define ATA_DEVICE "/dev/ata"
 #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
-}
+// global variable holding byte count of allocated memory
+long long bytes;
 
 
-// Interface to SCSI devices.  See os_linux.c
-int do_normal_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;
+/*
+ * dev_legacy.cpp
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
 
-        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("]");
-    }
+const char * dev_freebsd_cpp_cvsid = "$Id: os_freebsd.cpp 2879 2009-08-29 17:19:00Z chrfranke $"
+  DEV_INTERFACE_H_CVSID;
 
-  // check that "file descriptor" is valid
-  if (isnotopen(&fd,&con))
-      return -ENOTTY;
+extern smartmonctrl * con; // con->reportscsiioctl
 
+/////////////////////////////////////////////////////////////////////////////
 
-  if (!(cam_dev = cam_open_spec_device(con->devname,con->unitnum,O_RDWR,NULL))) {
-    warnx("%s",cam_errbuf);
-    return -1;
-  }
+#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
+int ata_identify_is_cached(int fd);
+#endif
 
-  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));
+namespace os_freebsd { // No need to publish anything, name provided for Doxygen
 
-  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);
+/////////////////////////////////////////////////////////////////////////////
+/// Implement shared open/close routines with old functions.
 
-  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;
-  }
+class freebsd_smart_device
+: virtual public /*implements*/ smart_device
+{
+public:
+  explicit freebsd_smart_device(const char * mode)
+    : smart_device(never_called),
+      m_fd(-1), m_mode(mode) { }
 
-  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;
-  }
+  virtual ~freebsd_smart_device() throw();
 
-  if (iop->sensep) {
-    memcpy(iop->sensep,&(ccb->csio.sense_data),sizeof(struct scsi_sense_data));
-    iop->resp_sense_len = sizeof(struct scsi_sense_data);
-  }
+  virtual bool is_open() const;
 
-  iop->scsi_status = ccb->csio.scsi_status;
+  virtual bool open();
 
-  cam_freeccb(ccb);
-  
-  if (cam_dev)
-    cam_close_device(cam_dev);
+  virtual bool close();
 
-  if (report > 0) {
-    int trunc;
+protected:
+  /// Return filedesc for derived classes.
+  int get_fd() const
+    { return m_fd; }
 
-    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;
-}
+private:
+  int m_fd; ///< filedesc, -1 if not open.
+  const char * m_mode; ///< Mode string for deviceopen().
+};
 
-/* Check and call the right interface. Maybe when the do_generic_scsi_cmd_io interface is better
-   we can take off this crude way of calling the right interface */
-int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
+
+freebsd_smart_device::~freebsd_smart_device() throw()
 {
-struct freebsd_dev_channel *fdchan;
-     switch(con->controller_type)
-     {
-         case CONTROLLER_CCISS:
-            // check that "file descriptor" is valid
-            if (isnotopen(&dev_fd,&fdchan))
-                 return -ENOTTY;
-#ifdef HAVE_DEV_CISS_CISSIO_H
-             return cciss_io_interface(fdchan->device, con->controller_port-1, iop, report);
-#else
-             {
-                 static int warned = 0;
-                 if (!warned) {
-                     pout("CCISS support is not available in this build of smartmontools,\n"
-                          "/usr/src/sys/dev/ciss/cissio.h was not available at build time.\n\n");
-                     warned = 1;
-                 }
-             }
-             errno = ENOSYS;
-             return -1;
-#endif
-             // not reached
-             break;
-         default:
-             return do_normal_scsi_cmnd_io(dev_fd, iop, report);
-             // not reached
-             break;
-     }
+  if (m_fd >= 0)
+    os_freebsd::freebsd_smart_device::close();
 }
 
-// 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) )
+// migration from the old_style 
+unsigned char m_controller_type;
+unsigned char m_controller_port; 
 
-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;
+// examples for smartctl
+static const char  smartctl_examples[] =
+       "=================================================== SMARTCTL EXAMPLES =====\n\n"
+         "  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"
+         ;
 
-  // 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;
+bool freebsd_smart_device::is_open() const
+{
+  return (m_fd >= 0);
+}
 
-  // 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;
+static int hpt_hba(const char* name) {
+  int i=0;
+  const char *hpt_node[]={"hptmv", "hptmv6", "hptrr", "hptiop", "hptmviop", "hpt32xx", "rr2320",
+                          "rr232x", "rr2310", "rr2310_00", "rr2300", "rr2340", "rr1740", NULL};
+  while (hpt_node[i]) {
+    if (!strncmp(name, hpt_node[i], strlen(hpt_node[i])))
+      return 1;
+    i++;
   }
+  return 0;
+}
 
-  // check that "file descriptor" is valid
-  if (isnotopen(&fd,&con))
-      return -1;
-
-  memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE);
+static int get_tw_channel_unit (const char* name, int* unit, int* dev) {
+  const char *p;
 
-  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;
+  /* 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;
-  }
 
-  ata->opcode = TWE_OP_ATA_PASSTHROUGH;
+  if (dev != NULL)
+    *dev=atoi(name + 3);
 
-  // Same for (almost) all commands - but some reset below
-  ata->request_id    = 0xFF;
-  ata->unit          = disknum;
-  ata->status        = 0;           
-  ata->flags         = 0x1;
-  ata->drive_head    = 0x0;
-  ata->sector_num    = 0;
+  /* no need for unit number */
+  if (unit != NULL)
+    *unit=0;
+  return 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;
+#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;
   
-  // SMART ATA COMMAND REGISTER value
-  ata->command       = ATA_SMART_CMD;
+  bzero(&iocmd, sizeof(struct ata_cmd));
+
+  if ((fd = open(ATA_DEVICE, O_RDWR)) < 0)
+    return -errno;
   
-  // 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++;
+  iocmd.cmd = ATAGMAXCHANNEL;
+  if (ioctl(fd, IOCATA, &iocmd) < 0) {
+    return -errno;
+    close(fd);
   }
-  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;
+  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 = _PATH_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_pass = "pass";
+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 const char * fbsd_dev_cciss = "ciss";
+
+int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *chan) {
+  int len;
+  int dev_prefix_len = strlen(fbsd_dev_prefix);
   
-  // 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;
+  // 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;
   }
-
-  // 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);
+  // 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;
   }
 
-  // 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.
+  // form /dev/pass* or pass*
+  if (!strncmp(fbsd_dev_scsi_pass, dev_name,
+               strlen(fbsd_dev_scsi_pass)))
+    goto handlescsi;
+
+  // 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 (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 (!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 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);
+
+  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;
   }
 
-  // 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;
+  if (hpt_hba(dev_name)) {
+    return CONTROLLER_HPT;
+  }
+
+  // form /dev/ciss*
+  if (!strncmp(fbsd_dev_cciss, dev_name,
+               strlen(fbsd_dev_cciss)))
+    return CONTROLLER_CCISS;
+
+  // 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;
     
-      errno=EIO;
-      return -1;
+    if (cam_get_device(dev_name,chan->devname,DEV_IDLEN,&(chan->unitnum)) == -1)
+      return CONTROLLER_UNKNOWN;
   }
+  return CONTROLLER_SCSI;
   
-  // copy sector count register (one byte!) to return data
-  if (command==CHECK_POWER_MODE)
-    *data=*(char *)&(ata->sector_count);
+}
+
+
+bool freebsd_smart_device::open()
+{
+       
+  const char *dev = get_dev_name();
+  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;
   
-  // look for nonexistent devices/ports
-  if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) {
-    errno=ENODEV;
-    return -1;
+  // If no free entry found, return error.  We have max allowed number
+  // of "file descriptors" already allocated.
+  if (i == FREEBSD_MAXDEV) {
+    errno = EMFILE;
+    return false;
   }
+
+  fdchan = (struct freebsd_dev_channel *)calloc(1,sizeof(struct freebsd_dev_channel));
+  if (fdchan == NULL) {
+    // errno already set by call to malloc()
+    return false;
+  }
+
+  parse_ok = parse_ata_chan_dev(dev,fdchan);
   
-  return 0;
-}
+  if (parse_ok == CONTROLLER_UNKNOWN) {
+    free(fdchan);
+    errno = ENOTTY;
+    return false; // can't handle what we don't know
+  }
 
-static int get_tw_channel_unit (const char* name, int* unit, int* dev) {
-  const char *p;
+  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 false;
+    }
+  }
 
-  /* 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 (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;     // preserve across free call
+      free(fdchan);
+      errno = myerror;
+      return false;
+    }
+  }
 
-  if (dev != NULL)
-    *dev=atoi(name + 3);
+  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;     // preserve across free call
+      free(fdchan);
+      errno = myerror;
+      return false;
+    }
+  }
+
+  if (parse_ok == CONTROLLER_HPT) {
+    if ((fdchan->device = ::open(dev,O_RDWR))<0) {
+      int myerror = errno;     // preserve across free call
+      free(fdchan);
+      errno = myerror;
+      return false;
+    }
+  }
+
+  if (parse_ok == CONTROLLER_CCISS) {
+    if ((fdchan->device = ::open(dev,O_RDWR))<0) {
+      int myerror = errno;     // preserve across free call
+      free(fdchan);
+      errno = myerror;
+      return false;
+    }
+  }
+
+  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;
+  m_fd = i+FREEBSD_FDOFFSET;
+  // endofold
+  if (m_fd < 0) {
+    set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno);
+    return false;
+  }
+  return true;
+}
+
+bool freebsd_smart_device::close()
+{
+  int fd = m_fd; m_fd = -1;
+  struct freebsd_dev_channel *fdchan;
+  int failed = 0;
+
+  // check for valid file descriptor
+  if (isnotopen(&fd, &fdchan))
+    return false;
+  
+
+  // did we allocate a SCSI device name?
+  if (fdchan->devname)
+    free(fdchan->devname);
+  
+  // close device, if open
+  if (fdchan->device)
+    failed=::close(fdchan->device);
+#ifndef IOCATAREQUEST
+  if (fdchan->atacommand)
+    failed=::close(fdchan->atacommand);
+#endif
+  
+  // 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;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement standard ATA support with old functions
+
+class freebsd_ata_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ freebsd_smart_device
+{
+public:
+  freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
+  virtual bool ata_identify_is_cached() const;
+#endif
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+};
+
+freebsd_ata_device::freebsd_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "ata", req_type),
+  freebsd_smart_device("ATA")
+{
+}
+
+int freebsd_ata_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+       int fd=get_fd();
+       #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=0xc24f<<8;                                                                                                                                         
+               request.u.ata.count=select;                                                                                                                                          
+               request.flags=ATA_CMD_CONTROL;
+               break;
+       case AUTOSAVE:
+               request.u.ata.feature=ATA_SMART_AUTOSAVE;
+               request.u.ata.lba=0xc24f<<8;
+               request.u.ata.count=select;
+               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;
+       case CHECK_POWER_MODE:
+               request.u.ata.command=ATA_CHECK_POWER_MODE;
+               request.u.ata.feature=0;
+               request.flags=ATA_CMD_CONTROL;
+               break;
+       case WRITE_LOG:
+               memcpy(buff, data, 512);
+               request.u.ata.feature=ATA_SMART_WRITE_LOG_SECTOR;
+               request.u.ata.lba=select|(0xc24f<<8);
+               request.u.ata.count=1;
+               request.flags=ATA_CMD_WRITE;
+               request.data=(char *)buff;
+               request.count=512;
+               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 (command == CHECK_POWER_MODE) {
+               data[0] = request.u.ata.count & 0xff;
+               return 0;
+       }
+       if (copydata)
+               memcpy(data, buff, 512);
+       
+       return 0;
+       #endif
+}
+
+#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
+bool freebsd_ata_device::ata_identify_is_cached() const
+{
+  return !!::ata_identify_is_cached(get_fd());
+}
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement AMCC/3ware RAID support with old functions
+
+class freebsd_escalade_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ freebsd_smart_device
+{
+public:
+  freebsd_escalade_device(smart_interface * intf, const char * dev_name,
+    int escalade_type, int disknum);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  int m_escalade_type; ///< Type string for escalade_command_interface().
+  int m_disknum; ///< Disk number.
+};
+
+freebsd_escalade_device::freebsd_escalade_device(smart_interface * intf, const char * dev_name,
+    int escalade_type, int disknum)
+: smart_device(intf, dev_name, "3ware", "3ware"),
+  freebsd_smart_device(
+    escalade_type==CONTROLLER_3WARE_9000_CHAR ? "ATA_3WARE_9000" :
+    escalade_type==CONTROLLER_3WARE_678K_CHAR ? "ATA_3WARE_678K" :
+    /*             CONTROLLER_3WARE_678K     */ "ATA"             ),
+  m_escalade_type(escalade_type), m_disknum(disknum)
+{
+  set_info().info_name = strprintf("%s [3ware_disk_%02d]", dev_name, disknum);
+}
+
+int freebsd_escalade_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+    // to hold true file descriptor
+    int fd = get_fd();
+  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 (m_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 (m_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 (m_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", m_escalade_type, m_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          = m_disknum;
+  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 (m_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", m_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, m_disknum);
+    errno=ENOSYS;
+    return -1;
+  }
+
+  // Now send the command down through an ioctl()
+  if (m_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 (m_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;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement Highpoint RAID support with old functions
+
+class freebsd_highpoint_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ freebsd_smart_device
+{
+public:
+  freebsd_highpoint_device(smart_interface * intf, const char * dev_name,
+    unsigned char controller, unsigned char channel, unsigned char port);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  unsigned char m_hpt_data[3]; ///< controller/channel/port
+};
+
+
+freebsd_highpoint_device::freebsd_highpoint_device(smart_interface * intf, const char * dev_name,
+  unsigned char controller, unsigned char channel, unsigned char port)
+: smart_device(intf, dev_name, "hpt", "hpt"),
+  freebsd_smart_device("ATA")
+{
+  m_hpt_data[0] = controller; m_hpt_data[1] = channel; m_hpt_data[2] = port;
+  set_info().info_name = strprintf("%s [hpt_disk_%u/%u/%u]", dev_name, m_hpt_data[0], m_hpt_data[1], m_hpt_data[2]);
+}
+
+int freebsd_highpoint_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+   int fd=get_fd(); 
+  int ids[2];
+  struct freebsd_dev_channel* fbcon;
+  HPT_IOCTL_PARAM param;
+  HPT_CHANNEL_INFO_V2 info;
+  unsigned char* buff[512 + 2 * sizeof(HPT_PASS_THROUGH_HEADER)];
+  PHPT_PASS_THROUGH_HEADER pide_pt_hdr, pide_pt_hdr_out;
+
+  // check that "file descriptor" is valid
+  if (isnotopen(&fd, &fbcon))
+      return -1;
+
+  // get internal deviceid
+  ids[0] = m_hpt_data[0] - 1;
+  ids[1] = m_hpt_data[1] - 1;
+
+  memset(&param, 0, sizeof(HPT_IOCTL_PARAM));
+
+  param.magic = HPT_IOCTL_MAGIC;
+  param.ctrl_code = HPT_IOCTL_GET_CHANNEL_INFO_V2;
+  param.in = (unsigned char *)ids;
+  param.in_size = sizeof(unsigned int) * 2;
+  param.out = (unsigned char *)&info;
+  param.out_size = sizeof(HPT_CHANNEL_INFO_V2);
+
+  if (m_hpt_data[2]==1) {
+    param.ctrl_code = HPT_IOCTL_GET_CHANNEL_INFO;
+    param.out_size = sizeof(HPT_CHANNEL_INFO);
+  }
+  if (ioctl(fbcon->device, HPT_DO_IOCONTROL, &param)!=0 ||
+      info.devices[m_hpt_data[2]-1]==0) {
+    return -1;
+  }
+
+  // perform smart action
+  memset(buff, 0, 512 + 2 * sizeof(HPT_PASS_THROUGH_HEADER));
+  pide_pt_hdr = (PHPT_PASS_THROUGH_HEADER)buff;
+
+  pide_pt_hdr->lbamid = 0x4f;
+  pide_pt_hdr->lbahigh = 0xc2;
+  pide_pt_hdr->command = ATA_SMART_CMD;
+  pide_pt_hdr->id = info.devices[m_hpt_data[2] - 1];
+
+  switch (command){
+  case READ_VALUES:
+    pide_pt_hdr->feature=ATA_SMART_READ_VALUES;
+    pide_pt_hdr->protocol=HPT_READ;
+    break;
+  case READ_THRESHOLDS:
+    pide_pt_hdr->feature=ATA_SMART_READ_THRESHOLDS;
+    pide_pt_hdr->protocol=HPT_READ;
+    break;
+  case READ_LOG:
+    pide_pt_hdr->feature=ATA_SMART_READ_LOG_SECTOR;
+    pide_pt_hdr->lbalow=select;
+    pide_pt_hdr->protocol=HPT_READ;
+    break;
+  case IDENTIFY:
+    pide_pt_hdr->command=ATA_IDENTIFY_DEVICE;
+    pide_pt_hdr->protocol=HPT_READ;
+    break;
+  case ENABLE:
+    pide_pt_hdr->feature=ATA_SMART_ENABLE;
+    break;
+  case DISABLE:
+    pide_pt_hdr->feature=ATA_SMART_DISABLE;
+    break;
+  case AUTO_OFFLINE:
+    pide_pt_hdr->feature=ATA_SMART_AUTO_OFFLINE;
+    pide_pt_hdr->sectorcount=select;
+    break;
+  case AUTOSAVE:
+    pide_pt_hdr->feature=ATA_SMART_AUTOSAVE;
+    pide_pt_hdr->sectorcount=select;
+    break;
+  case IMMEDIATE_OFFLINE:
+    pide_pt_hdr->feature=ATA_SMART_IMMEDIATE_OFFLINE;
+    pide_pt_hdr->lbalow=select;
+    break;
+  case STATUS_CHECK:
+  case STATUS:
+    pide_pt_hdr->feature=ATA_SMART_STATUS;
+    break;
+  case CHECK_POWER_MODE:
+    pide_pt_hdr->command=ATA_CHECK_POWER_MODE;
+    break;
+  case WRITE_LOG:
+    memcpy(buff+sizeof(HPT_PASS_THROUGH_HEADER), data, 512);
+    pide_pt_hdr->feature=ATA_SMART_WRITE_LOG_SECTOR;
+    pide_pt_hdr->lbalow=select;
+    pide_pt_hdr->protocol=HPT_WRITE;
+    break;
+  default:
+    pout("Unrecognized command %d in highpoint_command_interface()\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", command);
+    errno=ENOSYS;
+    return -1;
+  }
+  if (pide_pt_hdr->protocol!=0) {
+    pide_pt_hdr->sectors = 1;
+    pide_pt_hdr->sectorcount = 1;
+  }
+
+  memset(&param, 0, sizeof(HPT_IOCTL_PARAM));
+
+  param.magic = HPT_IOCTL_MAGIC;
+  param.ctrl_code = HPT_IOCTL_IDE_PASS_THROUGH;
+  param.in = (unsigned char *)buff;
+  param.in_size = sizeof(HPT_PASS_THROUGH_HEADER) + (pide_pt_hdr->protocol==HPT_READ ? 0 : pide_pt_hdr->sectors * 512);
+  param.out = (unsigned char *)buff+param.in_size;
+  param.out_size = sizeof(HPT_PASS_THROUGH_HEADER) + (pide_pt_hdr->protocol==HPT_READ ? pide_pt_hdr->sectors * 512 : 0);
+
+  pide_pt_hdr_out = (PHPT_PASS_THROUGH_HEADER)param.out;
+
+  if ((ioctl(fbcon->device, HPT_DO_IOCONTROL, &param)!=0) ||
+      (pide_pt_hdr_out->command & 1)) {
+    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;
+
+    high = pide_pt_hdr_out->lbahigh;
+    low = pide_pt_hdr_out->lbamid;
+
+    // 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)pide_pt_hdr_out->command,
+            (int)pide_pt_hdr_out->feature,
+            (int)pide_pt_hdr_out->sectorcount,
+            (int)pide_pt_hdr_out->lbalow,
+            (int)pide_pt_hdr_out->lbamid,
+            (int)pide_pt_hdr_out->lbahigh,
+            (int)pide_pt_hdr_out->sectors);
+    printwarning(BAD_SMART,buf);
+  }
+  else if (command==CHECK_POWER_MODE)
+    data[0] = pide_pt_hdr_out->sectorcount & 0xff;
+  else if (pide_pt_hdr->protocol==HPT_READ)
+    memcpy(data, (unsigned char *)buff + 2 * sizeof(HPT_PASS_THROUGH_HEADER), pide_pt_hdr->sectors * 512);
+  return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement standard SCSI support with old functions
+
+class freebsd_scsi_device
+: public /*implements*/ scsi_device,
+  public /*extends*/ freebsd_smart_device
+{
+public:
+  freebsd_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+  virtual smart_device * autodetect_open();
+
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+};
+
+freebsd_scsi_device::freebsd_scsi_device(smart_interface * intf,
+  const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "scsi", req_type),
+  freebsd_smart_device("SCSI")
+{
+}
+
+// Interface to SCSI devices.  See os_linux.c
+int do_normal_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 -EIO;
+  }
+
+  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 -EIO;
+  }
+
+  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 -EIO;
+  }
+
+  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;
+}
+
+
+/* Check and call the right interface. Maybe when the do_generic_scsi_cmd_io interface is better
+   we can take off this crude way of calling the right interface */
+int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
+{
+struct freebsd_dev_channel *fdchan;
+     switch(m_controller_type)
+     {
+         case CONTROLLER_CCISS:
+#ifdef HAVE_DEV_CISS_CISSIO_H
+            // check that "file descriptor" is valid
+            if (isnotopen(&dev_fd,&fdchan))
+                return -ENOTTY;
+             return cciss_io_interface(fdchan->device, m_controller_port-1, iop, report);
+#else
+             {
+                 static int warned = 0;
+                 if (!warned) {
+                     pout("CCISS support is not available in this build of smartmontools,\n"
+                          "/usr/src/sys/dev/ciss/cissio.h was not available at build time.\n\n");
+                     warned = 1;
+                 }
+             }
+             return -ENOSYS;
+#endif
+             // not reached
+             break;
+         default:
+             return do_normal_scsi_cmnd_io(dev_fd, iop, report);
+             // not reached
+             break;
+     }
+}
+
+bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+  unsigned char oldtype = m_controller_type, oldport = m_controller_port;
+  m_controller_type = CONTROLLER_SCSI; m_controller_port = 0;
+  int status = do_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl);
+  m_controller_type = oldtype; m_controller_port = oldport;
+  if (status < 0) {
+      set_err(-status);
+      return false;
+  }
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement CCISS RAID support with old functions
+
+class freebsd_cciss_device
+: public /*implements*/ scsi_device,
+  public /*extends*/ freebsd_smart_device
+{
+public:
+  freebsd_cciss_device(smart_interface * intf, const char * name, unsigned char disknum);
+
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+
+private:
+  unsigned char m_disknum; ///< Disk number.
+};
+
+
+freebsd_cciss_device::freebsd_cciss_device(smart_interface * intf,
+  const char * dev_name, unsigned char disknum)
+: smart_device(intf, dev_name, "cciss", "cciss"),
+  freebsd_smart_device("SCSI"),
+  m_disknum(disknum)
+{
+  set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum);
+}
+
+bool freebsd_cciss_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+  // See os_linux.cpp
+  unsigned char oldtype = m_controller_type, oldport = m_controller_port;
+  m_controller_type = CONTROLLER_CCISS; m_controller_port = m_disknum+1;
+  int status = do_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl);
+  m_controller_type = oldtype; m_controller_port = oldport;
+  if (status < 0) {
+      set_err(-status);
+      return false;
+  }
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// SCSI open with autodetection support
+
+smart_device * freebsd_scsi_device::autodetect_open()
+{
+  // Open device
+  if (!open())
+    return this;
+
+  // No Autodetection if device type was specified by user
+  if (*get_req_type())
+    return this;
+
+  // The code below is based on smartd.cpp:SCSIFilterKnown()
+
+  // Get INQUIRY
+  unsigned char req_buff[64] = {0, };
+  int req_len = 36;
+  if (scsiStdInquiry(this, 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(this, req_buff, req_len)) {
+      // device doesn't like INQUIRY commands
+      close();
+      set_err(EIO, "INQUIRY failed");
+      return this;
+    }
+  }
+
+  int avail_len = req_buff[4] + 5;
+  int len = (avail_len < req_len ? avail_len : req_len);
+  if (len < 36)
+      return this;
+
+  // Use INQUIRY to detect type
+  smart_device * newdev = 0;
+  try {
+    // 3ware ?
+    if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) {
+      close();
+#if defined(_WIN32) || defined(__CYGWIN__)
+      set_err(EINVAL, "AMCC/3ware controller, please try changing device to %s,N", get_dev_name());
+#else
+      set_err(EINVAL, "AMCC/3ware controller, please try adding '-d 3ware,N',\n"
+                      "you may need to replace %s with /dev/twaN or /dev/tweN", get_dev_name());
+#endif
+      return this;
+    }
+
+    // SAT or USB ?
+    newdev = smi()->autodetect_sat_device(this, req_buff, len);
+    if (newdev)
+      // NOTE: 'this' is now owned by '*newdev'
+      return newdev;
+  }
+  catch (...) {
+    // Cleanup if exception occurs after newdev was allocated
+    delete newdev;
+    throw;
+  }
+
+  // Nothing special found
+  return this;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Implement platform interface with old functions.
+
+class freebsd_smart_interface
+: public /*implements*/ smart_interface
+{
+public:
+  virtual const char * get_os_version_str();
+
+  virtual const char * get_app_examples(const char * appname);
+
+  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
+    const char * pattern = 0);
+
+protected:
+  virtual ata_device * get_ata_device(const char * name, const char * type);
+
+  virtual scsi_device * get_scsi_device(const char * name, const char * type);
+
+  virtual smart_device * autodetect_smart_device(const char * name);
+
+  virtual smart_device * get_custom_smart_device(const char * name, const char * type);
+
+  virtual const char * get_valid_custom_dev_types_str();
+};
+
+
+//////////////////////////////////////////////////////////////////////
+char sysname[256];
+const char * freebsd_smart_interface::get_os_version_str()
+{
+ struct utsname osname;
+ uname(&osname);
+ snprintf(sysname, sizeof(sysname),"%s %s %s",osname.sysname, osname.release,
+        osname.machine);
+  return sysname;
+}
+
+const char * freebsd_smart_interface::get_app_examples(const char * appname)
+{
+  if (!strcmp(appname, "smartctl"))
+    return smartctl_examples;
+   return 0;
+}
+
+ata_device * freebsd_smart_interface::get_ata_device(const char * name, const char * type)
+{
+  return new freebsd_ata_device(this, name, type);
+}
+
+scsi_device * freebsd_smart_interface::get_scsi_device(const char * name, const char * type)
+{
+  return new freebsd_scsi_device(this, name, type);
+}
 
-  /* no need for unit number */
-  if (unit != NULL)
-    *unit=0;
-  return 0;
+static int 
+cam_getumassno(char * devname) {
+       union ccb ccb;
+       int bufsize, fd;
+       unsigned int i;
+       int error = -1;
+       char devstring[256];
+       
+       if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) {
+               warn("couldn't open %s", XPT_DEVICE);
+               return(1);
+       }
+       bzero(&ccb, sizeof(union ccb));
+
+       ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
+       ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
+       ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
+
+       ccb.ccb_h.func_code = XPT_DEV_MATCH;
+       bufsize = sizeof(struct dev_match_result) * 100;
+       ccb.cdm.match_buf_len = bufsize;
+       ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize);
+       if (ccb.cdm.matches == NULL) {
+               warnx("can't malloc memory for matches");
+               close(fd);
+               return(1);
+       }
+       ccb.cdm.num_matches = 0;
+       /*
+        * We fetch all nodes, since we display most of them in the default
+        * case, and all in the verbose case.
+        */
+       ccb.cdm.num_patterns = 0;
+       ccb.cdm.pattern_buf_len = 0;
+       /*
+        * We do the ioctl multiple times if necessary, in case there are
+        * more than 100 nodes in the EDT.
+        */
+        
+       do {
+               if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
+                       warn("error sending CAMIOCOMMAND ioctl");
+                       error = -1;
+                       break;
+               }
+               if ((ccb.ccb_h.status != CAM_REQ_CMP)
+                || ((ccb.cdm.status != CAM_DEV_MATCH_LAST)
+                   && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
+                       warnx("got CAM error %#x, CDM error %d\n",
+                             ccb.ccb_h.status, ccb.cdm.status);
+                       error = -1;
+                       break;
+               }
+
+               struct bus_match_result *bus_result = 0;
+               for (i = 0; i < ccb.cdm.num_matches; i++) {
+                       switch (ccb.cdm.matches[i].type) {
+                       case DEV_MATCH_BUS: {
+                               // struct bus_match_result *bus_result;
+                               bus_result =
+                                       &ccb.cdm.matches[i].result.bus_result;
+                               break;
+                       }
+                       case DEV_MATCH_DEVICE: {
+                               /* we are not interested in device name */
+                               break;
+                       }
+                       case DEV_MATCH_PERIPH: {
+                               struct periph_match_result *periph_result;
+
+                               periph_result =
+                                     &ccb.cdm.matches[i].result.periph_result;
+
+                               snprintf(devstring,sizeof(devstring),"%s%d",periph_result->periph_name,periph_result->unit_number);
+                               if(strcmp(devstring,devname)==0){ /* found our device */
+                                   if(strcmp(bus_result->dev_name,"umass-sim")) {
+                                       close(fd);
+                                       return -1; /* non usb device found, giving up */
+                                   }
+                                   /* return bus number */
+                                   return  bus_result->unit_number;
+                               }
+                               break;
+                       }
+                       
+                       default:
+                               fprintf(stdout, "WARN: unknown match type\n");
+                               break;
+                       }
+               }
+
+       } while ((ccb.ccb_h.status == CAM_REQ_CMP)
+               && (ccb.cdm.status == CAM_DEV_MATCH_MORE));
+       close(fd);
+       free(ccb.cdm.matches);
+       return(error); /* no device found */
 }
 
 
-#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));
+// we are using CAM subsystem XPT enumerator to found all SCSI devices on system
+// despite of it's names
+//
+// If any errors occur, leave errno set as it was returned by the
+// system call, and return <0.
+//
+// Return values:
+// -1:   error
+// >=0: number of discovered devices
 
-  if ((fd = open("/dev/ata", O_RDWR)) < 0)
-    return -errno;
-  
-  iocmd.cmd = ATAGMAXCHANNEL;
-  if (ioctl(fd, IOCATA, &iocmd) < 0) {
-    return -errno;
-    close(fd);
+int get_dev_names_scsi(char*** names) {
+  int n = 0;
+  char** mp = NULL;
+  unsigned int i;
+  union ccb ccb;
+  int bufsize, fd = -1;
+  int skip_device = 0, skip_bus = 0, changed = 0;
+  char *devname = NULL;
+  int serrno=-1;
+
+  // in case of non-clean exit
+  *names=NULL;
+  ccb.cdm.matches = NULL;
+
+  if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) {
+    if (errno == ENOENT) /* There are no CAM device on this computer */
+      return 0;
+    serrno = errno;
+    pout("%s control device couldn't opened: %s\n", XPT_DEVICE, strerror(errno));
+    n = -1;
+    goto end;
   }
-  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;
+
+  // allocate space for up to MAX_NUM_DEV number of ATA devices
+  mp =  (char **)calloc(MAX_NUM_DEV, sizeof(char*));
+  if (mp == NULL) {
+    serrno=errno;
+    pout("Out of memory constructing scan device list (on line %d)\n", __LINE__);
+    n = -1;
+    goto end;
+  };
+
+  bzero(&ccb, sizeof(union ccb));
+
+  ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
+  ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
+  ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
+
+  ccb.ccb_h.func_code = XPT_DEV_MATCH;
+  bufsize = sizeof(struct dev_match_result) * MAX_NUM_DEV;
+  ccb.cdm.match_buf_len = bufsize;
+  ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize);
+  if (ccb.cdm.matches == NULL) {
+       serrno = errno;
+       pout("can't malloc memory for matches on line %d\n", __LINE__);
+       n = -1;
+       goto end;
+  }
+  ccb.cdm.num_matches = 0;
+
+  ccb.cdm.num_patterns = 0;
+  ccb.cdm.pattern_buf_len = 0;
+
+  /*
+   * We do the ioctl multiple times if necessary, in case there are
+   * more than MAX_NUM_DEV nodes in the EDT.
+   */
+  do {
+    if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
+      serrno = errno;
+      pout("error sending CAMIOCOMMAND ioctl: %s\n", strerror(errno));
+      n = -1;
       break;
     }
-    if (iocmd.u.param.type[1] && !strcmp(name,iocmd.u.param.name[1])) {
-      *dev = 1;
-      break;
+
+    if ((ccb.ccb_h.status != CAM_REQ_CMP)
+     || ((ccb.cdm.status != CAM_DEV_MATCH_LAST)
+        && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
+      pout("got CAM error %#x, CDM error %d\n", ccb.ccb_h.status, ccb.cdm.status);
+      serrno = ENXIO;
+      n = -1;
+      goto end;
     }
+
+    for (i = 0; i < ccb.cdm.num_matches && n < MAX_NUM_DEV; i++) {
+      struct bus_match_result *bus_result;
+      struct device_match_result *dev_result;
+      struct periph_match_result *periph_result;
+
+      if (ccb.cdm.matches[i].type == DEV_MATCH_BUS) {
+        bus_result = &ccb.cdm.matches[i].result.bus_result;
+
+        if (strcmp(bus_result->dev_name,"ata") == 0 /* ATAPICAM devices will be probed as ATA devices, skip'em there */
+         || strcmp(bus_result->dev_name,"xpt") == 0) /* skip XPT bus at all */
+          skip_bus = 1;
+        else
+          skip_bus = 0;
+        changed = 1;
+      } else if (ccb.cdm.matches[i].type == DEV_MATCH_DEVICE) {
+        dev_result = &ccb.cdm.matches[i].result.device_result;
+
+        if (dev_result->flags & DEV_RESULT_UNCONFIGURED || skip_bus == 1)
+          skip_device = 1;
+        else
+          skip_device = 0;
+
+//        /* Shall we skip non T_DIRECT devices ? */
+//        if (dev_result->inq_data.device != T_DIRECT)
+//          skip_device = 1;
+        changed = 1;
+      } else if (ccb.cdm.matches[i].type == DEV_MATCH_PERIPH && skip_device == 0) { 
+        /* One device may be populated as many peripherals (pass0 & da0 for example). 
+         * We are searching for latest name
+        */
+        periph_result =  &ccb.cdm.matches[i].result.periph_result;
+        free(devname);
+        asprintf(&devname, "%s%s%d", _PATH_DEV, periph_result->periph_name, periph_result->unit_number);
+        if (devname == NULL) {
+          serrno=errno;
+          pout("Out of memory constructing scan SCSI device list (on line %d)\n", __LINE__);
+          n = -1;
+          goto end;
+        };
+        changed = 0;
+      };
+      
+      if (changed == 1 && devname != NULL) {
+        mp[n] = devname;
+        devname = NULL;
+        bytes+=1+strlen(mp[n]);
+        n++;
+        changed = 0;
+      };
+    }
+
+  } while ((ccb.ccb_h.status == CAM_REQ_CMP) && (ccb.cdm.status == CAM_DEV_MATCH_MORE) && n < MAX_NUM_DEV);
+
+  if (devname != NULL) {
+    mp[n] = devname;
+    devname = NULL;
+    bytes+=1+strlen(mp[n]);
+    n++;
+  };
+
+  mp = (char **)reallocf(mp,n*(sizeof (char*))); // shrink to correct size
+  bytes += (n)*(sizeof(char*)); // and set allocated byte count
+
+end:
+  free(ccb.cdm.matches);
+  if (fd>-1)
+    close(fd);
+  if (n <= 0) {
+    free(mp);
+    mp = NULL;
   }
-  close(fd);
-  if (*unit == maxunit)
-    return -1;
-  else
-    return 0;
-#endif
+
+  *names=mp;
+
+  if (serrno>-1)
+    errno=serrno;
+  return(n);
 }
-#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 const char * fbsd_dev_cciss = "ciss";
+// we are using ATA subsystem enumerator to found all ATA devices on system
+// despite of it's names
+//
+// If any errors occur, leave errno set as it was returned by the
+// system call, and return <0.
 
-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;
+// Return values:
+// -1:   error
+// >=0: number of discovered devices
+int get_dev_names_ata(char*** names) {
+  struct ata_ioc_devices devices;
+  int fd=-1,maxchannel,serrno=-1,n=0;
+  char **mp = NULL;
+
+  *names=NULL;
+
+  if ((fd = open(ATA_DEVICE, O_RDWR)) < 0) {
+    if (errno == ENOENT) /* There are no ATA device on this computer */
+      return 0;
+    serrno = errno;
+    pout("%s control device can't be opened: %s\n", ATA_DEVICE, strerror(errno));
+    n = -1;
+    goto end;
+  };
   
-  // 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;
+  if (ioctl(fd, IOCATAGMAXCHANNEL, &maxchannel) < 0) {
+    serrno = errno;
+    pout("ioctl(IOCATAGMAXCHANNEL) on /dev/ata failed: %s\n", strerror(errno));
+    n = -1;
+    goto end;
+  };
+
+  // allocate space for up to MAX_NUM_DEV number of ATA devices
+  mp =  (char **)calloc(MAX_NUM_DEV, sizeof(char*));
+  if (mp == NULL) {
+    serrno=errno;
+    pout("Out of memory constructing scan device list (on line %d)\n", __LINE__);
+    n = -1;
+    goto end;
+  };
+
+  for (devices.channel = 0; devices.channel < maxchannel && n < MAX_NUM_DEV; devices.channel++) {
+    int j;
+    
+    if (ioctl(fd, IOCATADEVICES, &devices) < 0) {
+      if (errno == ENXIO)
+        continue; /* such channel not exist */
+      pout("ioctl(IOCATADEVICES) on %s channel %d failed: %s\n", ATA_DEVICE, devices.channel, strerror(errno));
+      n = -1;
+      goto end;
+    };
+    for (j=0;j<=1 && n<MAX_NUM_DEV;j++) {
+      if (devices.name[j][0] != '\0') {
+        asprintf(mp+n, "%s%s", _PATH_DEV, devices.name[j]);
+        if (mp[n] == NULL) {
+          pout("Out of memory constructing scan ATA device list (on line %d)\n", __LINE__);
+          n = -1;
+          goto end;
+        };
+        bytes+=1+strlen(mp[n]);
+        n++;
+      };
+    };
+  };  
+  mp = (char **)reallocf(mp,n*(sizeof (char*))); // shrink to correct size
+  bytes += (n)*(sizeof(char*)); // and set allocated byte count
+      
+end:
+  if (fd>=0)
+    close(fd);
+  if (n <= 0) {
+    free(mp);
+    mp = NULL;
   }
-  
-  // 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;
+  *names=mp;
 
-  // form /dev/nsa* or nsa*
-  if (!strncmp(fbsd_dev_scsi_tape2, dev_name,
-              strlen(fbsd_dev_scsi_tape2)))
-    goto handlescsi;
+  if (serrno>-1)
+    errno=serrno;
+  return n;
+}
 
-  // 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;
+
+
+bool freebsd_smart_interface::scan_smart_devices(smart_device_list & devlist,
+  const char * type, const char * pattern /*= 0*/)
+{
+  if (pattern) {
+    set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
+    return false;
   }
 
-  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;
-      }
+  // Make namelists
+  char * * atanames = 0; int numata = 0;
+  if (!type || !strcmp(type, "ata")) {
+    numata = get_dev_names_ata(&atanames);
+    if (numata < 0) {
+      set_err(ENOMEM);
+      return false;
     }
-    else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) {
-       return CONTROLLER_UNKNOWN;
+  }
+
+  char * * scsinames = 0; int numscsi = 0;
+  if (!type || !strcmp(type, "scsi")) {
+    numscsi = get_dev_names_scsi(&scsinames);
+    if (numscsi < 0) {
+      set_err(ENOMEM);
+      return false;
     }
-    return CONTROLLER_3WARE_678K_CHAR;
   }
-  // form /dev/ciss*
-  if (!strncmp(fbsd_dev_cciss, dev_name,
-               strlen(fbsd_dev_cciss)))
-    return CONTROLLER_CCISS;
 
-  // we failed to recognize any of the forms
-  return CONTROLLER_UNKNOWN;
+  // Add to devlist
+  int i;
+  if (type==NULL)
+    type="";
+  for (i = 0; i < numata; i++) {
+    ata_device * atadev = get_ata_device(atanames[i], type);
+    if (atadev)
+      devlist.add(atadev);
+  }
 
- 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;
+  for (i = 0; i < numscsi; i++) {
+    scsi_device * scsidev = get_scsi_device(scsinames[i], type);
+    if (scsidev)
+      devlist.add(scsidev);
   }
-  return CONTROLLER_SCSI;
-  
+  return true;
 }
 
-int guess_device_type (const char* dev_name) {
-  return parse_ata_chan_dev(dev_name,NULL);
+
+static char done[USB_MAX_DEVICES];
+// static unsigned short vendor_id = 0, product_id = 0, version = 0;
+
+static int usbdevinfo(int f, int a, int rec, int busno, unsigned short & vendor_id,
+                       unsigned short & product_id, unsigned short & version)
+{
+       struct usb_device_info di;
+       int e, p, i;
+       char devname[256];
+
+       snprintf(devname, sizeof(devname),"umass%d",busno);
+       
+       di.udi_addr = a;
+       e = ioctl(f, USB_DEVICEINFO, &di);
+       if (e) {
+               if (errno != ENXIO)
+                       printf("addr %d: I/O error\n", a);
+               return 0;
+       }
+       done[a] = 1;
+       
+               // list devices
+               for (i = 0; i < USB_MAX_DEVNAMES; i++) {
+                       if (di.udi_devnames[i][0]) {
+                               if(strcmp(di.udi_devnames[i],devname)==0) {
+                                // device found!
+                                   vendor_id = di.udi_vendorNo;
+                                   product_id = di.udi_productNo;
+                                   version = di.udi_releaseNo;
+                                   return 1;
+                                   // FIXME
+                               }
+                       }
+               }
+       if (!rec)
+               return 0;
+       for (p = 0; p < di.udi_nports; p++) {
+               int s = di.udi_ports[p];
+               if (s >= USB_MAX_DEVICES) {
+                       continue;
+               }
+               if (s == 0)
+                       printf("addr 0 should never happen!\n");
+               else {
+                       if(usbdevinfo(f, s, 1, busno, vendor_id, product_id, version)) return 1;
+               }
+       }
+       return 0;
 }
 
-// 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];
+static int usbdevlist(int busno,unsigned short & vendor_id,
+                       unsigned short & product_id, unsigned short & version)
+{
+    int  i, f, a, rc;
+    char buf[50];
+    int ncont;
+
+    for (ncont = 0, i = 0; i < 10; i++) {
+       snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
+       f = open(buf, O_RDONLY);
+       if (f >= 0) {
+           memset(done, 0, sizeof done);
+           for (a = 1; a < USB_MAX_DEVICES; a++) {
+               if (!done[a]) {
+                   rc = usbdevinfo(f, a, 1, busno,vendor_id, product_id, version);
+                   if(rc) return 1;
+               }
+               
+           }
+       close(f);
+       } else {
+           if (errno == ENOENT || errno == ENXIO)
+               continue;
+           warn("%s", buf);
+       }
+       ncont++;
+    }
+    return 0;
+}
+
+// Get USB bridge ID for "/dev/daX"
+static bool get_usb_id(const char * path, unsigned short & vendor_id,
+                       unsigned short & product_id, unsigned short & version)
+{
+  // Only "/dev/daX" supported
+  if (!(!strncmp(path, "/dev/da", 7) && !strchr(path + 7, '/')))
+    return false;
+   int bus = cam_getumassno((char *)path+5);
+   
+  if (bus == -1) 
+    return false;
+
+  usbdevlist(bus,vendor_id,
+                       product_id, version);
+
+  return true;
+}
 
-  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);
+smart_device * freebsd_smart_interface::autodetect_smart_device(const char * name)
+{
+  int guess = parse_ata_chan_dev(name,NULL);
+  unsigned short vendor_id = 0, product_id = 0, version = 0;
   
-  // Use glob to look for any directory entries matching the patterns
-  // first call inits with first pattern match, second call appends
-  // to first list. GLOB_NOCHECK 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|GLOB_NOCHECK, 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;
+  switch (guess) {
+  case CONTROLLER_ATA : 
+    return new freebsd_ata_device(this, name, "");
+  case CONTROLLER_SCSI: 
+    // Try to detect possible USB->(S)ATA bridge
+    if (get_usb_id(name, vendor_id, product_id, version)) {
+      const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version);
+      if (!usbtype)
+        return 0;
+      // Return SAT/USB device for this type
+      // (Note: freebsd_scsi_device::autodetect_open() will not be called in this case)
+      return get_sat_device(usbtype, new freebsd_scsi_device(this, name, ""));
     }
-    else if (retglob)
-      pout("Unexplained error in glob(3) of patterns (%s),(%s)\n",
-           pattern1, pattern2);
-    
-    //  Free memory and return
-    globfree(&globbuf);
-
-    return retval;
+    // non usb device, handle as normal scsi
+    return new freebsd_scsi_device(this, name, "");
+  case CONTROLLER_CCISS:
+    // device - cciss, but no ID known
+    set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer");
+    return 0;
   }
-
-  // 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;
+  
+  // TODO: Test autodetect device here
+  return 0;
+}
+
+
+smart_device * freebsd_smart_interface::get_custom_smart_device(const char * name, const char * type)
+{
+  // 3Ware ?
+  int disknum = -1, n1 = -1, n2 = -1;
+  if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    if (n2 != (int)strlen(type)) {
+      set_err(EINVAL, "Option -d 3ware,N requires N to be a non-negative integer");
+      return 0;
+    }
+    if (!(0 <= disknum && disknum <= 127)) {
+      set_err(EINVAL, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127", disknum);
+      return 0;
+    }
+    int contr = parse_ata_chan_dev(name,NULL);
+    if (contr != CONTROLLER_3WARE_9000_CHAR && contr != CONTROLLER_3WARE_678K_CHAR)
+      contr = CONTROLLER_3WARE_678K;
+     return new freebsd_escalade_device(this, name, contr, disknum);
+  } 
+
+  // Highpoint ?
+  int controller = -1, channel = -1; disknum = 1;
+  n1 = n2 = -1; int n3 = -1;
+  if (sscanf(type, "hpt,%n%d/%d%n/%d%n", &n1, &controller, &channel, &n2, &disknum, &n3) >= 2 || n1 == 4) {
+    int len = strlen(type);
+    if (!(n2 == len || n3 == len)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' supports 2-3 items");
+      return 0;
+    }
+    if (!(1 <= controller && controller <= 8)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied");
+      return 0;
+    }
+    if (!(1 <= channel && channel <= 8)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied");
+      return 0;
+    }
+    if (!(1 <= disknum && disknum <= 15)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid pmport number N supplied");
+      return 0;
+    }
+    return new freebsd_highpoint_device(this, name, controller, channel, disknum);
   }
 
-  // now step through the list returned by glob.  No link checking needed
-  // in FreeBSD
-  for (i=0; i<globbuf.gl_pathc; i++){
-    // because of the NO_CHECK in calls 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);
+  // CCISS ?
+  disknum = n1 = n2 = -1;
+  if (sscanf(type, "cciss,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    if (n2 != (int)strlen(type)) {
+      set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer");
+      return 0;
+    }
+    if (!(0 <= disknum && disknum <= 15)) {
+      set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 15", disknum);
+      return 0;
+    }
+    return new freebsd_cciss_device(this, name, disknum);
   }
 
-  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;
+  return 0;
 }
 
-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;
+const char * freebsd_smart_interface::get_valid_custom_dev_types_str()
+{
+  return "marvell, 3ware,N, hpt,L/M/N, cciss,N";
+}
+
+
+} // namespace
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Initialize platform interface and register with smi()
+
+void smart_interface::init()
+{
+  static os_freebsd::freebsd_smart_interface the_interface;
+  smart_interface::set(&the_interface);
 }
index a34dd8481b5b56fb62e975e52ec606ce2ff410bb..6793a43484e112a39b1aa52f61ab96e1e8bb35a1 100644 (file)
 #ifndef OS_FREEBSD_H_
 #define OS_FREEBSD_H_
 
-#define OS_FREEBSD_H_CVSID "$Id: os_freebsd.h,v 1.22 2008/03/04 22:09:47 ballen4705 Exp $\n"
+#define OS_FREEBSD_H_CVSID "$Id: os_freebsd.h,v 1.26 2009/01/14 02:39:00 sxzzsf Exp $\n"
 
 struct freebsd_dev_channel {
   int   channel;                // the ATA channel to work with
   int   device;                 // the device on the channel
+#ifndef IOCATAREQUEST
   int   atacommand;             // the ATA Command file descriptor (/dev/ata)
+#endif
   char* devname;                // the SCSI device name
   int   unitnum;                // the SCSI unit number
-  int   scsicontrol;            // the SCSI control interface
 };
 
 #define FREEBSD_MAXDEV 64
-#define FREEBSD_FDOFFSET 16;
+#define FREEBSD_FDOFFSET 16
 #define MAX_NUM_DEV 26
 
 #ifdef  HAVE_SYS_TWEREG_H
@@ -573,6 +574,56 @@ typedef struct tw_osli_ioctl_with_payload {
 
 #endif
 
+#define HPT_CTL_CODE(x) (x+0xFF00)
+#define HPT_IOCTL_GET_CHANNEL_INFO          HPT_CTL_CODE(3)
+#define HPT_IOCTL_GET_CHANNEL_INFO_V2       HPT_CTL_CODE(53)
+#define HPT_IOCTL_IDE_PASS_THROUGH          HPT_CTL_CODE(24)
+
+#define HPT_READ 1
+#define HPT_WRITE 2
+
+#define HPT_IOCTL_MAGIC   0xA1B2C3D4
+
+#define MAXDEV_PER_CHANNEL 2
+#define PMPORT_PER_CHANNEL 15 /* max devices connected to this channel via pmport */
+
+typedef struct _HPT_CHANNEL_INFO {
+  unsigned int reserve1;
+  unsigned int reserve2;
+  unsigned int devices[MAXDEV_PER_CHANNEL];
+} HPT_CHANNEL_INFO, *PHPT_CHANNEL_INFO;
+
+typedef struct _HPT_CHANNEL_INFO_V2 {
+  unsigned int reserve1;
+  unsigned int reserve2;
+  unsigned int devices[PMPORT_PER_CHANNEL];
+} HPT_CHANNEL_INFO_V2, *PHPT_CHANNEL_INFO_V2;
+
+typedef struct _HPT_IOCTL_PARAM {
+  unsigned int magic;     /* used to check if it's a valid ioctl packet */
+  unsigned int ctrl_code; /* operation control code */
+  void* in;               /* input data buffer */
+  unsigned int in_size;   /* size of input data buffer */
+  void* out;              /* output data buffer */
+  unsigned int out_size;  /* size of output data buffer */
+  void* returned_size;    /* count of chars returned */
+} HPT_IOCTL_PARAM, *PHPT_IOCTL_PARAM;
+#define HPT_DO_IOCONTROL       _IOW('H', 0, HPT_IOCTL_PARAM)
+
+typedef struct _HPT_PASS_THROUGH_HEADER {
+  unsigned int id;          /* disk ID */
+  unsigned char feature;
+  unsigned char sectorcount;
+  unsigned char lbalow;
+  unsigned char lbamid;
+  unsigned char lbahigh;
+  unsigned char driverhead;
+  unsigned char command;
+  unsigned char sectors;    /* data size in sectors, if the command has data transfer */
+  unsigned char protocol;   /* HPT_(READ,WRITE) or zero for non-DATA */
+  unsigned char reserve[3];
+}
+HPT_PASS_THROUGH_HEADER, *PHPT_PASS_THROUGH_HEADER;
 
 #ifndef __unused
 #define __unused __attribute__ ((__unused__))
index a194256e4764e08d6fc0f449e99b692f60cba325..d57e17f81962ed3b1c9505db72add3ca7346d39e 100644 (file)
@@ -5,6 +5,7 @@
  *
  * Copyright (C) YEAR YOUR_NAME <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2003-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008 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
@@ -70,9 +71,7 @@
 
 // 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
@@ -83,9 +82,8 @@
 // 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.26 2008/03/04 22:09:47 ballen4705 Exp $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_GENERIC_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
-
+const char *os_XXXX_c_cvsid="$Id: os_generic.cpp,v 1.28 2009/01/20 00:31:17 dlukes Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID OS_GENERIC_H_CVSID UTILITY_H_CVSID;
 
 // This is here to prevent compiler warnings for unused arguments of
 // functions.
@@ -138,7 +136,7 @@ static void unsupported(){
 // 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(){
+static void print_smartctl_examples(){
   printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
 #ifdef HAVE_GETOPT_LONG
   printf(
@@ -165,115 +163,123 @@ void print_smartctl_examples(){
   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;
+/////////////////////////////////////////////////////////////////////////////
+
+namespace generic { // No need to publish anything, name provided for Doxygen
+
+class generic_smart_interface
+: public /*implements*/ smart_interface
+{
+public:
+#ifdef HAVE_GET_OS_VERSION_STR
+  virtual const char * get_os_version_str();
+#endif
+
+  virtual const char * get_app_examples(const char * appname);
+
+  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
+    const char * pattern = 0);
+
+protected:
+  virtual ata_device * get_ata_device(const char * name, const char * type);
+
+  virtual scsi_device * get_scsi_device(const char * name, const char * type);
+
+  virtual smart_device * autodetect_smart_device(const char * name);
+
+  virtual smart_device * get_custom_smart_device(const char * name, const char * type);
+
+  virtual const char * get_valid_custom_dev_types_str();
+};
+
+
+//////////////////////////////////////////////////////////////////////
+
+#ifdef HAVE_GET_OS_VERSION_STR
+/// Return build host and OS version as static string
+const char * generic_smart_interface::get_os_version_str()
+{
+  return ::get_os_version_str();
 }
+#endif
 
-// 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;
+const char * generic_smart_interface::get_app_examples(const char * appname)
+{
+  if (!strcmp(appname, "smartctl"))
+    ::print_smartctl_examples(); // this prints to stdout ...
+  return 0; // ... so don't print again.
 }
 
-// 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);
+// Return ATA device object for the given device name or NULL
+// the type is always set to "ata"
+ata_device * generic_smart_interface::get_ata_device(const char * name, const char * type)
+{
+  ARGUSED(name);
   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;
+  return NULL;
 }
 
-// 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);
+// Return SCSI device object for the given device name or NULL
+// the type is always set to "scsi"
+scsi_device * generic_smart_interface::get_scsi_device(const char * name, const char * type)
+{
+  ARGUSED(name);
+  ARGUSED(type);
+
   unsupported();
-  return -1;
+  return NULL;
 }
 
-int marvell_command_interface(int fd, smart_command_set command, int select, char *data){
-  ARGUSED(fd);
-  ARGUSED(command);
-  ARGUSED(select);
-  ARGUSED(data);
+
+// Return device object for the given device name (autodetect the device type)
+smart_device * generic_smart_interface::autodetect_smart_device(const char * name)
+{
+  ARGUSED(name);
+
+  // for the given name return the apropriate device type 
   unsupported();
-  return -1;
+  return NULL;
 }
 
 
-int highpoint_command_interface(int fd, smart_command_set command, int select, char *data)
+// Fill devlist with all OS's disk devices of given type that match the pattern
+bool generic_smart_interface::scan_smart_devices(smart_device_list & devlist,
+  const char * type, const char * pattern /*= 0*/)
 {
-  ARGUSED(fd);
-  ARGUSED(command);
-  ARGUSED(select);
-  ARGUSED(data);
+  ARGUSED(devlist);
+  ARGUSED(type);
+  ARGUSED(pattern);
+
   unsupported();
-  return -1;
+  return false;
 }
 
-// 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);
+
+// Return device object of the given type with specified name or NULL
+smart_device * generic_smart_interface::get_custom_smart_device(const char * name, const char * type)
+{
+  ARGUSED(name);
+  ARGUSED(type);
 
   unsupported();
-  return -1;
+  return NULL;
 }
 
-#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;
+const char * generic_smart_interface::get_valid_custom_dev_types_str()
+{
+  return "";
+}
+
+} // namespace
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Initialize platform interface and register with smi()
+
+void smart_interface::init()
+{
+  static generic::generic_smart_interface the_interface;
+  smart_interface::set(&the_interface);
 }
index d3faa02dba795064b7305476247f0cacd92cf305..a2866eb9613f1a68e5b180da637c7fd9bee6c941 100644 (file)
@@ -1,10 +1,14 @@
 /*
- *  os_linux.c
+ *  os_linux.cpp
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
  * Copyright (C) 2003-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2003-8 Doug Gilbert <dougg@torque.net>
+ * Copyright (C) 2008   Hank Wu <hank@areca.com.tw>
+ * Copyright (C) 2008   Oliver Bock <brevilo@users.sourceforge.net>
+ * Copyright (C) 2008-9 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008   Jordan Hargrave <jordan_hargrave@dell.com>
  *
  *  Parts of this file are derived from code that was
  *
@@ -29,8 +33,7 @@
  * 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.
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
  *
  * This code was originally developed as a Senior Thesis by Michael Cornwell
  * at the Concurrent Systems Laboratory (now part of the Storage Systems
 #include <fcntl.h>
 #include <glob.h>
 
+#include <scsi/scsi.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 <sys/file.h>
 #include <unistd.h>
+#include <sys/uio.h>
+#include <sys/types.h>
 #ifndef makedev // old versions of types.h do not include sysmacros.h
 #include <sys/sysmacros.h>
 #endif
+#ifdef WITH_SELINUX
+#include <selinux/selinux.h>
+#endif
 
 #include "int64.h"
 #include "atacmds.h"
 #include "extern.h"
-extern smartmonctrl * con;
 #include "os_linux.h"
 #include "scsicmds.h"
 #include "utility.h"
 #include "extern.h"
 #include "cciss.h"
+#include "megaraid.h"
+
+#include "dev_interface.h"
+#include "dev_ata_cmd_set.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.100 2008/03/04 22:09:47 ballen4705 Exp $";
-
-const char *os_XXXX_c_cvsid="$Id: os_linux.cpp,v 1.100 2008/03/04 22:09:47 ballen4705 Exp $" \
+const char *os_XXXX_c_cvsid="$Id: os_linux.cpp 2879 2009-08-29 17:19:00Z chrfranke $" \
 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;
-
 /* for passing global control variables */
+// (con->reportscsiioctl only)
 extern smartmonctrl *con;
 
-/* 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 !
-  }
+namespace os_linux { // No need to publish anything, name provided for Doxygen
 
-  /* 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);
+/////////////////////////////////////////////////////////////////////////////
+/// Shared open/close routines
 
-  /* 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;
-  }
+class linux_smart_device
+: virtual public /*implements*/ smart_device
+{
+public:
+  explicit linux_smart_device(int flags, int retry_flags = -1)
+    : smart_device(never_called),
+      m_fd(-1),
+      m_flags(flags), m_retry_flags(retry_flags)
+      { }
 
-  /* Now check if nodes are correct */
-  for (index=0; index<16; index++) {
-    sprintf(nodestring, "/dev/%s%d", nodename, index);
+  virtual ~linux_smart_device() throw();
 
-    /* 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;
-      }
-    }
+  virtual bool is_open() const;
 
-    /* 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))) {
+  virtual bool open();
 
-      /* Delete the old node */
-      if (unlink(nodestring)) {
-        pout("problem unlinking stale 3ware device node %s", nodestring);
-        syserror("unlink");
-        return 4;
-      }
+  virtual bool close();
 
-      /* 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;
-}
+protected:
+  /// Return filedesc for derived classes.
+  int get_fd() const
+    { return m_fd; }
 
-static char prev_scsi_dev[128];
-
-// equivalent to open(path, flags)
-int deviceopen(const char *pathname, char *type){
-  int fd;
-
-  if (0 == strcmp(type,"SCSI")) {
-    strncpy(prev_scsi_dev, pathname, sizeof(prev_scsi_dev) - 1);
-    fd = open(pathname, O_RDWR | O_NONBLOCK);
-    if (fd < 0 && errno == EROFS)
-      fd = open(pathname, O_RDONLY | O_NONBLOCK);
-    return fd;
-  } else if (0 == strcmp(type,"ATA")) {
-    // smartd re-opens SCSI devices with "type"==ATA for some reason.
-    // If that was a SCSI generic device (e.g. /dev/sg0) then the
-    // sg driver wants O_RDWR to allow through ATA PASS-THROUGH commands.
-    // The purpose of the next code line is to limit the scope of
-    // this change as a release is pending (and smartd needs a rewrite).
-    if (0 == strncmp(pathname, prev_scsi_dev, sizeof(prev_scsi_dev)))
-      return open(pathname, O_RDWR | O_NONBLOCK);
-    else
-      return open(pathname, O_RDONLY | O_NONBLOCK);
-  } else if (0 == 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 (0 == 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 if(0 == strcmp(type, "CCISS")) {
-    // the device is a cciss smart array device.
-    return open(pathname, O_RDWR | O_NONBLOCK);
-  }
-  else
-    return -1;
+private:
+  int m_fd; ///< filedesc, -1 if not open.
+  int m_flags; ///< Flags for ::open()
+  int m_retry_flags; ///< Flags to retry ::open(), -1 if no retry
+};
 
-}
 
-// equivalent to close(file descriptor)
-int deviceclose(int fd){
-  return close(fd);
+linux_smart_device::~linux_smart_device() throw()
+{
+  if (m_fd >= 0)
+    ::close(m_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;
+bool linux_smart_device::is_open() const
+{
+  return (m_fd >= 0);
 }
 
-
-// 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;
+bool linux_smart_device::open()
+{
+  m_fd = ::open(get_dev_name(), m_flags);
+
+  if (m_fd < 0 && errno == EROFS && m_retry_flags != -1)
+    // Retry
+    m_fd = ::open(get_dev_name(), m_retry_flags);
+
+  if (m_fd < 0) {
+    if (errno == EBUSY && (m_flags & O_EXCL))
+      // device is locked
+      return set_err(EBUSY,
+        "The requested controller is used exclusively by another process!\n"
+        "(e.g. smartctl or smartd)\n"
+        "Please quit the impeding process or try again later...");
+    return set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno);
   }
 
-  // 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);
-        }
-      }
-    }
+  if (m_fd >= 0) {
+    // sets FD_CLOEXEC on the opened device file descriptor.  The
+    // descriptor is otherwise leaked to other applications (mail
+    // sender) which may be considered a security risk and may result
+    // in AVC messages on SELinux-enabled systems.
+    if (-1 == fcntl(m_fd, F_SETFD, FD_CLOEXEC))
+      // TODO: Provide an error printing routine in class smart_interface
+      pout("fcntl(set  FD_CLOEXEC) failed, errno=%d [%s]\n", errno, strerror(errno));
   }
 
-  // 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;
+  return true;
 }
 
-// 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
+// equivalent to close(file descriptor)
+bool linux_smart_device::close()
+{
+  int fd = m_fd; m_fd = -1;
+  if (::close(fd) < 0)
+    return set_err(errno);
+  return true;
+}
 
-  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;
+// examples for smartctl
+static const char  smartctl_examples[] =
+                 "=================================================== SMARTCTL EXAMPLES =====\n\n"
+                 "  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"
+                 "  smartctl --all --device=areca,3 /dev/sg2\n"
+                 "          (Prints all SMART info for 3rd ATA disk on Areca RAID controller)\n"
+  ;
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Linux ATA support
+
+class linux_ata_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ linux_smart_device
+{
+public:
+  linux_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
 
-  // if we found traditional links, we are done
-  if (retval>0)
-    return retval;
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+};
 
-  // else look for devfs entries without traditional links
-  return get_dev_names(devlist,"/dev/discs/disc*", name, maxdev);
+linux_ata_device::linux_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "ata", req_type),
+  linux_smart_device(O_RDONLY | O_NONBLOCK)
+{
 }
 
-
 // PURPOSE
 //   This is an interface routine meant to isolate the OS dependent
 //   parts of the code, and to provide a debugging interface.  Each
@@ -416,7 +248,8 @@ int make_device_names (char*** devlist, const char* name) {
 
 #define BUFFER_LENGTH (4+512)
 
-int ata_command_interface(int device, smart_command_set command, int select, char *data){
+int linux_ata_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
   unsigned char buff[BUFFER_LENGTH];
   // positive: bytes to write to caller.  negative: bytes to READ from
   // caller. zero: non-data command
@@ -537,7 +370,7 @@ int ata_command_interface(int device, smart_command_set command, int select, cha
     // 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=ioctl(get_fd(), 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;
@@ -565,7 +398,7 @@ int ata_command_interface(int device, smart_command_set command, int select, cha
     buff[4]=normal_lo;
     buff[5]=normal_hi;
 
-    if ((retval=ioctl(device, HDIO_DRIVE_TASK, buff))) {
+    if ((retval=ioctl(get_fd(), 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");
@@ -619,13 +452,13 @@ int ata_command_interface(int device, smart_command_set command, int select, cha
     // 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))
+    if (!ioctl(get_fd(), 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)))
+  if ((ioctl(get_fd(), HDIO_DRIVE_CMD, buff)))
     return -1;
 
   // CHECK POWER MODE command returns information in the Sector Count
@@ -715,7 +548,7 @@ static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
         }
         else
             j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
-        pout(buff);
+        pout("%s", buff);
     }
     memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
     io_hdr.interface_id = 'S';
@@ -864,7 +697,7 @@ static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
         }
         else
             j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
-        pout(buff);
+        pout("%s", buff);
     }
     switch (iop->dxfer_dir) {
         case DXFER_NONE:
@@ -984,161 +817,694 @@ static int do_normal_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop,
     }
 }
 
-/* Check and call the right interface. Maybe when the do_generic_scsi_cmd_io interface is better
-   we can take off this crude way of calling the right interface */
- int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
- {
-     switch(con->controller_type)
-     {
-         case CONTROLLER_CCISS:
-#ifdef HAVE_LINUX_CCISS_IOCTL_H
-             return cciss_io_interface(dev_fd, con->controller_port-1, iop, report);
-#else
-             {
-                 static int warned = 0;
-                 if (!warned) {
-                     pout("CCISS support is not available in this build of smartmontools,\n"
-                          "<linux/cciss_ioctl.h> was not available at build time.\n\n");
-                     warned = 1;
-                 }
-             }
-             errno = ENOSYS;
-             return -1;
-#endif
-             // not reached
-             break;
-         default:
-             return do_normal_scsi_cmnd_io(dev_fd, iop, report);
-             // not reached
-             break;
-     }
- }
-
 // >>>>>> End of general SCSI specific linux code
 
-// prototype
-void printwarning(smart_command_set command);
+/////////////////////////////////////////////////////////////////////////////
+/// Standard SCSI support
 
-// 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"
+class linux_scsi_device
+: public /*implements*/ scsi_device,
+  public /*extends*/ linux_smart_device
+{
+public:
+  linux_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
 
+  virtual smart_device * autodetect_open();
 
-/* 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) )
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+};
 
-int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){
+linux_scsi_device::linux_scsi_device(smart_interface * intf,
+  const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "scsi", req_type),
+  linux_smart_device(O_RDWR | O_NONBLOCK, O_RDONLY | O_NONBLOCK)
+{
+}
 
-  // 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];
+bool linux_scsi_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+  int status = do_normal_scsi_cmnd_io(get_fd(), iop, con->reportscsiioctl);
+  if (status < 0)
+      return set_err(-status);
+  return true;
+}
 
-  // only used for SCSI device interface
-  TW_Ioctl   *tw_ioctl=NULL;
-  TW_Output *tw_output=NULL;
+/////////////////////////////////////////////////////////////////////////////
+/// LSI MegaRAID support
 
-  // only used for 6000/7000/8000 char device interface
-  TW_New_Ioctl *tw_ioctl_char=NULL;
+class linux_megaraid_device
+: public /* implements */ scsi_device,
+  public /* extends */ linux_smart_device
+{
+public:
+  linux_megaraid_device(smart_interface *intf, const char *name, 
+    unsigned int bus, unsigned int tgt);
+
+  virtual ~linux_megaraid_device() throw();
+
+  virtual smart_device * autodetect_open();
+
+  virtual bool open();
+  virtual bool close();
+  virtual bool scsi_pass_through(scsi_cmnd_io *iop);
+
+private:
+  unsigned int m_disknum;
+  unsigned int m_busnum;
+  unsigned int m_hba;
+  int m_fd;
+
+  bool (linux_megaraid_device::*pt_cmd)(int cdblen, void *cdb, int dataLen, void *data,
+    int senseLen, void *sense, int report);
+  bool megasas_cmd(int cdbLen, void *cdb, int dataLen, void *data,
+    int senseLen, void *sense, int report);
+  bool megadev_cmd(int cdbLen, void *cdb, int dataLen, void *data,
+    int senseLen, void *sense, int report);
+};
 
-  // only used for 9000 character device interface
-  TW_Ioctl_Buf_Apache *tw_ioctl_apache=NULL;
+linux_megaraid_device::linux_megaraid_device(smart_interface *intf,
+  const char *dev_name, unsigned int bus, unsigned int tgt)
+ : smart_device(intf, dev_name, "megaraid", "megaraid"),
+   linux_smart_device(O_RDWR | O_NONBLOCK),
+   m_disknum(tgt), m_busnum(bus), m_hba(0),
+   m_fd(-1), pt_cmd(0)
+{
+  set_info().info_name = strprintf("%s [megaraid_disk_%02d]", dev_name, m_disknum);
+}
 
-  memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE);
+linux_megaraid_device::~linux_megaraid_device() throw()
+{
+  if (m_fd >= 0)
+    ::close(m_fd);
+}
 
-  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);
+smart_device * linux_megaraid_device::autodetect_open()
+{
+  // Open device
+  if (!open())
+    return this;
+
+  // The code below is based on smartd.cpp:SCSIFilterKnown()
+  if (strcmp(get_req_type(), "megaraid"))
+    return this;
+
+  // Get INQUIRY
+  unsigned char req_buff[64] = {0, };
+  int req_len = 36;
+  if (scsiStdInquiry(this, req_buff, req_len)) {
+      close();
+      set_err(EIO, "INQUIRY failed");
+      return this;
   }
-  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);
+
+  int avail_len = req_buff[4] + 5;
+  int len = (avail_len < req_len ? avail_len : req_len);
+  if (len < 36)
+      return this;
+
+  printf("Got MegaRAID inquiry.. %s\n", req_buff+8);
+
+  // Use INQUIRY to detect type
+  smart_device * newdev = 0;
+  try {
+    // SAT or USB ?
+    newdev = smi()->autodetect_sat_device(this, req_buff, len);
+    if (newdev)
+      // NOTE: 'this' is now owned by '*newdev'
+      return newdev;
   }
-  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;
+  catch (...) {
+    // Cleanup if exception occurs after newdev was allocated
+    delete newdev;
+    throw;
   }
 
-  // Same for (almost) all commands - but some reset below
-  passthru->byte0.opcode  = TW_OP_ATA_PASSTHRU;
-  passthru->request_id    = 0xFF;
-  passthru->unit          = disknum;
-  passthru->status        = 0;
-  passthru->flags         = 0x1;
-  passthru->drive_head    = 0x0;
-  passthru->sector_num    = 0;
+  // Nothing special found
+  return this;
+}
 
-  // 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;
+bool linux_megaraid_device::open()
+{
+  char line[128];
+  int   mjr, n1;
+  FILE *fp;
 
-  // 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;
+  if (!linux_smart_device::open())
+    return false;
+
+  /* Get device HBA */
+  struct sg_scsi_id sgid;
+  if (ioctl(get_fd(), SG_GET_SCSI_ID, &sgid) == 0) {
+    m_hba = sgid.host_no;
+  }
+  else if (ioctl(get_fd(), SCSI_IOCTL_GET_BUS_NUMBER, &m_hba) != 0) {
+    int err = errno;
+    linux_smart_device::close();
+    return set_err(err, "can't get hba");
+  }
+
+  /* Perform mknod of device ioctl node */
+  fp = fopen("/proc/devices", "r");
+  while (fgets(line, sizeof(line), fp) != NULL) {
+       n1=0;
+       if (sscanf(line, "%d megaraid_sas_ioctl%n", &mjr, &n1) == 1 && n1 == 22) {
+          n1=mknod("/dev/megaraid_sas_ioctl_node", S_IFCHR, makedev(mjr, 0));
+          printf("Creating /dev/megaraid_sas_ioctl_node = %d\n", n1 >= 0 ? 0 : errno);
+          if (n1 >= 0 || errno == EEXIST)
+             break;
+       }
+       else if (sscanf(line, "%d megadev%n", &mjr, &n1) == 1 && n1 == 11) {
+          n1=mknod("/dev/megadev0", S_IFCHR, makedev(mjr, 0));
+          printf("Creating /dev/megadev0 = %d\n", n1 >= 0 ? 0 : errno);
+          if (n1 >= 0 || errno == EEXIST)
+             break;
+       }
+  }
+  fclose(fp);
+
+  /* Open Device IOCTL node */
+  if ((m_fd = ::open("/dev/megaraid_sas_ioctl_node", O_RDWR)) >= 0) {
+    pt_cmd = &linux_megaraid_device::megasas_cmd;
+  }
+  else if ((m_fd = ::open("/dev/megadev0", O_RDWR)) >= 0) {
+    pt_cmd = &linux_megaraid_device::megadev_cmd;
+  }
+  else {
+    int err = errno;
+    linux_smart_device::close();
+    return set_err(err, "cannot open /dev/megaraid_sas_ioctl_node or /dev/megadev0");
+  }
+
+  return true;
+}
+
+bool linux_megaraid_device::close()
+{
+  if (m_fd >= 0)
+    ::close(m_fd);
+  m_fd = -1; m_hba = 0; pt_cmd = 0;
+  return linux_smart_device::close();
+}
+
+bool linux_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop)
+{
+  int report = con->reportscsiioctl; 
+
+  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("%s", buff);
+  }
+
+  /* Controller rejects Enable SMART and Test Unit Ready */
+  if (iop->cmnd[0] == 0x00)
+    return true;
+  if (iop->cmnd[0] == 0x85 && iop->cmnd[1] == 0x06) {
+    pout("Rejecting SMART/ATA command to controller\n");
+    return true;
+  }
+
+  if (pt_cmd == NULL)
+    return false;
+  return (this->*pt_cmd)(iop->cmnd_len, iop->cmnd, 
+    iop->dxfer_len, iop->dxferp,
+    iop->max_sense_len, iop->sensep, report);
+}
+
+/* Issue passthrough scsi command to PERC5/6 controllers */
+bool linux_megaraid_device::megasas_cmd(int cdbLen, void *cdb, 
+  int dataLen, void *data,
+  int /*senseLen*/, void * /*sense*/, int /*report*/)
+{
+  struct megasas_pthru_frame   *pthru;
+  struct megasas_iocpacket     uio;
+  int rc;
+
+  memset(&uio, 0, sizeof(uio));
+  pthru = (struct megasas_pthru_frame *)uio.frame.raw;
+  pthru->cmd = MFI_CMD_PD_SCSI_IO;
+  pthru->cmd_status = 0xFF;
+  pthru->scsi_status = 0x0;
+  pthru->target_id = m_disknum;
+  pthru->lun = 0;
+  pthru->cdb_len = cdbLen;
+  pthru->timeout = 0;
+  pthru->flags = MFI_FRAME_DIR_READ;
+  pthru->sge_count = 1;
+  pthru->data_xfer_len = dataLen;
+  pthru->sgl.sge32[0].phys_addr = (intptr_t)data;
+  pthru->sgl.sge32[0].length = (uint32_t)dataLen;
+  memcpy(pthru->cdb, cdb, cdbLen);
+
+  uio.host_no = m_hba;
+  uio.sge_count = 1;
+  uio.sgl_off = offsetof(struct megasas_pthru_frame, sgl);
+  uio.sgl[0].iov_base = data;
+  uio.sgl[0].iov_len = dataLen;
+
+  rc = 0;
+  errno = 0;
+  rc = ioctl(m_fd, MEGASAS_IOC_FIRMWARE, &uio);
+  if (pthru->cmd_status || rc != 0) {
+    if (pthru->cmd_status == 12) {
+      return set_err(EIO, "megasas_cmd: Device %d does not exist\n", m_disknum);
+    }
+    return set_err((errno ? errno : EIO), "megasas_cmd result: %d.%d = %d/%d",
+                   m_hba, m_disknum, errno,
+                   pthru->cmd_status);
+  }
+  return true;
+}
+
+/* Issue passthrough scsi commands to PERC2/3/4 controllers */
+bool linux_megaraid_device::megadev_cmd(int cdbLen, void *cdb, 
+  int dataLen, void *data,
+  int senseLen, void *sense, int /*report*/)
+{
+  struct uioctl_t uio;
+  int rc;
+
+  sense = NULL;
+  senseLen = 0;
+
+  /* Don't issue to the controller */
+  if (m_disknum == 7)
+    return false;
+
+  memset(&uio, 0, sizeof(uio));
+  uio.inlen  = dataLen;
+  uio.outlen = dataLen;
+
+  memset(data, 0, dataLen);
+  uio.ui.fcs.opcode = 0x80;             // M_RD_IOCTL_CMD
+  uio.ui.fcs.adapno = MKADAP(m_hba);
+
+  uio.data.pointer = (uint8_t *)data;
+
+  uio.mbox.cmd = MEGA_MBOXCMD_PASSTHRU;
+  uio.mbox.xferaddr = (intptr_t)&uio.pthru;
+
+  uio.pthru.ars     = 1;
+  uio.pthru.timeout = 2;
+  uio.pthru.channel = 0;
+  uio.pthru.target  = m_disknum;
+  uio.pthru.cdblen  = cdbLen;
+  uio.pthru.reqsenselen  = MAX_REQ_SENSE_LEN;
+  uio.pthru.dataxferaddr = (intptr_t)data;
+  uio.pthru.dataxferlen  = dataLen;
+  memcpy(uio.pthru.cdb, cdb, cdbLen);
+
+  rc=ioctl(m_fd, MEGAIOCCMD, &uio);
+  if (uio.pthru.scsistatus || rc != 0) {
+    return set_err((errno ? errno : EIO), "megadev_cmd result: %d.%d =  %d/%d",
+                   m_hba, m_disknum, errno,
+                   uio.pthru.scsistatus);
+  }
+  return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/// CCISS RAID support
+
+#ifdef HAVE_LINUX_CCISS_IOCTL_H
+
+class linux_cciss_device
+: public /*implements*/ scsi_device,
+  public /*extends*/ linux_smart_device
+{
+public:
+  linux_cciss_device(smart_interface * intf, const char * name, unsigned char disknum);
+
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+
+private:
+  unsigned char m_disknum; ///< Disk number.
+};
+
+linux_cciss_device::linux_cciss_device(smart_interface * intf,
+  const char * dev_name, unsigned char disknum)
+: smart_device(intf, dev_name, "cciss", "cciss"),
+  linux_smart_device(O_RDWR | O_NONBLOCK),
+  m_disknum(disknum)
+{
+  set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum);
+}
+
+bool linux_cciss_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+  int status = cciss_io_interface(get_fd(), m_disknum, iop, con->reportscsiioctl);
+  if (status < 0)
+      return set_err(-status);
+  return true;
+}
+
+#endif // HAVE_LINUX_CCISS_IOCTL_H
+
+/////////////////////////////////////////////////////////////////////////////
+/// AMCC/3ware RAID support
+
+class linux_escalade_device
+: public /*implements*/ ata_device,
+  public /*extends*/ linux_smart_device
+{
+public:
+  enum escalade_type_t {
+    AMCC_3WARE_678K,
+    AMCC_3WARE_678K_CHAR,
+    AMCC_3WARE_9000_CHAR
+  };
+
+  linux_escalade_device(smart_interface * intf, const char * dev_name,
+    escalade_type_t escalade_type, int disknum);
+
+  virtual bool open();
+
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
+
+private:
+  escalade_type_t m_escalade_type; ///< Controller type
+  int m_disknum; ///< Disk number.
+};
+
+linux_escalade_device::linux_escalade_device(smart_interface * intf, const char * dev_name,
+    escalade_type_t escalade_type, int disknum)
+: smart_device(intf, dev_name, "3ware", "3ware"),
+  linux_smart_device(O_RDONLY | O_NONBLOCK),
+  m_escalade_type(escalade_type), m_disknum(disknum)
+{
+  set_info().info_name = strprintf("%s [3ware_disk_%02d]", dev_name, disknum);
+}
+
+/* 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(const char *nodename, const 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;
+  int              retval = 0;
+#ifdef WITH_SELINUX
+  security_context_t orig_context = NULL;
+  security_context_t node_context = NULL;
+  int                selinux_enabled  = is_selinux_enabled();
+  int                selinux_enforced = security_getenforce();
+#endif
+
+
+  /* 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;
+  }
+#ifdef WITH_SELINUX
+  /* Prepare a database of contexts for files in /dev
+   * and save the current context */
+  if (selinux_enabled) {
+    if (matchpathcon_init_prefix(NULL, "/dev") < 0)
+      pout("Error initializing contexts database for /dev");
+    if (getfscreatecon(&orig_context) < 0) {
+      pout("Error retrieving original SELinux fscreate context");
+      if (selinux_enforced)
+        matchpathcon_fini();
+        return 6;
+      }
+  }
+#endif
+  /* Now check if nodes are correct */
+  for (index=0; index<16; index++) {
+    sprintf(nodestring, "/dev/%s%d", nodename, index);
+#ifdef WITH_SELINUX
+    /* Get context of the node and set it as the default */
+    if (selinux_enabled) {
+      if (matchpathcon(nodestring, S_IRUSR | S_IWUSR, &node_context) < 0) {
+        pout("Could not retrieve context for %s", nodestring);
+        if (selinux_enforced) {
+          retval = 6;
+          break;
+        }
+      }
+      if (setfscreatecon(node_context) < 0) {
+        pout ("Error setting default fscreate context");
+        if (selinux_enforced) {
+          retval = 6;
+          break;
+        }
+      }
+    }
+#endif
+    /* Try to stat the node */
+    if ((stat(nodestring, &stat_buf))) {
+      pout("Node %s does not exist and must be created. Check the udev rules.\n", nodestring);
+      /* 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");
+        retval = 3;
+        break;
+      } else {
+#ifdef WITH_SELINUX
+       if (selinux_enabled && node_context) {
+         freecon(node_context);
+         node_context = NULL;
+       }
+#endif
+        continue;
+      }
+    }
+
+    /* 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))) {
+      pout("Node %s has wrong major/minor number and must be created anew."
+          " Check the udev rules.\n", nodestring);
+      /* Delete the old node */
+      if (unlink(nodestring)) {
+        pout("problem unlinking stale 3ware device node %s", nodestring);
+        syserror("unlink");
+        retval = 4;
+        break;
+      }
+
+      /* Make a new node */
+      if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) {
+        pout("problem creating 3ware device nodes %s", nodestring);
+        syserror("mknod");
+        retval = 5;
+        break;
+      }
+    }
+#ifdef WITH_SELINUX
+    if (selinux_enabled && node_context) {
+      freecon(node_context);
+      node_context = NULL;
+    }
+#endif
+  }
+
+#ifdef WITH_SELINUX
+  if (selinux_enabled) {
+    if(setfscreatecon(orig_context) < 0) {
+      pout("Error re-setting original fscreate context");
+      if (selinux_enforced)
+        retval = 6;
+    }
+    if(orig_context)
+      freecon(orig_context);
+    if(node_context)
+      freecon(node_context);
+    matchpathcon_fini();
+  }
+#endif
+  return retval;
+}
+
+bool linux_escalade_device::open()
+{
+  if (m_escalade_type == AMCC_3WARE_9000_CHAR || m_escalade_type == AMCC_3WARE_678K_CHAR) {
+    // the device nodes for these controllers are dynamically assigned,
+    // so we need to check that they exist with the correct major
+    // numbers and if not, create them
+    const char * node   = (m_escalade_type == AMCC_3WARE_9000_CHAR ? "twa"    : "twe"    );
+    const char * driver = (m_escalade_type == AMCC_3WARE_9000_CHAR ? "3w-9xxx": "3w-xxxx");
+    if (setup_3ware_nodes(node, driver))
+      return set_err((errno ? errno : ENXIO), "setup_3ware_nodes(\"%s\", \"%s\") failed", node, driver);
+  }
+  // Continue with default open
+  return linux_smart_device::open();
+}
+
+// TODO: Function no longer useful
+//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) )
+
+bool linux_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+{
+  if (!ata_cmd_is_ok(in,
+    true, // data_out_support
+    false, // TODO: multi_sector_support
+    true) // ata_48bit_support
+  )
+    return false;
+
+  // 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);
+
+  // TODO: Handle controller differences by different classes
+  if (m_escalade_type==AMCC_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 (m_escalade_type==AMCC_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 (m_escalade_type==AMCC_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 {
+    return set_err(ENOSYS,
+      "Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n"
+      "Please contact " PACKAGE_BUGREPORT "\n", (int)m_escalade_type, m_disknum);
+  }
+
+  // Same for (almost) all commands - but some reset below
+  passthru->byte0.opcode  = TW_OP_ATA_PASSTHRU;
+  passthru->request_id    = 0xFF;
+  passthru->unit          = m_disknum;
+  passthru->status        = 0;
+  passthru->flags         = 0x1;
+
+  // Set registers
+  {
+    const ata_in_regs_48bit & r = in.in_regs;
+    passthru->features     = r.features_16;
+    passthru->sector_count = r.sector_count_16;
+    passthru->sector_num   = r.lba_low_16;
+    passthru->cylinder_lo  = r.lba_mid_16;
+    passthru->cylinder_hi  = r.lba_high_16;
+    passthru->drive_head   = r.device;
+    passthru->command      = r.command;
+  }
+
+  // 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
+  bool readdata = false;
+  if (in.direction == ata_cmd_in::data_in) {
+    readdata=true;
+    passthru->byte0.sgloff = 0x5;
+    passthru->size         = 0x7; // TODO: Other value for multi-sector ?
     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)
+    if (m_escalade_type==AMCC_3WARE_9000_CHAR && sizeof(long)==8)
       passthru->size++;
   }
-  else {
+  else if (in.direction == ata_cmd_in::no_data) {
     // Non data command -- but doesn't use large sector
     // count register values.
     passthru->byte0.sgloff = 0x0;
@@ -1146,202 +1512,725 @@ int escalade_command_interface(int fd, int disknum, int escalade_type, smart_com
     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 if (in.direction == ata_cmd_in::data_out) {
+    if (m_escalade_type == AMCC_3WARE_9000_CHAR)
+      memcpy(tw_ioctl_apache->data_buffer, in.buffer, in.size);
+    else if (m_escalade_type == AMCC_3WARE_678K_CHAR)
+      memcpy(tw_ioctl_char->data_buffer,   in.buffer, in.size);
     else {
       // COMMAND NOT SUPPORTED VIA SCSI IOCTL INTERFACE
       // memcpy(tw_output->output_data, data, 512);
-      printwarning(command);
-      errno=ENOTSUP;
-      return -1;
+      // printwarning(command); // TODO: Parameter no longer valid
+      return set_err(ENOTSUP, "DATA OUT not supported for this 3ware controller type");
     }
-    readdata=0;
-    passthru->features     = ATA_SMART_WRITE_LOG_SECTOR;
-    passthru->sector_count = 1;
-    passthru->sector_num   = select;
+    passthru->byte0.sgloff = 0x5;
+    passthru->size         = 0x7;  // TODO: Other value for multi-sector ?
     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;
+    if (m_escalade_type==AMCC_3WARE_9000_CHAR && sizeof(long)==8)
+      passthru->size++;
   }
+  else
+    set_err(EINVAL);
 
   // 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);
+  int ioctlreturn;
+  if (m_escalade_type==AMCC_3WARE_9000_CHAR)
+    ioctlreturn=ioctl(get_fd(), TW_IOCTL_FIRMWARE_PASS_THROUGH, tw_ioctl_apache);
+  else if (m_escalade_type==AMCC_3WARE_678K_CHAR)
+    ioctlreturn=ioctl(get_fd(), TW_CMD_PACKET_WITH_DATA, tw_ioctl_char);
   else
-    ioctlreturn=ioctl(fd, SCSI_IOCTL_SEND_COMMAND, tw_ioctl);
+    ioctlreturn=ioctl(get_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)){
+    if (AMCC_3WARE_678K==m_escalade_type
+        && in.in_regs.command==ATA_SMART_CMD
+        && (   in.in_regs.features == ATA_SMART_AUTO_OFFLINE
+            || in.in_regs.features == ATA_SMART_AUTOSAVE    )
+        && in.in_regs.lba_low) {
       // error here is probably a kernel driver whose version is too old
-      printwarning(command);
-      errno=ENOTSUP;
+      // printwarning(command); // TODO: Parameter no longer valid
+      return set_err(ENOTSUP, "Probably kernel driver too old");
     }
-    if (!errno)
-      errno=EIO;
-    return -1;
+    return set_err(EIO);
+  }
+
+  // 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 (AMCC_3WARE_678K==m_escalade_type) {
+    if (readdata)
+      passthru=NULL;
+    else
+      passthru=(TW_Passthru *)&(tw_output->output_data);
   }
 
-  // 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))) {
+    return set_err(EIO);
+  }
+
+  // If this is a read data command, copy data to output buffer
+  if (readdata) {
+    if (m_escalade_type==AMCC_3WARE_9000_CHAR)
+      memcpy(in.buffer, tw_ioctl_apache->data_buffer, in.size);
+    else if (m_escalade_type==AMCC_3WARE_678K_CHAR)
+      memcpy(in.buffer, tw_ioctl_char->data_buffer, in.size);
+    else
+      memcpy(in.buffer, tw_output->output_data, in.size);
+  }
+
+  // Return register values
+  {
+    ata_out_regs_48bit & r = out.out_regs;
+    r.error           = passthru->features;
+    r.sector_count_16 = passthru->sector_count;
+    r.lba_low_16      = passthru->sector_num;
+    r.lba_mid_16      = passthru->cylinder_lo;
+    r.lba_high_16     = passthru->cylinder_hi;
+    r.device          = passthru->drive_head;
+    r.status          = passthru->command;
+  }
+
+  // look for nonexistent devices/ports
+  if (   in.in_regs.command == ATA_IDENTIFY_DEVICE
+      && !nonempty((unsigned char *)in.buffer, in.size)) {
+    return set_err(ENODEV, "No drive on port %d", m_disknum);
+  }
+
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Areca RAID support
+
+class linux_areca_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ linux_smart_device
+{
+public:
+  linux_areca_device(smart_interface * intf, const char * dev_name, int disknum);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  int m_disknum; ///< Disk number.
+};
+
+
+// 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 Areca "arcmsr" 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
+//   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"
+
+
+/*DeviceType*/
+#define ARECA_SATA_RAID                        0x90000000
+/*FunctionCode*/
+#define FUNCTION_READ_RQBUFFER                 0x0801
+#define FUNCTION_WRITE_WQBUFFER                0x0802
+#define FUNCTION_CLEAR_RQBUFFER                0x0803
+#define FUNCTION_CLEAR_WQBUFFER                0x0804
+
+/* ARECA IO CONTROL CODE*/
+#define ARCMSR_IOCTL_READ_RQBUFFER             (ARECA_SATA_RAID | FUNCTION_READ_RQBUFFER)
+#define ARCMSR_IOCTL_WRITE_WQBUFFER            (ARECA_SATA_RAID | FUNCTION_WRITE_WQBUFFER)
+#define ARCMSR_IOCTL_CLEAR_RQBUFFER            (ARECA_SATA_RAID | FUNCTION_CLEAR_RQBUFFER)
+#define ARCMSR_IOCTL_CLEAR_WQBUFFER            (ARECA_SATA_RAID | FUNCTION_CLEAR_WQBUFFER)
+#define ARECA_SIG_STR                                                  "ARCMSR"
 
-  // 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.
+// The SRB_IO_CONTROL & SRB_BUFFER structures are used to communicate(to/from) to areca driver
+typedef struct _SRB_IO_CONTROL
+{
+       unsigned int HeaderLength;
+       unsigned char Signature[8];
+       unsigned int Timeout;
+       unsigned int ControlCode;
+       unsigned int ReturnCode;
+       unsigned int Length;
+} sSRB_IO_CONTROL;
+
+typedef struct _SRB_BUFFER
+{
+       sSRB_IO_CONTROL srbioctl;
+       unsigned char   ioctldatabuffer[1032]; // the buffer to put the command data to/from firmware
+} sSRB_BUFFER;
+
+// Looks in /proc/scsi to suggest correct areca devices
+// If hint not NULL, return device path guess
+int find_areca_in_proc(char *hint) {
+    const char* proc_format_string="host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\tonline\n";
+
+    // check data formwat
+    FILE *fp=fopen("/proc/scsi/sg/device_hdr", "r");
+    if (!fp) {
+        pout("Unable to open /proc/scsi/sg/device_hdr for reading\n");
+        return 1;
+     }
 
-  if (passthru && (passthru->status || (passthru->command & 0x21))) {
-    errno=EIO;
-    return -1;
-  }
+     // get line, compare to format
+     char linebuf[256];
+     linebuf[255]='\0';
+     char *out = fgets(linebuf, 256, fp);
+     fclose(fp);
+     if (!out) {
+         pout("Unable to read contents of /proc/scsi/sg/device_hdr\n");
+         return 2;
+     }
 
-  // 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);
-  }
+     if (strcmp(linebuf, proc_format_string)) {
+       // wrong format!
+       // Fix this by comparing only tokens not white space!!
+       pout("Unexpected format %s in /proc/scsi/sg/device_hdr\n", proc_format_string);
+       return 3;
+     }
 
-  // For STATUS_CHECK, we need to check register values
-  if (command==STATUS_CHECK) {
+    // Format is understood, now search for correct device
+    fp=fopen("/proc/scsi/sg/devices", "r");
+    if (!fp) return 1;
+    int host, chan, id, lun, type, opens, qdepth, busy, online;
+    int dev=-1;
+    int found=0;
+    // search all lines of /proc/scsi/sg/devices
+    while (9 == fscanf(fp, "%d %d %d %d %d %d %d %d %d", &host, &chan, &id, &lun, &type, &opens, &qdepth, &busy, &online)) {
+        dev++;
+       if (id == 16 && type == 3) {
+          // devices with id=16 and type=3 might be Areca controllers
+          if (!found && hint) {
+              sprintf(hint, "/dev/sg%d", dev);
+          }
+          pout("Device /dev/sg%d appears to be an Areca controller.\n", dev);
+           found++;
+        }
+    }
+    fclose(fp);
+    return 0;
+}
 
-    // 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;
+void dumpdata( unsigned char *block, int len)
+{
+       int ln = (len / 16) + 1;         // total line#
+       unsigned char c;
+       int pos = 0;
+
+       printf(" Address = %p, Length = (0x%x)%d\n", block, len, len);
+       printf("      0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F      ASCII      \n");
+       printf("=====================================================================\n");
+
+       for ( int l = 0; l < ln && len; l++ )
+       {
+               // printf the line# and the HEX data
+               // if a line data length < 16 then append the space to the tail of line to reach 16 chars
+               printf("%02X | ", l);
+               for ( pos = 0; pos < 16 && len; pos++, len-- )
+               {
+                       c = block[l*16+pos];    
+                       printf("%02X ", c);
+               }
+
+               if ( pos < 16 )
+               {
+                       for ( int loop = pos; loop < 16; loop++ )
+                       {
+                               printf("   ");
+                       }
+               }
+
+               // print ASCII char
+               for ( int loop = 0; loop < pos; loop++ )
+               {
+                       c = block[l*16+loop];
+                       if ( c >= 0x20 && c <= 0x7F )
+                       {
+                               printf("%c", c);
+                       }
+                       else
+                       {
+                               printf(".");
+                       }
+               }
+               printf("\n");
+       }   
+       printf("=====================================================================\n");
+}
 
-    // 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);
+int arcmsr_command_handler(int fd, unsigned long arcmsr_cmd, unsigned char *data, int data_len, void *ext_data /* reserved for further use */)
+{
+       ARGUSED(ext_data);
+
+       int ioctlreturn = 0;
+       sSRB_BUFFER sBuf;
+       struct scsi_cmnd_io io_hdr;  
+       int dir = DXFER_TO_DEVICE;
+
+       UINT8 cdb[10];
+       UINT8 sense[32];
+
+       unsigned char *areca_return_packet;
+       int total = 0;
+       int expected = -1;
+       unsigned char return_buff[2048];
+       unsigned char *ptr = &return_buff[0];
+       memset(return_buff, 0, sizeof(return_buff));
+
+       memset((unsigned char *)&sBuf, 0, sizeof(sBuf));
+       memset(&io_hdr, 0, sizeof(io_hdr));
+       memset(cdb, 0, sizeof(cdb));
+       memset(sense, 0, sizeof(sense));
+
+
+       sBuf.srbioctl.HeaderLength = sizeof(sSRB_IO_CONTROL);   
+       memcpy(sBuf.srbioctl.Signature, ARECA_SIG_STR, strlen(ARECA_SIG_STR));
+       sBuf.srbioctl.Timeout = 10000;      
+       sBuf.srbioctl.ControlCode = ARCMSR_IOCTL_READ_RQBUFFER;
+
+       switch ( arcmsr_cmd )
+       {
+       // command for writing data to driver
+       case ARCMSR_IOCTL_WRITE_WQBUFFER:   
+               if ( data && data_len )
+               {
+                       sBuf.srbioctl.Length = data_len;    
+                       memcpy((unsigned char *)sBuf.ioctldatabuffer, (unsigned char *)data, data_len);
+               }
+               // commands for clearing related buffer of driver
+       case ARCMSR_IOCTL_CLEAR_RQBUFFER:
+       case ARCMSR_IOCTL_CLEAR_WQBUFFER:
+               cdb[0] = 0x3B; //SCSI_WRITE_BUF command;
+               break;
+               // command for reading data from driver
+       case ARCMSR_IOCTL_READ_RQBUFFER:    
+               cdb[0] = 0x3C; //SCSI_READ_BUF command;
+               dir = DXFER_FROM_DEVICE;
+               break;
+       default:
+               // unknown arcmsr commands
+               return -1;
+       }
+
+       cdb[1] = 0x01;
+       cdb[2] = 0xf0;    
+       //
+       // cdb[5][6][7][8] areca defined command code( to/from driver )
+       //    
+       cdb[5] = (char)( arcmsr_cmd >> 24);
+       cdb[6] = (char)( arcmsr_cmd >> 16);
+       cdb[7] = (char)( arcmsr_cmd >> 8);
+       cdb[8] = (char)( arcmsr_cmd & 0x0F );
+
+       io_hdr.dxfer_dir = dir;
+       io_hdr.dxfer_len = sizeof(sBuf);
+       io_hdr.dxferp = (unsigned char *)&sBuf;  
+       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;
+
+       while ( 1 )
+       {
+               ioctlreturn = do_normal_scsi_cmnd_io(fd, &io_hdr, 0);
+               if ( ioctlreturn || io_hdr.scsi_status )
+               {
+                       // errors found
+                       break;
+               }
+
+               if ( arcmsr_cmd != ARCMSR_IOCTL_READ_RQBUFFER )
+               {
+                       // if succeeded, just returns the length of outgoing data
+                       return data_len;
+               }
+
+               if ( sBuf.srbioctl.Length )
+               {
+                       //dumpdata(&sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length);
+                       memcpy(ptr, &sBuf.ioctldatabuffer[0], sBuf.srbioctl.Length);
+                       ptr += sBuf.srbioctl.Length;
+                       total += sBuf.srbioctl.Length;
+                       // the returned bytes enough to compute payload length ?
+                       if ( expected < 0 && total >= 5 )
+                       {
+                               areca_return_packet = (unsigned char *)&return_buff[0];
+                               if ( areca_return_packet[0] == 0x5E && 
+                                        areca_return_packet[1] == 0x01 && 
+                                        areca_return_packet[2] == 0x61 )
+                               {
+                                       // valid header, let's compute the returned payload length,
+                                       // we expected the total length is 
+                                       // payload + 3 bytes header + 2 bytes length + 1 byte checksum
+                                       expected = areca_return_packet[4] * 256 + areca_return_packet[3] + 6;
+                               }
+                       }
+
+                       if ( total >= 7 && total >= expected )
+                       {
+                               //printf("total bytes received = %d, expected length = %d\n", total, expected);
+
+                               // ------ Okay! we received enough --------
+                               break;
+                       }
+               }
+       }
+
+       // Deal with the different error cases
+       if ( ioctlreturn )
+       {
+               printf("do_scsi_cmnd_io with write buffer failed code = %x\n", ioctlreturn);
+               return -2;
+       }
+
+
+       if ( io_hdr.scsi_status )
+       {
+               printf("io_hdr.scsi_status with write buffer failed code = %x\n", io_hdr.scsi_status);
+               return -3;
+       }
+
+
+       if ( data )
+       {
+               memcpy(data, return_buff, total);
+       }
+
+       return total;
+}
 
-  // look for nonexistent devices/ports
-  if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) {
-    errno=ENODEV;
-    return -1;
-  }
 
-  return 0;
+linux_areca_device::linux_areca_device(smart_interface * intf, const char * dev_name, int disknum)
+: smart_device(intf, dev_name, "areca", "areca"),
+  linux_smart_device(O_RDWR | O_EXCL | O_NONBLOCK),
+  m_disknum(disknum)
+{
+  set_info().info_name = strprintf("%s [areca_%02d]", dev_name, disknum);
+}
+
+// Areca RAID Controller
+int linux_areca_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
+       // ATA input registers
+       typedef struct _ATA_INPUT_REGISTERS
+       {
+               unsigned char features;
+               unsigned char sector_count;
+               unsigned char sector_number;
+               unsigned char cylinder_low; 
+               unsigned char cylinder_high;    
+               unsigned char device_head;  
+               unsigned char command;      
+               unsigned char reserved[8];
+               unsigned char data[512]; // [in/out] buffer for outgoing/incoming data
+       } sATA_INPUT_REGISTERS;
+
+       // ATA output registers
+       // Note: The output registers is re-sorted for areca internal use only
+       typedef struct _ATA_OUTPUT_REGISTERS
+       {
+               unsigned char error;
+               unsigned char status;
+               unsigned char sector_count;
+               unsigned char sector_number;
+               unsigned char cylinder_low; 
+               unsigned char cylinder_high;
+       }sATA_OUTPUT_REGISTERS;
+
+       // Areca packet format for outgoing:
+       // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
+       // B[3~4] : 2 bytes command length + variant data length, little endian
+       // B[5]   : 1 bytes areca defined command code, ATA passthrough command code is 0x1c
+       // B[6~last-1] : variant bytes payload data
+       // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
+       // 
+       // 
+       //   header 3 bytes  length 2 bytes   cmd 1 byte    payload data x bytes  cs 1 byte 
+       // +--------------------------------------------------------------------------------+
+       // + 0x5E 0x01 0x61 |   0x00 0x00   |     0x1c   | .................... |   0x00    |
+       // +--------------------------------------------------------------------------------+
+       // 
+
+       //Areca packet format for incoming:
+       // B[0~2] : 3 bytes header, fixed value 0x5E, 0x01, 0x61
+       // B[3~4] : 2 bytes payload length, little endian
+       // B[5~last-1] : variant bytes returned payload data
+       // B[last] : 1 byte checksum, simply sum(B[3] ~ B[last -1])
+       // 
+       // 
+       //   header 3 bytes  length 2 bytes   payload data x bytes  cs 1 byte 
+       // +-------------------------------------------------------------------+
+       // + 0x5E 0x01 0x61 |   0x00 0x00   | .................... |   0x00    |
+       // +-------------------------------------------------------------------+
+       unsigned char    areca_packet[640];
+       int areca_packet_len = sizeof(areca_packet);
+       unsigned char cs = 0;   
+
+       sATA_INPUT_REGISTERS *ata_cmd;
+
+       // For debugging
+#if 0
+       memset(sInq, 0, sizeof(sInq));
+       scsiStdInquiry(fd, (unsigned char *)sInq, (int)sizeof(sInq));
+       dumpdata((unsigned char *)sInq, sizeof(sInq));
+#endif
+       memset(areca_packet, 0, areca_packet_len);
+
+       // ----- BEGIN TO SETUP HEADERS -------
+       areca_packet[0] = 0x5E;
+       areca_packet[1] = 0x01;
+       areca_packet[2] = 0x61;
+       areca_packet[3] = (unsigned char)((areca_packet_len - 6) & 0xff);
+       areca_packet[4] = (unsigned char)(((areca_packet_len - 6) >> 8) & 0xff);
+       areca_packet[5] = 0x1c; // areca defined code for ATA passthrough command
+
+
+       // ----- BEGIN TO SETUP PAYLOAD DATA -----
+
+       memcpy(&areca_packet[7], "SmrT", 4);    // areca defined password
+
+       ata_cmd = (sATA_INPUT_REGISTERS *)&areca_packet[12];
+       ata_cmd->cylinder_low    = 0x4F;
+       ata_cmd->cylinder_high   = 0xC2;
+
+
+       if ( command == READ_VALUES     ||
+                command == READ_THRESHOLDS ||
+                command == READ_LOG ||
+                command == IDENTIFY ||
+                command == PIDENTIFY )
+       {
+               // the commands will return data
+               areca_packet[6] = 0x13;
+               ata_cmd->sector_count = 0x1;
+       }
+       else if ( command == WRITE_LOG )
+       {
+               // the commands will write data
+               areca_packet[6] = 0x14;
+       }
+       else
+       {
+               // the commands will return no data
+               areca_packet[6] = 0x15;
+       }
+
+
+       ata_cmd->command = ATA_SMART_CMD;
+       // Now set ATA registers depending upon command
+       switch ( command )
+       {
+       case CHECK_POWER_MODE:  
+               //printf("command = CHECK_POWER_MODE\n");
+               ata_cmd->command = ATA_CHECK_POWER_MODE;        
+               break;
+       case READ_VALUES:
+               //printf("command = READ_VALUES\n");
+               ata_cmd->features = ATA_SMART_READ_VALUES;
+               break;
+       case READ_THRESHOLDS:    
+               //printf("command = READ_THRESHOLDS\n");
+               ata_cmd->features = ATA_SMART_READ_THRESHOLDS;
+               break;
+       case READ_LOG: 
+               //printf("command = READ_LOG\n");
+               ata_cmd->features = ATA_SMART_READ_LOG_SECTOR;
+               ata_cmd->sector_number = select;        
+               break;
+       case WRITE_LOG:        
+               //printf("command = WRITE_LOG\n");    
+               ata_cmd->features = ATA_SMART_WRITE_LOG_SECTOR;
+               memcpy(ata_cmd->data, data, 512);
+               ata_cmd->sector_count = 1;
+               ata_cmd->sector_number = select;
+               break;
+       case IDENTIFY:
+               //printf("command = IDENTIFY\n");   
+               ata_cmd->command = ATA_IDENTIFY_DEVICE;         
+               break;
+       case PIDENTIFY:
+               //printf("command = PIDENTIFY\n");
+               errno=ENODEV;
+               return -1;
+       case ENABLE:
+               //printf("command = ENABLE\n");
+               ata_cmd->features = ATA_SMART_ENABLE;
+               break;
+       case DISABLE:
+               //printf("command = DISABLE\n");
+               ata_cmd->features = ATA_SMART_DISABLE;
+               break;
+       case AUTO_OFFLINE:
+               //printf("command = AUTO_OFFLINE\n");
+               ata_cmd->features = ATA_SMART_AUTO_OFFLINE;
+               // Enable or disable?
+               ata_cmd->sector_count = select;
+               break;
+       case AUTOSAVE:
+               //printf("command = AUTOSAVE\n");
+               ata_cmd->features = ATA_SMART_AUTOSAVE;
+               // Enable or disable?
+               ata_cmd->sector_count = select;
+               break;
+       case IMMEDIATE_OFFLINE:
+               //printf("command = IMMEDIATE_OFFLINE\n");
+               ata_cmd->features = ATA_SMART_IMMEDIATE_OFFLINE;
+               // What test type to run?
+               ata_cmd->sector_number = select;
+               break;
+       case STATUS_CHECK:
+               //printf("command = STATUS_CHECK\n");
+               ata_cmd->features = ATA_SMART_STATUS;           
+               break;
+       case STATUS:
+               //printf("command = STATUS\n");
+               ata_cmd->features = ATA_SMART_STATUS;       
+               break;
+       default:
+               //printf("command = UNKNOWN\n");
+               errno=ENOSYS;
+               return -1;
+       };
+
+       areca_packet[11] = m_disknum - 1;                  // drive number
+
+       // ----- BEGIN TO SETUP CHECKSUM -----
+       for ( int loop = 3; loop < areca_packet_len - 1; loop++ )
+       {
+               cs += areca_packet[loop]; 
+       }
+       areca_packet[areca_packet_len-1] = cs;
+
+       // ----- BEGIN TO SEND TO ARECA DRIVER ------
+       int expected = 0;       
+       unsigned char return_buff[2048];
+       memset(return_buff, 0, sizeof(return_buff));
+
+       expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_RQBUFFER, NULL, 0, NULL);
+        if (expected==-3) {
+           find_areca_in_proc(NULL);
+           return -1;
+       }
+
+       expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_CLEAR_WQBUFFER, NULL, 0, NULL);
+       expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_WRITE_WQBUFFER, areca_packet, areca_packet_len, NULL);
+       if ( expected > 0 )
+       {
+               expected = arcmsr_command_handler(get_fd(), ARCMSR_IOCTL_READ_RQBUFFER, return_buff, sizeof(return_buff), NULL);
+       }
+       if ( expected < 0 )
+       {
+               return -1;
+       }
+
+       // ----- VERIFY THE CHECKSUM -----
+       cs = 0;
+       for ( int loop = 3; loop < expected - 1; loop++ )
+       {
+               cs += return_buff[loop]; 
+       }
+
+       if ( return_buff[expected - 1] != cs )
+       {
+               errno = EIO;
+               return -1;
+       }
+
+       sATA_OUTPUT_REGISTERS *ata_out = (sATA_OUTPUT_REGISTERS *)&return_buff[5] ;
+       if ( ata_out->status )
+       {
+               if ( command == IDENTIFY )
+               {
+                       pout("The firmware of your Areca RAID controller appears to be outdated!\n" \
+                                "Please update your controller to firmware version 1.46 or later.\n" \
+                                "You may download it here: ftp://ftp.areca.com.tw/RaidCards/BIOS_Firmware\n\n");
+               }
+               errno = EIO;
+               return -1;
+       }
+
+       // returns with data
+       if ( command == READ_VALUES     ||
+                command == READ_THRESHOLDS ||
+                command == READ_LOG ||
+                command == IDENTIFY ||
+                command == PIDENTIFY )
+       {
+               memcpy(data, &return_buff[7], 512); 
+       }
+
+       if ( command == CHECK_POWER_MODE )
+       {
+               data[0] = ata_out->sector_count;
+       }
+
+       if ( command == STATUS_CHECK &&
+                ( ata_out->cylinder_low == 0xF4 && ata_out->cylinder_high == 0x2C ) )
+       {
+               return 1;
+       }
+
+       return 0;
 }
 
 
+/////////////////////////////////////////////////////////////////////////////
+/// Marvell support
+
+class linux_marvell_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ linux_smart_device
+{
+public:
+  linux_marvell_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+};
+
+linux_marvell_device::linux_marvell_device(smart_interface * intf,
+  const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "marvell", req_type),
+  linux_smart_device(O_RDONLY | O_NONBLOCK)
+{
+}
 
-int marvell_command_interface(int device,
-                              smart_command_set command,
-                              int select,
-                              char *data) {
+int linux_marvell_device::ata_command_interface(smart_command_set command, int select, char * data)
+{
   typedef struct {
     int  inlen;
     int  outlen;
@@ -1418,13 +2307,13 @@ int marvell_command_interface(int device,
     break;
   default:
     pout("Unrecognized command %d in mvsata_os_specific_handler()\n", command);
-    exit(1);
+    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))
+  if (ioctl(get_fd(), SCSI_IOCTL_SEND_COMMAND, (void *)&smart_command))
       return -1;
 
   if (command==CHECK_POWER_MODE) {
@@ -1466,6 +2355,34 @@ int marvell_command_interface(int device,
   return 0;
 }
 
+
+/////////////////////////////////////////////////////////////////////////////
+/// Highpoint RAID support
+
+class linux_highpoint_device
+: public /*implements*/ ata_device_with_command_set,
+  public /*extends*/ linux_smart_device
+{
+public:
+  linux_highpoint_device(smart_interface * intf, const char * dev_name,
+    unsigned char controller, unsigned char channel, unsigned char port);
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  unsigned char m_hpt_data[3]; ///< controller/channel/port
+};
+
+linux_highpoint_device::linux_highpoint_device(smart_interface * intf, const char * dev_name,
+  unsigned char controller, unsigned char channel, unsigned char port)
+: smart_device(intf, dev_name, "hpt", "hpt"),
+  linux_smart_device(O_RDONLY | O_NONBLOCK)
+{
+  m_hpt_data[0] = controller; m_hpt_data[1] = channel; m_hpt_data[2] = port;
+  set_info().info_name = strprintf("%s [hpt_disk_%u/%u/%u]", dev_name, m_hpt_data[0], m_hpt_data[1], m_hpt_data[2]);
+}
+
 // this implementation is derived from ata_command_interface with a header
 // packing for highpoint linux driver ioctl interface
 //
@@ -1487,10 +2404,7 @@ int marvell_command_interface(int device,
 //
 #define STRANGE_BUFFER_LENGTH (4+512*0xf8)
 
-int highpoint_command_interface(int device, smart_command_set command,
-                                int select, char *data)
-
-
+int linux_highpoint_device::ata_command_interface(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;
@@ -1499,9 +2413,9 @@ int highpoint_command_interface(int device, smart_command_set command,
   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
+  hpt[0] = m_hpt_data[0]; // controller id
+  hpt[1] = m_hpt_data[1]; // channel number
+  hpt[3] = m_hpt_data[2]; // pmport number
 
   buff[0]=ATA_SMART_CMD;
   switch (command){
@@ -1579,9 +2493,9 @@ int highpoint_command_interface(int device, smart_command_set command,
 
     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[0] = m_hpt_data[0]; // controller id
+    hpt[1] = m_hpt_data[1]; // channel number
+    hpt[3] = m_hpt_data[2]; // pmport number
     hpt[2] = HDIO_DRIVE_TASKFILE; // real hd ioctl
 
     taskfile->data           = 0;
@@ -1600,7 +2514,7 @@ int highpoint_command_interface(int device, smart_command_set command,
 
     memcpy(task+sizeof(ide_task_request_t)+4*sizeof(int), data, 512);
 
-    if ((retval=ioctl(device, HPTIO_CTL, task))) {
+    if ((retval=ioctl(get_fd(), HPTIO_CTL, task))) {
       if (retval==-EINVAL)
         pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n");
       return -1;
@@ -1617,7 +2531,7 @@ int highpoint_command_interface(int device, smart_command_set command,
 
     hpt[2] = HDIO_DRIVE_TASK;
 
-    if ((retval=ioctl(device, HPTIO_CTL, hpt_buff))) {
+    if ((retval=ioctl(get_fd(), 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");
@@ -1651,18 +2565,18 @@ int highpoint_command_interface(int device, smart_command_set command,
     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[0] = m_hpt_data[0]; // controller id
+    hpt[1] = m_hpt_data[1]; // channel number
+    hpt[3] = m_hpt_data[2]; // pmport number
 
     hpt[2] = HDIO_GET_IDENTITY;
-    if (!ioctl(device, HPTIO_CTL, deviceid) && (deviceid[4*sizeof(int)] & 0x8000))
+    if (!ioctl(get_fd(), 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)))
+  if ((ioctl(get_fd(), HPTIO_CTL, hpt_buff)))
     return -1;
 
   if (command==CHECK_POWER_MODE)
@@ -1675,6 +2589,7 @@ int highpoint_command_interface(int device, smart_command_set command,
 }
 
 
+#if 0 // TODO: Migrate from 'smart_command_set' to 'ata_in_regs' OR remove the function
 // Utility function for printing warnings
 void printwarning(smart_command_set command){
   static int printed[4]={0,0,0,0};
@@ -1703,6 +2618,313 @@ void printwarning(smart_command_set command){
 
   return;
 }
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// SCSI open with autodetection support
+
+smart_device * linux_scsi_device::autodetect_open()
+{
+  // Open device
+  if (!open())
+    return this;
+
+  // No Autodetection if device type was specified by user
+  if (*get_req_type())
+    return this;
+
+  // The code below is based on smartd.cpp:SCSIFilterKnown()
+
+  // Get INQUIRY
+  unsigned char req_buff[64] = {0, };
+  int req_len = 36;
+  if (scsiStdInquiry(this, 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(this, req_buff, req_len)) {
+      // device doesn't like INQUIRY commands
+      close();
+      set_err(EIO, "INQUIRY failed");
+      return this;
+    }
+  }
+
+  int avail_len = req_buff[4] + 5;
+  int len = (avail_len < req_len ? avail_len : req_len);
+  if (len < 36)
+      return this;
+
+  // Use INQUIRY to detect type
+  smart_device * newdev = 0;
+  try {
+    // 3ware ?
+    if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) {
+      close();
+      set_err(EINVAL, "AMCC/3ware controller, please try adding '-d 3ware,N',\n"
+                      "you may need to replace %s with /dev/twaN or /dev/tweN", get_dev_name());
+      return this;
+    }
+
+    // Marvell ?
+    if (len >= 42 && !memcmp(req_buff + 36, "MVSATA", 6)) {
+      //pout("Device %s: using '-d marvell' for ATA disk with Marvell driver\n", get_dev_name());
+      close();
+      newdev = new linux_marvell_device(smi(), get_dev_name(), get_req_type());
+      newdev->open(); // TODO: Can possibly pass open fd
+      delete this;
+      return newdev;
+    }
+
+    // SAT or USB ?
+    newdev = smi()->autodetect_sat_device(this, req_buff, len);
+    if (newdev)
+      // NOTE: 'this' is now owned by '*newdev'
+      return newdev;
+  }
+  catch (...) {
+    // Cleanup if exception occurs after newdev was allocated
+    delete newdev;
+    throw;
+  }
+
+  // Nothing special found
+  return this;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// USB bridge ID detection
+
+// Read USB ID from /sys file
+static bool read_id(const std::string & path, unsigned short & id)
+{
+  FILE * f = fopen(path.c_str(), "r");
+  if (!f)
+    return false;
+  int n = -1;
+  bool ok = (fscanf(f, "%hx%n", &id, &n) == 1 && n == 4);
+  fclose(f);
+  return ok;
+}
+
+// Get USB bridge ID for "/dev/sdX"
+static bool get_usb_id(const char * path, unsigned short & vendor_id,
+                       unsigned short & product_id, unsigned short & version)
+{
+  // Only "/dev/sdX" supported
+  if (!(!strncmp(path, "/dev/sd", 7) && !strchr(path + 7, '/')))
+    return false;
+
+  // Start search at dir referenced by symlink "/sys/block/sdX/device"
+  // -> "/sys/devices/.../usb*/.../host*/target*/..."
+  std::string dir = strprintf("/sys/block/%s/device", path + 5);
+
+  // Stop search at "/sys/devices"
+  struct stat st;
+  if (stat("/sys/devices", &st))
+    return false;
+  ino_t stop_ino = st.st_ino;
+
+  // Search in parent directories until "idVendor" is found,
+  // fail if "/sys/devices" reached or too many iterations
+  int cnt = 0;
+  do {
+    dir += "/..";
+    if (!(++cnt < 10 && !stat(dir.c_str(), &st) && st.st_ino != stop_ino))
+      return false;
+  } while (access((dir + "/idVendor").c_str(), 0));
+
+  // Read IDs
+  if (!(   read_id(dir + "/idVendor", vendor_id)
+        && read_id(dir + "/idProduct", product_id)
+        && read_id(dir + "/bcdDevice", version)   ))
+    return false;
+
+  if (con->reportscsiioctl > 1)
+    pout("USB ID = 0x%04x:0x%04x (0x%03x)\n", vendor_id, product_id, version);
+  return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+/// Linux interface
+
+class linux_smart_interface
+: public /*implements*/ smart_interface
+{
+public:
+  virtual const char * get_app_examples(const char * appname);
+
+  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
+    const char * pattern = 0);
+
+protected:
+  virtual ata_device * get_ata_device(const char * name, const char * type);
+
+  virtual scsi_device * get_scsi_device(const char * name, const char * type);
+
+  virtual smart_device * autodetect_smart_device(const char * name);
+
+  virtual smart_device * get_custom_smart_device(const char * name, const char * type);
+
+  virtual const char * get_valid_custom_dev_types_str();
+
+private:
+  bool get_dev_list(smart_device_list & devlist, const char * pattern,
+    bool scan_ata, bool scan_scsi, const char * req_type);
+
+  smart_device * missing_option(const char * opt);
+};
+
+const char * linux_smart_interface::get_app_examples(const char * appname)
+{
+  if (!strcmp(appname, "smartctl"))
+    return smartctl_examples;
+  return 0;
+}
+
+
+// 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
+bool linux_smart_interface::get_dev_list(smart_device_list & devlist,
+  const char * pattern, bool scan_ata, bool scan_scsi, const char * req_type)
+{
+  // Use glob to look for any directory entries matching the pattern
+  glob_t globbuf;
+  memset(&globbuf, 0, sizeof(globbuf));
+  int retglob = glob(pattern, GLOB_ERR, NULL, &globbuf);
+  if (retglob) {
+    //  glob failed: free memory and return
+    globfree(&globbuf);
+
+    if (retglob==GLOB_NOMATCH){
+      pout("glob(3) found no matches for pattern %s\n", pattern);
+      return true;
+    }
+
+    if (retglob==GLOB_NOSPACE)
+      set_err(ENOMEM, "glob(3) ran out of memory matching pattern %s", pattern);
+#ifdef GLOB_ABORTED // missing in old versions of glob.h
+    else if (retglob==GLOB_ABORTED)
+      set_err(EINVAL, "glob(3) aborted matching pattern %s", pattern);
+#endif
+    else
+      set_err(EINVAL, "Unexplained error in glob(3) of pattern %s", pattern);
+
+    return false;
+  }
+
+  // did we find too many paths?
+  const int max_pathc = 32;
+  int n = (int)globbuf.gl_pathc;
+  if (n > max_pathc) {
+    pout("glob(3) found %d > MAX=%d devices matching pattern %s: ignoring %d paths\n",
+         n, max_pathc, pattern, n - max_pathc);
+    n = max_pathc;
+  }
+
+  // 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 (int i = 0; i < n; i++){
+    // see if path is a link
+    char linkbuf[1024];
+    int retlink = readlink(globbuf.gl_pathv[i], linkbuf, sizeof(linkbuf)-1);
+
+    char tmpname[1024]={0};
+    const char * name = 0;
+    bool is_scsi = scan_scsi;
+    // if not a link (or a strange link), keep it
+    if (retlink<=0 || retlink>1023)
+      name = globbuf.gl_pathv[i];
+    else {
+      // or if it's a link that points to a disc, follow it
+      linkbuf[retlink] = 0;
+      const char *p;
+      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.
+        name = globbuf.gl_pathv[i];
+      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.
+        bool match_ata  = strstr(linkbuf, "ide");
+        bool match_scsi = strstr(linkbuf, "scsi");
+        if (((match_ata && scan_ata) || (match_scsi && scan_scsi)) && !(match_ata && match_scsi)) {
+          is_scsi = match_scsi;
+          snprintf(tmpname, sizeof(tmpname), "%s/disc", globbuf.gl_pathv[i]);
+          name = tmpname;
+        }
+      }
+    }
+
+    if (name) {
+      // Found a name, add device to list.
+      if (is_scsi)
+        devlist.add(new linux_scsi_device(this, name, req_type));
+      else
+        devlist.add(new linux_ata_device(this, name, req_type));
+    }
+  }
+
+  // free memory
+  globfree(&globbuf);
+
+  return true;
+}
+
+bool linux_smart_interface::scan_smart_devices(smart_device_list & devlist,
+  const char * type, const char * pattern /*= 0*/)
+{
+  if (pattern) {
+    set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
+    return false;
+  }
+
+  if (!type)
+    type = "";
+
+  bool scan_ata  = (!*type || !strcmp(type, "ata" ));
+  bool scan_scsi = (!*type || !strcmp(type, "scsi"));
+  if (!(scan_ata || scan_scsi))
+    return true;
+
+  if (scan_ata)
+    get_dev_list(devlist,"/dev/hd[a-t]", true, false, type);
+  if (scan_scsi)
+    get_dev_list(devlist, "/dev/sd[a-z]", false, true, type);
+
+  // if we found traditional links, we are done
+  if (devlist.size() > 0)
+    return true;
+
+  // else look for devfs entries without traditional links
+  // TODO: Add udev support
+  return get_dev_list(devlist, "/dev/discs/disc*", scan_ata, scan_scsi, type);
+}
+
+ata_device * linux_smart_interface::get_ata_device(const char * name, const char * type)
+{
+  return new linux_ata_device(this, name, type);
+}
+
+scsi_device * linux_smart_interface::get_scsi_device(const char * name, const char * type)
+{
+  return new linux_scsi_device(this, name, type);
+}
+
+smart_device * linux_smart_interface::missing_option(const char * opt)
+{
+  set_err(EINVAL, "requires option '%s'", opt);
+  return 0;
+}
 
 // Guess device type (ata or scsi) based on device name (Linux
 // specific) SCSI device name in linux can be sd, sr, scd, st, nst,
@@ -1718,20 +2940,23 @@ 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";
 static const char * lin_dev_cciss_dir = "cciss/";
+static const char * lin_dev_areca = "sg";
 
-int guess_device_type(const char * dev_name) {
-  int len;
+smart_device * linux_smart_interface::autodetect_smart_device(const char * name)
+{
+  const char * dev_name = name; // TODO: Remove this hack
   int dev_prefix_len = strlen(lin_dev_prefix);
 
   // if dev_name null, or string length zero
+  int len;
   if (!dev_name || !(len = strlen(dev_name)))
-    return CONTROLLER_UNKNOWN;
+    return 0;
 
   // 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;
+      return 0;
     // else advance pointer to following characters
     dev_name += dev_prefix_len;
   }
@@ -1739,71 +2964,183 @@ int guess_device_type(const char * dev_name) {
   // form /dev/h* or h*
   if (!strncmp(lin_dev_ata_disk_plus, dev_name,
                strlen(lin_dev_ata_disk_plus)))
-    return CONTROLLER_ATA;
+    return new linux_ata_device(this, name, "");
 
   // 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;
+    return new linux_ata_device(this, name, "");
 
   // form /dev/s* or s*
   if (!strncmp(lin_dev_scsi_disk_plus, dev_name,
-               strlen(lin_dev_scsi_disk_plus)))
-    return CONTROLLER_SCSI;
+               strlen(lin_dev_scsi_disk_plus))) {
+
+    // Try to detect possible USB->(S)ATA bridge
+    unsigned short vendor_id = 0, product_id = 0, version = 0;
+    if (get_usb_id(name, vendor_id, product_id, version)) {
+      const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version);
+      if (!usbtype)
+        return 0;
+      // Linux USB layer does not support 16 byte SAT pass through command
+      if (!strcmp(usbtype, "sat"))
+        usbtype = "sat,12";
+      // Return SAT/USB device for this type
+      // (Note: linux_scsi_device::autodetect_open() will not be called in this case)
+      return get_sat_device(usbtype, new linux_scsi_device(this, name, ""));
+    }
+
+    // No USB bridge found, assume regular SCSI device
+    return new linux_scsi_device(this, name, "");
+  }
 
   // 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;
+    return new linux_scsi_device(this, name, "");
 
   // form /dev/ns* or ns*
   if (!strncmp(lin_dev_scsi_tape1, dev_name,
                strlen(lin_dev_scsi_tape1)))
-    return CONTROLLER_SCSI;
+    return new linux_scsi_device(this, name, "");
 
   // form /dev/os* or os*
   if (!strncmp(lin_dev_scsi_tape2, dev_name,
                strlen(lin_dev_scsi_tape2)))
-    return CONTROLLER_SCSI;
+    return new linux_scsi_device(this, name, "");
 
   // form /dev/nos* or nos*
   if (!strncmp(lin_dev_scsi_tape3, dev_name,
                strlen(lin_dev_scsi_tape3)))
-    return CONTROLLER_SCSI;
+    return new linux_scsi_device(this, name, "");
 
   // form /dev/twa*
   if (!strncmp(lin_dev_3ware_9000_char, dev_name,
                strlen(lin_dev_3ware_9000_char)))
-    return CONTROLLER_3WARE_9000_CHAR;
+    return missing_option("-d 3ware,N");
 
   // form /dev/twe*
   if (!strncmp(lin_dev_3ware_678k_char, dev_name,
                strlen(lin_dev_3ware_678k_char)))
-    return CONTROLLER_3WARE_678K_CHAR;
+    return missing_option("-d 3ware,N");
+
   // form /dev/cciss*
   if (!strncmp(lin_dev_cciss_dir, dev_name,
                strlen(lin_dev_cciss_dir)))
-    return CONTROLLER_CCISS;
+    return missing_option("-d cciss,N");
+
+  // form /dev/sg*
+  if ( !strncmp(lin_dev_areca, dev_name,
+                strlen(lin_dev_areca)) )
+    return missing_option("-d areca,N");
 
   // we failed to recognize any of the forms
-  return CONTROLLER_UNKNOWN;
+  return 0;
 }
 
+smart_device * linux_smart_interface::get_custom_smart_device(const char * name, const char * type)
+{
+  // Marvell ?
+  if (!strcmp(type, "marvell"))
+    return new linux_marvell_device(this, name, type);
+
+  // 3Ware ?
+  int disknum = -1, n1 = -1, n2 = -1;
+  if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    if (n2 != (int)strlen(type)) {
+      set_err(EINVAL, "Option -d 3ware,N requires N to be a non-negative integer");
+      return 0;
+    }
+    if (!(0 <= disknum && disknum <= 127)) {
+      set_err(EINVAL, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127", disknum);
+      return 0;
+    }
+
+    if (!strncmp(name, "/dev/twa", 8))
+      return new linux_escalade_device(this, name, linux_escalade_device::AMCC_3WARE_9000_CHAR, disknum);
+    else if (!strncmp(name, "/dev/twe", 8))
+      return new linux_escalade_device(this, name, linux_escalade_device::AMCC_3WARE_678K_CHAR, disknum);
+    else
+      return new linux_escalade_device(this, name, linux_escalade_device::AMCC_3WARE_678K, disknum);
+  }
 
-#if 0
+  // Areca?
+  disknum = n1 = n2 = -1;
+  if (sscanf(type, "areca,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    if (n2 != (int)strlen(type)) {
+      set_err(EINVAL, "Option -d areca,N requires N to be a non-negative integer");
+      return 0;
+    }
+    if (!(1 <= disknum && disknum <= 24)) {
+      set_err(EINVAL, "Option -d areca,N (N=%d) must have 1 <= N <= 24", disknum);
+      return 0;
+    }
+    return new linux_areca_device(this, name, disknum);
+  }
+
+  // Highpoint ?
+  int controller = -1, channel = -1; disknum = 1;
+  n1 = n2 = -1; int n3 = -1;
+  if (sscanf(type, "hpt,%n%d/%d%n/%d%n", &n1, &controller, &channel, &n2, &disknum, &n3) >= 2 || n1 == 4) {
+    int len = strlen(type);
+    if (!(n2 == len || n3 == len)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' supports 2-3 items");
+      return 0;
+    }
+    if (!(1 <= controller && controller <= 8)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied");
+      return 0;
+    }
+    if (!(1 <= channel && channel <= 8)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied");
+      return 0;
+    }
+    if (!(1 <= disknum && disknum <= 15)) {
+      set_err(EINVAL, "Option '-d hpt,L/M/N' invalid pmport number N supplied");
+      return 0;
+    }
+    return new linux_highpoint_device(this, name, controller, channel, disknum);
+  }
+
+#ifdef HAVE_LINUX_CCISS_IOCTL_H
+  // CCISS ?
+  disknum = n1 = n2 = -1;
+  if (sscanf(type, "cciss,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
+    if (n2 != (int)strlen(type)) {
+      set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer");
+      return 0;
+    }
+    if (!(0 <= disknum && disknum <= 15)) {
+      set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 15", disknum);
+      return 0;
+    }
+    return new linux_cciss_device(this, name, disknum);
+  }
+#endif // HAVE_LINUX_CCISS_IOCTL_H
 
-[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
+  // MegaRAID ?
+  if (sscanf(type, "megaraid,%d", &disknum) == 1) {
+    return new linux_megaraid_device(this, name, 0, disknum);
+  }
+  return 0;
+}
 
+const char * linux_smart_interface::get_valid_custom_dev_types_str()
+{
+  return "marvell, areca,N, 3ware,N, hpt,L/M/N, megaraid,N"
+#ifdef HAVE_LINUX_CCISS_IOCTL_H
+                                              ", cciss,N"
 #endif
+    ;
+}
+
+} // namespace
+
+
+/////////////////////////////////////////////////////////////////////////////
+/// Initialize platform interface and register with smi()
+
+void smart_interface::init()
+{
+  static os_linux::linux_smart_interface the_interface;
+  smart_interface::set(&the_interface);
+}
index 74f42960022c3572bf130b1293244532e9a787d2..5f41b6f7f6f9a2c01e7baf467d15d93a3d8aba21 100644 (file)
 #include "os_netbsd.h"
 #include <unistd.h>
 
-const char *os_XXXX_c_cvsid = "$Id: os_netbsd.cpp,v 1.24 2008/03/04 22:09:47 ballen4705 Exp $" \
+const char *os_XXXX_c_cvsid = "$Id: os_netbsd.cpp,v 1.25 2008/06/12 21:46:31 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
+  BAD_SMART, NO_3WARE, NO_ARECA, MAX_MSG
 };
 
 /* Utility function for printing warnings */
@@ -350,6 +350,13 @@ escalade_command_interface(int fd, int disknum, int escalade_type, smart_command
   return -1;
 }
 
+int
+areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data)
+{
+  printwarning(NO_ARECA, NULL);
+  return -1;
+}
+
 int
 do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
 {
index b1cea82db9050b00b137a32433a47ba8085dcc03..e0d572073d4b1209ad4050e622f296399f5dacc7 100644 (file)
 #include "utility.h"
 #include "os_openbsd.h"
 
-const char *os_XXXX_c_cvsid = "$Id: os_openbsd.cpp,v 1.16 2008/03/04 22:09:47 ballen4705 Exp $" \
+const char *os_XXXX_c_cvsid = "$Id: os_openbsd.cpp,v 1.17 2008/06/12 21:46:31 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
+  BAD_SMART, NO_3WARE, NO_ARECA, MAX_MSG
 };
 
 /* Utility function for printing warnings */
@@ -361,6 +361,13 @@ escalade_command_interface(int fd, int disknum, int escalade_type, smart_command
   return -1;
 }
 
+int
+areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data)
+{
+  printwarning(NO_ARECA, NULL);
+  return -1;
+}
+
 int
 do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
 {
diff --git a/os_os2.cpp b/os_os2.cpp
new file mode 100644 (file)
index 0000000..a174170
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ * os_os2.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-8 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.9 2008/06/12 21:46:31 ballen4705 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;
+}
+
+int areca_command_interface(int fd, int disknum, 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..57d3ad3
--- /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-8 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.5 2008/03/04 22:09:47 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_qnxnto.cpp b/os_qnxnto.cpp
new file mode 100644 (file)
index 0000000..7f4526d
--- /dev/null
@@ -0,0 +1,643 @@
+
+
+// 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_qnxnto.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_qnxnto.cpp,v 1.3 2008/06/12 21:46:31 ballen4705 Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_QNXNTO_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
+//----------------------------------------------------------------------------------------------
+// private Functions
+static int ata_sense_data(void *sdata,int *error,int *key,int *asc,int *ascq);
+static int ata_interpret_sense(struct cam_pass_thru *cpt,void *sense,int *status,int rcount);
+static int ata_pass_thru(int fd,struct cam_pass_thru *pcpt);
+//----------------------------------------------------------------------------------------------
+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/hd0                       (Prints all SMART information)\n\n"
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hd0\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl -t long /dev/hd0              (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hd0\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/hd0                       (Prints all SMART information)\n"
+         "  smartctl -s on -o on -S on /dev/hd0         (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/hd0              (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/hd0\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.
+static const char *net_dev_prefix = "/dev/";
+static const char *net_dev_ata_disk = "hd";
+
+int guess_device_type (const char* dev_name)
+{
+int len,dev_prefix_len;
+  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);
+  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)
+{
+  if(!strcmp(type, "ATA"))
+    return(open(pathname,O_RDWR|O_NONBLOCK));
+  else
+    return(-1);
+}
+
+// Like close().  Acts only on integer handles returned by
+// deviceopen() above.
+int deviceclose(int fd)
+{
+  return(close(fd));
+}
+//----------------------------------------------------------------------------------------------
+// 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)
+{
+struct cam_pass_thru cpt;
+ATA_SENSE            sense;
+CDB                  *cdb;
+int                  status,rc;
+  memset(&cpt,0x00,sizeof(struct cam_pass_thru));
+  cdb=(CDB *)cpt.cam_cdb;
+  rc=-1;
+  switch(command)
+   {
+    case READ_VALUES:
+         cpt.cam_flags                  = CAM_DIR_IN;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_dxfer_len              = 512;
+         cpt.cam_data_ptr               = (uint32_t)data;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_PIO_DATA_IN;
+         cdb->ata_pass_thru.flags       = ATA_FLG_T_DIR|ATA_FLG_TLEN_STPSIU;
+         cdb->ata_pass_thru.command     = ATA_SMART_CMD;
+         cdb->ata_pass_thru.features    = ATA_SMART_READ_VALUES;
+         cdb->ata_pass_thru.lba_mid     = ATA_SMART_LBA_MID_SIG;
+         cdb->ata_pass_thru.lba_high    = ATA_SMART_LBA_HI_SIG;
+         break;
+    case READ_THRESHOLDS:
+         cpt.cam_flags                  = CAM_DIR_IN;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_dxfer_len              = 512;
+         cpt.cam_data_ptr               = (uint32_t)data;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_PIO_DATA_IN;
+         cdb->ata_pass_thru.flags       = ATA_FLG_T_DIR|ATA_FLG_TLEN_STPSIU;
+         cdb->ata_pass_thru.command     = ATA_SMART_CMD;
+         cdb->ata_pass_thru.features    = ATA_SMART_READ_THRESHOLDS;
+         cdb->ata_pass_thru.lba_mid     = ATA_SMART_LBA_MID_SIG;
+         cdb->ata_pass_thru.lba_high    = ATA_SMART_LBA_HI_SIG;
+         break;
+    case READ_LOG:
+         cpt.cam_flags                  = CAM_DIR_IN;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_dxfer_len              = 512;
+         cpt.cam_data_ptr               = (uint32_t)data;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_PIO_DATA_IN;
+         cdb->ata_pass_thru.flags       = ATA_FLG_T_DIR|ATA_FLG_TLEN_STPSIU;
+         cdb->ata_pass_thru.command     = ATA_SMART_CMD;
+         cdb->ata_pass_thru.features    = ATA_SMART_READ_LOG_SECTOR;
+         cdb->ata_pass_thru.sector_count= 1;
+         cdb->ata_pass_thru.lba_low     = select;
+         cdb->ata_pass_thru.lba_mid     = ATA_SMART_LBA_MID_SIG;
+         cdb->ata_pass_thru.lba_high    = ATA_SMART_LBA_HI_SIG;
+         break;
+    case WRITE_LOG:
+         return(-1);
+         break;
+    case IDENTIFY:
+         cpt.cam_flags                  = CAM_DIR_IN;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_dxfer_len              = 512;
+         cpt.cam_data_ptr               = (uint32_t)data;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_PIO_DATA_IN;
+         cdb->ata_pass_thru.flags       = ATA_FLG_T_DIR|ATA_FLG_TLEN_STPSIU;
+         cdb->ata_pass_thru.command     = ATA_IDENTIFY_DEVICE;
+         break;
+    case PIDENTIFY:
+         cpt.cam_flags                  = CAM_DIR_IN;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_dxfer_len              = 512;
+         cpt.cam_data_ptr               = (uint32_t)data;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_PIO_DATA_IN;
+         cdb->ata_pass_thru.flags       = ATA_FLG_T_DIR|ATA_FLG_TLEN_STPSIU;
+         cdb->ata_pass_thru.command     = ATA_IDENTIFY_PACKET_DEVICE;
+         break;
+    case ENABLE:
+         cpt.cam_flags                  = CAM_DIR_NONE;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_DATA_NONE;
+         cdb->ata_pass_thru.command     = ATA_SMART_CMD;
+         cdb->ata_pass_thru.features    = ATA_SMART_ENABLE;
+         cdb->ata_pass_thru.lba_mid     = ATA_SMART_LBA_MID_SIG;
+         cdb->ata_pass_thru.lba_high    = ATA_SMART_LBA_HI_SIG;
+         break;
+    case DISABLE:
+         cpt.cam_flags                  = CAM_DIR_NONE;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_DATA_NONE;
+         cdb->ata_pass_thru.command     = ATA_SMART_CMD;
+         cdb->ata_pass_thru.features    = ATA_SMART_DISABLE;
+         cdb->ata_pass_thru.lba_mid     = ATA_SMART_LBA_MID_SIG;
+         cdb->ata_pass_thru.lba_high    = ATA_SMART_LBA_HI_SIG;
+         break;
+    case AUTO_OFFLINE:
+    // NOTE: According to ATAPI 4 and UP, this command is obsolete 
+         cpt.cam_flags                  = CAM_DIR_NONE;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_DATA_NONE;
+         cdb->ata_pass_thru.command     = ATA_SMART_CMD;
+         cdb->ata_pass_thru.features    = ATA_SMART_AUTO_OFFLINE;
+         cdb->ata_pass_thru.lba_low     = select;
+         cdb->ata_pass_thru.lba_mid     = ATA_SMART_LBA_MID_SIG;
+         cdb->ata_pass_thru.lba_high    = ATA_SMART_LBA_HI_SIG;
+         break;
+    case AUTOSAVE:
+         cpt.cam_flags                  = CAM_DIR_NONE;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_DATA_NONE;
+         cdb->ata_pass_thru.command     = ATA_SMART_CMD;
+         cdb->ata_pass_thru.features    = ATA_SMART_AUTOSAVE;
+         cdb->ata_pass_thru.sector_count= select;
+         cdb->ata_pass_thru.lba_mid     = ATA_SMART_LBA_MID_SIG;
+         cdb->ata_pass_thru.lba_high    = ATA_SMART_LBA_HI_SIG;
+         break;
+    case IMMEDIATE_OFFLINE:
+    // NOTE: According to ATAPI 4 and UP, this command is obsolete 
+         cpt.cam_flags                  = CAM_DIR_NONE;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_DATA_NONE;
+         cdb->ata_pass_thru.command     = ATA_SMART_CMD;
+         cdb->ata_pass_thru.features    = ATA_SMART_IMMEDIATE_OFFLINE;
+         cdb->ata_pass_thru.lba_low     = select;
+         cdb->ata_pass_thru.lba_mid     = ATA_SMART_LBA_MID_SIG;
+         cdb->ata_pass_thru.lba_high    = ATA_SMART_LBA_HI_SIG;
+         break;
+    case STATUS_CHECK:
+    // same command, no HDIO in NetBSD 
+    case STATUS:
+         cpt.cam_flags                  = CAM_DIR_NONE;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_DATA_NONE;
+         cdb->ata_pass_thru.flags       = ATA_FLG_CK_COND;
+         cdb->ata_pass_thru.command     = ATA_SMART_CMD;
+         cdb->ata_pass_thru.features    = ATA_SMART_STATUS;
+         cdb->ata_pass_thru.lba_mid     = ATA_SMART_LBA_MID_SIG;
+         cdb->ata_pass_thru.lba_high    = ATA_SMART_LBA_HI_SIG;
+         break;
+    case CHECK_POWER_MODE:
+         cpt.cam_flags                  = CAM_DIR_NONE;
+         cpt.cam_cdb_len                = 16;
+         cpt.cam_sense_len              = sizeof(sense);
+         cpt.cam_sense_ptr              = (uint32_t)&sense;
+         cdb->ata_pass_thru.opcode      = SC_ATA_PT16;
+         cdb->ata_pass_thru.protocol    = ATA_PROTO_DATA_NONE;
+         cdb->ata_pass_thru.flags       = ATA_FLG_CK_COND;
+         cdb->ata_pass_thru.command     = ATA_CHECK_POWER_MODE;
+         break;
+    default:
+         pout("Unrecognized command %d in ata_command_interface()\n", command);
+         errno=ENOSYS;
+         return(-1);
+   }
+// execute now
+  if((status=ata_pass_thru(fd,&cpt))==EOK)
+   {
+    rc=status==EOK?0:-1;
+    if(cpt.cam_status!=CAM_REQ_CMP)
+     {
+      ata_interpret_sense(&cpt,&sense,&status,0);
+      if(command==STATUS||command==STATUS_CHECK)
+        rc=((sense.desc.lba_high<<8)|sense.desc.lba_mid)==ATA_SMART_SIG?0:1;
+     }
+   }
+  if(command==CHECK_POWER_MODE)
+    data[0]=cdb->ata_pass_thru.sector_count;
+// finish
+  return(rc);
+}
+//----------------------------------------------------------------------------------------------
+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;
+}
+
+int areca_command_interface(int fd,int disknum,smart_command_set command,int select,char *data)
+{
+  ARGUSED(fd);
+  ARGUSED(disknum);
+  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;
+}
+//----------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------
+static int ata_sense_data(void *sdata,int *error,int *key,int *asc,int *ascq)
+{
+SCSI_SENSE     *sf;
+SCSI_SENSE_DESCRIPTOR  *sd;
+  sf=(SCSI_SENSE *)sdata;
+  sd=(SCSI_SENSE_DESCRIPTOR *)sdata;
+  *error=sf->error;
+  if(*error & SENSE_DATA_FMT_DESCRIPTOR)
+   {
+    *key=sd->sense & SK_MSK;
+    *asc=sd->asc;
+    *ascq=sd->ascq;
+   }
+  else
+   {
+    *key=sf->sense & SK_MSK;
+    *asc=sf->asc;
+    *ascq=sf->ascq;
+   }
+  return(CAM_SUCCESS);
+}
+//----------------------------------------------------------------------------------------------
+static int ata_interpret_sense(struct cam_pass_thru *cpt,void *sense,int *status,int rcount)
+{
+int retry;
+int key;
+int asc;
+int ascq;
+int error;
+  *status=EIO;
+  retry=CAM_TRUE;
+  if(cpt->cam_status&CAM_AUTOSNS_VALID)
+   {
+    ata_sense_data(sense,&error,&key,&asc,&ascq);
+    switch(key)
+     {
+      case SK_NO_SENSE:                                        // No sense data (no error)
+           retry=CAM_FALSE;
+           *status=EOK;
+           break;
+      case SK_RECOVERED:                                       // Recovered error
+          switch(asc)
+            {
+             case ASC_ATA_PASS_THRU:
+                  switch(ascq)
+                   {
+                    case ASCQ_ATA_PASS_THRU_INFO_AVAIL:
+                         break;
+                    default:
+                         break;
+                   }
+                  break;
+             default:
+                  break;
+            }
+           retry=CAM_FALSE;
+           *status=EOK;
+           break;
+      case SK_NOT_RDY:                                 // Device not ready
+           *status=EAGAIN;
+           switch(asc)
+            {
+             case ASC_NOT_READY:
+                  switch(ascq)
+                   {
+                    case ASCQ_BECOMING_READY:
+                    case ASCQ_CAUSE_NOT_REPORTABLE:
+                    default:
+                    retry=CAM_FALSE;
+                    break;
+                   }
+                  break;
+             case ASC_MEDIA_NOT_PRESENT:
+                  *status=ENXIO;
+                  retry=CAM_FALSE;
+                  break;
+            }
+           break;
+      case SK_MEDIUM:                                          // Medium error
+      case SK_HARDWARE:                                        // Hardware error
+           retry=CAM_FALSE;
+           *status=EIO;
+           break;
+      case SK_ILLEGAL:                                 // Illegal Request (bad command)
+           retry=CAM_FALSE;
+           *status=EINVAL;
+           break;
+      case SK_UNIT_ATN:                                        // Unit Attention
+           switch(asc)
+            {
+             case ASC_MEDIUM_CHANGED:
+                  *status=ESTALE;
+                  retry=CAM_FALSE;
+                  break;
+             case ASC_BUS_RESET:
+                  break;
+            }
+           break;
+      case SK_DATA_PROT:                                       // Data Protect
+           retry=CAM_FALSE;
+           *status=EROFS;
+           break;
+      case SK_VENDOR:                                          // Vendor Specific
+      case SK_CPY_ABORT:                                       // Copy Aborted
+           retry=CAM_FALSE;
+           *status=EIO;
+           break;
+      case SK_CMD_ABORT:                                       // Aborted Command
+           retry=CAM_FALSE;
+           *status=ECANCELED;
+           break;
+      case SK_EQUAL:                                           // Equal
+      case SK_VOL_OFL:                                 // Volume Overflow
+      case SK_MISCMP:                                          // Miscompare
+      case SK_RESERVED:                                        // Reserved
+           break; 
+     }
+    if(*status==EOK)
+     {
+      switch(cpt->cam_status&CAM_STATUS_MASK) 
+       {
+        case CAM_REQ_CMP_ERR:                  // CCB request completed with an err
+             retry=CAM_FALSE;
+             *status=EIO;
+             break;
+        case CAM_BUSY:                                 // CAM subsystem is busy
+             *status=EAGAIN;
+             break;
+        case CAM_REQ_INVALID:                  // CCB request is invalid
+        case CAM_PATH_INVALID:                 // Path ID supplied is invalid
+        case CAM_DEV_NOT_THERE:                        // SCSI device not installed/there
+        case CAM_SEL_TIMEOUT:                  // Target selection timeout
+        case CAM_LUN_INVALID:                  // LUN supplied is invalid
+        case CAM_TID_INVALID:                  // Target ID supplied is invalid
+             retry=CAM_FALSE;
+             *status=ENXIO;
+             break;
+        case CAM_CMD_TIMEOUT:                  // Command timeout
+             *status=rcount?EAGAIN:EIO;
+             break;
+        case CAM_MSG_REJECT_REC:               // Message reject received
+        case CAM_SCSI_BUS_RESET:               // SCSI bus reset sent/received
+        case CAM_UNCOR_PARITY:                 // Uncorrectable parity err occurred
+        case CAM_AUTOSENSE_FAIL:               // Autosense: Request sense cmd fail
+        case CAM_NO_HBA:                               // No HBA detected Error
+        case CAM_DATA_RUN_ERR:                 // Data overrun/underrun error
+             retry=CAM_FALSE;
+             *status=EIO;
+             break;
+        case CAM_UNEXP_BUSFREE:                        // Unexpected BUS free
+        case CAM_SEQUENCE_FAIL:                        // Target bus phase sequence failure
+             *status=EIO;
+             break;
+        case CAM_PROVIDE_FAIL:                 // Unable to provide requ. capability
+             retry=CAM_FALSE;
+             *status=ENOTTY;
+             break;
+        case CAM_CCB_LEN_ERR:                  // CCB length supplied is inadequate
+        case CAM_BDR_SENT:                             // A SCSI BDR msg was sent to target
+        case CAM_REQ_TERMIO:                   // CCB request terminated by the host
+        case CAM_FUNC_NOTAVAIL:                        // The requ. func is not available
+        case CAM_NO_NEXUS:                             // Nexus is not established
+        case CAM_IID_INVALID:                  // The initiator ID is invalid
+        case CAM_CDB_RECVD:                            // The SCSI CDB has been received
+             retry=CAM_FALSE;
+             *status=EIO;
+             break;
+        case CAM_SCSI_BUSY:                            // SCSI bus busy
+             *status=EAGAIN;
+             break;
+       }
+     }
+   }
+  return(retry);
+}
+//----------------------------------------------------------------------------------------------
+static int ata_pass_thru(int fd,struct cam_pass_thru *pcpt)
+{
+int    icnt;
+int    status;
+iov_t  iov[3];
+struct cam_pass_thru   cpt;
+  cpt=*pcpt;
+  icnt=1;
+  SETIOV(&iov[0],&cpt,sizeof(cpt));
+  cpt.cam_timeout=cpt.cam_timeout?cpt.cam_timeout:CAM_TIME_DEFAULT;
+  if(cpt.cam_sense_len)
+   {
+    SETIOV(&iov[1],cpt.cam_sense_ptr,cpt.cam_sense_len);
+    cpt.cam_sense_ptr=sizeof(cpt);
+    icnt++;
+   }
+  if(cpt.cam_dxfer_len)
+   {
+    SETIOV(&iov[2],(void *)cpt.cam_data_ptr,cpt.cam_dxfer_len);
+    cpt.cam_data_ptr=(paddr_t)sizeof(cpt)+cpt.cam_sense_len;
+    icnt++;
+   }
+  if((status=devctlv(fd,DCMD_CAM_PASS_THRU,icnt,icnt,iov,iov,NULL)))
+    pout("ata_pass_thru devctl:  %s\n",strerror(status));
+  pcpt->cam_status=cpt.cam_status;
+  pcpt->cam_scsi_status=cpt.cam_scsi_status;
+  return(status);
+}
+//----------------------------------------------------------------------------------------------
diff --git a/os_qnxnto.h b/os_qnxnto.h
new file mode 100644 (file)
index 0000000..2feb235
--- /dev/null
@@ -0,0 +1,896 @@
+/*
+ * os_generic.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) Joerg Hering       <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-8 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.
+ *
+ * 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/
+ *
+ */
+#ifndef OS_QNXNTO_H_
+#define OS_QNXNTO_H_
+#define OS_QNXNTO_H_CVSID "$Id: os_qnxnto.h,v 1.2 2008/03/04 22:09:47 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
+#include <sys/cpt.h>
+
+#ifndef __TYPES_H_INCLUDED
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <gulliver.h>
+#include <sys/cpt.h>
+#include <sys/dcmd_cam.h>
+#include <sys/cam_device.h>
+#include "atacmds.h"
+
+//----------------------------------------------------------------------------------------------------------
+typedef struct _ata_pass_thru   ATA_PASS_THRU;
+typedef struct _eide_identify   EIDE_IDENTIFY;
+typedef struct _ata_sense       ATA_SENSE;
+
+typedef void CCB;
+struct _sim_hba;
+struct _resmgr_context;
+
+
+typedef struct _drive_attribute
+ {
+  int  id;
+  int  threshold;
+  char *name;
+ }DRIVE_ATTRIBUTE;
+
+//----------------------------------------------------------------------------------------------------------
+/* UNIVOS OSD defines and data structures. */
+
+#define INQLEN  36              /* Inquiry string length to store. */
+
+#define CAM_SUCCESS     0       /* For signaling general success */
+#define CAM_FAILURE     1       /* For signaling general failure */
+
+#define CAM_FALSE       0       /* General purpose flag value */
+#define CAM_TRUE        1       /* General purpose flag value */
+
+//----------------------------------------------------------------------------------------------------------
+// Group 3 and 4, command codes 60H-9FH are reserved
+#define SC_ATA_PT16     0x85   // ATA Pass-through
+//----------------------------------------------------------------------------------------------------------
+#define ATA_SMART_LBA_MID_SIG  0x4f
+#define ATA_SMART_LBA_HI_SIG   0xc2
+#define ATA_SMART_SIG          0xc24f
+//----------------------------------------------------------------------------------------------------------
+       struct _ata_pass_thru {
+               uchar_t         opcode;
+#define ATA_PROTO_MSK                          0x1e
+#define ATA_PROTO_RESPONSE                     (15 << 1)
+#define ATA_PROTO_FPDMA                                (12 << 1)
+#define ATA_PROTO_UDMA_DATA_OUT                (11 << 1)
+#define ATA_PROTO_UDMA_DATA_IN         (10 << 1)
+#define ATA_PROTO_DEVICE_RESET         (9 << 1)
+#define ATA_PROTO_DEVICE_DIAGNOSTIC    (8 << 1)
+#define ATA_PROTO_DMA_QUEUED           (7 << 1)
+#define ATA_PROTO_DMA                          (6 << 1)
+#define ATA_PROTO_PIO_DATA_OUT         (5 << 1)
+#define ATA_PROTO_PIO_DATA_IN          (4 << 1)
+#define ATA_PROTO_DATA_NONE                    (3 << 1)
+#define ATA_PROTO_SRST                         (1 << 1)
+#define ATA_PROTO_HRST                         (0 << 1)
+#define ATA_PROTO_EXTEND                       0x01
+               uchar_t         protocol;      // multiple count, protocol
+#define ATA_MCOUNT_MSK                         0xe0
+
+#define ATA_FLG_CK_COND                                0x20
+#define ATA_FLG_T_DIR                          0x08            // data from device
+#define ATA_FLG_BYT_BLOK                       0x04
+#define ATA_FLG_TLEN_STPSIU                    0x03
+#define ATA_FLG_TLEN_SECTOR_COUNT      0x02
+#define ATA_FLG_TLEN_FEATURE           0x01
+               uchar_t         flags;
+
+               uchar_t         efeatures;
+               uchar_t         features;
+               uchar_t         esector_count;
+               uchar_t         sector_count;
+               uchar_t         elba_low;
+               uchar_t         lba_low;
+               uchar_t         elba_mid;
+               uchar_t         lba_mid;
+               uchar_t         elba_high;
+               uchar_t         lba_high;
+               uchar_t         device;
+               uchar_t         command;
+               uchar_t         control;
+       } ata_pass_thru_;
+//----------------------------------------------------------------------------------------------------------
+#define SENSE_DATA_FMT_DESCRIPTOR      0x02
+
+// Fixed Format Sense Data Structure
+// Note: The field "error" has the following format:
+//        bit    7     - Address valid bit
+//               bits 6-4  - Error class
+//               bits 3-0  - Error code 
+//
+// Error classes 0-6 are vendor unique and also indicate that the
+// sense data is in _nonextended_ format. (i.e. not usually used)
+//             struct _scsi_nonextended_sense {
+//                     uchar_t sd_err;
+//                     ulong_t sd_block_address;
+//             };
+//
+//     An error class of 7 and an error code of 0 (70H) indicate SCSI-1
+//     extended sense data format (or SCSI-2 sense data format).
+//
+//     An error class of 7 and an error code of 1 (71H) indicate SCSI-2
+//     deferred errors.
+//
+//     Error codes 74H to 7EH are reserved and error code 7FH indicates
+//     a vendor-specific sense data format.
+typedef struct _scsi_sense {
+       uchar_t         error;                          // Error Code
+       uchar_t         segment;                        // Segment number
+       uchar_t         sense;                          // Sense key/flags
+       uchar_t         info[4];                        // Information (32bit big-endian value)
+       uchar_t         asl;                            // Additional Sense Length
+       uchar_t         csinfo[4];                      // Command-Specific Information
+       uchar_t         asc;                            // Additional Sense Code
+       uchar_t         ascq;                           // Additional Sense Code Qualifier
+       uchar_t         fruc;                           // Field Replaceable Unit Code
+       uchar_t         sks;                            // Sense Key Specific
+       ushort_t        sks_data;                       // Sense Key Specific Data (16bit big-endian)
+       ushort_t        asb;                            // Additional Sense uchar_ts (Max 256-18)
+} SCSI_SENSE;
+
+// Descriptor Format Sense Data Structure
+//     error code of 72 current, 73 deferred
+//     extended sense data format (or SCSI-2 sense data format).
+typedef struct _scsi_sense_descriptor {
+       uchar_t         error;                          // Error Code
+       uchar_t         sense;                          // Sense key/flags
+       uchar_t         asc;                            // Additional Sense Code
+       uchar_t         ascq;                           // Additional Sense Code Qualifier
+       uchar_t         rsvd[3];
+       uchar_t         asl;                            // Additional Sense Length
+} SCSI_SENSE_DESCRIPTOR;
+
+typedef struct _scsi_sense_desriptor_header {
+       uchar_t                 descriptor_type;
+       uchar_t                 descriptor_len;
+} SCSI_SENSE_DESCRIPTOR_HEADER;
+
+#define SENSE_DTYPE_INFORMATION                0x00
+#define SENSE_DTYPE_CSI                                0x01    // Command Specific Information
+#define SENSE_DTYPE_SKS                                0x02    // Sense Key Specific
+#define SENSE_DTYPE_FRU                                0x03    // Field Replaceable Unit
+#define SENSE_DTYPE_STREAM                     0x04
+#define SENSE_DTYPE_BLOCK                      0x05
+#define SENSE_DTYPE_OSD_OBJ_IDENT      0x06    // OSD Object Identification
+#define SENSE_DTYPE_OSD_INTEGRITY      0x07    // OSD Response Integrity Check Value
+#define SENSE_DTYPE_OSD_ATR_IDENT      0x08    // OSD Attribute Identification
+#define SENSE_DTYPE_ATA                                0x09
+
+typedef struct _ata_status_descriptor {
+       uchar_t                 descriptor_type;
+#define ATA_SD_DLEN                                    0x0c
+       uchar_t                 descriptor_len;                 /* 0xc */
+#define ATA_SD_FLG_EXTEND                      0x01
+       uchar_t                 flags;
+       uchar_t                 error;
+       uchar_t                 esector_count;                  /* (15:8) */
+       uchar_t                 sector_count;                   /* (7:0) */
+       uchar_t                 elba_low;                               /* (15:8) */
+       uchar_t                 lba_low;                                /* (7:0) */
+       uchar_t                 elba_mid;                               /* (15:8) */
+       uchar_t                 lba_mid;                                /* (7:0) */
+       uchar_t                 elba_high;                              /* (15:8) */
+       uchar_t                 lba_high;                               /* (7:0) */
+       uchar_t                 device;
+       uchar_t                 status;
+} ATA_STATUS_DESCRIPTOR;
+
+//----------------------------------------------------------------------------------------------------------
+// Sense Keys
+#define SK_MSK                 0x0F    // mask to sd_sense field for key
+
+#define SK_NO_SENSE            0               // No sense data (no error)
+               #define ASCQ_FILEMARK_DETECTED                  0x01
+               #define ASCQ_EOPM_DETECTED                              0x02    // End of Partition/Medium Detected
+               #define ASCQ_SETMARK_DETECTED                   0x03
+               #define ASCQ_BOPM_DETECTED                              0x04    // Beginning of Partition/Medium Detected
+
+#define SK_RECOVERED   1               // Recovered error
+               #define ASC_ATA_PASS_THRU                                       0x00
+                       #define ASCQ_ATA_PASS_THRU_INFO_AVAIL   0x1d
+
+#define SK_NOT_RDY             2               // Device not ready
+       #define ASC_NO_SEEK_COMPLETE                            0x02
+       #define ASC_NOT_READY                                           0x04
+               #define ASCQ_CAUSE_NOT_REPORTABLE                       0x00
+               #define ASCQ_BECOMING_READY                                     0x01
+               #define ASCQ_INIT_COMMAND_REQUIRED                      0x02
+               #define ASCQ_MANUAL_INTERVENTION_REQUIRED       0x03
+               #define ASCQ_FORMAT_IN_PROGRESS                         0x04
+               #define ASCQ_UNKNOWN_CHANGED                            0xff    // NTO extension for fdc's
+       #define ASC_MEDIA_FORMAT                                        0x30            // bad format
+       #define ASC_MEDIA_NOT_PRESENT                           0x3a
+       #define ASC_NOT_CONFIGURED                                      0x3e
+
+#define SK_MEDIUM              3               // Medium error
+       #define ASC_UNRECOVERABLE_READ_ERROR    0x11
+       #define ASC_RECORD_NOT_FOUND                    0x14
+               #define ASCQ_RECORD_NOT_FOUND           0x01
+       #define ASC_UNABLE_TO_RECOVER_TOC               0x57
+       #define ASC_INCOMPATIBLE_MEDIUM                 0x64
+
+#define SK_HARDWARE            4               // Hardware error
+       #define ASC_INTERNAL_TARGET_FAILURE             0x44
+       #define ASC_MEDIA_LOAD_EJECT_FAILURE    0x53
+               #define ASCQ_UNRECOVERABLE_CIRC                         0x06
+
+#define SK_ILLEGAL             5               // Illegal Request (bad command)
+       #define ASC_INVALID_COMMAND                     0x20
+       #define ASC_INVALID_FIELD                       0x24
+       #define ASC_INVALID_FIELD_PARAMETER     0x26
+       #define ASC_COMMAND_SEQUENCE_ERROR      0x2c
+               #define ASCQ_READ_SCRAMBLED             0x03
+       #define ASC_ILLEGAL_MODE                        0x64
+       #define ASC_COPY_PROTECTION                     0x6f
+
+#define SK_UNIT_ATN            6               // Unit Attention
+       #define ASC_MEDIUM_CHANGED                                      0x28
+       #define ASC_BUS_RESET                                           0x29
+       #define ASC_INSUFFICIENT_TIME_FOR_OPERATION     0x2e
+       #define ASC_OPERATOR_REQUEST                            0x5a
+               #define ASCQ_OPERATOR_MEDIUM_REMOVAL    0x01
+
+#define SK_DATA_PROT   7               // Data Protect
+       #define ASC_WRITE_PROTECTED                     0x27
+
+#define SK_BLNK_CHK            8               // Blank Check
+#define SK_VENDOR              9               // Vendor Specific
+#define SK_CPY_ABORT   10              // Copy Aborted
+#define SK_CMD_ABORT   11              // Aborted Command
+#define SK_EQUAL               12              // Equal
+#define SK_VOL_OFL             13              // Volume Overflow
+#define SK_MISCMP              14              // Miscompare
+#define SK_RESERVED            15              // Reserved
+//----------------------------------------------------------------------------------------------------------
+// Command Descriptor Block structure definitions
+
+// CDB Flags
+#define CF_LINK                        0x01    // Linked-command indication
+#define CF_FLAG                        0x02    // Linked-command with flag bit
+#define CF_VENDOR0             0x40    // Vendor unique bits
+#define CF_VENDOR1             0x80
+
+#define CF_FUA                 0x08
+#define CF_DPO                 0x10
+
+typedef union _cdb {
+       // generic 6 byte command descriptor block
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         lba_byte1;
+               uchar_t         lba_byte0;                              // LSB
+               uchar_t         transfer_len;
+               uchar_t         control;
+       } gen6;
+
+       // generic 10 byte command descriptor block
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         lba_byte3;
+               uchar_t         lba_byte4;
+               uchar_t         lba_byte1;
+               uchar_t         lba_byte0;
+               uchar_t         rsvd;
+               uchar_t         transfer_len[2];
+               uchar_t         control;
+       } gen10;
+
+       // generic 12 byte command descriptor block
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         lba_byte3;
+               uchar_t         lba_byte4;
+               uchar_t         lba_byte1;
+               uchar_t         lba_byte0;
+               uchar_t         transfer_len[4];
+               uchar_t         rsvd10;
+               uchar_t         control;
+       } gen12;
+
+       struct _format_unit {
+        uchar_t         op_code;
+#define FU_RSVD0        0xc0                    // reserved bits
+#define FU_FMTDAT       0x10
+#define FU_CMPLIST      0x08
+        uchar_t         defect_list_fmt;
+        uchar_t         track_num;
+        ushort_t        interleave;
+        uchar_t         rsvd1[7];
+       } format_unit;
+
+       struct _format_unit_old {
+        uchar_t         op_code;
+        uchar_t         rsvd0;
+        uchar_t         medium_type_code;
+        uchar_t         rsvd1;
+        uchar_t         interleave;
+        uchar_t         rsvd2;
+#define FMT_RSVD3               0x80
+#define FMT_SECT_SIZE_CD        0x70
+#define FMT_IMMED               0x08
+#define FMT_HEAD                0x04
+#define FMT_ST                  0x02
+#define FMT_CERT                0x01
+        uchar_t         cert;
+        uchar_t         track_addr;
+        uchar_t         rsvd4[4];
+       } format_unit_old;
+
+#define RW_OPT_RELADR  0x01
+#define RW_OPT_CORRCT  0x02                                    // Disable Corrections
+#define RW_OPT_FUA             0x08                                    // Force Unit Access
+#define RW_OPT_DPO             0x10                                    // Disable Page Out
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_lba;
+               uchar_t         lba[2];
+               uchar_t         transfer_len;
+               uchar_t         control;
+       } read_write6;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         lba[4];
+               uchar_t         rsvd2;
+               uchar_t         transfer_len[2];
+               uchar_t         control;
+       } read_write10;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         lba[4];
+               uchar_t         transfer_len[4];
+               uchar_t         rsvd2;
+               uchar_t         control;
+       } read_write12;
+
+#define MSEL_OPT_PF            0x10                    // Page Format
+#define MSEL_OPT_SP            0x01                    // Save Page
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         rsvd2;
+               uchar_t         rsvd3;
+               uchar_t         param_length;
+               uchar_t         control;
+       } mode_select;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         rsvd2;
+               uchar_t         rsvd3;
+               uchar_t         rsvd4;
+               uchar_t         rsvd5;
+               uchar_t         rsvd6;
+               uchar_t         param_length[2];
+               uchar_t         control;
+       } mode_select10;
+
+       struct {
+               uchar_t         opcode;
+#define LS_OPT_SP              0x01                    // Save Parameters
+#define LS_OPT_PCR             0x02                    // Parameter Code Reset
+               uchar_t         lun_opt;
+#define LS_PC_CUR_THRESHOLD            0x00
+#define LS_PC_CUR_CUMULATIVE   0x01
+#define LS_PC_DFLT_THRESHOLD   0x02
+#define LS_PC_DFLT_CUMULATIVE  0x03
+               uchar_t         pc;                                     // Page Control
+               uchar_t         rsvd3;
+               uchar_t         rsvd4;
+               uchar_t         rsvd5;
+               uchar_t         rsvd6;
+               uchar_t         param_length[2];
+               uchar_t         control;
+       } log_select;
+
+       struct {
+               uchar_t         opcode;
+#define MSNS_OPT_DBD   0x08                    // Disable Block Descriptors
+               uchar_t         lun_opt;
+#define PC_CURRENT             0x00
+#define PC_CHANGEABLE  0x40
+#define PC_DEFAULT             0x80
+#define PC_SAVED               0xC0
+#define PC_MSK                 0xC0
+               uchar_t         pc_page;
+               uchar_t         subpage;
+               uchar_t         allocation_length;
+               uchar_t         control;
+       } mode_sense;
+
+       struct _mode_sense10 {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         pc_page;
+               uchar_t         subpage;
+               uchar_t         rsvd4;
+               uchar_t         rsvd5;
+               uchar_t         rsvd6;
+               uchar_t         allocation_length[2];
+               uchar_t         control;
+       } mode_sense10;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         pc_page;
+               uchar_t         rsvd3;
+               uchar_t         rsvd4;
+               uchar_t         parameter_pointer[2];
+               uchar_t         allocation_length[2];
+               uchar_t         control;
+       } log_sense;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         rsvd2;
+               uchar_t         rsvd3;
+               uchar_t         prevent;
+               uchar_t         control;
+       } removal;
+
+       struct {
+               uchar_t         opcode;
+#define LD_OPT_IMMED   0x01
+               uchar_t         lun_opt;
+               uchar_t         rsvd2;
+               uchar_t         rsvd3;
+#define LD_CMD_START   0x01
+#define LD_CMD_LOEJ            0x02
+#define LD_CMD_STOP            0x00
+#define LD_CMD_EJECT   0x02
+#define LD_CMD_LOAD            0x03
+
+// Sequential-Access
+#define LD_CMD_SA_HOLD         0x08
+#define LD_CMD_SA_EOT          0x04
+#define LD_CMD_SA_RT           0x02                    // re-tension
+#define LD_CMD_SA_LOEJ         0x01
+
+// Block
+#define LD_CMD_PC_MSK          0xf0
+#define LD_CMD_PC_NC           0
+#define LD_CMD_PC_ACTIVE       1
+#define LD_CMD_PC_IDLE         2
+#define LD_CMD_PC_STANDBY      3
+#define LD_CMD_PC_SLEEP                5
+
+               uchar_t         cmd;
+               uchar_t         control;
+       } load;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+#define SC_OPT_RELADR  0x01
+#define SC_OPT_IMMED   0x02
+               uchar_t         lba[4];
+               uchar_t         num_blocks[2];
+               uchar_t         control;
+       } synchronize_cache;
+
+// cdrom commands
+       struct {
+               uchar_t         opcode;
+               uchar_t         rsvd1;
+               uchar_t         rsvd2;
+               uchar_t         rsvd3;
+               uchar_t         rsvd4;
+               uchar_t         rsvd5;
+               uchar_t         rsvd6;
+               uchar_t         allocation_length[2];
+               uchar_t         control;
+       } read_disc_information;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         rsvd2;
+               uchar_t         rsvd3;
+               uchar_t         rsvd4;
+               uchar_t         rsvd5;
+               uchar_t         rsvd6;
+               uchar_t         rsvd7;
+               uchar_t         resume;
+               uchar_t         control;
+       } pause_resume;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         rsvd2;
+               uchar_t         start_minute;
+               uchar_t         start_second;
+               uchar_t         start_frame;
+               uchar_t         end_minute;
+               uchar_t         end_second;
+               uchar_t         end_frame;
+               uchar_t         control;
+       } play_audio_msf;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         rsvd2;
+               uchar_t         rsvd3;
+               uchar_t         start_track;
+               uchar_t         start_index;
+               uchar_t         rsvd6;
+               uchar_t         end_track;
+               uchar_t         end_index;
+               uchar_t         control;
+       } play_audio_ti;
+
+       struct {
+               uchar_t         opcode;
+#define CD_SCAN_DIR_FORWARD            0x00
+#define CD_SCAN_DIR_REVERSE            0x10
+               uchar_t         opt;
+               uchar_t         start_address[4];
+#define CD_SCAN_TYPE_LBA               0x00
+#define CD_SCAN_TYPE_MSF               0x40
+#define CD_SCAN_TYPE_TRK               0x80
+#define CD_SCAN_TYPE_MSK               0xc0
+               uchar_t         rsvd6;
+               uchar_t         rsvd7;
+               uchar_t         rsvd8;
+               uchar_t         type;
+               uchar_t         rsvd10;
+               uchar_t         rsvd11;
+       } cd_scan;
+
+       struct {
+               uchar_t         opcode;
+#define RTOC_OPT_MSF   0x02
+               uchar_t         lun_opt;
+#define RTOC_FMT_TOC           0x0
+#define RTOC_FMT_SESSION       0x1
+#define RTOC_FMT_QSUBCODE      0x2
+#define RTOC_FMT_QSUBCHNL      0x3
+#define RTOC_FMT_ATIP          0x4
+#define RTOC_FMT_CDTEXT                0x5
+               uchar_t         format;
+               uchar_t         rsvd3;
+               uchar_t         rsvd4;
+               uchar_t         rsvd5;
+               uchar_t         start_track;
+               uchar_t         allocation_length[2];
+#define RTOC_CNTL_FMT_SESSION  0x40
+               uchar_t         control_format;
+       } read_toc;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         rsvd2[6];
+               uchar_t         allocation_length[2];
+               uchar_t         rsvd3[2];
+       } mechanism_status;
+
+       struct {
+               uchar_t         opcode;
+#define EXCHANGE_OPT_IMMED     0x01
+               uchar_t         lun_opt;
+               uchar_t         rsvd2;
+               uchar_t         rsvd3;
+#define EXCHANGE_CMD_START     0x01
+#define EXCHANGE_CMD_LOEJ      0x02
+               uchar_t         cmd;
+               uchar_t         rsvd5;
+               uchar_t         rsvd6;
+               uchar_t         rsvd7;
+               uchar_t         slot;
+               uchar_t         rsvd9;
+               uchar_t         rsvd10;
+               uchar_t         rsvd11;
+       } exchange;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         rt;
+               uchar_t         feature_number[2];
+               uchar_t         rsvd4;
+               uchar_t         rsvd5;
+               uchar_t         rsvd6;
+               uchar_t         allocation_length[2];
+               uchar_t         control;
+       } get_configuration;
+
+       struct {
+               uchar_t         opcode;
+#define GE_OPT_POLLED                  0x01
+               uchar_t         opt;
+               uchar_t         rsvd2;
+               uchar_t         rsvd3;
+#define NCR_OPERATIONAL_CHANGE 0x02
+#define NCR_POWER_MANAGEMENT   0x04
+#define NCR_EXTERNAL_REQUEST   0x08
+#define NCR_MEDIA                              0x10
+#define NCR_MULTI_INITIATOR            0x20
+#define NCR_DEVICE_BUSY                        0x40
+               uchar_t         ncr;         // notification class request
+               uchar_t         rsvd5;
+               uchar_t         rsvd6;
+               uchar_t         allocation_length[2];
+               uchar_t         control;
+       } get_event;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         rsvd2;
+               uchar_t         rsvd3;
+               uchar_t         rsvd4;
+               uchar_t         rsvd5;
+               uchar_t         rsvd6;
+               uchar_t         allocation_length[2];
+               uchar_t         control;
+       } read_formated_capacities;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         read_speed[2];
+               uchar_t         write_speed[2];
+               uchar_t         rsvd2[6];
+       } cd_speed;             
+
+       struct {
+               uchar_t         opcode;
+#define RSCHNL_OPT_MSF         0x02
+               uchar_t         lun_opt;
+#define RSCHNL_DATA_SUBQ       0x40
+               uchar_t         data;
+               uchar_t         data_format;
+               uchar_t         rsvd4;
+               uchar_t         rsvd5;
+               uchar_t         track;
+               uchar_t         allocation_length[2];
+               uchar_t         control;
+       } read_subchannel;
+
+#define CD_FRAME_SYNC_SIZE         12
+#define CD_FRAME_HDR_SIZE           4
+#define CD_FRAME_SUB_HDR_SIZE       8
+#define CD_FRAME_EDC_SIZE           4
+#define CD_FRAME_ECC_SIZE         276
+#define CD_FRAME_AUX_SIZE           8
+#define CD_FRAME_ZERO_SIZE          8
+#define CD_FRAME_SPARE_SIZE         4
+#define CD_FRAME_C2_ERR_SIZE      294
+#define CD_FRAME_BLOCK_ERR_SIZE     2
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_stype;
+// expected sector type
+#define RDCD_EST_ANY_SECTOR                            (0 << 2)
+#define RDCD_EST_CDDA_SECTOR                   (1 << 2)
+#define RDCD_EST_YELLOW_MODE1_SECTOR   (2 << 2)
+#define RDCD_EST_YELLOW_MODE2_SECTOR   (3 << 2)
+#define RDCD_EST_XA_SECTOR                             (4 << 2)
+#define RDCD_EST_XA_FORM2_SECTOR               (5 << 2)
+#define RDCD_EST_MSK                                   (7 << 2)
+               uchar_t         lba[4];
+               uchar_t         transfer_len[3];
+               uchar_t         flags;
+#define RDCD_FLG_SYNC                  0x80
+#define RDCD_FLG_UDATA                 0x10
+#define RDCD_FLG_ECC                   0x08
+#define RDCD_FLG_CD_ERR                        0x02
+#define RDCD_FLG_CD_BLOCK_ERR  0x04
+#define RDCD_FLG_HC_NONE               ( 0x00 << 5 )
+#define RDCD_FLG_HC_HDR                        ( 0x01 << 5 )
+#define RDCD_FLG_HC_SUBHEADER  ( 0x02 << 5 )
+#define RDCD_FLG_HC_ALL_HEADERS        ( 0x03 << 5 )
+               uchar_t         subch_selection;
+               uchar_t         rsvd3;
+       } read_cd;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_stype;
+               uchar_t         rsvd2;
+               uchar_t         start_minute;
+               uchar_t         start_second;
+               uchar_t         start_frame;
+               uchar_t         end_minute;
+               uchar_t         end_second;
+               uchar_t         end_frame;
+               uchar_t         flags;
+               uchar_t         subch_selection;
+               uchar_t         rsvd11;
+       } read_cd_msf;
+
+       struct _ata_pass_thru {
+               uchar_t         opcode;
+#define ATA_PROTO_MSK                          0x1e
+#define ATA_PROTO_RESPONSE                     (15 << 1)
+#define ATA_PROTO_FPDMA                                (12 << 1)
+#define ATA_PROTO_UDMA_DATA_OUT                (11 << 1)
+#define ATA_PROTO_UDMA_DATA_IN         (10 << 1)
+#define ATA_PROTO_DEVICE_RESET         (9 << 1)
+#define ATA_PROTO_DEVICE_DIAGNOSTIC    (8 << 1)
+#define ATA_PROTO_DMA_QUEUED           (7 << 1)
+#define ATA_PROTO_DMA                          (6 << 1)
+#define ATA_PROTO_PIO_DATA_OUT         (5 << 1)
+#define ATA_PROTO_PIO_DATA_IN          (4 << 1)
+#define ATA_PROTO_DATA_NONE                    (3 << 1)
+#define ATA_PROTO_SRST                         (1 << 1)
+#define ATA_PROTO_HRST                         (0 << 1)
+#define ATA_PROTO_EXTEND                       0x01
+               uchar_t         protocol;      // multiple count, protocol
+#define ATA_MCOUNT_MSK                         0xe0
+
+#define ATA_FLG_CK_COND                                0x20
+#define ATA_FLG_T_DIR                          0x08            // data from device
+#define ATA_FLG_BYT_BLOK                       0x04
+#define ATA_FLG_TLEN_STPSIU                    0x03
+#define ATA_FLG_TLEN_SECTOR_COUNT      0x02
+#define ATA_FLG_TLEN_FEATURE           0x01
+               uchar_t         flags;
+
+               uchar_t         efeatures;
+               uchar_t         features;
+               uchar_t         esector_count;
+               uchar_t         sector_count;
+               uchar_t         elba_low;
+               uchar_t         lba_low;
+               uchar_t         elba_mid;
+               uchar_t         lba_mid;
+               uchar_t         elba_high;
+               uchar_t         lba_high;
+               uchar_t         device;
+               uchar_t         command;
+               uchar_t         control;
+       } ata_pass_thru;
+
+// sequential access commands
+       struct {
+               uchar_t         opcode;
+#define ERASE_OPT_LONG 0x01
+               uchar_t         opt;
+               uchar_t         rsvd[3];
+               uchar_t         control;
+       } erase;
+
+       struct {
+               uchar_t         opcode;
+#define LOCATE_OPT_CP  0x2
+#define LOCATE_OPT_BT  0x4
+               uchar_t         opt;
+               uchar_t         rsvd2;
+               uchar_t         ba[4];                  // block address
+               uchar_t         rsvd7;
+               uchar_t         partition;
+               uchar_t         control;
+       } locate;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         opt;
+               uchar_t         rsvd2[3];
+               uchar_t         control;
+       } read_block_limits;
+
+#define RP_OPT_BT      0x01                    // block address type
+#define RP_OPT_LNG     0x02                    // long format
+#define RP_OPT_TCLP    0x04                    // total current logical position
+       struct {
+               uchar_t         opcode;
+               uchar_t         lun_opt;
+               uchar_t         rsvd2[7];
+               uchar_t         control;
+       } read_position;
+
+#define SRW_OPT_FIXED  0x01
+#define SRW_OPT_SILI   0x02
+       struct {
+               uchar_t         opcode;
+               uchar_t         opt;
+               uchar_t         transfer_len[3];
+               uchar_t         control;
+       } sa_read_write;
+
+       struct {
+               uchar_t         opcode;
+               uchar_t         opt;
+               uchar_t         rsvd[3];
+               uchar_t         control;
+       } rewind;
+
+       struct {
+               uchar_t         opcode;
+#define SPACE_CODE_BLOCKS              0x00
+#define SPACE_CODE_FMRKS               0x01
+#define SPACE_CODE_SEQ_FMRKS   0x02
+#define SPACE_CODE_EOD                 0x03
+#define SPACE_CODE_SMRKS               0x04
+#define SPACE_CODE_SEQ_SMRKS   0x05
+               uchar_t         lun_code;
+               uchar_t         count[3];
+               uchar_t         control;
+       } space;
+
+       struct {
+               uchar_t         opcode;
+#define WF_OPT_IMMED   0x01
+#define WF_OPT_WSMK            0x02
+               uchar_t         opt;
+               uchar_t         transfer_length[3];
+               uchar_t         control;
+       } write_filemarks;
+
+       struct {
+               uchar_t         opcode;
+#define RD_OPT_MEDIA   0x01
+               uchar_t         opt;
+               uchar_t         rsvd[5];
+               uchar_t         allocation_length[2];
+               uchar_t         control;
+       } report_density;
+
+       struct {
+               uchar_t         opcode;
+#define FM_OPT_IMMED   0x01
+#define FM_OPT_VERIFY  0x02
+               uchar_t         opt;
+#define FM_FMT_DFLT                            0x00
+#define FM_FMT_PARTITION               0x01
+#define FM_FMT_FORMAT_PARTITION        0x02
+               uchar_t         format;
+               uchar_t         transfer_length[2];
+               uchar_t         control;
+       } format_media;
+} CDB;
+//----------------------------------------------------------------------------------------------------------
+
+struct _ata_sense
+ {
+  SCSI_SENSE_DESCRIPTOR               sense;
+  ATA_STATUS_DESCRIPTOR               desc;
+ };
+//----------------------------------------------------------------------------------------------------------
+
+
+#endif /* OS_QNXNTO_H_ */
index 13dcd7f229c78bedb9b98a07eedad601666d6ca9..a3601d0785f2669a9553ecfca843229e0d11da54 100644 (file)
@@ -39,9 +39,9 @@
 
 extern long long bytes;
 
-static const char *filenameandversion="$Id: os_solaris.cpp,v 1.30 2008/03/04 22:09:47 ballen4705 Exp $";
+static const char *filenameandversion="$Id: os_solaris.cpp,v 1.32 2008/06/12 21:46:31 ballen4705 Exp $";
 
-const char *os_XXXX_c_cvsid="$Id: os_solaris.cpp,v 1.30 2008/03/04 22:09:47 ballen4705 Exp $" \
+const char *os_XXXX_c_cvsid="$Id: os_solaris.cpp,v 1.32 2008/06/12 21:46:31 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
@@ -336,7 +336,7 @@ int ata_command_interface(int fd, smart_command_set command, int select, char *d
        return smart_status_check(fd);
     default:
        pout("Unrecognized command %d in ata_command_interface() of os_solaris.c\n", command);
-       exit(1);
+       EXIT(1);
        break;
     }
 #else /* __sparc */
@@ -362,6 +362,15 @@ int escalade_command_interface(int fd, int disknum, int escalade_type, smart_com
   return -1;
 }
 
+int areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data){
+  ARGUSED(fd);  ARGUSED(disknum);
+  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>
index 159af67cdf82515771e84fd099554a56c34d1c3b..d9d99e93c406ae28ad71811f93f027597a0cd59b 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2004-8 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2004-9 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
@@ -11,8 +11,7 @@
  * 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.
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
  *
  */
 
 extern smartmonctrl * con; // con->permissive,reportataioctl
 #include "scsicmds.h"
 #include "utility.h"
-extern int64_t bytes; // malloc() byte count
+
+#include "dev_interface.h"
+#include "dev_ata_cmd_set.h"
 
 #include <errno.h>
+
 #ifdef _DEBUG
 #include <assert.h>
 #else
+#undef assert
 #define assert(x) /**/
 #endif
+
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <stddef.h> // offsetof()
@@ -44,134 +48,299 @@ extern int64_t bytes; // malloc() byte count
 
 
 // Needed by '-V' option (CVS versioning) of smartd/smartctl
-const char *os_XXXX_c_cvsid="$Id: os_win32.cpp,v 1.61 2008/03/04 22:09:47 ballen4705 Exp $"
+const char *os_XXXX_c_cvsid="$Id: os_win32.cpp,v 1.75 2009/06/02 19:43:06 chrfranke Exp $"
 ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 
+/////////////////////////////////////////////////////////////////////////////
+
+namespace os_win32 { // no need to publish anything, name provided for Doxygen
+
+#ifdef _MSC_VER
+#pragma warning(disable:4250)
+#endif
+
+class win_smart_device
+: virtual public /*implements*/ smart_device
+{
+public:
+  win_smart_device()
+    : smart_device(never_called),
+      m_fh(INVALID_HANDLE_VALUE)
+    { }
+
+  virtual ~win_smart_device() throw();
+
+  virtual bool is_open() const;
+
+  virtual bool close();
+
+protected:
+  /// Set handle for open() in derived classes.
+  void set_fh(HANDLE fh)
+    { m_fh = fh; }
+
+  /// Return handle for derived classes.
+  HANDLE get_fh() const
+    { return m_fh; }
+
+private:
+  HANDLE m_fh; ///< File handle
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+class win_ata_device
+: public /*implements*/ ata_device,
+  public /*extends*/ win_smart_device
+{
+public:
+  win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+  virtual ~win_ata_device() throw();
+
+  virtual bool open();
+
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
+
+  virtual bool ata_identify_is_cached() const;
+
+private:
+  bool open(int phydrive, int logdrive, const char * options, int port);
+
+  std::string m_options;
+  bool m_usr_options; // options set by user?
+  bool m_admin; // open with admin access?
+  int m_drive, m_port;
+  int m_smartver_state;
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+class win_scsi_device
+: public /*implements*/ scsi_device,
+  virtual public /*extends*/ win_smart_device
+{
+public:
+  win_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+  virtual bool open();
+
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+
+private:
+  bool open(int pd_num, int ld_num, int tape_num, int sub_addr);
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+class win_aspi_device
+: public /*implements*/ scsi_device
+{
+public:
+  win_aspi_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+  virtual bool is_open() const;
+
+  virtual bool open();
+
+  virtual bool close();
+
+  virtual bool scsi_pass_through(scsi_cmnd_io * iop);
+
+private:
+  int m_adapter;
+  unsigned char m_id;
+};
+
+
+//////////////////////////////////////////////////////////////////////
+
+class win_tw_cli_device
+: public /*implements*/ ata_device_with_command_set
+{
+public:
+  win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type);
+
+  virtual bool is_open() const;
+
+  virtual bool open();
+
+  virtual bool close();
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+private:
+  bool m_ident_valid, m_smart_valid;
+  ata_identify_device m_ident_buf;
+  ata_smart_values m_smart_buf;
+};
+
+
+//////////////////////////////////////////////////////////////////////
+// Platform specific interfaces
+
+// Common to all windows flavors
+class win_smart_interface
+: public /*implements part of*/ smart_interface
+{
+public:
+  virtual const char * get_os_version_str();
+
+  virtual const char * get_app_examples(const char * appname);
+
+  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
+    const char * pattern = 0);
+
+protected:
+  virtual ata_device * get_ata_device(const char * name, const char * type);
+
+//virtual scsi_device * get_scsi_device(const char * name, const char * type);
+
+  virtual smart_device * autodetect_smart_device(const char * name);
+
+  virtual bool ata_scan(smart_device_list & devlist) = 0;
+
+  virtual bool scsi_scan(smart_device_list & devlist) = 0;
+};
+
+// Win9x/ME reduced functionality
+class win9x_smart_interface
+: public /*extends*/ win_smart_interface
+{
+protected:
+  virtual scsi_device * get_scsi_device(const char * name, const char * type);
+
+  virtual bool ata_scan(smart_device_list & devlist);
+
+  virtual bool scsi_scan(smart_device_list & devlist);
+};
+
+// WinNT,2000,XP,...
+class winnt_smart_interface
+: public /*extends*/ win_smart_interface
+{
+protected:
+  virtual scsi_device * get_scsi_device(const char * name, const char * type);
+
+  virtual smart_device * autodetect_smart_device(const char * name);
+
+  virtual bool ata_scan(smart_device_list & devlist);
+
+  virtual bool scsi_scan(smart_device_list & devlist);
+};
+
+
+//////////////////////////////////////////////////////////////////////
+
 // Running on Win9x/ME ?
 static inline bool is_win9x()
 {
-       return !!(GetVersion() & 0x80000000);
+  return !!(GetVersion() & 0x80000000);
 }
 
 // Running on 64-bit Windows as 32-bit app ?
 static bool is_wow64()
 {
-       HMODULE hk = GetModuleHandleA("kernel32");
-       if (!hk)
-               return false;
-       BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) =
-               (BOOL (WINAPI *)(HANDLE, PBOOL))GetProcAddress(hk, "IsWow64Process");
-       if (!IsWow64Process_p)
-               return false;
-       BOOL w64 = FALSE;
-       if (!IsWow64Process_p(GetCurrentProcess(), &w64))
-               return false;
-       return !!w64;
+  HMODULE hk = GetModuleHandleA("kernel32");
+  if (!hk)
+    return false;
+  BOOL (WINAPI * IsWow64Process_p)(HANDLE, PBOOL) =
+    (BOOL (WINAPI *)(HANDLE, PBOOL))GetProcAddress(hk, "IsWow64Process");
+  if (!IsWow64Process_p)
+    return false;
+  BOOL w64 = FALSE;
+  if (!IsWow64Process_p(GetCurrentProcess(), &w64))
+    return false;
+  return !!w64;
 }
 
-
-#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(64)-sp2.1")+13];
-       char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1;
-       const int vlen = sizeof(vstr)-(sizeof(SMARTMONTOOLS_BUILD_HOST)-3);
-
-       // 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));
-
-       OSVERSIONINFOEXA vi; 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;
-
-       const char * w;
-       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;
-       }
-
-       const char * w64 = (is_wow64() ? "(64)" : "");
-       if (!w)
-               snprintf(vptr, vlen, "-%s%lu.%lu%s",
-                       (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
-                       vi.dwMajorVersion, vi.dwMinorVersion, w64);
-       else if (vi.wServicePackMinor)
-               snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
-       else if (vi.wServicePackMajor)
-               snprintf(vptr, vlen, "-%s%s-sp%u", w, w64, vi.wServicePackMajor);
-       else
-               snprintf(vptr, vlen, "-%s%s", w, w64);
-       return vstr;
-}
-
-
-static int get_phy_drive_type(int drive);
-static int get_log_drive_type(int drive);
-
-#define ATARAID_FDOFFSET 0x0200
-
-static int ata_open(int phydrive, int logdrive, const char * options, int port);
-static void ata_close(int fd);
-static int ata_scan_win9x(unsigned long * drives);
-static int ata_scan(unsigned long * drives, int * rdriveno, unsigned long * rdrives);
-static const char * ata_get_def_options(void);
-
-#define TW_CLI_FDOFFSET 0x0300
-
-static int tw_cli_open(const char * name);
-static void tw_cli_close();
-
-#define ASPI_FDOFFSET 0x0100
+const char * win_smart_interface::get_os_version_str()
+{
+  static char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-1+sizeof("-2003r2(64)-sp2.1")+13]
+    = SMARTMONTOOLS_BUILD_HOST;
+  if (vstr[1] < '6')
+    vstr[1] = '6';
+  char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-1;
+  const int vlen = sizeof(vstr)-sizeof(SMARTMONTOOLS_BUILD_HOST);
+  assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
+
+  OSVERSIONINFOEXA vi; 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;
+
+  const char * w;
+  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 = (vi.wProductType == VER_NT_WORKSTATION   ?   "vista"
+                                                   :   "2008" );  break;
+    case VER_PLATFORM_WIN32_NT     <<16|0x0600| 1:
+      w = (vi.wProductType == VER_NT_WORKSTATION   ?   "win7"
+                                                   :   "2008r2"); break;
+    default: w = 0; break;
+  }
+
+  const char * w64 = (is_wow64() ? "(64)" : "");
+  if (!w)
+    snprintf(vptr, vlen, "-%s%lu.%lu%s",
+      (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
+      vi.dwMajorVersion, vi.dwMinorVersion, w64);
+  else if (vi.wServicePackMinor)
+    snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
+  else if (vi.wServicePackMajor)
+    snprintf(vptr, vlen, "-%s%s-sp%u", w, w64, vi.wServicePackMajor);
+  else
+    snprintf(vptr, vlen, "-%s%s", w, w64);
+  return vstr;
+}
 
-static int aspi_open(unsigned adapter, unsigned id);
-static void aspi_close(int fd);
-static int aspi_scan(unsigned long * drives);
+// Return value for device detection functions
+enum win_dev_type { DEV_UNKNOWN = 0, DEV_ATA, DEV_SCSI, DEV_USB };
 
-#define SPT_FDOFFSET 0x0400
+static win_dev_type get_phy_drive_type(int drive);
+static win_dev_type get_log_drive_type(int drive);
+static bool get_usb_id(int drive, unsigned short & vendor_id,
+                       unsigned short & product_id);
 
-static int spt_open(int pd_num, int ld_num, int tape_num, int sub_addr);
-static void spt_close(int fd);
-static int spt_scan(unsigned long * drives);
+static const char * ata_get_def_options(void);
 
 
 static int is_permissive()
 {
-       if (!con->permissive) {
-               pout("To continue, add one or more '-T permissive' options.\n");
-               return 0;
-       }
-       con->permissive--;
-       return 1;
+  if (!con->permissive) {
+    pout("To continue, add one or more '-T permissive' options.\n");
+    return 0;
+  }
+  con->permissive--;
+  return 1;
 }
 
 // return number for drive letter, -1 on error
@@ -179,263 +348,150 @@ static int is_permissive()
 // Accepts trailing '"' to fix broken "X:\" parameter passing from .bat files
 static int drive_letter(const char * s)
 {
-       return (   (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))
-               && s[1] == ':'
-               && (!s[2] || (   strchr("/\\\"", s[2])
-                             && (!s[3] || (s[3] == '.' && !s[4])))              ) ?
-               (s[0] & 0x1f) - 1 : -1);
+  return (   (('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))
+          && s[1] == ':'
+          && (!s[2] || (   strchr("/\\\"", s[2])
+                        && (!s[3] || (s[3] == '.' && !s[4])))              ) ?
+          (s[0] & 0x1f) - 1 : -1);
 }
 
 // Skip trailing "/dev/", do not allow "/dev/X:"
 static const char * skipdev(const char * s)
 {
-       return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? 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, "scsi", 4))
-               return CONTROLLER_SCSI;
-       if (is_win9x())
-               return CONTROLLER_ATA;
-       if (!strncmp(dev_name, "hd", 2))
-               return CONTROLLER_ATA;
-       if (!strncmp(dev_name, "tw_cli", 6))
-               return CONTROLLER_ATA;
-       if (!strncmp(dev_name, "st", 2))
-               return CONTROLLER_SCSI;
-       if (!strncmp(dev_name, "nst", 3))
-               return CONTROLLER_SCSI;
-       if (!strncmp(dev_name, "tape", 4))
-               return CONTROLLER_SCSI;
-       int logdrive = drive_letter(dev_name);
-       if (logdrive >= 0) {
-               int type = get_log_drive_type(logdrive);
-               return (type != CONTROLLER_UNKNOWN ? type : CONTROLLER_SCSI);
-       }
-       char drive[1+1] = "";
-       if (sscanf(dev_name, "sd%1[a-z]", drive) == 1)
-               return get_phy_drive_type(drive[0]-'a');
-       int phydrive = -1;
-       if (sscanf(dev_name, "pd%d", &phydrive) == 1 && phydrive >= 0)
-               return get_phy_drive_type(phydrive);
-       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;
-       
-       bool win9x = is_win9x();
-       if (!strcmp(type, "ATA")) {
-               // bit i set => drive i present
-               if (win9x) {
-                       n = ata_scan_win9x(drives);
-                       path = "/dev/hda";
-               }
-               else {
-                       n = ata_scan(drives, rdriveno, rdrives);
-                       path = "/dev/sda";
-               }
-               nmax = 10;
-       }
-       else if (!strcmp(type, "SCSI")) {
-               if (win9x) {
-                       // bit i set => drive with ID (i & 0x7) on adapter (i >> 3) present
-                       n = aspi_scan(drives);
-                       path = "/dev/scsi00";
-                       nmax = 10*8;
-               }
-               else {
-                       // bit i set => drive i present
-                       n = spt_scan(drives);
-                       path = "/dev/sda";
-                       nmax = 10;
-               }
-       }
-       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/sd%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)
-{
-       pathname = skipdev(pathname);
-       int len = strlen(pathname);
-
-       if (!strcmp(type, "ATA")) {
-               // [sh]d[a-z](:[saicmfp]+)? => Physical drive 0-25, with options
-               char drive[1+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1;
-               if (   sscanf(pathname, "%*[sh]d%1[a-z]%n:%7[saicmfp]%n", drive, &n1, options, &n2) >= 1
-                   && ((n1 == len && !options[0]) || n2 == len)                                       ) {
-                       return ata_open(drive[0] - 'a', -1, options, -1);
-               }
-               // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options
-               drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;
-               unsigned port = ~0;
-               if (   sscanf(pathname, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive, &port, &n1, options, &n2) >= 2
-                   && port < 32 && ((n1 == len && !options[0]) || n2 == len)                                     ) {
-                       return ata_open(drive[0] - 'a', -1, options, port);
-               }
-               // pd<m>,N => Physical drive <m>, RAID port N
-               int phydrive = -1; port = ~0; n1 = -1; n2 = -1;
-               if (   sscanf(pathname, "pd%d%n,%u%n", &phydrive, &n1, &port, &n2) >= 1
-                   && phydrive >= 0 && ((n1 == len && (int)port < 0) || (n2 == len && port < 32))) {
-                       return ata_open(phydrive, -1, "", (int)port);
-               }
-               // [a-zA-Z]: => Physical drive behind logical drive 0-25
-               int logdrive = drive_letter(pathname);
-               if (logdrive >= 0) {
-                       return ata_open(-1, logdrive, "", -1);
-               }
-               // tw_cli/... => Parse tw_cli output
-               if (!strncmp(pathname, "tw_cli/", 7)) {
-                       return tw_cli_open(pathname+7);
-               }
-       } else if (!strcmp(type, "SCSI")) {
-               // scsi[0-9][0-f] => ASPI Adapter 0-9, ID 0-15, LUN 0
-               unsigned adapter = ~0, id = ~0; int n1 = -1;
-               if (sscanf(pathname,"scsi%1u%1x%n", &adapter, &id, &n1) == 2 && n1 == len) {
-                       return aspi_open(adapter, id);
-               }
-               // sd[a-z],N => Physical drive 0-25, RAID port N
-               char drive[1+1] = ""; int sub_addr = -1; n1 = -1; int n2 = -1;
-               if (   sscanf(pathname, "sd%1[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1
-                   && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))      ) {
-                       return spt_open(drive[0] - 'a', -1, -1, sub_addr);
-               }
-               // pd<m>,N => Physical drive <m>, RAID port N
-               int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1;
-               if (   sscanf(pathname, "pd%d%n,%d%n", &pd_num, &n1, &sub_addr, &n2) >= 1
-                   && pd_num >= 0 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))) {
-                       return spt_open(pd_num, -1, -1, sub_addr);
-               }
-               // [a-zA-Z]: => Physical drive behind logical drive 0-25
-               int logdrive = drive_letter(pathname);
-               if (logdrive >= 0) {
-                       return spt_open(-1, logdrive, -1, -1);
-               }
-               // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
-               int tape_num = -1; n1 = -1;
-               if (sscanf(pathname, "st%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
-                       return spt_open(-1, -1, tape_num, -1);
-               }
-               tape_num = -1; n1 = -1;
-               if (sscanf(pathname, "nst%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
-                       return spt_open(-1, -1, tape_num, -1);
-               }
-               // tape<m> => tape drive <m>
-               tape_num = -1; n1 = -1;
-               if (sscanf(pathname, "tape%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
-                       return spt_open(-1, -1, tape_num, -1);
-               }
-       }
-
-       errno = EINVAL;
-       return -1;
-}
-
-
-// Like close().  Acts only on handles returned by above function.
-int deviceclose(int fd)
-{
-       if ((fd & 0xff00) == ASPI_FDOFFSET)
-               aspi_close(fd);
-       else if (fd >= SPT_FDOFFSET)
-               spt_close(fd);
-       else if (fd == TW_CLI_FDOFFSET)
-               tw_cli_close();
-       else
-               ata_close(fd);
-       return 0;
-}
-
-
-// print examples for smartctl
-void print_smartctl_examples(){
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
+  return (!strncmp(s, "/dev/", 5) && drive_letter(s+5) < 0 ? s+5 : s);
+}
+
+ata_device * win_smart_interface::get_ata_device(const char * name, const char * type)
+{
+  const char * testname = skipdev(name);
+  if (!strncmp(testname, "tw_cli", 6))
+    return new win_tw_cli_device(this, name, type);
+  return new win_ata_device(this, name, type);
+}
+
+scsi_device * win9x_smart_interface::get_scsi_device(const char * name, const char * type)
+{
+  return new win_aspi_device(this, name, type);
+}
+
+scsi_device * winnt_smart_interface::get_scsi_device(const char * name, const char * type)
+{
+  const char * testname = skipdev(name);
+  if (!strncmp(testname, "scsi", 4))
+    return new win_aspi_device(this, name, type);
+  return new win_scsi_device(this, name, type);
+}
+
+static win_dev_type get_dev_type(const char * name, int & phydrive)
+{
+  phydrive = -1;
+  name = skipdev(name);
+  if (!strncmp(name, "st", 2))
+    return DEV_SCSI;
+  if (!strncmp(name, "nst", 3))
+    return DEV_SCSI;
+  if (!strncmp(name, "tape", 4))
+    return DEV_SCSI;
+
+  int logdrive = drive_letter(name);
+  if (logdrive >= 0) {
+    win_dev_type type = get_log_drive_type(logdrive);
+    return (type != DEV_UNKNOWN ? type : DEV_SCSI);
+  }
+
+  char drive[1+1] = "";
+  if (sscanf(name, "sd%1[a-z]", drive) == 1) {
+    phydrive = drive[0] - 'a';
+    return get_phy_drive_type(phydrive);
+  }
+
+  phydrive = -1;
+  if (sscanf(name, "pd%d", &phydrive) == 1 && phydrive >= 0)
+    return get_phy_drive_type(phydrive);
+  return DEV_UNKNOWN;
+}
+
+smart_device * win_smart_interface::autodetect_smart_device(const char * name)
+{
+  const char * testname = skipdev(name);
+  if (!strncmp(testname, "hd", 2))
+    return new win_ata_device(this, name, "");
+  if (!strncmp(testname, "scsi", 4))
+    return new win_aspi_device(this, name, "");
+  if (!strncmp(testname, "tw_cli", 6))
+    return new win_tw_cli_device(this, name, "");
+  return 0;
+}
+
+smart_device * winnt_smart_interface::autodetect_smart_device(const char * name)
+{
+  smart_device * dev = win_smart_interface::autodetect_smart_device(name);
+  if (dev)
+    return dev;
+
+  int phydrive = -1;
+  win_dev_type type = get_dev_type(name, phydrive);
+
+  if (type == DEV_ATA)
+    return new win_ata_device(this, name, "");
+  if (type == DEV_SCSI)
+    return new win_scsi_device(this, name, "");
+
+  if (type == DEV_USB) {
+    // Get USB bridge ID
+    unsigned short vendor_id = 0, product_id = 0;
+    if (!(phydrive >= 0 && get_usb_id(phydrive, vendor_id, product_id))) {
+      set_err(EINVAL, "Unable to read USB device ID");
+      return 0;
+    }
+    // Get type name for this ID
+    const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id);
+    if (!usbtype)
+      return 0;
+    // Return SAT/USB device for this type
+    return get_sat_device(usbtype, new win_scsi_device(this, name, ""));
+  }
+
+  return 0;
+}
+
+
+// makes a list of ATA or SCSI devices for the DEVICESCAN directive
+bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
+  const char * type, const char * pattern /* = 0*/)
+{
+  if (pattern) {
+    set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
+    return false;
+  }
+
+  if (!type || !strcmp(type, "ata")) {
+    if (!ata_scan(devlist))
+      return false;
+  }
+
+  if (!type || !strcmp(type, "scsi")) {
+    if (!scsi_scan(devlist))
+      return false;
+  }
+  return true;
+}
+
+
+// get examples for smartctl
+const char * win_smart_interface::get_app_examples(const char * appname)
+{
+  if (strcmp(appname, "smartctl"))
+    return 0;
+  static char buf[2048]; // TODO: Let this function return std::string ?
+  snprintf(buf, sizeof(buf),
+         "=================================================== 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/sda\n"
@@ -456,6 +512,7 @@ void print_smartctl_examples(){
          "  'f': IOCTL_STORAGE_*,        'm': IOCTL_SCSI_MINIPORT_*.\n"
          "  The default on this system is /dev/sdX:%s\n", ata_get_def_options()
   );
+  return buf;
 }
 
 
@@ -470,7 +527,7 @@ void print_smartctl_examples(){
 #define METHOD_BUFFERED             0
 #define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
 
-#define FILE_DEVICE_DISK          7
+#define FILE_DEVICE_DISK       7
 #define IOCTL_DISK_BASE        FILE_DEVICE_DISK
 
 #define SMART_GET_VERSION \
@@ -493,12 +550,12 @@ ASSERT_CONST(SMART_RCV_DRIVE_DATA    , 0x07c088);
 #pragma pack(1)
 
 typedef struct _GETVERSIONOUTPARAMS {
-       UCHAR  bVersion;
-       UCHAR  bRevision;
-       UCHAR  bReserved;
-       UCHAR  bIDEDeviceMap;
-       ULONG  fCapabilities;
-       ULONG  dwReserved[4];
+  UCHAR  bVersion;
+  UCHAR  bRevision;
+  UCHAR  bReserved;
+  UCHAR  bIDEDeviceMap;
+  ULONG  fCapabilities;
+  ULONG  dwReserved[4];
 } GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS;
 
 ASSERT_SIZEOF(GETVERSIONOUTPARAMS, 24);
@@ -507,50 +564,50 @@ 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];
+  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;
+  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];
+  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];
+  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));
@@ -571,16 +628,16 @@ ASSERT_SIZEOF(SENDCMDINPARAMS_EX, sizeof(SENDCMDINPARAMS));
 */
 
 typedef struct _DRIVERSTATUS {
-       UCHAR  bDriverError;
-       UCHAR  bIDEError;
-       UCHAR  bReserved[2];
-       ULONG  dwReserved[2];
+  UCHAR  bDriverError;
+  UCHAR  bIDEError;
+  UCHAR  bReserved[2];
+  ULONG  dwReserved[2];
 } DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS;
 
 typedef struct _SENDCMDOUTPARAMS {
-       ULONG  cBufferSize;
-       DRIVERSTATUS  DriverStatus;
-       UCHAR  bBuffer[1];
+  ULONG  cBufferSize;
+  DRIVERSTATUS  DriverStatus;
+  UCHAR  bBuffer[1];
 } SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS;
 
 ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1);
@@ -592,17 +649,17 @@ ASSERT_SIZEOF(SENDCMDOUTPARAMS, 16+1);
 
 static void print_ide_regs(const IDEREGS * r, int out)
 {
-       pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=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);
+  pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, SN=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);
-       }
+  pout("    Input : "); print_ide_regs(ri, 0);
+  if (ro) {
+    pout("    Output: "); print_ide_regs(ro, 1);
+  }
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -611,34 +668,34 @@ static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
 
 static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
 {
-       GETVERSIONOUTPARAMS vers; memset(&vers, 0, sizeof(vers));
-       const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
-       DWORD num_out;
-
-       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 (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);
-       }
-
-       if (ata_version_ex)
-               *ata_version_ex = vers_ex;
-
-       // TODO: Check vers.fCapabilities here?
-       return vers.bIDEDeviceMap;
+  GETVERSIONOUTPARAMS vers; memset(&vers, 0, sizeof(vers));
+  const GETVERSIONINPARAMS_EX & vers_ex = (const GETVERSIONINPARAMS_EX &)vers;
+  DWORD num_out;
+
+  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 (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);
+  }
+
+  if (ata_version_ex)
+    *ata_version_ex = vers_ex;
+
+  // TODO: Check vers.fCapabilities here?
+  return vers.bIDEDeviceMap;
 }
 
 
@@ -646,90 +703,93 @@ static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version
 
 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
-               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*/
-                        || err == ERROR_NOT_SUPPORTED ? 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;
+  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;
+  }
+
+  if (datasize == 512) {
+    code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
+    inpar.cBufferSize = size_out = 512;
+  }
+  else if (datasize == 0) {
+    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;
+  }
+  else {
+    errno = EINVAL;
+    return -1;
+  }
+
+  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*/
+             || err == ERROR_NOT_SUPPORTED ? 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;
 }
 
 
@@ -751,9 +811,9 @@ ASSERT_CONST(IOCTL_IDE_PASS_THROUGH, 0x04d028);
 #pragma pack(1)
 
 typedef struct {
-       IDEREGS IdeReg;
-       ULONG   DataBufferSize;
-       UCHAR   DataBuffer[1];
+  IDEREGS IdeReg;
+  ULONG   DataBufferSize;
+  UCHAR   DataBuffer[1];
 } ATA_PASS_THROUGH;
 
 ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1);
@@ -764,75 +824,75 @@ ASSERT_SIZEOF(ATA_PASS_THROUGH, 12+1);
 /////////////////////////////////////////////////////////////////////////////
 
 static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
-{ 
-       if (datasize > 512) {
-               errno = EINVAL;
-               return -1;
-       }
-       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 || err == ERROR_NOT_SUPPORTED ? 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;
+{
+  if (datasize > 512) {
+    errno = EINVAL;
+    return -1;
+  }
+  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 || err == ERROR_NOT_SUPPORTED ? 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;
 }
 
 
@@ -841,23 +901,23 @@ static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, u
 // 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)
+  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];
+  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);
@@ -866,101 +926,122 @@ ASSERT_SIZEOF(ATA_PASS_THROUGH_EX, 40);
 #define ATA_FLAGS_DATA_IN       0x02
 #define ATA_FLAGS_DATA_OUT      0x04
 #define ATA_FLAGS_48BIT_COMMAND 0x08
+#define ATA_FLAGS_USE_DMA       0x10
+#define ATA_FLAGS_NO_MULTIPLE   0x20 // Vista
 
 
 /////////////////////////////////////////////////////////////////////////////
 
-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;
-
-       const unsigned char magic = 0xcf;
-
-       ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; 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;
-       unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
-       ab.apt.DataBufferOffset = size;
-       if (datasize > 0) {
-               if (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 if (datasize < 0) {
-               if (-datasize > (int)sizeof(ab.ucDataBuf)) {
-                       errno = EINVAL;
-                       return -1;
-               }
-               ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT;
-               ab.apt.DataTransferLength = -datasize;
-               size += -datasize;
-               memcpy(ab.ucDataBuf, data, -datasize);
-       }
-       else {
-               assert(ab.apt.AtaFlags == 0);
-               assert(ab.apt.DataTransferLength == 0);
-       }
-
-       assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
-       IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
-       *ctfregs = *regs;
-
-       DWORD num_out;
-       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 failed, Error=%ld\n", err);
-                       print_ide_regs_io(regs, NULL);
-               }
-               errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
-               return -1;
-       }
-
-       // Check ATA status
-       if (ctfregs->bCommandReg/*Status*/ & 0x01) {
-               if (con->reportataioctl) {
-                       pout("  IOCTL_ATA_PASS_THROUGH command failed:\n");
-                       print_ide_regs_io(regs, ctfregs);
-               }
-               errno = EIO;
-               return -1;
-       }
-
-       // Check and copy data
-       if (datasize > 0) {
-               if (   num_out != size
-                   || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
-                       if (con->reportataioctl) {
-                               pout("  IOCTL_ATA_PASS_THROUGH 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 suceeded, bytes returned: %lu\n", num_out);
-               print_ide_regs_io(regs, ctfregs);
-       }
-       *regs = *ctfregs;
-
-       return 0;
+// Warning:
+// IOCTL_ATA_PASS_THROUGH[_DIRECT] can only handle one interrupt/DRQ data
+// transfer per command. Therefore, multi-sector transfers are only supported
+// for the READ/WRITE MULTIPLE [EXT] commands. Other commands like READ/WRITE SECTORS
+// or READ/WRITE LOG EXT work only with single sector transfers.
+// The latter are supported on Vista (only) through new ATA_FLAGS_NO_MULTIPLE.
+// See:
+// http://social.msdn.microsoft.com/Forums/en-US/storageplatformata/thread/eb408507-f221-455b-9bbb-d1069b29c4da
+
+static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev_regs, char * data, int datasize)
+{
+  const int max_sectors = 32; // TODO: Allocate dynamic buffer
+
+  typedef struct {
+    ATA_PASS_THROUGH_EX apt;
+    ULONG Filler;
+    UCHAR ucDataBuf[max_sectors * 512];
+  } ATA_PASS_THROUGH_EX_WITH_BUFFERS;
+
+  const unsigned char magic = 0xcf;
+
+  ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; 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;
+  unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
+  ab.apt.DataBufferOffset = size;
+
+  if (datasize > 0) {
+    if (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 if (datasize < 0) {
+    if (-datasize > (int)sizeof(ab.ucDataBuf)) {
+      errno = EINVAL;
+      return -1;
+    }
+    ab.apt.AtaFlags = ATA_FLAGS_DATA_OUT;
+    ab.apt.DataTransferLength = -datasize;
+    size += -datasize;
+    memcpy(ab.ucDataBuf, data, -datasize);
+  }
+  else {
+    assert(ab.apt.AtaFlags == 0);
+    assert(ab.apt.DataTransferLength == 0);
+  }
+
+  assert(sizeof(ab.apt.CurrentTaskFile) == sizeof(IDEREGS));
+  IDEREGS * ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile;
+  IDEREGS * ptfregs = (IDEREGS *)ab.apt.PreviousTaskFile;
+  *ctfregs = *regs;
+
+  if (prev_regs) {
+    *ptfregs = *prev_regs;
+    ab.apt.AtaFlags |= ATA_FLAGS_48BIT_COMMAND;
+  }
+
+  DWORD num_out;
+  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 failed, Error=%ld\n", err);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
+    return -1;
+  }
+
+  // Check ATA status
+  if (ctfregs->bCommandReg/*Status*/ & (0x01/*Err*/|0x08/*DRQ*/)) {
+    if (con->reportataioctl) {
+      pout("  IOCTL_ATA_PASS_THROUGH command failed:\n");
+      print_ide_regs_io(regs, ctfregs);
+    }
+    errno = EIO;
+    return -1;
+  }
+
+  // Check and copy data
+  if (datasize > 0) {
+    if (   num_out != size
+        || (ab.ucDataBuf[0] == magic && !nonempty(ab.ucDataBuf+1, datasize-1))) {
+      if (con->reportataioctl) {
+        pout("  IOCTL_ATA_PASS_THROUGH 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 suceeded, bytes returned: %lu\n", num_out);
+    print_ide_regs_io(regs, ctfregs);
+  }
+  *regs = *ctfregs;
+  if (prev_regs)
+    *prev_regs = *ptfregs;
+
+  return 0;
 }
 
 
@@ -969,7 +1050,7 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, i
 // ATA PASS THROUGH via SCSI PASS THROUGH (WinNT4 only)
 
 #define IOCTL_SCSI_PASS_THROUGH \
-       CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+  CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 
 ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004);
 
@@ -980,19 +1061,19 @@ ASSERT_CONST(IOCTL_SCSI_PASS_THROUGH, 0x04d004);
 #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];
+  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);
@@ -1002,80 +1083,80 @@ 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 || err == ERROR_NOT_SUPPORTED ? 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;
+  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 || err == ERROR_NOT_SUPPORTED ? 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;
 }
 
 
@@ -1089,17 +1170,17 @@ static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char
 // command codes (e.g. READ_LOG) in the disk class driver (disk.sys)
 
 #define IOCTL_SCSI_MINIPORT \
-       CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+  CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 
 ASSERT_CONST(IOCTL_SCSI_MINIPORT, 0x04d008);
 
 typedef struct _SRB_IO_CONTROL {
-       ULONG HeaderLength;
-       UCHAR Signature[8];
-       ULONG Timeout;
-       ULONG ControlCode;
-       ULONG ReturnCode;
-       ULONG Length;
+  ULONG HeaderLength;
+  UCHAR Signature[8];
+  ULONG Timeout;
+  ULONG ControlCode;
+  ULONG ReturnCode;
+  ULONG Length;
 } SRB_IO_CONTROL, *PSRB_IO_CONTROL;
 
 ASSERT_SIZEOF(SRB_IO_CONTROL, 28);
@@ -1124,128 +1205,128 @@ ASSERT_SIZEOF(SRB_IO_CONTROL, 28);
 
 static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize)
 {
-       // Select code
-       DWORD code = 0; const char * name = 0;
-       if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) {
-               code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY";
-       }
-       else if (regs->bCommandReg == ATA_SMART_CMD) switch (regs->bFeaturesReg) {
-         case ATA_SMART_READ_VALUES:
-               code = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; name = "READ_SMART_ATTRIBS"; break;
-         case ATA_SMART_READ_THRESHOLDS:
-               code = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; name = "READ_SMART_THRESHOLDS"; break;
-         case ATA_SMART_ENABLE:
-               code = IOCTL_SCSI_MINIPORT_ENABLE_SMART; name = "ENABLE_SMART"; break;
-         case ATA_SMART_DISABLE:
-               code = IOCTL_SCSI_MINIPORT_DISABLE_SMART; name = "DISABLE_SMART"; break;
-         case ATA_SMART_STATUS:
-               code = IOCTL_SCSI_MINIPORT_RETURN_STATUS; name = "RETURN_STATUS"; break;
-         case ATA_SMART_AUTOSAVE:
-               code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; name = "ENABLE_DISABLE_AUTOSAVE"; break;
-       //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
-       //      code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
-         case ATA_SMART_IMMEDIATE_OFFLINE:
-               code = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; name = "EXECUTE_OFFLINE_DIAGS"; break;
-         case ATA_SMART_AUTO_OFFLINE:
-               code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; name = "ENABLE_DISABLE_AUTO_OFFLINE"; break;
-         case ATA_SMART_READ_LOG_SECTOR:
-               code = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; name = "READ_SMART_LOG"; break;
-         case ATA_SMART_WRITE_LOG_SECTOR:
-               code = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; name = "WRITE_SMART_LOG"; break;
-       }
-       if (!code) {
-               errno = ENOSYS;
-               return -1;
-       }
-
-       // Set SRB
-       struct {
-               SRB_IO_CONTROL srbc;
-               union {
-                       SENDCMDINPARAMS in;
-                       SENDCMDOUTPARAMS out;
-               } params;
-               char space[512-1];
-       } sb;
-       ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
-       memset(&sb, 0, sizeof(sb));
-
-       unsigned size;
-       if (datasize > 0) {
-               if (datasize > (int)sizeof(sb.space)+1) {
-                       errno = EINVAL;
-                       return -1;
-               }
-               size = datasize;
-       }
-       else if (datasize < 0) {
-               if (-datasize > (int)sizeof(sb.space)+1) {
-                       errno = EINVAL;
-                       return -1;
-               }
-               size = -datasize;
-               memcpy(sb.params.in.bBuffer, data, size);
-       }
-       else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
-               size = sizeof(IDEREGS);
-       else
-               size = 0;
-       sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
-       memcpy(sb.srbc.Signature, "SCSIDISK", 8); // atapi.sys
-       sb.srbc.Timeout = 60; // seconds
-       sb.srbc.ControlCode = code;
-       //sb.srbc.ReturnCode = 0;
-       sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size;
-       sb.params.in.irDriveRegs = *regs;
-       sb.params.in.cBufferSize = size;
-
-       // Call miniport ioctl
-       size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
-       DWORD num_out;
-       if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
-               &sb, size, &sb, size, &num_out, NULL)) {
-               long err = GetLastError();
-               if (con->reportataioctl) {
-                       pout("  IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err);
-                       print_ide_regs_io(regs, NULL);
-               }
-               errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
-               return -1;
-       }
-
-       // Check result
-       if (sb.srbc.ReturnCode) {
-               if (con->reportataioctl) {
-                       pout("  IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name, sb.srbc.ReturnCode);
-                       print_ide_regs_io(regs, NULL);
-               }
-               errno = EIO;
-               return -1;
-       }
-
-       if (sb.params.out.DriverStatus.bDriverError) {
-               if (con->reportataioctl) {
-                       pout("  IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
-                               sb.params.out.DriverStatus.bDriverError, sb.params.out.DriverStatus.bIDEError);
-                       print_ide_regs_io(regs, NULL);
-               }
-               errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO);
-               return -1;
-       }
-
-       if (con->reportataioctl > 1) {
-               pout("  IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name,
-                       num_out, sb.params.out.cBufferSize);
-               print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
-                                        (const IDEREGS *)(sb.params.out.bBuffer) : 0));
-       }
-
-       if (datasize > 0)
-               memcpy(data, sb.params.out.bBuffer, datasize);
-       else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
-               *regs = *(const IDEREGS *)(sb.params.out.bBuffer);
-
-       return 0;
+  // Select code
+  DWORD code = 0; const char * name = 0;
+  if (regs->bCommandReg == ATA_IDENTIFY_DEVICE) {
+    code = IOCTL_SCSI_MINIPORT_IDENTIFY; name = "IDENTIFY";
+  }
+  else if (regs->bCommandReg == ATA_SMART_CMD) switch (regs->bFeaturesReg) {
+    case ATA_SMART_READ_VALUES:
+      code = IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS; name = "READ_SMART_ATTRIBS"; break;
+    case ATA_SMART_READ_THRESHOLDS:
+      code = IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS; name = "READ_SMART_THRESHOLDS"; break;
+    case ATA_SMART_ENABLE:
+      code = IOCTL_SCSI_MINIPORT_ENABLE_SMART; name = "ENABLE_SMART"; break;
+    case ATA_SMART_DISABLE:
+      code = IOCTL_SCSI_MINIPORT_DISABLE_SMART; name = "DISABLE_SMART"; break;
+    case ATA_SMART_STATUS:
+      code = IOCTL_SCSI_MINIPORT_RETURN_STATUS; name = "RETURN_STATUS"; break;
+    case ATA_SMART_AUTOSAVE:
+      code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE; name = "ENABLE_DISABLE_AUTOSAVE"; break;
+  //case ATA_SMART_SAVE: // obsolete since ATA-6, not used by smartmontools
+  //  code = IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES; name = "SAVE_ATTRIBUTE_VALUES"; break;
+    case ATA_SMART_IMMEDIATE_OFFLINE:
+      code = IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS; name = "EXECUTE_OFFLINE_DIAGS"; break;
+    case ATA_SMART_AUTO_OFFLINE:
+      code = IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE; name = "ENABLE_DISABLE_AUTO_OFFLINE"; break;
+    case ATA_SMART_READ_LOG_SECTOR:
+      code = IOCTL_SCSI_MINIPORT_READ_SMART_LOG; name = "READ_SMART_LOG"; break;
+    case ATA_SMART_WRITE_LOG_SECTOR:
+      code = IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG; name = "WRITE_SMART_LOG"; break;
+  }
+  if (!code) {
+    errno = ENOSYS;
+    return -1;
+  }
+
+  // Set SRB
+  struct {
+    SRB_IO_CONTROL srbc;
+    union {
+      SENDCMDINPARAMS in;
+      SENDCMDOUTPARAMS out;
+    } params;
+    char space[512-1];
+  } sb;
+  ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(SENDCMDINPARAMS)-1+512);
+  memset(&sb, 0, sizeof(sb));
+
+  unsigned size;
+  if (datasize > 0) {
+    if (datasize > (int)sizeof(sb.space)+1) {
+      errno = EINVAL;
+      return -1;
+    }
+    size = datasize;
+  }
+  else if (datasize < 0) {
+    if (-datasize > (int)sizeof(sb.space)+1) {
+      errno = EINVAL;
+      return -1;
+    }
+    size = -datasize;
+    memcpy(sb.params.in.bBuffer, data, size);
+  }
+  else if (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
+    size = sizeof(IDEREGS);
+  else
+    size = 0;
+  sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
+  memcpy(sb.srbc.Signature, "SCSIDISK", 8); // atapi.sys
+  sb.srbc.Timeout = 60; // seconds
+  sb.srbc.ControlCode = code;
+  //sb.srbc.ReturnCode = 0;
+  sb.srbc.Length = sizeof(SENDCMDINPARAMS)-1 + size;
+  sb.params.in.irDriveRegs = *regs;
+  sb.params.in.cBufferSize = size;
+
+  // Call miniport ioctl
+  size += sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDINPARAMS)-1;
+  DWORD num_out;
+  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
+    &sb, size, &sb, size, &num_out, NULL)) {
+    long err = GetLastError();
+    if (con->reportataioctl) {
+      pout("  IOCTL_SCSI_MINIPORT_%s failed, Error=%ld\n", name, err);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO);
+    return -1;
+  }
+
+  // Check result
+  if (sb.srbc.ReturnCode) {
+    if (con->reportataioctl) {
+      pout("  IOCTL_SCSI_MINIPORT_%s failed, ReturnCode=0x%08lx\n", name, sb.srbc.ReturnCode);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = EIO;
+    return -1;
+  }
+
+  if (sb.params.out.DriverStatus.bDriverError) {
+    if (con->reportataioctl) {
+      pout("  IOCTL_SCSI_MINIPORT_%s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
+        sb.params.out.DriverStatus.bDriverError, sb.params.out.DriverStatus.bIDEError);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = (!sb.params.out.DriverStatus.bIDEError ? ENOSYS : EIO);
+    return -1;
+  }
+
+  if (con->reportataioctl > 1) {
+    pout("  IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %lu (buffer %lu)\n", name,
+      num_out, sb.params.out.cBufferSize);
+    print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
+                             (const IDEREGS *)(sb.params.out.bBuffer) : 0));
+  }
+
+  if (datasize > 0)
+    memcpy(data, sb.params.out.bBuffer, datasize);
+  else if (datasize == 0 && code == IOCTL_SCSI_MINIPORT_RETURN_STATUS)
+    *regs = *(const IDEREGS *)(sb.params.out.bBuffer);
+
+  return 0;
 }
 
 
@@ -1255,59 +1336,59 @@ static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, cha
 
 static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, int datasize, int port)
 {
-       struct {
-               SRB_IO_CONTROL srbc;
-               IDEREGS regs;
-               UCHAR buffer[512];
-       } sb;
-       ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512);
-
-       if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) {
-               errno = EINVAL;
-               return -1;
-       }
-       memset(&sb, 0, sizeof(sb));
-       strcpy((char *)sb.srbc.Signature, "<3ware>");
-       sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
-       sb.srbc.Timeout = 60; // seconds
-       sb.srbc.ControlCode = 0xA0000000;
-       sb.srbc.ReturnCode = 0;
-       sb.srbc.Length = sizeof(IDEREGS) + (datasize > 0 ? datasize : 1);
-       sb.regs = *regs;
-       sb.regs.bReserved = port;
-
-       DWORD num_out;
-       if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
-               &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
-               long err = GetLastError();
-               if (con->reportataioctl) {
-                       pout("  ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
-                       print_ide_regs_io(regs, NULL);
-               }
-               errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
-               return -1;
-       }
-
-       if (sb.srbc.ReturnCode) {
-               if (con->reportataioctl) {
-                       pout("  ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb.srbc.ReturnCode);
-                       print_ide_regs_io(regs, NULL);
-               }
-               errno = EIO;
-               return -1;
-       }
-
-       // Copy data
-       if (datasize > 0)
-               memcpy(data, sb.buffer, datasize);
-
-       if (con->reportataioctl > 1) {
-               pout("  ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out);
-               print_ide_regs_io(regs, &sb.regs);
-       }
-       *regs = sb.regs;
-
-       return 0;
+  struct {
+    SRB_IO_CONTROL srbc;
+    IDEREGS regs;
+    UCHAR buffer[512];
+  } sb;
+  ASSERT_SIZEOF(sb, sizeof(SRB_IO_CONTROL)+sizeof(IDEREGS)+512);
+
+  if (!(0 <= datasize && datasize <= (int)sizeof(sb.buffer) && port >= 0)) {
+    errno = EINVAL;
+    return -1;
+  }
+  memset(&sb, 0, sizeof(sb));
+  strcpy((char *)sb.srbc.Signature, "<3ware>");
+  sb.srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
+  sb.srbc.Timeout = 60; // seconds
+  sb.srbc.ControlCode = 0xA0000000;
+  sb.srbc.ReturnCode = 0;
+  sb.srbc.Length = sizeof(IDEREGS) + (datasize > 0 ? datasize : 1);
+  sb.regs = *regs;
+  sb.regs.bReserved = port;
+
+  DWORD num_out;
+  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
+    &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
+    long err = GetLastError();
+    if (con->reportataioctl) {
+      pout("  ATA via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+    return -1;
+  }
+
+  if (sb.srbc.ReturnCode) {
+    if (con->reportataioctl) {
+      pout("  ATA via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", sb.srbc.ReturnCode);
+      print_ide_regs_io(regs, NULL);
+    }
+    errno = EIO;
+    return -1;
+  }
+
+  // Copy data
+  if (datasize > 0)
+    memcpy(data, sb.buffer, datasize);
+
+  if (con->reportataioctl > 1) {
+    pout("  ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %lu\n", num_out);
+    print_ide_regs_io(regs, &sb.regs);
+  }
+  *regs = sb.regs;
+
+  return 0;
 }
 
 
@@ -1318,33 +1399,33 @@ static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * d
 
 static int update_3ware_devicemap_ioctl(HANDLE hdevice)
 {
-       SRB_IO_CONTROL srbc;
-       memset(&srbc, 0, sizeof(srbc));
-       strcpy((char *)srbc.Signature, "<3ware>");
-       srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
-       srbc.Timeout = 60; // seconds
-       srbc.ControlCode = 0xCC010014;
-       srbc.ReturnCode = 0;
-       srbc.Length = 0;
-
-       DWORD num_out;
-       if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
-               &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) {
-               long err = GetLastError();
-               if (con->reportataioctl)
-                       pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
-               errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
-               return -1;
-       }
-       if (srbc.ReturnCode) {
-               if (con->reportataioctl)
-                       pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc.ReturnCode);
-               errno = EIO;
-               return -1;
-       }
-       if (con->reportataioctl > 1)
-               pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
-       return 0;
+  SRB_IO_CONTROL srbc;
+  memset(&srbc, 0, sizeof(srbc));
+  strcpy((char *)srbc.Signature, "<3ware>");
+  srbc.HeaderLength = sizeof(SRB_IO_CONTROL);
+  srbc.Timeout = 60; // seconds
+  srbc.ControlCode = 0xCC010014;
+  srbc.ReturnCode = 0;
+  srbc.Length = 0;
+
+  DWORD num_out;
+  if (!DeviceIoControl(hdevice, IOCTL_SCSI_MINIPORT,
+    &srbc, sizeof(srbc), &srbc, sizeof(srbc), &num_out, NULL)) {
+    long err = GetLastError();
+    if (con->reportataioctl)
+      pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, Error=%ld\n", err);
+    errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+    return -1;
+  }
+  if (srbc.ReturnCode) {
+    if (con->reportataioctl)
+      pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT failed, ReturnCode=0x%08lx\n", srbc.ReturnCode);
+    errno = EIO;
+    return -1;
+  }
+  if (con->reportataioctl > 1)
+    pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
+  return 0;
 }
 
 
@@ -1359,21 +1440,21 @@ static int update_3ware_devicemap_ioctl(HANDLE hdevice)
 
 static int get_clipboard(char * data, int datasize)
 {
-       if (!OpenClipboard(NULL))
-               return -1;
-       HANDLE h = GetClipboardData(CF_TEXT);
-       if (!h) {
-               CloseClipboard();
-               return 0;
-       }
-       const void * p = GlobalLock(h);
-       int n = GlobalSize(h);
-       if (n > datasize)
-               n = datasize;
-       memcpy(data, p, n);
-       GlobalFree(h);
-       CloseClipboard();
-       return n;
+  if (!OpenClipboard(NULL))
+    return -1;
+  HANDLE h = GetClipboardData(CF_TEXT);
+  if (!h) {
+    CloseClipboard();
+    return 0;
+  }
+  const void * p = GlobalLock(h);
+  int n = GlobalSize(h);
+  if (n > datasize)
+    n = datasize;
+  memcpy(data, p, n);
+  GlobalFree(h);
+  CloseClipboard();
+  return n;
 }
 
 
@@ -1382,232 +1463,241 @@ static int get_clipboard(char * data, int datasize)
 
 static int run_cmd(const char * cmd, char * dataout, int outsize)
 {
-       // Create stdout pipe
-       SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE};
-       HANDLE pipe_out_w, h;
-       if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize))
-               return -1;
-       HANDLE self = GetCurrentProcess();
-       HANDLE pipe_out_r;
-       if (!DuplicateHandle(self, h, self, &pipe_out_r,
-               GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
-               CloseHandle(pipe_out_w);
-               return -1;
-       }
-       HANDLE pipe_err_w;
-       if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
-               0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
-               CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
-               return -1;
-       }
-
-       // Create process
-       STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
-       si.hStdInput  = INVALID_HANDLE_VALUE;
-       si.hStdOutput = pipe_out_w; si.hStdError  = pipe_err_w;
-       si.dwFlags = STARTF_USESTDHANDLES;
-       PROCESS_INFORMATION pi;
-       if (!CreateProcess(
-               NULL, const_cast<char *>(cmd),
-               NULL, NULL, TRUE/*inherit*/,
-               CREATE_NO_WINDOW/*do not create a new console window*/,
-               NULL, NULL, &si, &pi)) {
-               CloseHandle(pipe_err_w); CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
-               return -1;
-       }
-       CloseHandle(pi.hThread);
-       CloseHandle(pipe_err_w); CloseHandle(pipe_out_w);
-
-       // Copy stdout to output buffer
-       int i = 0;
-       while (i < outsize) {
-               DWORD num_read;
-               if (!ReadFile(pipe_out_r, dataout+i, outsize-i, &num_read, NULL) || num_read == 0)
-                       break;
-               i += num_read;
-       }
-       CloseHandle(pipe_out_r);
-       // Wait for process
-       WaitForSingleObject(pi.hProcess, INFINITE);
-       CloseHandle(pi.hProcess);
-       return i;
+  // Create stdout pipe
+  SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE};
+  HANDLE pipe_out_w, h;
+  if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize))
+    return -1;
+  HANDLE self = GetCurrentProcess();
+  HANDLE pipe_out_r;
+  if (!DuplicateHandle(self, h, self, &pipe_out_r,
+    GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
+    CloseHandle(pipe_out_w);
+    return -1;
+  }
+  HANDLE pipe_err_w;
+  if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
+    0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
+    CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
+    return -1;
+  }
+
+  // Create process
+  STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
+  si.hStdInput  = INVALID_HANDLE_VALUE;
+  si.hStdOutput = pipe_out_w; si.hStdError  = pipe_err_w;
+  si.dwFlags = STARTF_USESTDHANDLES;
+  PROCESS_INFORMATION pi;
+  if (!CreateProcess(
+    NULL, const_cast<char *>(cmd),
+    NULL, NULL, TRUE/*inherit*/,
+    CREATE_NO_WINDOW/*do not create a new console window*/,
+    NULL, NULL, &si, &pi)) {
+    CloseHandle(pipe_err_w); CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
+    return -1;
+  }
+  CloseHandle(pi.hThread);
+  CloseHandle(pipe_err_w); CloseHandle(pipe_out_w);
+
+  // Copy stdout to output buffer
+  int i = 0;
+  while (i < outsize) {
+    DWORD num_read;
+    if (!ReadFile(pipe_out_r, dataout+i, outsize-i, &num_read, NULL) || num_read == 0)
+      break;
+    i += num_read;
+  }
+  CloseHandle(pipe_out_r);
+  // Wait for process
+  WaitForSingleObject(pi.hProcess, INFINITE);
+  CloseHandle(pi.hProcess);
+  return i;
 }
 
 
 static const char * findstr(const char * str, const char * sub)
 {
-       const char * s = strstr(str, sub);
-       return (s ? s+strlen(sub) : "");
+  const char * s = strstr(str, sub);
+  return (s ? s+strlen(sub) : "");
 }
 
 
 static void copy_swapped(unsigned char * dest, const char * src, int destsize)
 {
-       int srclen = strcspn(src, "\r\n");
-       int i;
-       for (i = 0; i < destsize-1 && i < srclen-1; i+=2) {
-               dest[i] = src[i+1]; dest[i+1] = src[i];
-       }
-       if (i < destsize-1 && i < srclen)
-               dest[i+1] = src[i];
-}
-
-
-static ata_identify_device * tw_cli_identbuf = 0;
-static ata_smart_values * tw_cli_smartbuf = 0;
-
-static int tw_cli_open(const char * name)
-{
-       // Read tw_cli or 3DM browser output into buffer
-       char buffer[4096];
-       int size = -1, n1 = -1;
-       if (!strcmp(name, "clip")) { // tw_cli/clip => read clipboard
-               size = get_clipboard(buffer, sizeof(buffer));
-       }
-       else if (!strcmp(name, "stdin")) {  // tw_cli/stdin => read stdin
-               size = fread(buffer, 1, sizeof(buffer), stdin);
-       }
-       else if (sscanf(name, "c%*u/p%*u%n", &n1) >= 0 && n1 == (int)strlen(name)) {
-               // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
-               char cmd[100];
-               snprintf(cmd, sizeof(cmd), "tw_cli /%s show all", name);
-               if (con->reportataioctl > 1)
-                       pout("tw_cli/%s: Run: \"%s\"\n", name, cmd);
-               size = run_cmd(cmd, buffer, sizeof(buffer));
-       }
-       else {
-               errno = EINVAL; return -1;
-       }
-
-       if (con->reportataioctl > 1)
-               pout("tw_cli/%s: Read %d bytes\n", name, size);
-       if (size <= 0) {
-               errno = ENOENT; return -1;
-       }
-       if (size >= (int)sizeof(buffer)) {
-               errno = EIO; return -1;
-       }
-       buffer[size] = 0;
-       if (con->reportataioctl > 1)
-               pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":""));
-
-       // Fake identify sector
-       ASSERT_SIZEOF(ata_identify_device, 512);
-       ata_identify_device * id = (ata_identify_device *)malloc(sizeof(ata_identify_device));
-       memset(id, 0, sizeof(*id));
-       copy_swapped(id->model    , findstr(buffer, " Model = "   ), sizeof(id->model));
-       copy_swapped(id->fw_rev   , findstr(buffer, " Firmware Version = "), sizeof(id->fw_rev));
-       copy_swapped(id->serial_no, findstr(buffer, " Serial = "  ), sizeof(id->serial_no));
-       unsigned long nblocks = 0; // "Capacity = N.N GB (N Blocks)"
-       sscanf(findstr(buffer, "Capacity = "), "%*[^(\r\n](%lu", &nblocks);
-       if (nblocks) {
-               id->words047_079[49-47] = 0x0200; // size valid
-               id->words047_079[60-47] = (unsigned short)(nblocks    ); // secs_16
-               id->words047_079[61-47] = (unsigned short)(nblocks>>16); // secs_32
-       }
-       id->major_rev_num = 0x1<<3; // ATA-3
-       id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
-       id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
-
-       // Parse smart data hex dump
-       const char * s = findstr(buffer, "Drive Smart Data:");
-       if (!*s) {
-               s = findstr(buffer, "S.M.A.R.T. (Controller"); // from 3DM browser window
-               if (*s) {
-                       const char * s1 = findstr(s, "<td class"); // html version
-                       if (*s1)
-                               s = s1;
-                       s += strcspn(s, "\r\n");
-               }
-               else
-                       s = buffer; // try raw hex dump without header
-       }
-       unsigned char * sd = (unsigned char *)malloc(512);
-       int i = 0;
-       for (;;) {
-               unsigned x = ~0; int n = -1;
-               if (!(sscanf(s, "%x %n", &x, &n) == 1 && !(x & ~0xff)))
-                       break;
-               sd[i] = (unsigned char)x;
-               if (!(++i < 512 && n > 0))
-                       break;
-               s += n;
-               if (*s == '<') // "<br>"
-                       s += strcspn(s, "\r\n");
-       }
-       if (i < 512) {
-               free(sd);
-               if (!id->model[1]) {
-                       // No useful data found
-                       free(id);
-                       char * err = strstr(buffer, "Error:");
-                       if (!err)
-                               err = strstr(buffer, "error :");
-                       if (err) {
-                               // Print tw_cli error message
-                               err[strcspn(err, "\r\n")] = 0;
-                               pout("%s\n", err);
-                       }
-                       errno = EIO;
-                       return -1;
-               }
-               sd = 0;
-       }
-
-       tw_cli_identbuf = id;
-       tw_cli_smartbuf = (ata_smart_values *)sd;
-       return TW_CLI_FDOFFSET;
-}
-
-
-static void tw_cli_close()
-{
-       if (tw_cli_identbuf) {
-               free(tw_cli_identbuf); tw_cli_identbuf = 0;
-       }
-       if (tw_cli_smartbuf) {
-               free(tw_cli_smartbuf); tw_cli_smartbuf = 0;
-       }
-}
-
-
-static int tw_cli_command_interface(smart_command_set command, int /*select*/, char * data)
-{
-       switch (command) {
-         case IDENTIFY:
-               if (!tw_cli_identbuf)
-                       break;
-               memcpy(data, tw_cli_identbuf, 512);
-               return 0;
-         case READ_VALUES:
-               if (!tw_cli_smartbuf)
-                       break;
-               memcpy(data, tw_cli_smartbuf, 512);
-               return 0;
-         case READ_THRESHOLDS:
-               if (!tw_cli_smartbuf)
-                       break;
-               // Fake zero thresholds
-               {
-                       const ata_smart_values   * sv = tw_cli_smartbuf;
-                       ata_smart_thresholds_pvt * tr = (ata_smart_thresholds_pvt *)data;
-                       memset(tr, 0, 512);
-                       // TODO: Indicate missing thresholds in ataprint.cpp:PrintSmartAttribWithThres()
-                       // (ATA_SMART_READ_THRESHOLDS is marked obsolete since ATA-5)
-                       for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++)
-                               tr->chksum -= tr->thres_entries[i].id = sv->vendor_attributes[i].id;
-               }
-               return 0;
-         case ENABLE:
-         case STATUS:
-         case STATUS_CHECK: // Fake "good" SMART status
-               return 0;
-         default:
-               break;
-       }
-       // Arrive here for all unsupported commands
-       errno = ENOSYS;
-       return -1;
+  int srclen = strcspn(src, "\r\n");
+  int i;
+  for (i = 0; i < destsize-1 && i < srclen-1; i+=2) {
+    dest[i] = src[i+1]; dest[i+1] = src[i];
+  }
+  if (i < destsize-1 && i < srclen)
+    dest[i+1] = src[i];
+}
+
+
+// TODO: This is OS independent
+
+win_tw_cli_device::win_tw_cli_device(smart_interface * intf, const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "tw_cli", req_type),
+  m_ident_valid(false), m_smart_valid(false)
+{
+  memset(&m_ident_buf, 0, sizeof(m_ident_buf));
+  memset(&m_smart_buf, 0, sizeof(m_smart_buf));
+}
+
+
+bool win_tw_cli_device::is_open() const
+{
+  return (m_ident_valid || m_smart_valid);
+}
+
+
+bool win_tw_cli_device::open()
+{
+  m_ident_valid = m_smart_valid = false;
+  const char * name = skipdev(get_dev_name());
+  // Read tw_cli or 3DM browser output into buffer
+  char buffer[4096];
+  int size = -1, n1 = -1, n2 = -1;
+  if (!strcmp(name, "tw_cli/clip")) { // read clipboard
+    size = get_clipboard(buffer, sizeof(buffer));
+  }
+  else if (!strcmp(name, "tw_cli/stdin")) {  // read stdin
+    size = fread(buffer, 1, sizeof(buffer), stdin);
+  }
+  else if (sscanf(name, "tw_cli/%nc%*u/p%*u%n", &n1, &n2) >= 0 && n2 == (int)strlen(name)) {
+    // tw_cli/cx/py => read output from "tw_cli /cx/py show all"
+    char cmd[100];
+    snprintf(cmd, sizeof(cmd), "tw_cli /%s show all", name+n1);
+    if (con->reportataioctl > 1)
+      pout("%s: Run: \"%s\"\n", name, cmd);
+    size = run_cmd(cmd, buffer, sizeof(buffer));
+  }
+  else {
+    return set_err(EINVAL);
+  }
+
+  if (con->reportataioctl > 1)
+    pout("%s: Read %d bytes\n", name, size);
+  if (size <= 0)
+    return set_err(ENOENT);
+  if (size >= (int)sizeof(buffer))
+    return set_err(EIO);
+
+  buffer[size] = 0;
+  if (con->reportataioctl > 1)
+    pout("[\n%.100s%s\n]\n", buffer, (size>100?"...":""));
+
+  // Fake identify sector
+  ASSERT_SIZEOF(ata_identify_device, 512);
+  ata_identify_device * id = &m_ident_buf;
+  memset(id, 0, sizeof(*id));
+  copy_swapped(id->model    , findstr(buffer, " Model = "   ), sizeof(id->model));
+  copy_swapped(id->fw_rev   , findstr(buffer, " Firmware Version = "), sizeof(id->fw_rev));
+  copy_swapped(id->serial_no, findstr(buffer, " Serial = "  ), sizeof(id->serial_no));
+  unsigned long nblocks = 0; // "Capacity = N.N GB (N Blocks)"
+  sscanf(findstr(buffer, "Capacity = "), "%*[^(\r\n](%lu", &nblocks);
+  if (nblocks) {
+    id->words047_079[49-47] = 0x0200; // size valid
+    id->words047_079[60-47] = (unsigned short)(nblocks    ); // secs_16
+    id->words047_079[61-47] = (unsigned short)(nblocks>>16); // secs_32
+  }
+  id->major_rev_num = 0x1<<3; // ATA-3
+  id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
+  id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
+
+  // Parse smart data hex dump
+  const char * s = findstr(buffer, "Drive Smart Data:");
+  if (!*s) {
+    s = findstr(buffer, "S.M.A.R.T. (Controller"); // from 3DM browser window
+    if (*s) {
+      const char * s1 = findstr(s, "<td class"); // html version
+      if (*s1)
+        s = s1;
+      s += strcspn(s, "\r\n");
+    }
+    else
+      s = buffer; // try raw hex dump without header
+  }
+  unsigned char * sd = (unsigned char *)&m_smart_buf;
+  int i = 0;
+  for (;;) {
+    unsigned x = ~0; int n = -1;
+    if (!(sscanf(s, "%x %n", &x, &n) == 1 && !(x & ~0xff)))
+      break;
+    sd[i] = (unsigned char)x;
+    if (!(++i < 512 && n > 0))
+      break;
+    s += n;
+    if (*s == '<') // "<br>"
+      s += strcspn(s, "\r\n");
+  }
+  if (i < 512) {
+    if (!id->model[1]) {
+      // No useful data found
+      char * err = strstr(buffer, "Error:");
+      if (!err)
+        err = strstr(buffer, "error :");
+      if (err && (err = strchr(err, ':'))) {
+        // Show tw_cli error message
+        err++;
+        err[strcspn(err, "\r\n")] = 0;
+        return set_err(EIO, err);
+      }
+      return set_err(EIO);
+    }
+    sd = 0;
+  }
+
+  m_ident_valid = true;
+  m_smart_valid = !!sd;
+  return true;
+}
+
+
+bool win_tw_cli_device::close()
+{
+  m_ident_valid = m_smart_valid = false;
+  return true;
+}
+
+
+int win_tw_cli_device::ata_command_interface(smart_command_set command, int /*select*/, char * data)
+{
+  switch (command) {
+    case IDENTIFY:
+      if (!m_ident_valid)
+        break;
+      memcpy(data, &m_ident_buf, 512);
+      return 0;
+    case READ_VALUES:
+      if (!m_smart_valid)
+        break;
+      memcpy(data, &m_smart_buf, 512);
+      return 0;
+    case READ_THRESHOLDS:
+      if (!m_smart_valid)
+        break;
+      // Fake zero thresholds
+      {
+        const ata_smart_values   * sv = &m_smart_buf;
+        ata_smart_thresholds_pvt * tr = (ata_smart_thresholds_pvt *)data;
+        memset(tr, 0, 512);
+        // TODO: Indicate missing thresholds in ataprint.cpp:PrintSmartAttribWithThres()
+        // (ATA_SMART_READ_THRESHOLDS is marked obsolete since ATA-5)
+        for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++)
+          tr->chksum -= tr->thres_entries[i].id = sv->vendor_attributes[i].id;
+      }
+      return 0;
+    case ENABLE:
+    case STATUS:
+    case STATUS_CHECK: // Fake "good" SMART status
+      return 0;
+    default:
+      break;
+  }
+  // Arrive here for all unsupported commands
+  set_err(ENOSYS);
+  return -1;
 }
 
 
@@ -1620,72 +1710,72 @@ static int tw_cli_command_interface(smart_command_set command, int /*select*/, c
 #define FILE_ANY_ACCESS             0
 
 #define IOCTL_STORAGE_QUERY_PROPERTY \
-       CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
+  CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
 typedef enum _STORAGE_BUS_TYPE {
-       BusTypeUnknown      = 0x00,
-       BusTypeScsi         = 0x01,
-       BusTypeAtapi        = 0x02,
-       BusTypeAta          = 0x03,
-       BusType1394         = 0x04,
-       BusTypeSsa          = 0x05,
-       BusTypeFibre        = 0x06,
-       BusTypeUsb          = 0x07,
-       BusTypeRAID         = 0x08,
-       BusTypeiScsi        = 0x09,
-       BusTypeSas          = 0x0A,
-       BusTypeSata         = 0x0B,
-       BusTypeSd           = 0x0C,
-       BusTypeMmc          = 0x0D,
-       BusTypeMax          = 0x0E,
-       BusTypeMaxReserved  = 0x7F
+  BusTypeUnknown      = 0x00,
+  BusTypeScsi         = 0x01,
+  BusTypeAtapi        = 0x02,
+  BusTypeAta          = 0x03,
+  BusType1394         = 0x04,
+  BusTypeSsa          = 0x05,
+  BusTypeFibre        = 0x06,
+  BusTypeUsb          = 0x07,
+  BusTypeRAID         = 0x08,
+  BusTypeiScsi        = 0x09,
+  BusTypeSas          = 0x0A,
+  BusTypeSata         = 0x0B,
+  BusTypeSd           = 0x0C,
+  BusTypeMmc          = 0x0D,
+  BusTypeMax          = 0x0E,
+  BusTypeMaxReserved  = 0x7F
 } STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
 
 typedef struct _STORAGE_DEVICE_DESCRIPTOR {
-       ULONG Version;
-       ULONG Size;
-       UCHAR DeviceType;
-       UCHAR DeviceTypeModifier;
-       BOOLEAN RemovableMedia;
-       BOOLEAN CommandQueueing;
-       ULONG VendorIdOffset;
-       ULONG ProductIdOffset;
-       ULONG ProductRevisionOffset;
-       ULONG SerialNumberOffset;
-       STORAGE_BUS_TYPE BusType;
-       ULONG RawPropertiesLength;
-       UCHAR RawDeviceProperties[1];
+  ULONG Version;
+  ULONG Size;
+  UCHAR DeviceType;
+  UCHAR DeviceTypeModifier;
+  BOOLEAN RemovableMedia;
+  BOOLEAN CommandQueueing;
+  ULONG VendorIdOffset;
+  ULONG ProductIdOffset;
+  ULONG ProductRevisionOffset;
+  ULONG SerialNumberOffset;
+  STORAGE_BUS_TYPE BusType;
+  ULONG RawPropertiesLength;
+  UCHAR RawDeviceProperties[1];
 } STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
 
 typedef enum _STORAGE_QUERY_TYPE {
-       PropertyStandardQuery = 0,
-       PropertyExistsQuery,
-       PropertyMaskQuery,
-       PropertyQueryMaxDefined
+  PropertyStandardQuery = 0,
+  PropertyExistsQuery,
+  PropertyMaskQuery,
+  PropertyQueryMaxDefined
 } STORAGE_QUERY_TYPE, *PSTORAGE_QUERY_TYPE;
 
 typedef enum _STORAGE_PROPERTY_ID {
-       StorageDeviceProperty = 0,
-       StorageAdapterProperty,
-       StorageDeviceIdProperty,
-       StorageDeviceUniqueIdProperty,
-       StorageDeviceWriteCacheProperty,
-       StorageMiniportProperty,
-       StorageAccessAlignmentProperty
+  StorageDeviceProperty = 0,
+  StorageAdapterProperty,
+  StorageDeviceIdProperty,
+  StorageDeviceUniqueIdProperty,
+  StorageDeviceWriteCacheProperty,
+  StorageMiniportProperty,
+  StorageAccessAlignmentProperty
 } STORAGE_PROPERTY_ID, *PSTORAGE_PROPERTY_ID;
 
 typedef struct _STORAGE_PROPERTY_QUERY {
-       STORAGE_PROPERTY_ID PropertyId;
-       STORAGE_QUERY_TYPE QueryType;
-       UCHAR AdditionalParameters[1];
+  STORAGE_PROPERTY_ID PropertyId;
+  STORAGE_QUERY_TYPE QueryType;
+  UCHAR AdditionalParameters[1];
 } STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
 
 
 /////////////////////////////////////////////////////////////////////////////
 
 union STORAGE_DEVICE_DESCRIPTOR_DATA {
-       STORAGE_DEVICE_DESCRIPTOR desc;
-       char raw[256];
+  STORAGE_DEVICE_DESCRIPTOR desc;
+  char raw[256];
 };
 
 // Get STORAGE_DEVICE_DESCRIPTOR_DATA for device.
@@ -1693,32 +1783,32 @@ union STORAGE_DEVICE_DESCRIPTOR_DATA {
 
 static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTOR_DATA * data)
 {
-       STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, 0};
-       memset(data, 0, sizeof(*data));
-
-       DWORD num_out;
-       if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
-               &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {
-               if (con->reportataioctl > 1 || con->reportscsiioctl > 1)
-                       pout("  IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
-               errno = ENOSYS;
-               return -1;
-       }
-
-       if (con->reportataioctl > 1 || con->reportscsiioctl > 1) {
-               pout("  IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
-                    "    Vendor:   \"%s\"\n"
-                    "    Product:  \"%s\"\n"
-                    "    Revision: \"%s\"\n"
-                    "    Removable: %s\n"
-                    "    BusType:   0x%02x\n",
-                    (data->desc.VendorIdOffset        ? data->raw+data->desc.VendorIdOffset : ""),
-                    (data->desc.ProductIdOffset       ? data->raw+data->desc.ProductIdOffset : ""),
-                    (data->desc.ProductRevisionOffset ? data->raw+data->desc.ProductRevisionOffset : ""),
-                    (data->desc.RemovableMedia? "Yes":"No"), data->desc.BusType
-               );
-       }
-       return 0;
+  STORAGE_PROPERTY_QUERY query = {StorageDeviceProperty, PropertyStandardQuery, {0} };
+  memset(data, 0, sizeof(*data));
+
+  DWORD num_out;
+  if (!DeviceIoControl(hdevice, IOCTL_STORAGE_QUERY_PROPERTY,
+    &query, sizeof(query), data, sizeof(*data), &num_out, NULL)) {
+    if (con->reportataioctl > 1 || con->reportscsiioctl > 1)
+      pout("  IOCTL_STORAGE_QUERY_PROPERTY failed, Error=%ld\n", GetLastError());
+    errno = ENOSYS;
+    return -1;
+  }
+
+  if (con->reportataioctl > 1 || con->reportscsiioctl > 1) {
+    pout("  IOCTL_STORAGE_QUERY_PROPERTY returns:\n"
+         "    Vendor:   \"%s\"\n"
+         "    Product:  \"%s\"\n"
+         "    Revision: \"%s\"\n"
+         "    Removable: %s\n"
+         "    BusType:   0x%02x\n",
+         (data->desc.VendorIdOffset        ? data->raw+data->desc.VendorIdOffset : ""),
+         (data->desc.ProductIdOffset       ? data->raw+data->desc.ProductIdOffset : ""),
+         (data->desc.ProductRevisionOffset ? data->raw+data->desc.ProductRevisionOffset : ""),
+         (data->desc.RemovableMedia? "Yes":"No"), data->desc.BusType
+    );
+  }
+  return 0;
 }
 
 
@@ -1730,8 +1820,8 @@ static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTO
   CTL_CODE(IOCTL_STORAGE_BASE, 0x0440, METHOD_BUFFERED, FILE_ANY_ACCESS)
 
 typedef struct _STORAGE_PREDICT_FAILURE {
-       ULONG  PredictFailure;
-       UCHAR  VendorSpecific[512];
+  ULONG  PredictFailure;
+  UCHAR  VendorSpecific[512];
 } STORAGE_PREDICT_FAILURE, *PSTORAGE_PREDICT_FAILURE;
 
 ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE, 4+512);
@@ -1746,121 +1836,254 @@ ASSERT_SIZEOF(STORAGE_PREDICT_FAILURE, 4+512);
 
 static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
 {
-       STORAGE_PREDICT_FAILURE pred;
-       memset(&pred, 0, sizeof(pred));
-
-       DWORD num_out;
-       if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE,
-               0, 0, &pred, sizeof(pred), &num_out, NULL)) {
-               if (con->reportataioctl > 1)
-                       pout("  IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());
-               errno = ENOSYS;
-               return -1;
-       }
-
-       if (con->reportataioctl > 1) {
-               pout("  IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
-                    "    PredictFailure: 0x%08lx\n"
-                    "    VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
-                    pred.PredictFailure,
-                    pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2],
-                    pred.VendorSpecific[sizeof(pred.VendorSpecific)-1]
-               );
-       }
-       if (data)
-               memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific));
-       return (!pred.PredictFailure ? 0 : 1);
+  STORAGE_PREDICT_FAILURE pred;
+  memset(&pred, 0, sizeof(pred));
+
+  DWORD num_out;
+  if (!DeviceIoControl(hdevice, IOCTL_STORAGE_PREDICT_FAILURE,
+    0, 0, &pred, sizeof(pred), &num_out, NULL)) {
+    if (con->reportataioctl > 1)
+      pout("  IOCTL_STORAGE_PREDICT_FAILURE failed, Error=%ld\n", GetLastError());
+    errno = ENOSYS;
+    return -1;
+  }
+
+  if (con->reportataioctl > 1) {
+    pout("  IOCTL_STORAGE_PREDICT_FAILURE returns:\n"
+         "    PredictFailure: 0x%08lx\n"
+         "    VendorSpecific: 0x%02x,0x%02x,0x%02x,...,0x%02x\n",
+         pred.PredictFailure,
+         pred.VendorSpecific[0], pred.VendorSpecific[1], pred.VendorSpecific[2],
+         pred.VendorSpecific[sizeof(pred.VendorSpecific)-1]
+    );
+  }
+  if (data)
+    memcpy(data, pred.VendorSpecific, sizeof(pred.VendorSpecific));
+  return (!pred.PredictFailure ? 0 : 1);
 }
 
 
 /////////////////////////////////////////////////////////////////////////////
 
-// get CONTROLLER_* for open handle
-static int get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex)
+// get DEV_* for open handle
+static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONINPARAMS_EX * ata_version_ex)
 {
-       // Try SMART_GET_VERSION first to detect ATA SMART support
-       // for drivers reporting BusTypeScsi (3ware)
-       if (admin && smart_get_version(hdevice, ata_version_ex) >= 0)
-               return CONTROLLER_ATA;
-
-       // Get BusType from device descriptor
-       STORAGE_DEVICE_DESCRIPTOR_DATA data;
-       if (storage_query_property_ioctl(hdevice, &data))
-               return CONTROLLER_UNKNOWN;
-
-       switch (data.desc.BusType) {
-               case BusTypeAta:
-               case BusTypeSata:
-                       if (ata_version_ex)
-                               memset(ata_version_ex, 0, sizeof(*ata_version_ex));
-                       return CONTROLLER_ATA;
-               case BusTypeScsi:
-               case BusTypeiScsi:
-               case BusTypeSas:
-                       return CONTROLLER_SCSI;
-               default:
-                       return CONTROLLER_UNKNOWN;
-       }
-       /*NOTREACHED*/
+  // Try SMART_GET_VERSION first to detect ATA SMART support
+  // for drivers reporting BusTypeScsi (3ware)
+  if (admin && smart_get_version(hdevice, ata_version_ex) >= 0)
+    return DEV_ATA;
+
+  // Get BusType from device descriptor
+  STORAGE_DEVICE_DESCRIPTOR_DATA data;
+  if (storage_query_property_ioctl(hdevice, &data))
+    return DEV_UNKNOWN;
+
+  switch (data.desc.BusType) {
+    case BusTypeAta:
+    case BusTypeSata:
+      if (ata_version_ex)
+        memset(ata_version_ex, 0, sizeof(*ata_version_ex));
+      return DEV_ATA;
+    case BusTypeScsi:
+    case BusTypeiScsi:
+    case BusTypeSas:
+      return DEV_SCSI;
+    case BusTypeUsb:
+      return DEV_USB;
+    default:
+      return DEV_UNKNOWN;
+  }
+  /*NOTREACHED*/
 }
 
-// get CONTROLLER_* for device path
-static int get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
+// get DEV_* for device path
+static win_dev_type get_controller_type(const char * path, GETVERSIONINPARAMS_EX * ata_version_ex = 0)
 {
-       bool admin = true;
-       HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
-               FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
-       if (h == INVALID_HANDLE_VALUE) {
-               admin = false;
-               h = CreateFileA(path, 0,
-                       FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
-               if (h == INVALID_HANDLE_VALUE)
-                       return CONTROLLER_UNKNOWN;
-       }
-       if (con->reportataioctl > 1 || con->reportscsiioctl > 1)
-               pout(" %s: successfully opened%s\n", path, (!admin ? " (without admin rights)" :""));
-       int type = get_controller_type(h, admin, ata_version_ex);
-       CloseHandle(h);
-       return type;
+  bool admin = true;
+  HANDLE h = CreateFileA(path, GENERIC_READ|GENERIC_WRITE,
+    FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+  if (h == INVALID_HANDLE_VALUE) {
+    admin = false;
+    h = CreateFileA(path, 0,
+      FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
+    if (h == INVALID_HANDLE_VALUE)
+      return DEV_UNKNOWN;
+  }
+  if (con->reportataioctl > 1 || con->reportscsiioctl > 1)
+    pout(" %s: successfully opened%s\n", path, (!admin ? " (without admin rights)" :""));
+  win_dev_type type = get_controller_type(h, admin, ata_version_ex);
+  CloseHandle(h);
+  return type;
 }
 
-// get CONTROLLER_* for physical drive number
-static int get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
+// get DEV_* for physical drive number
+static win_dev_type get_phy_drive_type(int drive, GETVERSIONINPARAMS_EX * ata_version_ex)
 {
-       char path[30];
-       snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
-       return get_controller_type(path, ata_version_ex);
+  char path[30];
+  snprintf(path, sizeof(path)-1, "\\\\.\\PhysicalDrive%d", drive);
+  return get_controller_type(path, ata_version_ex);
 }
 
-static int get_phy_drive_type(int drive)
+static win_dev_type get_phy_drive_type(int drive)
 {
-       return get_phy_drive_type(drive, 0);
+  return get_phy_drive_type(drive, 0);
 }
 
-// get CONTROLLER_* for logical drive number
-static int get_log_drive_type(int drive)
+// get DEV_* for logical drive number
+static win_dev_type get_log_drive_type(int drive)
 {
-       char path[30];
-       snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
-       return get_controller_type(path);
+  char path[30];
+  snprintf(path, sizeof(path)-1, "\\\\.\\%c:", 'A'+drive);
+  return get_controller_type(path);
 }
 
 // Build IDENTIFY information from STORAGE_DEVICE_DESCRIPTOR
 static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device * id)
 {
-       STORAGE_DEVICE_DESCRIPTOR_DATA data;
-       if (storage_query_property_ioctl(hdevice, &data))
-               return -1;
+  STORAGE_DEVICE_DESCRIPTOR_DATA data;
+  if (storage_query_property_ioctl(hdevice, &data))
+    return -1;
+
+  memset(id, 0, sizeof(*id));
+  if (data.desc.ProductIdOffset)
+    copy_swapped(id->model, data.raw+data.desc.ProductIdOffset, sizeof(id->model));
+  if (data.desc.ProductRevisionOffset)
+    copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev));
+  id->major_rev_num = 0x1<<3; // ATA-3
+  id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
+  id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
+  return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// USB ID detection using WMI
 
-       memset(id, 0, sizeof(*id));
-       if (data.desc.ProductIdOffset)
-               copy_swapped(id->model, data.raw+data.desc.ProductIdOffset, sizeof(id->model));
-       if (data.desc.ProductRevisionOffset)
-               copy_swapped(id->fw_rev, data.raw+data.desc.ProductRevisionOffset, sizeof(id->fw_rev));
-       id->major_rev_num = 0x1<<3; // ATA-3
-       id->command_set_1 = 0x0001; id->command_set_2 = 0x4000; // SMART supported, words 82,83 valid
-       id->cfs_enable_1  = 0x0001; id->csf_default   = 0x4000; // SMART enabled, words 85,87 valid
-       return 0;
+// Run a command, split stdout into lines.
+// Return number of lines read, -1 on error.
+static int run_cmd(std::vector<std::string> & lines, const char * cmd, ...)
+{
+  lines.clear();
+
+  va_list ap; va_start(ap, cmd);
+  std::string cmdline = vstrprintf(cmd, ap);
+  va_end(ap);
+
+  if (con->reportscsiioctl > 1)
+    pout("Run: \"%s\"\n", cmdline.c_str());
+
+  char buffer[16*1024];
+  int size = run_cmd(cmdline.c_str(), buffer, sizeof(buffer));
+
+  if (con->reportscsiioctl > 1)
+    pout("Read %d bytes\n", size);
+  if (!(0 < size && size < (int)sizeof(buffer)-1))
+    return -1;
+
+  buffer[size] = 0;
+
+  for (int i = 0; buffer[i]; ) {
+      int len = strcspn(buffer+i, "\r\n");
+      lines.push_back(std::string(buffer+i, len));
+      i += len;
+      i += strspn(buffer+i, "\r\n");
+  }
+  if (con->reportscsiioctl > 1) {
+    for (unsigned i = 0; i < lines.size(); i++)
+      printf("'%s'\n", lines[i].c_str());
+  }
+  return lines.size();
+}
+
+// Quote string for WMI
+static std::string wmi_quote(const char * s, int len)
+{
+  std::string r;
+  for (int i = 0; i < len; i++) {
+    char c = s[i];
+    if (c == '\\')
+      r += '\\';
+    r += c;
+  }
+  return r;
+}
+
+// Get USB ID for a physical drive number
+static bool get_usb_id(int drive, unsigned short & vendor_id, unsigned short & product_id)
+{
+  // Get device name
+  std::vector<std::string> result;
+  if (run_cmd(result,
+        "wmic PATH Win32_DiskDrive WHERE DeviceID=\"\\\\\\\\.\\\\PHYSICALDRIVE%d\" GET Model",
+        drive) != 2)
+    return false;
+
+  std::string name = result[1];
+
+  // Get USB_CONTROLLER -> DEVICE associations
+  std::vector<std::string> assoc;
+  int n = run_cmd(assoc, "wmic PATH Win32_USBControllerDevice GET Antecedent,Dependent");
+  if (n < 2)
+    return false;
+
+  regular_expression regex("^([^ ]+) .*Win32_PnPEntity.DeviceID=\"(USBSTOR\\\\[^\"]*)\" *$",
+                           REG_EXTENDED);
+  if (regex.empty()) // TODO: throw in constructor?
+    return false;
+
+  int usbstoridx = -1;
+  std::string usbcontr;
+  for (int i = 2; i < n; i++) {
+    // Find next 'USB_CONTROLLER  USBSTORAGE_DEVICE' pair
+    regmatch_t match[3];
+    const char * s = assoc[i].c_str();
+    if (!regex.execute(s, 3, match))
+      continue;
+
+    // USBSTOR device found, compare Name
+    if (run_cmd(result,
+          "wmic PATH Win32_PnPEntity WHERE DeviceID=\"%s\" GET Name",
+          wmi_quote(s + match[2].rm_so, match[2].rm_eo - match[2].rm_so).c_str()
+          ) != 2)
+      continue;
+    if (result[1] != name)
+      continue;
+
+    // Name must be uniqe
+    if (usbstoridx >= 0)
+      return false;
+
+    usbstoridx = i;
+    usbcontr.assign(s + match[1].rm_so, match[1].rm_eo - match[1].rm_so);
+  }
+
+  // Found ?
+  if (usbstoridx <= 0)
+    return false;
+
+  // The entry preceding USBSTOR should be the USB bridge device
+  regex.compile("^([^ ]+) .*Win32_PnPEntity.DeviceID=\"USB\\\\VID_(....&PID_....)[^\"]*\" *$",
+                REG_EXTENDED);
+  if (regex.empty())
+    return false;
+  regmatch_t match[3];
+  const char * s = assoc[usbstoridx-1].c_str();
+  if (!regex.execute(s, 3, match))
+    return false;
+
+  // Both devices must be associated to same controller
+  if (usbcontr != std::string(s + match[1].rm_so, match[1].rm_eo - match[1].rm_so))
+    return false;
+
+  // Parse USB ID
+  int nc = -1;
+  if (!(sscanf(s + match[2].rm_so, "%4hx&PID_%4hx%n",
+               &vendor_id, &product_id, &nc) == 2 && nc == 4+5+4))
+    return false;
+
+  if (con->reportscsiioctl > 1)
+    pout("USB ID = 0x%04x:0x%04x\n", vendor_id, product_id);
+  return true;
 }
 
 
@@ -1872,116 +2095,106 @@ static int get_identify_from_device_property(HANDLE hdevice, ata_identify_device
 
 static int get_device_power_state(HANDLE hdevice)
 {
-       static HINSTANCE h_kernel_dll = 0;
+  static HINSTANCE h_kernel_dll = 0;
 #ifdef __CYGWIN__
-       static DWORD kernel_dll_pid = 0;
+  static DWORD kernel_dll_pid = 0;
 #endif
-       static BOOL (WINAPI * GetDevicePowerState_p)(HANDLE, BOOL *) = 0;
+  static BOOL (WINAPI * GetDevicePowerState_p)(HANDLE, BOOL *) = 0;
 
-       BOOL state = TRUE;
+  BOOL state = TRUE;
 
-       if (!GetDevicePowerState_p
+  if (!GetDevicePowerState_p
 #ifdef __CYGWIN__
-           || kernel_dll_pid != GetCurrentProcessId() // detect fork()
+      || 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;
-               }
+     ) {
+    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();
+    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;
+  }
+
+  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 bool ata_admin; // true if opened with admin rights
-static const char * ata_def_options;
-static char * ata_usr_options;
-const int max_ata_driveno = 25;
-static int ata_driveno; // Drive number
-static int ata_driveno_is_log = -1; // 0=physical drivenumber, 1=logical drive number, -1=unknown
-static char ata_smartver_state[max_ata_driveno+1]; // 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;
-       }
+  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;
+  }
 }
 
 
@@ -1989,598 +2202,616 @@ static int smartvsd_error()
 
 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 "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
-                                // STORAGE_*, SCSI_MINIPORT_*
+  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 "pasifm"; // GetDevicePowerState(), ATA_, SMART_*, IDE_PASS_THROUGH,
+                     // STORAGE_*, SCSI_MINIPORT_*
 }
 
 
-// Open ATA device
+// Common routines for devices with HANDLEs
 
-static int ata_open(int phydrive, int logdrive, const char * options, int port)
-{
-       // TODO: This version does not allow to open more than 1 ATA devices
-       if (h_ata_ioctl) {
-               errno = ENFILE;
-               return -1;
-       }
-
-       // Using both physical and logical drive names (in smartd.conf) not supported yet
-       if (!(   ata_driveno_is_log < 0
-             || (phydrive >= 0 && !ata_driveno_is_log)
-             || (logdrive >= 0 &&  ata_driveno_is_log))) {
-               pout("Using both /dev/hdX and X: is not supported\n");
-               errno = EINVAL;
-               return -1;
-       }
-
-       // path depends on Windows Version
-       bool win9x = is_win9x();
-       char devpath[30];
-       if (win9x && 0 <= phydrive && phydrive <= 7)
-               // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details
-               strcpy(devpath, (phydrive <= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE"));
-       else if (!win9x && 0 <= phydrive && phydrive <= max_ata_driveno)
-               snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", phydrive);
-       else if (!win9x && 0 <= logdrive && logdrive <= max_ata_driveno) {
-               snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive);
-       }
-       else {
-               errno = ENOENT;
-               return -1;
-       }
-
-       // Open device
-       h_ata_ioctl = INVALID_HANDLE_VALUE;
-       if (win9x || !(*options && !options[strspn(options, "fp")])) {
-               // Open with admin rights
-               ata_admin = true;
-               h_ata_ioctl = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
-                       FILE_SHARE_READ|FILE_SHARE_WRITE,
-                       NULL, OPEN_EXISTING, 0, 0);
-       }
-       if (!win9x && h_ata_ioctl == INVALID_HANDLE_VALUE) {
-               // Open without admin rights
-               ata_admin = false;
-               h_ata_ioctl = CreateFileA(devpath, 0,
-                       FILE_SHARE_READ|FILE_SHARE_WRITE,
-                       NULL, OPEN_EXISTING, 0, 0);
-       }
-       if (h_ata_ioctl == INVALID_HANDLE_VALUE) {
-               long err = GetLastError();
-               pout("Cannot open device %s, Error=%ld\n", devpath, err);
-               if (err == ERROR_FILE_NOT_FOUND)
-                       errno = (win9x && phydrive <= 3 ? smartvsd_error() : ENOENT);
-               else if (err == ERROR_ACCESS_DENIED)
-                       errno = EACCES;
-               else
-                       errno = EIO;
-               h_ata_ioctl = 0;
-               return -1;
-       }
-
-       if (con->reportataioctl > 1)
-               pout("%s: successfully opened%s\n", devpath, (!ata_admin ? " (without admin rights)" :""));
-
-       // Set default options according to Windows version
-       if (!ata_def_options)
-               ata_def_options = ata_get_def_options();
-       // Save user options
-       if (port >= 0 && !*options)
-               options = "s3"; // RAID: SMART_* and SCSI_MINIPORT
-       assert(!ata_usr_options);
-       if (*options)
-               ata_usr_options = strdup(options);
-
-       // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
-       if (phydrive >= 0) {
-               ata_driveno = phydrive; ata_driveno_is_log = 0;
-       }
-       else {
-               assert(logdrive >= 0);
-               ata_driveno = logdrive; ata_driveno_is_log = 1;
-       }
-       if (!win9x && port < 0)
-               return 0;
-
-       // Win9X/ME: Get drive map
-       // RAID: Get port map
-       GETVERSIONINPARAMS_EX vers_ex;
-       int devmap = smart_get_version(h_ata_ioctl, (port >= 0 ? &vers_ex : 0));
-
-       unsigned long portmap = 0;
-       if (port >= 0 && devmap >= 0) {
-               // 3ware RAID: check vendor id
-               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);
-                       devmap = -1;
-               }
-               else
-                       portmap = vers_ex.dwDeviceMapEx;
-       }
-       if (devmap < 0) {
-               pout("%s: ATA driver has no SMART support\n", devpath);
-               if (!is_permissive()) {
-                       ata_close(0);
-                       errno = ENOSYS;
-                       return -1;
-               }
-               devmap = 0x0f;
-       }
-       ata_smartver_state[ata_driveno] = 1;
-
-       if (port >= 0) {
-               // 3ware RAID: update devicemap first
-               if (!update_3ware_devicemap_ioctl(h_ata_ioctl)) {
-                       if (   smart_get_version(h_ata_ioctl, &vers_ex) >= 0
-                           && vers_ex.wIdentifier == SMART_VENDOR_3WARE    )
-                               portmap = vers_ex.dwDeviceMapEx;
-               }
-               // 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 (ATARAID_FDOFFSET | port);
-       }
-
-       // Win9x/ME: Check device presence & type
-       if (((devmap >> (ata_driveno & 0x3)) & 0x11) != 0x01) {
-               unsigned char atapi = (devmap >> (ata_driveno & 0x3)) & 0x10;
-               pout("%s: Drive %d %s (IDEDeviceMap=0x%02x).\n", devpath,
-                    ata_driveno, (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 (ata_driveno & 0x3);
-}
-
-
-static void ata_close(int /*fd*/)
-{
-       CloseHandle(h_ata_ioctl);
-       h_ata_ioctl = 0;
-       if (ata_usr_options) {
-               free(ata_usr_options);
-               ata_usr_options = 0;
-       }
-}
-
-
-// Scan for ATA drives on Win9x/ME, fill bitmask of drives present, return #drives
-
-static int ata_scan_win9x(unsigned long * drives)
-{
-       // Open device
-       const char devpath[] = "\\\\.\\SMARTVSD";
-       HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
-               FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
-       if (h == INVALID_HANDLE_VALUE) {
-               if (con->reportataioctl > 1)
-                       pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError());
-               return 0; // SMARTVSD.VXD missing or no ATA devices
-       }
-
-       // Get drive map
-       int devmap = smart_get_version(h);
-       CloseHandle(h);
-       if (devmap < 0)
-               return 0; // Should not happen
-
-       // Check ATA device presence, remove ATAPI devices
-       drives[0] = (devmap & 0xf) & ~((devmap >> 4) & 0xf);
-       return (drives[0]&1) + ((drives[0]>>1)&1) + ((drives[0]>>2)&1) + ((drives[0]>>3)&1);
-}
-
-
-// Scan for ATA drives, fill bitmask of drives present, return #drives
-
-static int ata_scan(unsigned long * drives, int * rdriveno, unsigned long * rdrives)
-{
-       int cnt = 0;
-       for (int i = 0; i <= 9; i++) {
-               GETVERSIONINPARAMS_EX vers_ex;
-               if (get_phy_drive_type(i, &vers_ex) != CONTROLLER_ATA)
-                       continue;
-
-               // Interpret RAID drive map if present
-               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;
-               }
-
-               // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returns ATA/SATA
-               drives[0] |= (1L << i);
-               cnt++;
-       }
-
-       return cnt;
+win_smart_device::~win_smart_device() throw()
+{
+  if (m_fh != INVALID_HANDLE_VALUE)
+    ::CloseHandle(m_fh);
 }
 
+bool win_smart_device::is_open() const
+{
+  return (m_fh != INVALID_HANDLE_VALUE);
+}
 
-/////////////////////////////////////////////////////////////////////////////
+bool win_smart_device::close()
+{
+  if (m_fh == INVALID_HANDLE_VALUE)
+    return true;
+  BOOL rc = ::CloseHandle(m_fh);
+  m_fh = INVALID_HANDLE_VALUE;
+  return !!rc;
+}
 
-// Interface to ATA devices.  See os_linux.c
-int ata_command_interface(int fd, smart_command_set command, int select, char * data)
-{
-       if (fd == TW_CLI_FDOFFSET) // Parse tw_cli output
-               return tw_cli_command_interface(command, select, data);
-
-       int port = -1;
-       if ((fd & ~0x1f) == ATARAID_FDOFFSET) {
-               // 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
-       IDEREGS regs; memset(&regs, 0, sizeof(regs));
-       regs.bCommandReg = ATA_SMART_CMD;
-       regs.bCylHighReg = SMART_CYL_HI; regs.bCylLowReg = SMART_CYL_LOW;
-       int datasize = 0;
-
-       // Try by default: SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
-       // and SCSI_MINIPORT_* if requested by user
-       const char * valid_options = (ata_usr_options ? "saicmf" : "saicf");
-
-       switch (command) {
-         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 = "pai3"; // 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;
-               // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME
-               // Try SCSI_MINIPORT also to skip buggy class driver
-               valid_options = (ata_usr_options || is_win9x() ? "saicm3" : "aicm3");
-               datasize = 512;
-               break;
-         case WRITE_LOG:
-               regs.bFeaturesReg = ATA_SMART_WRITE_LOG_SECTOR;
-               regs.bSectorNumberReg = select;
-               regs.bSectorCountReg = 1;
-               // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
-               // but SCSI_MINIPORT_* only if requested by user
-               valid_options = (ata_usr_options ? "am" : "a");
-               datasize = -512; // DATA_OUT!
-               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:
-               // Requires CL,CH register return
-               valid_options = (ata_usr_options ? "saimf" : "saif");
-         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;
-               // SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME
-               valid_options = (ata_usr_options || select != 127/*ABORT*/ || is_win9x() ?
-                       "saicm3" : "aicm3");
-               break;
-         default:
-               pout("Unrecognized command %d in win32_ata_command_interface()\n"
-                "Please contact " PACKAGE_BUGREPORT "\n", command);
-               errno = ENOSYS;
-               return -1;
-       }
-
-       if (!ata_admin) {
-               // Restrict to IOCTL_STORAGE_*
-               if (strchr(valid_options, 'f'))
-                       valid_options = "f";
-               else if (strchr(valid_options, 'p'))
-                       valid_options = "p";
-               else {
-                       errno = ENOSYS;
-                       return -1;
-               }
-       }
-
-       // Try all valid ioctls in the order specified in ata_*_options
-       bool powered_up = false;
-       assert(ata_def_options);
-       const char * options = (ata_usr_options ? ata_usr_options : ata_def_options);
-       for (int i = 0; ; i++) {
-               char opt = options[i];
-
-               if (!opt) {
-                       if (command == CHECK_POWER_MODE && powered_up) {
-                               // Power up reported by GetDevicePowerState() and no ioctl available
-                               // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
-                               regs.bSectorCountReg = 0xff;
-                               break;
-                       }
-                       // No IOCTL found
-                       errno = ENOSYS;
-                       return -1;
-               }
-               if (!strchr(valid_options, opt))
-                       // Invalid for this command
-                       continue;
-
-               errno = 0;
-               assert(datasize == 0 || datasize == 512 || (strchr("am", opt) && datasize == -512));
-               int rc;
-               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 driver not supporting SMART.\n");
-                                               pout("If this is a SCSI disk, please try adding '-d scsi'.\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 'm':
-                       rc = ata_via_scsi_miniport_smart_ioctl(h_ata_ioctl, &regs, data, datasize);
-                       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 'f':
-                       switch (command) {
-                         case READ_VALUES:
-                               rc = storage_predict_failure_ioctl(h_ata_ioctl, data);
-                               if (rc > 0)
-                                       rc = 0;
-                               break;
-                         case READ_THRESHOLDS:
-                               {
-                                       ata_smart_values sv;
-                                       rc = storage_predict_failure_ioctl(h_ata_ioctl, (char *)&sv);
-                                       if (rc < 0)
-                                               break;
-                                       rc = 0;
-                                       // Fake zero thresholds
-                                       ata_smart_thresholds_pvt * tr = (ata_smart_thresholds_pvt *)data;
-                                       memset(tr, 0, 512);
-                                       for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++)
-                                               tr->chksum -= tr->thres_entries[i].id = sv.vendor_attributes[i].id;
-                               }
-                               break;
-                         case IDENTIFY:
-                               rc = get_identify_from_device_property(h_ata_ioctl, (ata_identify_device *)data);
-                               break;
-                         case ENABLE:
-                               rc = 0;
-                               break;
-                         case STATUS_CHECK:
-                         case STATUS:
-                               rc = storage_predict_failure_ioctl(h_ata_ioctl);
-                               if (rc > 0) {
-                                       if (command == STATUS_CHECK) {
-                                               regs.bCylHighReg = 0x2c; regs.bCylLowReg = 0xf4;
-                                       }
-                                       rc = 0;
-                               }
-                               break;
-                         default:
-                               errno = ENOSYS; rc = -1;
-                       }
-                       break;
-                 case '3':
-                       rc = ata_via_3ware_miniport_ioctl(h_ata_ioctl, &regs, data, datasize, port);
-                       break;
-                 case 'p':
-                       assert(command == CHECK_POWER_MODE && datasize == 0);
-                       rc = get_device_power_state(h_ata_ioctl);
-                       if (rc == 0) {
-                               // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
-                               // spin up the drive => simulate ATA result STANDBY.
-                               regs.bSectorCountReg = 0x00;
-                       }
-                       else if (rc > 0) {
-                               // Power up reported by GetDevicePowerState(), but this reflects the actual mode
-                               // only if it is selected by the device driver => try a passthrough ioctl to get the
-                               // actual mode, if none available simulate ACTIVE/IDLE.
-                               powered_up = true;
-                               errno = ENOSYS; rc = -1;
-                       }
-                       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
+// ATA
 
-// Return true if OS caches the ATA identify sector
-int ata_identify_is_cached(int fd)
+win_ata_device::win_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "ata", req_type),
+  m_usr_options(false),
+  m_admin(false),
+  m_drive(0),
+  m_port(-1),
+  m_smartver_state(0)
+{
+}
+
+win_ata_device::~win_ata_device() throw()
 {
-       // Not RAID and WinNT4/2000/XP => true, RAID or Win9x/ME => false
-       return (!(fd & 0xff00) && !is_win9x());
 }
 
 
-// Print not implemeted warning once
-static void pr_not_impl(const char * what, int * warned)
+// Open ATA device
+
+bool win_ata_device::open()
 {
-       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;
+  const char * name = skipdev(get_dev_name()); int len = strlen(name);
+  // [sh]d[a-z](:[saicmfp]+)? => Physical drive 0-25, with options
+  char drive[1+1] = "", options[8+1] = ""; int n1 = -1, n2 = -1;
+  if (   sscanf(name, "%*[sh]d%1[a-z]%n:%7[saicmfp]%n", drive, &n1, options, &n2) >= 1
+      && ((n1 == len && !options[0]) || n2 == len)                                       ) {
+    return open(drive[0] - 'a', -1, options, -1);
+  }
+  // [sh]d[a-z],N(:[saicmfp3]+)? => Physical drive 0-25, RAID port N, with options
+  drive[0] = 0; options[0] = 0; n1 = -1; n2 = -1;
+  unsigned port = ~0;
+  if (   sscanf(name, "%*[sh]d%1[a-z],%u%n:%8[saicmfp3]%n", drive, &port, &n1, options, &n2) >= 2
+      && port < 32 && ((n1 == len && !options[0]) || n2 == len)                                     ) {
+    return open(drive[0] - 'a', -1, options, port);
+  }
+  // pd<m>,N => Physical drive <m>, RAID port N
+  int phydrive = -1; port = ~0; n1 = -1; n2 = -1;
+  if (   sscanf(name, "pd%d%n,%u%n", &phydrive, &n1, &port, &n2) >= 1
+      && phydrive >= 0 && ((n1 == len && (int)port < 0) || (n2 == len && port < 32))) {
+    return open(phydrive, -1, "", (int)port);
+  }
+  // [a-zA-Z]: => Physical drive behind logical drive 0-25
+  int logdrive = drive_letter(name);
+  if (logdrive >= 0) {
+    return open(-1, logdrive, "", -1);
+  }
+
+  return set_err(EINVAL);
 }
 
-// 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*/)
+
+bool win_ata_device::open(int phydrive, int logdrive, const char * options, int port)
 {
-       static int warned = 0;
-       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;
+  // path depends on Windows Version
+  bool win9x = is_win9x(); // TODO: Member variable
+  char devpath[30];
+  if (win9x && 0 <= phydrive && phydrive <= 7)
+    // Use patched "smartvse.vxd" for drives 4-7, see INSTALL file for details
+    strcpy(devpath, (phydrive <= 3 ? "\\\\.\\SMARTVSD" : "\\\\.\\SMARTVSE"));
+  else if (!win9x && 0 <= phydrive && phydrive <= 255)
+    snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", phydrive);
+  else if (!win9x && 0 <= logdrive && logdrive <= 'Z'-'A')
+    snprintf(devpath, sizeof(devpath)-1, "\\\\.\\%c:", 'A'+logdrive);
+  else
+    return set_err(ENOENT);
+
+  // Open device
+  HANDLE h = INVALID_HANDLE_VALUE;
+  if (win9x || !(*options && !options[strspn(options, "fp")])) {
+    // Open with admin rights
+    m_admin = true;
+    h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
+      FILE_SHARE_READ|FILE_SHARE_WRITE,
+      NULL, OPEN_EXISTING, 0, 0);
+  }
+  if (!win9x && h == INVALID_HANDLE_VALUE) {
+    // Open without admin rights
+    m_admin = false;
+    h = CreateFileA(devpath, 0,
+      FILE_SHARE_READ|FILE_SHARE_WRITE,
+      NULL, OPEN_EXISTING, 0, 0);
+  }
+  if (h == INVALID_HANDLE_VALUE) {
+    long err = GetLastError();
+    pout("Cannot open device %s, Error=%ld\n", devpath, err);
+    if (err == ERROR_FILE_NOT_FOUND)
+      set_err((win9x && phydrive <= 3 ? smartvsd_error() : ENOENT), "%s: not found", devpath);
+    else if (err == ERROR_ACCESS_DENIED)
+      set_err(EACCES, "%s: access denied", devpath);
+    else
+      set_err(EIO, "%s: Error=%ld", devpath, err);
+    return false;
+  }
+  set_fh(h);
+
+  if (con->reportataioctl > 1)
+    pout("%s: successfully opened%s\n", devpath, (!m_admin ? " (without admin rights)" :""));
+
+  m_usr_options = false;
+  if (*options) {
+    // Save user options
+    m_options = options; m_usr_options = true;
+  }
+  else if (port >= 0)
+    // RAID: SMART_* and SCSI_MINIPORT
+    m_options = "s3";
+  else {
+    // Set default options according to Windows version
+    static const char * def_options = ata_get_def_options();
+    m_options = def_options;
+  }
+
+  // NT4/2000/XP: SMART_GET_VERSION may spin up disk, so delay until first real SMART_* call
+  m_drive = 0; m_port = port;
+  if (!win9x && port < 0)
+    return true;
+
+  // Win9X/ME: Get drive map
+  // RAID: Get port map
+  GETVERSIONINPARAMS_EX vers_ex;
+  int devmap = smart_get_version(h, (port >= 0 ? &vers_ex : 0));
+
+  unsigned long portmap = 0;
+  if (port >= 0 && devmap >= 0) {
+    // 3ware RAID: check vendor id
+    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);
+      devmap = -1;
+    }
+    else
+      portmap = vers_ex.dwDeviceMapEx;
+  }
+  if (devmap < 0) {
+    pout("%s: ATA driver has no SMART support\n", devpath);
+    if (!is_permissive()) {
+      close();
+      return set_err(ENOSYS);
+    }
+    devmap = 0x0f;
+  }
+  m_smartver_state = 1;
+
+  if (port >= 0) {
+    // 3ware RAID: update devicemap first
+
+    if (!update_3ware_devicemap_ioctl(h)) {
+      if (   smart_get_version(h, &vers_ex) >= 0
+          && vers_ex.wIdentifier == SMART_VENDOR_3WARE    )
+        portmap = vers_ex.dwDeviceMapEx;
+    }
+    // Check port existence
+    if (!(portmap & (1L << port))) {
+      if (!is_permissive()) {
+        close();
+        return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);
+      }
+    }
+    return true;
+  }
+
+  // Win9x/ME: Check device presence & type
+  if (((devmap >> (phydrive & 0x3)) & 0x11) != 0x01) {
+    unsigned char atapi = (devmap >> (phydrive & 0x3)) & 0x10;
+    // 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()) {
+      close();
+      return set_err((atapi ? ENOSYS : ENOENT), "%s: Drive %d %s (IDEDeviceMap=0x%02x)",
+        devpath, phydrive, (atapi?"is an ATAPI device":"does not exist"), devmap);
+    }
+  }
+  // Drive number must be passed to ioctl
+  m_drive = (phydrive & 0x3);
+  return true;
 }
 
-// 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*/)
+
+// Scan for ATA drives on Win9x/ME
+
+bool win9x_smart_interface::ata_scan(smart_device_list & devlist)
 {
-       static int warned = 0;
-       pr_not_impl("Marvell chip-set command routine marvell_command_interface()", &warned);
-       errno = ENOSYS;
-       return -1;
+  // Open device
+  const char devpath[] = "\\\\.\\SMARTVSD";
+  HANDLE h = CreateFileA(devpath, GENERIC_READ|GENERIC_WRITE,
+    FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
+  if (h == INVALID_HANDLE_VALUE) {
+    if (con->reportataioctl > 1)
+      pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError());
+    return true; // SMARTVSD.VXD missing or no ATA devices
+  }
+
+  // Get drive map
+  int devmap = smart_get_version(h);
+  CloseHandle(h);
+  if (devmap < 0)
+    return true; // Should not happen
+
+  // Check ATA device presence, remove ATAPI devices
+  devmap = (devmap & 0xf) & ~((devmap >> 4) & 0xf);
+  char name[20];
+  for (int i = 0; i < 4; i++) {
+    if (!(devmap & (1 << i)))
+      continue;
+    sprintf(name, "/dev/hd%c", 'a'+i);
+    devlist.add( new win_ata_device(this, name, "ata") );
+  }
+  return true;
 }
 
-// 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*/)
+
+// Scan for ATA drives
+
+bool winnt_smart_interface::ata_scan(smart_device_list & devlist)
 {
-       static int warned = 0;
-       pr_not_impl("HighPoint raid controller command routine highpoint_command_interface()", &warned);
-       errno = ENOSYS;
-       return -1;
+  const int max_raid = 2;
+  bool raid_seen[max_raid] = {false, false};
+
+  char name[20];
+  for (int i = 0; i <= 9; i++) {
+    GETVERSIONINPARAMS_EX vers_ex;
+    if (get_phy_drive_type(i, &vers_ex) != DEV_ATA)
+      continue;
+
+    // Interpret RAID drive map if present
+    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 >= max_raid || raid_seen[vers_ex.wControllerId])
+        continue;
+      raid_seen[vers_ex.wControllerId] = true;
+      // Add physical drives
+      for (int pi = 0; pi < 32; pi++) {
+        if (vers_ex.dwDeviceMapEx & (1L << pi)) {
+            sprintf(name, "/dev/sd%c,%u", 'a'+i, pi);
+            devlist.add( new win_ata_device(this, name, "ata") );
+        }
+      }
+      continue;
+    }
+
+    // Driver supports SMART_GET_VERSION or STORAGE_QUERY_PROPERTY returns ATA/SATA
+    sprintf(name, "/dev/sd%c", 'a'+i);
+    devlist.add( new win_ata_device(this, name, "ata") );
+  }
+
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Interface to ATA devices
+bool win_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+{
+  // No multi-sector support for now, see above
+  // warning about IOCTL_ATA_PASS_THROUGH
+  if (!ata_cmd_is_ok(in,
+    true, // data_out_support
+    false, // !multi_sector_support
+    true) // ata_48bit_support
+  )
+    return false;
+
+  // Determine ioctl functions valid for this ATA cmd
+  const char * valid_options = 0;
+
+  switch (in.in_regs.command) {
+    case ATA_IDENTIFY_DEVICE:
+    case ATA_IDENTIFY_PACKET_DEVICE:
+      // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
+      // and SCSI_MINIPORT_* if requested by user
+      valid_options = (m_usr_options ? "saicmf" : "saicf");
+      break;
+
+    case ATA_CHECK_POWER_MODE:
+      // Try GetDevicePowerState() first, ATA/IDE_PASS_THROUGH may spin up disk
+      valid_options = "pai3";
+      break;
+
+    case ATA_SMART_CMD:
+      switch (in.in_regs.features) {
+        case ATA_SMART_READ_VALUES:
+        case ATA_SMART_READ_THRESHOLDS:
+        case ATA_SMART_AUTOSAVE:
+        case ATA_SMART_ENABLE:
+        case ATA_SMART_DISABLE:
+        case ATA_SMART_AUTO_OFFLINE:
+          // SMART_*, ATA_, IDE_, SCSI_PASS_THROUGH, STORAGE_PREDICT_FAILURE
+          // and SCSI_MINIPORT_* if requested by user
+          valid_options = (m_usr_options ? "saicmf" : "saicf");
+          break;
+
+        case ATA_SMART_IMMEDIATE_OFFLINE:
+          // SMART_SEND_DRIVE_COMMAND supports ABORT_SELF_TEST only on Win9x/ME
+          valid_options = (m_usr_options || in.in_regs.lba_low != 127/*ABORT*/ || is_win9x() ?
+                           "saicm3" : "aicm3");
+          break;
+
+        case ATA_SMART_READ_LOG_SECTOR:
+          // SMART_RCV_DRIVE_DATA supports this only on Win9x/ME
+          // Try SCSI_MINIPORT also to skip buggy class driver
+          // SMART functions do not support multi sector I/O.
+          if (in.size == 512)
+            valid_options = (m_usr_options || is_win9x() ? "saicm3" : "aicm3");
+          else
+            valid_options = "a";
+          break;
+
+        case ATA_SMART_WRITE_LOG_SECTOR:
+          // ATA_PASS_THROUGH, SCSI_MINIPORT, others don't support DATA_OUT
+          // but SCSI_MINIPORT_* only if requested by user and single sector.
+          valid_options = (in.size == 512 && m_usr_options ? "am" : "a");
+          break;
+
+        case ATA_SMART_STATUS:
+          // May require lba_mid,lba_high register return
+          if (in.out_needed.is_set())
+            valid_options = (m_usr_options ? "saimf" : "saif");
+          else
+            valid_options = (m_usr_options ? "saicmf" : "saicf");
+          break;
+
+        default:
+          // Unknown SMART command, handle below
+          break;
+      }
+      break;
+
+    default:
+      // Other ATA command, handle below
+      break;
+  }
+
+  if (!valid_options) {
+    // No special ATA command found above, select a generic pass through ioctl.
+    if (!(   in.direction == ata_cmd_in::no_data
+          || (in.direction == ata_cmd_in::data_in && in.size == 512))
+         ||  in.in_regs.is_48bit_cmd()                               )
+      // DATA_OUT, more than one sector, 48-bit command: ATA_PASS_THROUGH only
+      valid_options = "a";
+    else if (in.out_needed.is_set())
+      // Need output registers: ATA/IDE_PASS_THROUGH
+      valid_options = "ai";
+    else
+      valid_options = "aic";
+  }
+
+  if (!m_admin) {
+    // Restrict to IOCTL_STORAGE_*
+    if (strchr(valid_options, 'f'))
+      valid_options = "f";
+    else if (strchr(valid_options, 'p'))
+      valid_options = "p";
+    else
+      return set_err(ENOSYS, "Function requires admin rights");
+  }
+
+  // Set IDEREGS
+  IDEREGS regs, prev_regs;
+  {
+    const ata_in_regs & lo = in.in_regs;
+    regs.bFeaturesReg     = lo.features;
+    regs.bSectorCountReg  = lo.sector_count;
+    regs.bSectorNumberReg = lo.lba_low;
+    regs.bCylLowReg       = lo.lba_mid;
+    regs.bCylHighReg      = lo.lba_high;
+    regs.bDriveHeadReg    = lo.device;
+    regs.bCommandReg      = lo.command;
+    regs.bReserved        = 0;
+  }
+  if (in.in_regs.is_48bit_cmd()) {
+    const ata_in_regs & hi = in.in_regs.prev;
+    prev_regs.bFeaturesReg     = hi.features;
+    prev_regs.bSectorCountReg  = hi.sector_count;
+    prev_regs.bSectorNumberReg = hi.lba_low;
+    prev_regs.bCylLowReg       = hi.lba_mid;
+    prev_regs.bCylHighReg      = hi.lba_high;
+    prev_regs.bDriveHeadReg    = hi.device;
+    prev_regs.bCommandReg      = hi.command;
+    prev_regs.bReserved        = 0;
+  }
+
+  // Set data direction
+  int datasize = 0;
+  char * data = 0;
+  switch (in.direction) {
+    case ata_cmd_in::no_data:
+      break;
+    case ata_cmd_in::data_in:
+      datasize = (int)in.size;
+      data = (char *)in.buffer;
+      break;
+    case ata_cmd_in::data_out:
+      datasize = -(int)in.size;
+      data = (char *)in.buffer;
+      break;
+    default:
+      return set_err(EINVAL, "win_ata_device::ata_pass_through: invalid direction=%d",
+          (int)in.direction);
+  }
+
+
+  // Try all valid ioctls in the order specified in m_options
+  bool powered_up = false;
+  bool out_regs_set = false;
+  const char * options = m_options.c_str();
+
+  for (int i = 0; ; i++) {
+    char opt = options[i];
+
+    if (!opt) {
+      if (in.in_regs.command == ATA_CHECK_POWER_MODE && powered_up) {
+        // Power up reported by GetDevicePowerState() and no ioctl available
+        // to detect the actual mode of the drive => simulate ATA result ACTIVE/IDLE.
+        regs.bSectorCountReg = 0xff;
+        out_regs_set = true;
+        break;
+      }
+      // No IOCTL found
+      return set_err(ENOSYS);
+    }
+    if (!strchr(valid_options, opt))
+      // Invalid for this command
+      continue;
+
+    errno = 0;
+    assert(   datasize == 0 || datasize == 512
+           || (datasize == -512 && strchr("am", opt))
+           || (datasize > 512 && opt == 'a'));
+    int rc;
+    switch (opt) {
+      default: assert(0);
+      case 's':
+        // call SMART_GET_VERSION once for each drive
+        if (m_smartver_state > 1) {
+          rc = -1; errno = ENOSYS;
+          break;
+        }
+        if (!m_smartver_state) {
+          assert(m_port == -1);
+          if (smart_get_version(get_fh()) < 0) {
+            if (!con->permissive) {
+              m_smartver_state = 2;
+              rc = -1; errno = ENOSYS;
+              break;
+            }
+            con->permissive--;
+          }
+          m_smartver_state = 1;
+        }
+        rc = smart_ioctl(get_fh(), m_drive, &regs, data, datasize, m_port);
+        out_regs_set = (in.in_regs.features == ATA_SMART_STATUS);
+        break;
+      case 'm':
+        rc = ata_via_scsi_miniport_smart_ioctl(get_fh(), &regs, data, datasize);
+        break;
+      case 'a':
+        rc = ata_pass_through_ioctl(get_fh(), &regs,
+          (in.in_regs.is_48bit_cmd() ? &prev_regs : 0),
+          data, datasize);
+        out_regs_set = true;
+        break;
+      case 'i':
+        rc = ide_pass_through_ioctl(get_fh(), &regs, data, datasize);
+        out_regs_set = true;
+        break;
+      case 'c':
+        rc = ata_via_scsi_pass_through_ioctl(get_fh(), &regs, data, datasize);
+        break;
+      case 'f':
+        if (in.in_regs.command == ATA_IDENTIFY_DEVICE) {
+            rc = get_identify_from_device_property(get_fh(), (ata_identify_device *)data);
+        }
+        else if (in.in_regs.command == ATA_SMART_CMD) switch (in.in_regs.features) {
+          case ATA_SMART_READ_VALUES:
+            rc = storage_predict_failure_ioctl(get_fh(), data);
+            if (rc > 0)
+              rc = 0;
+            break;
+          case ATA_SMART_READ_THRESHOLDS:
+            {
+              ata_smart_values sv;
+              rc = storage_predict_failure_ioctl(get_fh(), (char *)&sv);
+              if (rc < 0)
+                break;
+              rc = 0;
+              // Fake zero thresholds
+              ata_smart_thresholds_pvt * tr = (ata_smart_thresholds_pvt *)data;
+              memset(tr, 0, 512);
+              for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++)
+                tr->chksum -= tr->thres_entries[i].id = sv.vendor_attributes[i].id;
+            }
+            break;
+          case ATA_SMART_ENABLE:
+            rc = 0;
+            break;
+          case ATA_SMART_STATUS:
+            rc = storage_predict_failure_ioctl(get_fh());
+            if (rc >= 0) {
+              if (rc > 0) {
+                regs.bCylHighReg = 0x2c; regs.bCylLowReg = 0xf4;
+                rc = 0;
+              }
+              out_regs_set = true;
+            }
+            break;
+          default:
+            errno = ENOSYS; rc = -1;
+        }
+        else {
+            errno = ENOSYS; rc = -1;
+        }
+        break;
+      case '3':
+        rc = ata_via_3ware_miniport_ioctl(get_fh(), &regs, data, datasize, m_port);
+        out_regs_set = true;
+        break;
+      case 'p':
+        assert(in.in_regs.command == ATA_CHECK_POWER_MODE && in.size == 0);
+        rc = get_device_power_state(get_fh());
+        if (rc == 0) {
+          // Power down reported by GetDevicePowerState(), using a passthrough ioctl would
+          // spin up the drive => simulate ATA result STANDBY.
+          regs.bSectorCountReg = 0x00;
+          out_regs_set = true;
+        }
+        else if (rc > 0) {
+          // Power up reported by GetDevicePowerState(), but this reflects the actual mode
+          // only if it is selected by the device driver => try a passthrough ioctl to get the
+          // actual mode, if none available simulate ACTIVE/IDLE.
+          powered_up = true;
+          rc = -1; errno = ENOSYS;
+        }
+        break;
+    }
+
+    if (!rc)
+      // Working ioctl found
+      break;
+
+    if (errno != ENOSYS)
+      // Abort on I/O error
+      return set_err(errno);
+
+    out_regs_set = false;
+    // CAUTION: *_ioctl() MUST NOT change "regs" Parameter in the ENOSYS case
+  }
+
+  // Return IDEREGS if set
+  if (out_regs_set) {
+    ata_out_regs & lo = out.out_regs;
+    lo.error        = regs.bFeaturesReg;
+    lo.sector_count = regs.bSectorCountReg;
+    lo.lba_low      = regs.bSectorNumberReg;
+    lo.lba_mid      = regs.bCylLowReg;
+    lo.lba_high     = regs.bCylHighReg;
+    lo.device       = regs.bDriveHeadReg;
+    lo.status       = regs.bCommandReg;
+    if (in.in_regs.is_48bit_cmd()) {
+      ata_out_regs & hi = out.out_regs.prev;
+      hi.sector_count = prev_regs.bSectorCountReg;
+      hi.lba_low      = prev_regs.bSectorNumberReg;
+      hi.lba_mid      = prev_regs.bCylLowReg;
+      hi.lba_high     = prev_regs.bCylHighReg;
+    }
+  }
+  return true;
+}
+
+// Return true if OS caches the ATA identify sector
+bool win_ata_device::ata_identify_is_cached() const
+{
+  // Not RAID and WinNT4/2000/XP => true, RAID or Win9x/ME => false
+  // TODO: Set according to ioctl used.
+  return (m_port < 0 && !is_win9x());
 }
 
 
@@ -2595,50 +2826,50 @@ int highpoint_command_interface(int /*fd*/, smart_command_set /*command*/, int /
 // 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
+  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_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_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_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
@@ -2647,10 +2878,10 @@ typedef struct {
 // 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_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()
@@ -2708,22 +2939,22 @@ static DWORD aspi_dll_pid; // PID of DLL owner to detect fork()
 
 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;
+  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;
 }
 
 
@@ -2731,379 +2962,390 @@ static int aspi_call(ASPI_SRB * srb)
 
 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;
+  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":""));
+  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();
+  // save PID to detect fork() in aspi_entry_valid()
+  aspi_dll_pid = GetCurrentProcessId();
 #endif
-       assert(aspi_entry_valid());
-       return 0;
+  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 (ASPI_FDOFFSET | ((adapter & 0xf)<<4) | (id & 0xf));
-}
-
-
-static void aspi_close(int /*fd*/)
-{
-       // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
-}
-
-
-// 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;
+  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;
 }
 
 
-/////////////////////////////////////////////////////////////////////////////
+win_aspi_device::win_aspi_device(smart_interface * intf,
+  const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "scsi", req_type),
+  m_adapter(-1), m_id(0)
+{
+}
+
+bool win_aspi_device::is_open() const
+{
+  return (m_adapter >= 0);
+}
+
+bool win_aspi_device::open()
+{
+  // scsi[0-9][0-f] => ASPI Adapter 0-9, ID 0-15, LUN 0
+  unsigned adapter = ~0, id = ~0; int n1 = -1;
+  const char * name = skipdev(get_dev_name());
+  if (!(sscanf(name,"scsi%1u%1x%n", &adapter, &id, &n1) == 2 && n1 == (int)strlen(name)
+        && adapter <= 9 && id < 16))
+    return set_err(EINVAL);
+
+  if (!aspi_entry_valid()) {
+    if (aspi_open_dll(1/*verbose*/))
+      return set_err(ENOENT);
+  }
+
+  // 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())
+      return set_err(ENOENT);
+  }
+
+  // Device present ?
+  ASPI_SRB srb;
+  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))
+    return set_err(EIO);
+  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())
+      return set_err(srb.h.status == ASPI_STATUS_INVALID_TARGET ? ENOENT : EIO);
+  }
+  else if (con->reportscsiioctl)
+    pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype);
+
+  m_adapter = (int)adapter; m_id = (unsigned char)id;
+  return true;
+}
+
+
+bool win_aspi_device::close()
+{
+  // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
+  return true;
+}
+
+
+// Scan for ASPI drives
 
-// Interface to ASPI SCSI devices.  See scsicmds.h and os_linux.c
-static int do_aspi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
-{
-       ASPI_SRB srb;
-
-       if (!aspi_entry_valid())
-               return -EBADF;
-       if (!((fd & ~0xff) == ASPI_FDOFFSET))
-               return -EBADF;
-
-       if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12 || iop->cmnd_len == 16)) {
-               pout("do_aspi_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_aspi_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;
+bool win9x_smart_interface::scsi_scan(smart_device_list & devlist)
+{
+  if (!aspi_entry_valid()) {
+    if (aspi_open_dll(con->reportscsiioctl/*default is quiet*/))
+      return true;
+  }
+
+  for (unsigned ad = 0; ad < num_aspi_adapters; ad++) {
+    ASPI_SRB srb;
+
+    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))
+      break;
+
+    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) {
+      for (int 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 (unsigned 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);
+        char name[20];
+        sprintf(name, "/dev/scsi%u%u", ad, id);
+        devlist.add( new win_aspi_device(this, name, "scsi") );
+      }
+      else if (con->reportscsiioctl)
+        pout("  ID %u: Device Type=0x%02x (ignored)\n", id, srb.t.devtype);
+    }
+  }
+  return true;
+}
+
+
+// Interface to ASPI SCSI devices
+bool win_aspi_device::scsi_pass_through(scsi_cmnd_io * iop)
+{
+  int report = con->reportscsiioctl; // TODO
+
+  if (m_adapter < 0) {
+    set_err(EBADF);
+    return false;
+  }
+
+  if (!aspi_entry_valid()) {
+    set_err(EBADF);
+    return false;
+  }
+
+  if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12 || iop->cmnd_len == 16)) {
+    set_err(EINVAL, "bad CDB length");
+    return false;
+  }
+
+  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);
+  }
+
+  ASPI_SRB srb;
+  memset(&srb, 0, sizeof(srb));
+  srb.h.cmd = ASPI_CMD_EXECUTE_IO;
+  srb.h.adapter = m_adapter;
+  srb.i.target_id = m_id;
+  //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:
+      set_err(EINVAL, "bad dxfer_dir");
+      return false;
+  }
+
+  iop->resp_sense_len = 0;
+  iop->scsi_status = 0;
+  iop->resid = 0;
+
+  if (aspi_io_call(&srb, (iop->timeout ? iop->timeout : 60))) {
+    // Timeout
+    set_err(EIO, "ASPI Timeout"); return false;
+  }
+
+  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 true;
+    }
+    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);
+      set_err(EIO);
+      return false;
+    }
+  }
+
+  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 true;
 }
 
 
@@ -3112,270 +3354,304 @@ static int do_aspi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
 // Only supported in NT and later
 /////////////////////////////////////////////////////////////////////////////
 
-#define SPT_MAXDEV 64
+win_scsi_device::win_scsi_device(smart_interface * intf,
+  const char * dev_name, const char * req_type)
+: smart_device(intf, dev_name, "scsi", req_type)
+{
+}
 
-struct spt_dev_info {
-       HANDLE h_spt_ioctl;
-       int   sub_addr;         // addressing disks within a RAID, for example
-};
+bool win_scsi_device::open()
+{
+  const char * name = skipdev(get_dev_name()); int len = strlen(name);
+  // sd[a-z],N => Physical drive 0-26, RAID port N
+  char drive[1+1] = ""; int sub_addr = -1; int n1 = -1; int n2 = -1;
+  if (   sscanf(name, "sd%1[a-z]%n,%d%n", drive, &n1, &sub_addr, &n2) >= 1
+      && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))  ) {
+    return open(drive[0] - 'a', -1, -1, sub_addr);
+  }
+  // pd<m>,N => Physical drive <m>, RAID port N
+  int pd_num = -1; sub_addr = -1; n1 = -1; n2 = -1;
+  if (   sscanf(name, "pd%d%n,%d%n", &pd_num, &n1, &sub_addr, &n2) >= 1
+      && pd_num >= 0 && ((n1 == len && sub_addr == -1) || (n2 == len && sub_addr >= 0))) {
+    return open(pd_num, -1, -1, sub_addr);
+  }
+  // [a-zA-Z]: => Physical drive behind logical drive 0-25
+  int logdrive = drive_letter(name);
+  if (logdrive >= 0) {
+    return open(-1, logdrive, -1, -1);
+  }
+  // n?st<m> => tape drive <m> (same names used in Cygwin's /dev emulation)
+  int tape_num = -1; n1 = -1;
+  if (sscanf(name, "st%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
+    return open(-1, -1, tape_num, -1);
+  }
+  tape_num = -1; n1 = -1;
+  if (sscanf(name, "nst%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
+    return open(-1, -1, tape_num, -1);
+  }
+  // tape<m> => tape drive <m>
+  tape_num = -1; n1 = -1;
+  if (sscanf(name, "tape%d%n", &tape_num, &n1) == 1 && tape_num >= 0 && n1 == len) {
+    return open(-1, -1, tape_num, -1);
+  }
+
+  return set_err(EINVAL);
+}
 
-// Private table of open devices: guaranteed zero on startup since
-// part of static data.
-static struct spt_dev_info * spt_dev_arr[SPT_MAXDEV];
-
-
-static int spt_open(int pd_num, int ld_num, int tape_num, int sub_addr)
-{
-       int k;
-       struct spt_dev_info * sdip;
-       char b[128];
-       HANDLE h;
-
-       for (k = 0; k < SPT_MAXDEV; k++)
-               if (! spt_dev_arr[k])
-                       break;
-
-       // If no free entry found, return error.  We have max allowed number
-       // of "file descriptors" already allocated.
-       if (k == SPT_MAXDEV) {
-               if (con->reportscsiioctl)
-                       pout("spt_open: too many open file descriptors (%d)\n",
-                            SPT_MAXDEV);
-               errno = EMFILE;
-               return -1;
-       }
-       sdip = (struct spt_dev_info *)malloc(sizeof(struct spt_dev_info));
-       if (NULL == sdip) {
-               errno = ENOMEM;
-               return -1;
-       }
-       spt_dev_arr[k] = sdip;
-       sdip->sub_addr = sub_addr;
-
-       b[sizeof(b) - 1] = '\0';
-       if (pd_num >= 0)
-               snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num);
-       else if (ld_num >= 0)
-               snprintf(b, sizeof(b) - 1, "\\\\.\\%c:", 'A' + ld_num);
-       else if (tape_num >= 0)
-               snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num);
-       else {
-               if (con->reportscsiioctl)
-                       pout("spt_open: bad parameters\n");
-               errno = EINVAL;
-               goto err_out;
-       }
-
-       // Open device
-       if ((h = CreateFileA(b, GENERIC_READ|GENERIC_WRITE,
-                            FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
-                            OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
-               if (con->reportscsiioctl)
-                       pout(" %s: Open failed, Error=%ld\n", b, GetLastError());
-               errno = ENODEV;
-               goto err_out;
-       }
-       sdip->h_spt_ioctl = h;
-       return k + SPT_FDOFFSET;
-
-err_out:
-       spt_dev_arr[k] = NULL;
-       free(sdip);
-       return -1;
-}
-
-
-static void spt_close(int fd)
-{
-       struct spt_dev_info * sdip;
-       int index = fd - SPT_FDOFFSET;
-
-       if ((index < 0) || (index >= SPT_MAXDEV)) {
-               if (con->reportscsiioctl)
-                       pout("spt_close: bad fd range\n");
-               return;
-       }
-       sdip = spt_dev_arr[index];
-       if (NULL == sdip) {
-               if (con->reportscsiioctl)
-                       pout("spt_close: fd already closed\n");
-               return;
-       }
-       free(sdip);
-       spt_dev_arr[index] = NULL;
-}
-
-
-static int spt_scan(unsigned long * drives)
-{
-       int cnt = 0;
-       for (int i = 0; i <= 9; i++) {
-               if (get_phy_drive_type(i) != CONTROLLER_SCSI)
-                       continue;
-               // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
-               drives[0] |= (1L << i);
-               cnt++;
-       }
-       return cnt;
+bool win_scsi_device::open(int pd_num, int ld_num, int tape_num, int /*sub_addr*/)
+{
+  char b[128];
+  b[sizeof(b) - 1] = '\0';
+  if (pd_num >= 0)
+    snprintf(b, sizeof(b) - 1, "\\\\.\\PhysicalDrive%d", pd_num);
+  else if (ld_num >= 0)
+    snprintf(b, sizeof(b) - 1, "\\\\.\\%c:", 'A' + ld_num);
+  else if (tape_num >= 0)
+    snprintf(b, sizeof(b) - 1, "\\\\.\\TAPE%d", tape_num);
+  else {
+    set_err(EINVAL);
+    return false;
+  }
+
+  // Open device
+  HANDLE h = CreateFileA(b, GENERIC_READ|GENERIC_WRITE,
+           FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
+           OPEN_EXISTING, 0, 0);
+  if (h == INVALID_HANDLE_VALUE) {
+    set_err(ENODEV, "%s: Open failed, Error=%ld", b, GetLastError());
+    return false;
+  }
+  set_fh(h);
+  return true;
+}
+
+
+bool winnt_smart_interface::scsi_scan(smart_device_list & devlist)
+{
+  char name[20];
+  for (int i = 0; i <= 9; i++) {
+    if (get_phy_drive_type(i) != DEV_SCSI)
+      continue;
+    // STORAGE_QUERY_PROPERTY returned SCSI/SAS/...
+    sprintf(name, "/dev/sd%c", 'a'+i);
+    devlist.add( new win_scsi_device(this, name, "scsi") );
+  }
+  return true;
 }
 
 
 #define IOCTL_SCSI_PASS_THROUGH_DIRECT  \
-       CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+  CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 
 typedef struct _SCSI_PASS_THROUGH_DIRECT {
-       USHORT          Length;
-       UCHAR           ScsiStatus;
-       UCHAR           PathId;
-       UCHAR           TargetId;
-       UCHAR           Lun;
-       UCHAR           CdbLength;
-       UCHAR           SenseInfoLength;
-       UCHAR           DataIn;
-       ULONG           DataTransferLength;
-       ULONG           TimeOutValue;
-       PVOID           DataBuffer;
-       ULONG           SenseInfoOffset;
-       UCHAR           Cdb[16];
+  USHORT          Length;
+  UCHAR           ScsiStatus;
+  UCHAR           PathId;
+  UCHAR           TargetId;
+  UCHAR           Lun;
+  UCHAR           CdbLength;
+  UCHAR           SenseInfoLength;
+  UCHAR           DataIn;
+  ULONG           DataTransferLength;
+  ULONG           TimeOutValue;
+  PVOID           DataBuffer;
+  ULONG           SenseInfoOffset;
+  UCHAR           Cdb[16];
 } SCSI_PASS_THROUGH_DIRECT;
 
 typedef struct {
-       SCSI_PASS_THROUGH_DIRECT spt;
-       ULONG           Filler;
-       UCHAR           ucSenseBuf[64];
+  SCSI_PASS_THROUGH_DIRECT spt;
+  ULONG           Filler;
+  UCHAR           ucSenseBuf[64];
 } SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
 
 
+// Issue command via IOCTL_SCSI_PASS_THROUGH instead of *_DIRECT.
+// Used if DataTransferLength not supported by *_DIRECT.
+static long scsi_pass_through_indirect(HANDLE h,
+  SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER * sbd)
+{
+  struct SCSI_PASS_THROUGH_WITH_BUFFERS {
+    SCSI_PASS_THROUGH spt;
+    ULONG Filler;
+    UCHAR ucSenseBuf[sizeof(sbd->ucSenseBuf)];
+    UCHAR ucDataBuf[512];
+  };
+
+  SCSI_PASS_THROUGH_WITH_BUFFERS sb;
+  memset(&sb, 0, sizeof(sb));
+
+  // DATA_OUT not implemented yet
+  if (!(   sbd->spt.DataIn == SCSI_IOCTL_DATA_IN
+        && sbd->spt.DataTransferLength <= sizeof(sb.ucDataBuf)))
+    return ERROR_INVALID_PARAMETER;
+
+  sb.spt.Length = sizeof(sb.spt);
+  sb.spt.CdbLength = sbd->spt.CdbLength;
+  memcpy(sb.spt.Cdb, sbd->spt.Cdb, sizeof(sb.spt.Cdb));
+  sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
+  sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
+  sb.spt.DataIn = sbd->spt.DataIn;
+  sb.spt.DataTransferLength = sbd->spt.DataTransferLength;
+  sb.spt.DataBufferOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
+  sb.spt.TimeOutValue = sbd->spt.TimeOutValue;
+
+  DWORD num_out;
+  if (!DeviceIoControl(h, IOCTL_SCSI_PASS_THROUGH,
+         &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
+    return GetLastError();
+
+  sbd->spt.ScsiStatus = sb.spt.ScsiStatus;
+  if (sb.spt.ScsiStatus & SCSI_STATUS_CHECK_CONDITION)
+    memcpy(sbd->ucSenseBuf, sb.ucSenseBuf, sizeof(sbd->ucSenseBuf));
+
+  sbd->spt.DataTransferLength = sb.spt.DataTransferLength;
+  if (sbd->spt.DataIn == SCSI_IOCTL_DATA_IN && sb.spt.DataTransferLength > 0)
+    memcpy(sbd->spt.DataBuffer, sb.ucDataBuf, sb.spt.DataTransferLength);
+  return 0;
+}
+
+
 // Interface to SPT SCSI devices.  See scsicmds.h and os_linux.c
-static int do_spt_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
-{
-       struct spt_dev_info * sdip;
-       int index = fd - SPT_FDOFFSET;
-       SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
-       DWORD num_out;
-
-       if ((index < 0) || (index >= SPT_MAXDEV)) {
-               if (report)
-                       pout("do_spt_cmnd_io: bad fd range\n");
-               return -EBADF;
-       }
-       sdip = spt_dev_arr[index];
-       if (NULL == sdip) {
-               if (report)
-                       pout("do_spt_cmnd_io: fd already closed\n");
-               return -EBADF;
-       }
-
-       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(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
-               }
-               else
-                       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
-               pout(buff);
-       }
-       if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
-               if (report)
-                       pout("do_spt_cmnd_io: cmnd_len too large\n");
-               return -EINVAL;
-       }
-
-       memset(&sb, 0, sizeof(sb));
-       sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
-       sb.spt.CdbLength = iop->cmnd_len;
-       memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
-       sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
-       sb.spt.SenseInfoOffset =
-               offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
-       sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
-       switch (iop->dxfer_dir) {
-               case DXFER_NONE:
-                       sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
-                       break;
-               case DXFER_FROM_DEVICE:
-                       sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
-                       sb.spt.DataTransferLength = iop->dxfer_len;
-                       sb.spt.DataBuffer = iop->dxferp;
-                       break;
-               case DXFER_TO_DEVICE:
-                       sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
-                       sb.spt.DataTransferLength = iop->dxfer_len;
-                       sb.spt.DataBuffer = iop->dxferp;
-                       break;
-               default:
-                       pout("do_spt_cmnd_io: bad dxfer_dir\n");
-                       return -EINVAL;
-       }
-
-       if (! DeviceIoControl(sdip->h_spt_ioctl, IOCTL_SCSI_PASS_THROUGH_DIRECT,
-               &sb, sizeof(sb), &sb, sizeof(sb), &num_out, NULL)) {
-               long err = GetLastError();
-
-               if (report)
-                       pout("  IOCTL_SCSI_PASS_THROUGH_DIRECT failed, Error=%ld\n", err);
-               return -(err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
-       }
-
-       iop->scsi_status = sb.spt.ScsiStatus;
-       if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
-               int slen = sb.ucSenseBuf[7] + 8;
-
-               if (slen > (int)sizeof(sb.ucSenseBuf))
-                       slen = sizeof(sb.ucSenseBuf);
-               if (slen > (int)iop->max_sense_len)
-                       slen = iop->max_sense_len;
-               memcpy(iop->sensep, sb.ucSenseBuf, slen);
-               iop->resp_sense_len = slen;
-               if (report) {
-                       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
-               iop->resp_sense_len = 0;
-
-       if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
-               iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
-       else
-               iop->resid = 0;
-
-       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;
-}
-
-
-// Decides which SCSI implementation based on pseudo fd.
-// Declaration and explanation in scsicmds.h
-int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
-{
-       if ((fd & ~0xff) == ASPI_FDOFFSET)
-               return do_aspi_cmnd_io(fd, iop, report);
-       else
-               return do_spt_cmnd_io(fd, iop, report);
+bool win_scsi_device::scsi_pass_through(struct scsi_cmnd_io * iop)
+{
+  int report = con->reportscsiioctl; // TODO
+
+  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(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+    }
+    else
+      j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+    pout(buff);
+  }
+
+  SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sb;
+  if (iop->cmnd_len > (int)sizeof(sb.spt.Cdb)) {
+    set_err(EINVAL, "cmnd_len too large");
+    return false;
+  }
+
+  memset(&sb, 0, sizeof(sb));
+  sb.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
+  sb.spt.CdbLength = iop->cmnd_len;
+  memcpy(sb.spt.Cdb, iop->cmnd, iop->cmnd_len);
+  sb.spt.SenseInfoLength = sizeof(sb.ucSenseBuf);
+  sb.spt.SenseInfoOffset =
+    offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, ucSenseBuf);
+  sb.spt.TimeOutValue = (iop->timeout ? iop->timeout : 60);
+
+  bool direct = true;
+  switch (iop->dxfer_dir) {
+    case DXFER_NONE:
+      sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+      break;
+    case DXFER_FROM_DEVICE:
+      sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+      sb.spt.DataTransferLength = iop->dxfer_len;
+      sb.spt.DataBuffer = iop->dxferp;
+      // IOCTL_SCSI_PASS_THROUGH_DIRECT does not support single byte
+      // transfers (needed for SMART STATUS check of JMicron USB briges)
+      if (sb.spt.DataTransferLength == 1)
+        direct = false;
+      break;
+    case DXFER_TO_DEVICE:
+      sb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
+      sb.spt.DataTransferLength = iop->dxfer_len;
+      sb.spt.DataBuffer = iop->dxferp;
+      break;
+    default:
+      set_err(EINVAL, "bad dxfer_dir");
+      return false;
+  }
+
+  long err = 0;
+  if (direct) {
+    DWORD num_out;
+    if (!DeviceIoControl(get_fh(), IOCTL_SCSI_PASS_THROUGH_DIRECT,
+           &sb, sizeof(sb), &sb, sizeof(sb), &num_out, 0))
+      err = GetLastError();
+  }
+  else
+    err = scsi_pass_through_indirect(get_fh(), &sb);
+
+  if (err)
+    return set_err((err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO),
+      "IOCTL_SCSI_PASS_THROUGH%s failed, Error=%ld",
+      (direct ? "_DIRECT" : ""), err);
+
+  iop->scsi_status = sb.spt.ScsiStatus;
+  if (SCSI_STATUS_CHECK_CONDITION & iop->scsi_status) {
+    int slen = sb.ucSenseBuf[7] + 8;
+
+    if (slen > (int)sizeof(sb.ucSenseBuf))
+      slen = sizeof(sb.ucSenseBuf);
+    if (slen > (int)iop->max_sense_len)
+      slen = iop->max_sense_len;
+    memcpy(iop->sensep, sb.ucSenseBuf, slen);
+    iop->resp_sense_len = slen;
+    if (report) {
+      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
+    iop->resp_sense_len = 0;
+
+  if ((iop->dxfer_len > 0) && (sb.spt.DataTransferLength > 0))
+    iop->resid = iop->dxfer_len - sb.spt.DataTransferLength;
+  else
+    iop->resid = 0;
+
+  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 true;
 }
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+} // namespace
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Initialize platform interface and register with smi()
+void smart_interface::init()
+{
+  // Select interface for Windows flavor
+  if (os_win32::is_win9x()) {
+    static os_win32::win9x_smart_interface the_win9x_interface;
+    smart_interface::set(&the_win9x_interface);
+  }
+  else {
+    static os_win32::winnt_smart_interface the_winnt_interface;
+    smart_interface::set(&the_winnt_interface);
+  }
+}
+
index 16d21432f106b6f7170151f16945f7f25d3a2609..064add38b18b2342e3af8e9f944f6efaf08a28a1 100644 (file)
@@ -1,20 +1,20 @@
 ;
 ; installer.nsi - NSIS install script for smartmontools
 ;
-; Copyright (C) 2006-8 Christian Franke <smartmontools-support@lists.sourceforge.net>
+; Copyright (C) 2006-9 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.29)
+; Process with makensis to create installer (tested with NSIS 2.45)
 ;
-; $Id: installer.nsi,v 1.4 2008/03/04 22:09:48 ballen4705 Exp $
+; $Id: installer.nsi 2878 2009-08-26 20:03:06Z chrfranke $
 ;
 
 
 ;--------------------------------------------------------------------
 ; Command line arguments:
-; makensis /DINPDIR=<input-dir> /DOUTFILE=<output-file> installer.nsi
+; makensis /DINPDIR=<input-dir> /DOUTFILE=<output-file> /DVERSTR=<version-string> installer.nsi
 
 !ifndef INPDIR
   !define INPDIR "."
@@ -106,6 +106,15 @@ SectionGroup "!Program files"
 
   SectionEnd
 
+  Section "smartctl-nc (GSmartControl)" SMARTCTL_NC_SECTION
+
+    SectionIn 1 2
+
+    SetOutPath "$INSTDIR\bin"
+    File "${INPDIR}\bin\smartctl-nc.exe"
+
+  SectionEnd
+
 SectionGroupEnd
 
 Section "!Documentation" DOC_SECTION
@@ -143,6 +152,9 @@ Section "Uninstaller" UNINST_SECTION
 
   ; Write uninstall keys and program
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "DisplayName" "smartmontools"
+!ifdef VERSTR
+  WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "DisplayVersion" "${VERSTR}"
+!endif
   ;WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "Publisher" "smartmontools"
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "UninstallString" '"$INSTDIR\uninst-smartmontools.exe"'
   ;WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "URLInfoAbout" "http://smartmontools.sourceforge.net/"
@@ -378,6 +390,7 @@ Section "Uninstall"
 
   ; Remove files
   Delete "$INSTDIR\bin\smartctl.exe"
+  Delete "$INSTDIR\bin\smartctl-nc.exe"
   Delete "$INSTDIR\bin\smartd.exe"
   Delete "$INSTDIR\bin\syslogevt.exe"
   Delete "$INSTDIR\bin\smartctl-run.bat"
@@ -462,6 +475,7 @@ FunctionEnd
 
 Function SkipProgPath
   !insertmacro CheckSection ${SMARTCTL_SECTION}
+  !insertmacro CheckSection ${SMARTCTL_NC_SECTION}
   !insertmacro CheckSection ${SMARTD_SECTION}
   !insertmacro CheckSection ${DOC_SECTION}
   !insertmacro CheckSection ${MENU_SECTION}
diff --git a/os_win32/smartctl_vc8.vcproj b/os_win32/smartctl_vc8.vcproj
new file mode 100644 (file)
index 0000000..1f229fe
--- /dev/null
@@ -0,0 +1,1051 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="8,00"
+       Name="smartctl_vc8"
+       ProjectGUID="{3AFEDCDD-D289-4543-A91D-EFBA6C710247}"
+       RootNamespace="smartctl_vc8"
+       Keyword="Win32Proj"
+       >
+       <Platforms>
+               <Platform
+                       Name="Win32"
+               />
+       </Platforms>
+       <ToolFiles>
+       </ToolFiles>
+       <Configurations>
+               <Configuration
+                       Name="Debug|Win32"
+                       OutputDirectory=".\smartctl_vc8.d"
+                       IntermediateDirectory=".\smartctl_vc8.d"
+                       ConfigurationType="1"
+                       CharacterSet="2"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="0"
+                               AdditionalIncludeDirectories=".,..\posix"
+                               PreprocessorDefinitions="_DEBUG;HAVE_CONFIG_H;_ERRCODE_DEFINED;errno_t=int;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE"
+                               MinimalRebuild="true"
+                               BasicRuntimeChecks="3"
+                               RuntimeLibrary="3"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               Detect64BitPortabilityProblems="false"
+                               DebugInformationFormat="4"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               LinkIncremental="2"
+                               GenerateDebugInformation="true"
+                               SubSystem="1"
+                               TargetMachine="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory=".\smartctl_vc8.r"
+                       IntermediateDirectory=".\smartctl_vc8.r"
+                       ConfigurationType="1"
+                       CharacterSet="2"
+                       WholeProgramOptimization="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalIncludeDirectories=".,..\posix"
+                               PreprocessorDefinitions="NDEBUG;HAVE_CONFIG_H;_ERRCODE_DEFINED;errno_t=int;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE"
+                               RuntimeLibrary="2"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               Detect64BitPortabilityProblems="false"
+                               DebugInformationFormat="3"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               OutputFile="smartctl.exe"
+                               LinkIncremental="1"
+                               GenerateDebugInformation="true"
+                               ProgramDatabaseFile="$(IntDir)\$(TargetName).pdb"
+                               SubSystem="1"
+                               OptimizeReferences="2"
+                               EnableCOMDATFolding="2"
+                               TargetMachine="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+       </Configurations>
+       <References>
+       </References>
+       <Files>
+               <Filter
+                       Name="os_win32"
+                       >
+                       <File
+                               RelativePath=".\daemon_win32.cpp"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+                       <File
+                               RelativePath=".\daemon_win32.h"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCustomBuildTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCustomBuildTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+                       <File
+                               RelativePath=".\hostname_win32.cpp"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+                       <File
+                               RelativePath=".\hostname_win32.h"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCustomBuildTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCustomBuildTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+                       <File
+                               RelativePath=".\installer.nsi"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\syslog.h"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCustomBuildTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCustomBuildTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+                       <File
+                               RelativePath=".\syslog_win32.cpp"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+               </Filter>
+               <Filter
+                       Name="posix"
+                       >
+                       <File
+                               RelativePath="..\posix\getopt.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\getopt.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\getopt1.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\regcomp.c"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+                       <File
+                               RelativePath="..\posix\regex.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\regex.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\regex_internal.c"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+                       <File
+                               RelativePath="..\posix\regex_internal.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\regexec.c"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+               </Filter>
+               <File
+                       RelativePath="..\atacmdnames.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\atacmdnames.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\atacmds.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\atacmds.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\ataprint.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\ataprint.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\AUTHORS"
+                       >
+               </File>
+               <File
+                       RelativePath="..\autogen.sh"
+                       >
+               </File>
+               <File
+                       RelativePath="..\cciss.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\cciss.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\CHANGELOG"
+                       >
+               </File>
+               <File
+                       RelativePath="..\config.h.in"
+                       >
+               </File>
+               <File
+                       RelativePath=".\config_vc8.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                                       Description="Copy $(InputPath) config.h"
+                                       CommandLine="copy $(InputPath) config.h&#x0D;&#x0A;"
+                                       Outputs="config.h"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                                       Description="Copy $(InputPath) config.h"
+                                       CommandLine="copy $(InputPath) config.h&#x0D;&#x0A;"
+                                       Outputs="config.h"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\configure.in"
+                       >
+               </File>
+               <File
+                       RelativePath="..\dev_ata_cmd_set.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\dev_ata_cmd_set.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\dev_interface.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\dev_interface.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\dev_legacy.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\dev_tunnelled.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\do_release"
+                       >
+               </File>
+               <File
+                       RelativePath="..\Doxyfile"
+                       >
+               </File>
+               <File
+                       RelativePath="..\extern.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\INSTALL"
+                       >
+               </File>
+               <File
+                       RelativePath="..\int64.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\knowndrives.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\knowndrives.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\Makefile.am"
+                       >
+               </File>
+               <File
+                       RelativePath="..\NEWS"
+                       >
+               </File>
+               <File
+                       RelativePath="..\os_darwin.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_darwin.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_freebsd.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_freebsd.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_generic.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_generic.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_linux.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_linux.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_netbsd.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_netbsd.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_openbsd.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_openbsd.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_os2.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_os2.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_qnxnto.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_qnxnto.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_solaris.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_solaris.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_win32.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\README"
+                       >
+               </File>
+               <File
+                       RelativePath="..\scsiata.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\scsicmds.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\scsicmds.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\scsiprint.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\scsiprint.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\smartctl.8.in"
+                       >
+               </File>
+               <File
+                       RelativePath="..\smartctl.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\smartctl.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\smartd.8.in"
+                       >
+               </File>
+               <File
+                       RelativePath="..\smartd.conf.5.in"
+                       >
+               </File>
+               <File
+                       RelativePath="..\smartd.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\smartd.initd.in"
+                       >
+               </File>
+               <File
+                       RelativePath=".\svnversion_vc8.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                                       Description="Copy $(InputPath) svnversion.h"
+                                       CommandLine="copy $(InputPath) svnversion.h&#x0D;&#x0A;"
+                                       Outputs="svnversion.h"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                                       Description="Copy $(InputPath) svnversion.h"
+                                       CommandLine="copy $(InputPath) svnversion.h&#x0D;&#x0A;"
+                                       Outputs="svnversion.h"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\TODO"
+                       >
+               </File>
+               <File
+                       RelativePath="..\utility.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\utility.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\WARNINGS"
+                       >
+               </File>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
diff --git a/os_win32/smartd_vc8.vcproj b/os_win32/smartd_vc8.vcproj
new file mode 100644 (file)
index 0000000..f9ce4e9
--- /dev/null
@@ -0,0 +1,1003 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="8,00"
+       Name="smartd_vc8"
+       ProjectGUID="{C0762191-C2AC-40B6-A2EB-F1658BBDC4C6}"
+       RootNamespace="smartd_vc8"
+       Keyword="Win32Proj"
+       >
+       <Platforms>
+               <Platform
+                       Name="Win32"
+               />
+       </Platforms>
+       <ToolFiles>
+       </ToolFiles>
+       <Configurations>
+               <Configuration
+                       Name="Debug|Win32"
+                       OutputDirectory=".\smartd_vc8.d"
+                       IntermediateDirectory=".\smartd_vc8.d"
+                       ConfigurationType="1"
+                       CharacterSet="2"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="0"
+                               AdditionalIncludeDirectories=".,..\posix"
+                               PreprocessorDefinitions="_DEBUG;HAVE_CONFIG_H;_ERRCODE_DEFINED;errno_t=int;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE"
+                               MinimalRebuild="true"
+                               BasicRuntimeChecks="3"
+                               RuntimeLibrary="3"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               Detect64BitPortabilityProblems="false"
+                               DebugInformationFormat="4"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               LinkIncremental="2"
+                               GenerateDebugInformation="true"
+                               SubSystem="1"
+                               TargetMachine="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory=".\smartd_vc8.r"
+                       IntermediateDirectory=".\smartd_vc8.r"
+                       ConfigurationType="1"
+                       CharacterSet="2"
+                       WholeProgramOptimization="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               AdditionalIncludeDirectories=".,..\posix"
+                               PreprocessorDefinitions="NDEBUG;HAVE_CONFIG_H;_ERRCODE_DEFINED;errno_t=int;_USE_32BIT_TIME_T;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_DEPRECATE"
+                               RuntimeLibrary="2"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               Detect64BitPortabilityProblems="false"
+                               DebugInformationFormat="3"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               OutputFile="smartd.exe"
+                               LinkIncremental="1"
+                               GenerateDebugInformation="true"
+                               ProgramDatabaseFile="$(IntDir)\$(TargetName).pdb"
+                               SubSystem="1"
+                               OptimizeReferences="2"
+                               EnableCOMDATFolding="2"
+                               TargetMachine="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+       </Configurations>
+       <References>
+       </References>
+       <Files>
+               <Filter
+                       Name="os_win32"
+                       >
+                       <File
+                               RelativePath=".\daemon_win32.cpp"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\daemon_win32.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\hostname_win32.cpp"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\hostname_win32.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\installer.nsi"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\syslog.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath=".\syslog_win32.cpp"
+                               >
+                       </File>
+               </Filter>
+               <Filter
+                       Name="posix"
+                       >
+                       <File
+                               RelativePath="..\posix\getopt.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\getopt.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\getopt1.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\regcomp.c"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+                       <File
+                               RelativePath="..\posix\regex.c"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\regex.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\regex_internal.c"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+                       <File
+                               RelativePath="..\posix\regex_internal.h"
+                               >
+                       </File>
+                       <File
+                               RelativePath="..\posix\regexec.c"
+                               >
+                               <FileConfiguration
+                                       Name="Debug|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                               <FileConfiguration
+                                       Name="Release|Win32"
+                                       ExcludedFromBuild="true"
+                                       >
+                                       <Tool
+                                               Name="VCCLCompilerTool"
+                                       />
+                               </FileConfiguration>
+                       </File>
+               </Filter>
+               <File
+                       RelativePath="..\atacmdnames.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\atacmdnames.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\atacmds.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\atacmds.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\ataprint.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\ataprint.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\AUTHORS"
+                       >
+               </File>
+               <File
+                       RelativePath="..\autogen.sh"
+                       >
+               </File>
+               <File
+                       RelativePath="..\cciss.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\cciss.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\CHANGELOG"
+                       >
+               </File>
+               <File
+                       RelativePath="..\config.h.in"
+                       >
+               </File>
+               <File
+                       RelativePath=".\config_vc8.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                                       Description="Copy $(InputPath) config.h"
+                                       CommandLine="copy $(InputPath) config.h&#x0D;&#x0A;"
+                                       Outputs="config.h"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                                       Description="Copy $(InputPath) config.h"
+                                       CommandLine="copy $(InputPath) config.h&#x0D;&#x0A;"
+                                       Outputs="config.h"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\configure.in"
+                       >
+               </File>
+               <File
+                       RelativePath="..\dev_ata_cmd_set.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\dev_ata_cmd_set.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\dev_interface.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\dev_interface.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\dev_legacy.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\dev_tunnelled.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\do_release"
+                       >
+               </File>
+               <File
+                       RelativePath="..\Doxyfile"
+                       >
+               </File>
+               <File
+                       RelativePath="..\extern.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\INSTALL"
+                       >
+               </File>
+               <File
+                       RelativePath="..\int64.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\knowndrives.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\knowndrives.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\Makefile.am"
+                       >
+               </File>
+               <File
+                       RelativePath="..\NEWS"
+                       >
+               </File>
+               <File
+                       RelativePath="..\os_darwin.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_darwin.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_freebsd.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_freebsd.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_generic.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_generic.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_linux.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_linux.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_netbsd.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_netbsd.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_openbsd.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_openbsd.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_os2.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_os2.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_qnxnto.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_qnxnto.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_solaris.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_solaris.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\os_win32.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\README"
+                       >
+               </File>
+               <File
+                       RelativePath="..\scsiata.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\scsicmds.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\scsicmds.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\scsiprint.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\scsiprint.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\smartctl.8.in"
+                       >
+               </File>
+               <File
+                       RelativePath="..\smartctl.cpp"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCLCompilerTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\smartctl.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               ExcludedFromBuild="true"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\smartd.8.in"
+                       >
+               </File>
+               <File
+                       RelativePath="..\smartd.conf.5.in"
+                       >
+               </File>
+               <File
+                       RelativePath="..\smartd.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\smartd.initd.in"
+                       >
+               </File>
+               <File
+                       RelativePath=".\svnversion_vc8.h"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                                       Description="Copy $(InputPath) svnversion.h"
+                                       CommandLine="copy $(InputPath) svnversion.h&#x0D;&#x0A;"
+                                       Outputs="svnversion.h"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                                       Description="Copy $(InputPath) svnversion.h"
+                                       CommandLine="copy $(InputPath) svnversion.h&#x0D;&#x0A;"
+                                       Outputs="svnversion.h"
+                               />
+                       </FileConfiguration>
+               </File>
+               <File
+                       RelativePath="..\TODO"
+                       >
+               </File>
+               <File
+                       RelativePath="..\utility.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\utility.h"
+                       >
+               </File>
+               <File
+                       RelativePath="..\WARNINGS"
+                       >
+               </File>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
diff --git a/os_win32/smartmontools_vc8.sln b/os_win32/smartmontools_vc8.sln
new file mode 100644 (file)
index 0000000..77be5f8
--- /dev/null
@@ -0,0 +1,32 @@
+
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "smartctl_vc8", "smartctl_vc8.vcproj", "{3AFEDCDD-D289-4543-A91D-EFBA6C710247}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "smartd_vc8", "smartd_vc8.vcproj", "{C0762191-C2AC-40B6-A2EB-F1658BBDC4C6}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "syslogevt_vc8", "syslogevt_vc8.vcproj", "{FAB7557B-86EA-405D-B49D-33AB3F4D3E33}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Win32 = Debug|Win32
+               Release|Win32 = Release|Win32
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {3AFEDCDD-D289-4543-A91D-EFBA6C710247}.Debug|Win32.ActiveCfg = Debug|Win32
+               {3AFEDCDD-D289-4543-A91D-EFBA6C710247}.Debug|Win32.Build.0 = Debug|Win32
+               {3AFEDCDD-D289-4543-A91D-EFBA6C710247}.Release|Win32.ActiveCfg = Release|Win32
+               {3AFEDCDD-D289-4543-A91D-EFBA6C710247}.Release|Win32.Build.0 = Release|Win32
+               {C0762191-C2AC-40B6-A2EB-F1658BBDC4C6}.Debug|Win32.ActiveCfg = Debug|Win32
+               {C0762191-C2AC-40B6-A2EB-F1658BBDC4C6}.Debug|Win32.Build.0 = Debug|Win32
+               {C0762191-C2AC-40B6-A2EB-F1658BBDC4C6}.Release|Win32.ActiveCfg = Release|Win32
+               {C0762191-C2AC-40B6-A2EB-F1658BBDC4C6}.Release|Win32.Build.0 = Release|Win32
+               {FAB7557B-86EA-405D-B49D-33AB3F4D3E33}.Debug|Win32.ActiveCfg = Debug|Win32
+               {FAB7557B-86EA-405D-B49D-33AB3F4D3E33}.Debug|Win32.Build.0 = Debug|Win32
+               {FAB7557B-86EA-405D-B49D-33AB3F4D3E33}.Release|Win32.ActiveCfg = Release|Win32
+               {FAB7557B-86EA-405D-B49D-33AB3F4D3E33}.Release|Win32.Build.0 = Release|Win32
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+EndGlobal
diff --git a/os_win32/syslogevt.c b/os_win32/syslogevt.c
new file mode 100644 (file)
index 0000000..73e1230
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * os_win32/syslogevt.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-8 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.5 2008/03/04 22:09:48 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.5 $ Copyright (C) 2004-8 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..a1d421d
--- /dev/null
@@ -0,0 +1,161 @@
+;/*
+; * os_win32/syslogevt.mc
+; *
+; * Home page of code is: http://smartmontools.sourceforge.net
+; *
+; * Copyright (C) 2004-8 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.5 2008/03/04 22:09:48 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_vc8.vcproj b/os_win32/syslogevt_vc8.vcproj
new file mode 100644 (file)
index 0000000..b7fe8b7
--- /dev/null
@@ -0,0 +1,205 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="8,00"
+       Name="syslogevt_vc8"
+       ProjectGUID="{FAB7557B-86EA-405D-B49D-33AB3F4D3E33}"
+       RootNamespace="syslogevt_vc8"
+       Keyword="Win32Proj"
+       >
+       <Platforms>
+               <Platform
+                       Name="Win32"
+               />
+       </Platforms>
+       <ToolFiles>
+       </ToolFiles>
+       <Configurations>
+               <Configuration
+                       Name="Debug|Win32"
+                       OutputDirectory="syslogevt_vc8.d"
+                       IntermediateDirectory="syslogevt_vc8.d"
+                       ConfigurationType="1"
+                       CharacterSet="2"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               Optimization="0"
+                               PreprocessorDefinitions="_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+                               MinimalRebuild="true"
+                               BasicRuntimeChecks="3"
+                               RuntimeLibrary="3"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               Detect64BitPortabilityProblems="true"
+                               DebugInformationFormat="4"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               LinkIncremental="2"
+                               GenerateDebugInformation="true"
+                               SubSystem="1"
+                               TargetMachine="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory="syslogevt_vc8.r"
+                       IntermediateDirectory="syslogevt_vc8.r"
+                       ConfigurationType="1"
+                       CharacterSet="2"
+                       WholeProgramOptimization="1"
+                       >
+                       <Tool
+                               Name="VCPreBuildEventTool"
+                       />
+                       <Tool
+                               Name="VCCustomBuildTool"
+                       />
+                       <Tool
+                               Name="VCXMLDataGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCWebServiceProxyGeneratorTool"
+                       />
+                       <Tool
+                               Name="VCMIDLTool"
+                       />
+                       <Tool
+                               Name="VCCLCompilerTool"
+                               PreprocessorDefinitions="NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
+                               RuntimeLibrary="2"
+                               UsePrecompiledHeader="0"
+                               WarningLevel="3"
+                               Detect64BitPortabilityProblems="true"
+                               DebugInformationFormat="3"
+                       />
+                       <Tool
+                               Name="VCManagedResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCResourceCompilerTool"
+                       />
+                       <Tool
+                               Name="VCPreLinkEventTool"
+                       />
+                       <Tool
+                               Name="VCLinkerTool"
+                               OutputFile="syslogevt.exe"
+                               LinkIncremental="1"
+                               GenerateDebugInformation="true"
+                               ProgramDatabaseFile="$(IntDir)\$(TargetName).pdb"
+                               SubSystem="1"
+                               OptimizeReferences="2"
+                               EnableCOMDATFolding="2"
+                               TargetMachine="1"
+                       />
+                       <Tool
+                               Name="VCALinkTool"
+                       />
+                       <Tool
+                               Name="VCManifestTool"
+                       />
+                       <Tool
+                               Name="VCXDCMakeTool"
+                       />
+                       <Tool
+                               Name="VCBscMakeTool"
+                       />
+                       <Tool
+                               Name="VCFxCopTool"
+                       />
+                       <Tool
+                               Name="VCAppVerifierTool"
+                       />
+                       <Tool
+                               Name="VCWebDeploymentTool"
+                       />
+                       <Tool
+                               Name="VCPostBuildEventTool"
+                       />
+               </Configuration>
+       </Configurations>
+       <References>
+       </References>
+       <Files>
+               <File
+                       RelativePath=".\syslogevt.c"
+                       >
+               </File>
+               <File
+                       RelativePath=".\syslogevt.mc"
+                       >
+                       <FileConfiguration
+                               Name="Debug|Win32"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                                       Description="mc -r $(IntDir) syslogevt.mc"
+                                       CommandLine="mc -r $(IntDir) syslogevt.mc&#x0D;&#x0A;"
+                                       Outputs="$(IntDir)\syslogevt.rc;$(IntDir)\msg00001.bin;syslogevt.h"
+                               />
+                       </FileConfiguration>
+                       <FileConfiguration
+                               Name="Release|Win32"
+                               >
+                               <Tool
+                                       Name="VCCustomBuildTool"
+                                       Description="mc -r $(IntDir) syslogevt.mc"
+                                       CommandLine="mc -r $(IntDir) syslogevt.mc&#x0D;&#x0A;"
+                                       Outputs="$(IntDir)\syslogevt.rc;$(IntDir)\msg00001.bin;syslogevt.h"
+                               />
+                       </FileConfiguration>
+               </File>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
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 084dcc91a485949fc9fb9f0f87efbe1141d57f07..4ae87022cd6f92c1df1c5ba0613da018a0719d66 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2006 Douglas Gilbert <dougg@torque.net>
+ * Copyright (C) 2006-9 Douglas Gilbert <dougg@torque.net>
+ * Copyright (C) 2009   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
  * ATA PASS THROUGH SCSI (16) and ATA PASS THROUGH SCSI (12) defined in
  * section 12 of that document.
  *
+ * sat-r09.pdf is the most recent, easily accessible draft prior to the
+ * original SAT standard (ANSI INCITS 431-2007). By mid-2009 the second
+ * version of the SAT standard (SAT-2) is nearing standardization. In
+ * their wisdom an incompatible change has been introduced in draft
+ * sat2r08a.pdf in the area of the ATA RETURN DESCRIPTOR. A new "fixed
+ * format" ATA RETURN buffer has been defined (sat2r08b.pdf section
+ * 12.2.7) for the case when DSENSE=0 in the Control mode page.
+ * Unfortunately this is the normal case. If the change stands our
+ * code will need to be extended for this case.
+ *
  * 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
 
 #include <stdio.h>
 #include <string.h>
+#include <ctype.h>
 
 #include "config.h"
 #include "int64.h"
 #include "extern.h"
 #include "scsicmds.h"
-#include "scsiata.h"
+#include "atacmds.h" // ataReadHDIdentity()
 #include "utility.h"
+#include "dev_interface.h"
+#include "dev_ata_cmd_set.h" // ata_device_with_command_set
+#include "dev_tunnelled.h" // tunnelled_device<>
 
-const char *scsiata_c_cvsid="$Id: scsiata.cpp,v 1.8 2007/12/03 02:14:20 dpgilbert Exp $"
-CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIATA_H_CVSID UTILITY_H_CVSID;
+const char * scsiata_cpp_cvsid = "$Id: scsiata.cpp 2876 2009-08-20 22:53:18Z dlukes $";
 
 /* for passing global control variables */
 extern smartmonctrl *con;
 
+/* 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). */
+/// Abridged SCSI sense data
+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). */
+static int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_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. */
+static const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
+                                                     int sense_len, int desc_type);
+
+#define SAT_ATA_PASSTHROUGH_12LEN 12
+#define SAT_ATA_PASSTHROUGH_16LEN 16
+
 #define DEF_SAT_ATA_PASSTHRU_SIZE 16
 #define ATA_RETURN_DESCRIPTOR 9
 
 
+namespace sat { // no need to publish anything, name provided for Doxygen
+
+/// SAT support.
+/// Implements ATA by tunnelling through SCSI.
+
+class sat_device
+: public tunnelled_device<
+    /*implements*/ ata_device
+    /*by tunnelling through a*/, scsi_device
+  >
+{
+public:
+  sat_device(smart_interface * intf, scsi_device * scsidev,
+    const char * req_type, int passthrulen = 0);
+
+  virtual ~sat_device() throw();
+
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
+
+private:
+  int m_passthrulen;
+};
+
+
+sat_device::sat_device(smart_interface * intf, scsi_device * scsidev,
+  const char * req_type, int passthrulen /*= 0*/)
+: smart_device(intf, scsidev->get_dev_name(), "sat", req_type),
+  tunnelled_device<ata_device, scsi_device>(scsidev),
+  m_passthrulen(passthrulen)
+{
+  set_info().info_name = strprintf("%s [SAT]", scsidev->get_info_name());
+}
+
+sat_device::~sat_device() throw()
+{
+}
+
+
 // 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
@@ -112,7 +201,7 @@ extern smartmonctrl *con;
 //   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 .
+//   one used is chosen via this->m_passthrulen .
 // DETAILED DESCRIPTION OF ARGUMENTS
 //   device: is the file descriptor provided by (a SCSI dvice type) open()
 //   command: defines the different ATA operations.
@@ -128,9 +217,15 @@ extern smartmonctrl *con;
 //   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)
+bool sat_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
 {
+  if (!ata_cmd_is_ok(in,
+    true, // data_out_support
+    true, // multi_sector_support
+    true) // ata_48bit_support
+  )
+    return false;
+
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
     struct sg_scsi_sense_hdr ssh;
@@ -138,136 +233,87 @@ int sat_command_interface(int device, smart_command_set command, int select,
     unsigned char sense[32];
     const unsigned char * ardp;
     int status, ard_len, have_sense;
-    int copydata = 0;
-    int outlen = 0;
     int extend = 0;
     int ck_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;
-        ck_cond = 1;
-        copydata = 1;
+    // Set data direction
+    // TODO: This works only for commands where sector_count holds count!
+    switch (in.direction) {
+      case ata_cmd_in::no_data:
         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;
+      case ata_cmd_in::data_in:
+        protocol = 4;  // PIO data-in
+        t_length = 2;  // sector_count holds count
         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;
+      case ata_cmd_in::data_out:
+        protocol = 5;  // PIO data-out
+        t_length = 2;  // sector_count holds count
+        t_dir = 0;     // to device
         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;
-        ck_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 */
-        ck_cond = 1;
-        break;
-    default:
-        pout("Unrecognized command %d in sat_command_interface()\n"
-             "Please contact " PACKAGE_BUGREPORT "\n", command);
-        errno=ENOSYS;
-        return -1;
+      default:
+        return set_err(EINVAL, "sat_device::ata_pass_through: invalid direction=%d",
+            (int)in.direction);
     }
-    if (ATA_SMART_CMD == ata_command) {
-        lba_mid = 0x4f;
-        lba_high = 0xc2;
+
+    // Check condition if any output register needed
+    if (in.out_needed.is_set())
+        ck_cond = 1;
+
+    if ((SAT_ATA_PASSTHROUGH_12LEN == m_passthrulen) ||
+        (SAT_ATA_PASSTHROUGH_16LEN == m_passthrulen))
+        passthru_size = m_passthrulen;
+
+    // Set extend bit on 48-bit ATA command
+    if (in.in_regs.is_48bit_cmd()) {
+      if (passthru_size != SAT_ATA_PASSTHROUGH_16LEN)
+        return set_err(ENOSYS, "48-bit ATA commands require SAT ATA PASS-THROUGH (16)");
+      extend = 1;
     }
 
-    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] = (ck_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;
+
+    if (passthru_size == SAT_ATA_PASSTHROUGH_12LEN) {
+        // ATA PASS-THROUGH (12)
+        const ata_in_regs & lo = in.in_regs;
+        cdb[3] = lo.features;
+        cdb[4] = lo.sector_count;
+        cdb[5] = lo.lba_low;
+        cdb[6] = lo.lba_mid;
+        cdb[7] = lo.lba_high;
+        cdb[8] = lo.device;
+        cdb[9] = lo.command;
+    }
+    else {
+        // ATA PASS-THROUGH (16)
+        const ata_in_regs & lo = in.in_regs;
+        const ata_in_regs & hi = in.in_regs.prev;
+        // Note: all 'in.in_regs.prev.*' are always zero for 28-bit commands
+        cdb[ 3] = hi.features;
+        cdb[ 4] = lo.features;
+        cdb[ 5] = hi.sector_count;
+        cdb[ 6] = lo.sector_count;
+        cdb[ 7] = hi.lba_low;
+        cdb[ 8] = lo.lba_low;
+        cdb[ 9] = hi.lba_mid;
+        cdb[10] = lo.lba_mid;
+        cdb[11] = hi.lba_high;
+        cdb[12] = lo.lba_high;
+        cdb[13] = lo.device;
+        cdb[14] = lo.command;
+    }
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     if (0 == t_length) {
@@ -275,13 +321,13 @@ int sat_command_interface(int device, smart_command_set command, int select,
         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 */
+        io_hdr.dxfer_len = in.size;
+        io_hdr.dxferp = (unsigned char *)in.buffer;
+        memset(in.buffer, 0, in.size); // 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.dxfer_len = in.size;
+        io_hdr.dxferp = (unsigned char *)in.buffer;
     }
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = passthru_size;
@@ -289,12 +335,12 @@ int sat_command_interface(int device, smart_command_set command, int select,
     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) {
+    scsi_device * scsidev = get_tunnel_dev();
+    if (!scsidev->scsi_pass_through(&io_hdr)) {
         if (con->reportscsiioctl > 0)
-            pout("sat_command_interface: do_scsi_cmnd_io() failed, "
-                 "status=%d\n", status);
-        return -1;
+            pout("sat_device::ata_pass_through: scsi_pass_through() failed, "
+                 "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg());
+        return set_err(scsidev->get_err());
     }
     ardp = NULL;
     ard_len = 0;
@@ -316,16 +362,16 @@ int sat_command_interface(int device, smart_command_set command, int select,
         status = scsiSimpleSenseFilter(&sinfo);
         if (0 != status) {
             if (con->reportscsiioctl > 0) {
-                pout("sat_command_interface: scsi error: %s\n",
+                pout("sat_device::ata_pass_through: scsi error: %s\n",
                      scsiErrString(status));
                 if (ardp && (con->reportscsiioctl > 1)) {
                     pout("Values from ATA Return Descriptor are:\n");
                     dStrHex((const char *)ardp, ard_len, 1);
                 }
             }
-            if (t_dir && (t_length > 0) && (copydata > 0))
-                memset(data, 0, copydata);
-            return -1;
+            if (t_dir && (t_length > 0) && (in.direction == ata_cmd_in::data_in))
+                memset(in.buffer, 0, in.size);
+            return set_err(EIO, "scsi error %s", scsiErrString(status));
         }
     }
     if (ck_cond) {     /* expecting SAT specific sense data */
@@ -335,20 +381,21 @@ int sat_command_interface(int device, smart_command_set command, int select,
                     pout("Values from ATA Return Descriptor are:\n");
                     dStrHex((const char *)ardp, ard_len, 1);
                 }
-                if (ATA_CHECK_POWER_MODE == ata_command)
-                    data[0] = ardp[5];      /* sector count (0:7) */
-                else if (STATUS_CHECK == command) {
-                    if ((ardp[9] == 0x4f) && (ardp[11] == 0xc2))
-                        return 0;    /* GOOD smart status */
-                    if ((ardp[9] == 0xf4) && (ardp[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 Return Descriptor are:\n");
-                    dStrHex((const char *)ardp, ard_len, 1);
-                    return -1;
+                // Set output registers
+                ata_out_regs & lo = out.out_regs;
+                lo.error        = ardp[ 3];
+                lo.sector_count = ardp[ 5];
+                lo.lba_low      = ardp[ 7];
+                lo.lba_mid      = ardp[ 9];
+                lo.lba_high     = ardp[11];
+                lo.device       = ardp[12];
+                lo.status       = ardp[13];
+                if (in.in_regs.is_48bit_cmd()) {
+                    ata_out_regs & hi = out.out_regs.prev;
+                    hi.sector_count = ardp[ 4];
+                    hi.lba_low      = ardp[ 6];
+                    hi.lba_mid      = ardp[ 8];
+                    hi.lba_high     = ardp[10];
                 }
             }
         }
@@ -367,33 +414,37 @@ int sat_command_interface(int device, smart_command_set command, int select,
                         pout("Values from ATA Return Descriptor are:\n");
                         dStrHex((const char *)ardp, ard_len, 1);
                     }
-                    return -1;
+                    return set_err(EIO, "SAT command failed");
                 }
             }
         }
     }
-    return 0;
+    return true;
 }
 
+} // namespace
+
+/////////////////////////////////////////////////////////////////////////////
+
 /* 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)
+   is false otherwise attempt IDENTIFY PACKET DEVICE. If successful
+   return true, else false */
+
+static bool has_sat_pass_through(ata_device * dev, bool packet_interface = false)
 {
+    ata_cmd_in in;
+    in.in_regs.command = (packet_interface ? ATA_IDENTIFY_PACKET_DEVICE : ATA_IDENTIFY_DEVICE);
     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;
+    in.set_data_in(data, 1);
+    return dev->ata_pass_through(in);
 }
 
+/////////////////////////////////////////////////////////////////////////////
+
 /* 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)
+static 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));
@@ -427,8 +478,8 @@ int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len,
 }
 
 
-const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
-                                              int sense_len, int desc_type)
+static 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;
@@ -451,3 +502,948 @@ const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
     }
     return NULL;
 }
+
+
+// Call scsi_pass_through and check sense.
+// TODO: Provide as member function of class scsi_device (?)
+static bool scsi_pass_through_and_check(scsi_device * scsidev,  scsi_cmnd_io * iop,
+                                        const char * msg = "")
+{
+  // Provide sense buffer
+  unsigned char sense[32] = {0, };
+  iop->sensep = sense;
+  iop->max_sense_len = sizeof(sense);
+  iop->timeout = SCSI_TIMEOUT_DEFAULT;
+
+  // Run cmd
+  if (!scsidev->scsi_pass_through(iop)) {
+    if (con->reportscsiioctl > 0)
+      pout("%sscsi_pass_through() failed, errno=%d [%s]\n",
+           msg, scsidev->get_errno(), scsidev->get_errmsg());
+    return false;
+  }
+
+  // Check sense
+  scsi_sense_disect sinfo;
+  scsi_do_sense_disect(iop, &sinfo);
+  int err = scsiSimpleSenseFilter(&sinfo);
+  if (err) {
+    if (con->reportscsiioctl > 0)
+      pout("%sscsi error: %s\n", msg, scsiErrString(err));
+    return scsidev->set_err(EIO, "scsi error %s", scsiErrString(err));
+  }
+
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+namespace sat {
+
+/// Cypress USB Brigde support.
+
+class usbcypress_device
+: public tunnelled_device<
+    /*implements*/ ata_device_with_command_set
+    /*by tunnelling through a*/, scsi_device
+  >
+{
+public:
+  usbcypress_device(smart_interface * intf, scsi_device * scsidev,
+    const char * req_type, unsigned char signature);
+
+  virtual ~usbcypress_device() throw();
+
+protected:
+  virtual int ata_command_interface(smart_command_set command, int select, char * data);
+
+  unsigned char m_signature;
+};
+
+
+usbcypress_device::usbcypress_device(smart_interface * intf, scsi_device * scsidev,
+  const char * req_type, unsigned char signature)
+: smart_device(intf, scsidev->get_dev_name(), "sat", req_type),
+  tunnelled_device<ata_device_with_command_set, scsi_device>(scsidev),
+  m_signature(signature)
+{
+  set_info().info_name = strprintf("%s [USB Cypress]", scsidev->get_info_name());
+}
+
+usbcypress_device::~usbcypress_device() throw()
+{
+}
+
+
+/* see cy7c68300c_8.pdf for more information */
+#define USBCYPRESS_PASSTHROUGH_LEN 16
+int usbcypress_device::ata_command_interface(smart_command_set command, int select, char *data)
+{
+    struct scsi_cmnd_io io_hdr;
+    unsigned char cdb[USBCYPRESS_PASSTHROUGH_LEN];
+    unsigned char sense[32];
+    int copydata = 0;
+    int outlen = 0;
+    int ck_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 = USBCYPRESS_PASSTHROUGH_LEN;
+
+    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;
+        ck_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;
+        ck_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 */
+        ck_cond = 1;
+        break;
+    default:
+        pout("Unrecognized command %d in usbcypress_device::ata_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;
+    }
+
+    cdb[0] = m_signature; // bVSCBSignature : vendor-specific command
+    cdb[1] = 0x24; // bVSCBSubCommand : 0x24 for ATACB
+    cdb[2] = 0x0;
+    if (ata_command == ATA_IDENTIFY_DEVICE || ata_command == ATA_IDENTIFY_PACKET_DEVICE)
+        cdb[2] |= (1<<7); //set  IdentifyPacketDevice for these cmds
+    cdb[3] = 0xff - (1<<0) - (1<<6); //features, sector count, lba low, lba med
+                                     // lba high, command are valid
+    cdb[4] = byte_block; //TransferBlockCount : 512
+
+
+    cdb[6] = feature;
+    cdb[7] = sector_count;
+    cdb[8] = lba_low;
+    cdb[9] = lba_mid;
+    cdb[10] = lba_high;
+    cdb[12] = 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;
+
+    scsi_device * scsidev = get_tunnel_dev();
+    if (!scsidev->scsi_pass_through(&io_hdr)) {
+        if (con->reportscsiioctl > 0)
+            pout("usbcypress_device::ata_command_interface: scsi_pass_through() failed, "
+                 "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg());
+        set_err(scsidev->get_err());
+        return -1;
+    }
+
+    // if there is a sense the command failed or the
+    // device doesn't support usbcypress
+    if (io_hdr.scsi_status == SCSI_STATUS_CHECK_CONDITION && 
+            sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len, NULL)) {
+        return -1;
+    }
+    if (ck_cond) {
+        unsigned char ardp[8];
+        int ard_len = 8;
+        /* XXX this is racy if there other scsi command between
+         * the first usbcypress command and this one
+         */
+        //pout("If you got strange result, please retry without traffic on the disc\n");
+        /* we use the same command as before, but we set
+         * * the read taskfile bit, for not executing usbcypress command,
+         * * but reading register selected in srb->cmnd[4]
+         */
+        cdb[2] = (1<<0); /* ask read taskfile */
+        memset(sense, 0, sizeof(sense));
+
+        /* transfert 8 bytes */
+        memset(&io_hdr, 0, sizeof(io_hdr));
+        io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+        io_hdr.dxfer_len = ard_len;
+        io_hdr.dxferp = (unsigned char *)ardp;
+        memset(ardp, 0, ard_len); /* prefill with zeroes */
+
+        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;
+
+
+        if (!scsidev->scsi_pass_through(&io_hdr)) {
+            if (con->reportscsiioctl > 0)
+                pout("usbcypress_device::ata_command_interface: scsi_pass_through() failed, "
+                     "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg());
+            set_err(scsidev->get_err());
+            return -1;
+        }
+        // if there is a sense the command failed or the
+        // device doesn't support usbcypress
+        if (io_hdr.scsi_status == SCSI_STATUS_CHECK_CONDITION && 
+                sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len, NULL)) {
+            return -1;
+        }
+
+
+        if (con->reportscsiioctl > 1) {
+            pout("Values from ATA Return Descriptor are:\n");
+            dStrHex((const char *)ardp, ard_len, 1);
+        }
+
+        if (ATA_CHECK_POWER_MODE == ata_command)
+            data[0] = ardp[2];      /* sector count (0:7) */
+        else if (STATUS_CHECK == command) {
+            if ((ardp[4] == 0x4f) && (ardp[5] == 0xc2))
+                return 0;    /* GOOD smart status */
+            if ((ardp[4] == 0xf4) && (ardp[5] == 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("This may be due to a race in usbcypress\n");
+            pout("Retry without other disc access\n");
+            pout("Please get assistance from " PACKAGE_HOMEPAGE "\n");
+            pout("Values from ATA Return Descriptor are:\n");
+            dStrHex((const char *)ardp, ard_len, 1);
+            return -1;
+        }
+    }
+    return 0;
+}
+
+#if 0 // Not used, see autodetect_sat_device() below.
+static int isprint_string(const char *s)
+{
+    while (*s) {
+        if (isprint(*s) == 0)
+            return 0;
+        s++;
+    }
+    return 1;
+}
+
+/* Attempt an IDENTIFY DEVICE ATA or IDENTIFY PACKET DEVICE command
+   If successful return 1, else 0 */
+// TODO: Combine with has_sat_pass_through above
+static int has_usbcypress_pass_through(ata_device * atadev, const char *manufacturer, const char *product)
+{
+    struct ata_identify_device drive;
+    char model[40], serial[20], firm[8];
+
+    /* issue the command and do a checksum if possible */
+    if (ataReadHDIdentity(atadev, &drive) < 0)
+        return 0;
+
+    /* check if model string match, revision doesn't work for me */
+    format_ata_string(model, drive.model, 40);
+    if (*model == 0 || isprint_string(model) == 0)
+        return 0;
+
+    if (manufacturer && strncmp(manufacturer, model, 8))
+        pout("manufacturer doesn't match in pass_through test\n");
+    if (product &&
+            strlen(model) > 8 && strncmp(product, model+8, strlen(model)-8))
+        pout("product doesn't match in pass_through test\n");
+
+    /* check serial */
+    format_ata_string(serial, drive.serial_no, 20);
+    if (isprint_string(serial) == 0)
+        return 0;
+    format_ata_string(firm, drive.fw_rev, 8);
+    if (isprint_string(firm) == 0)
+        return 0;
+    return 1;
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+/// JMicron USB Bridge support.
+
+class usbjmicron_device
+: public tunnelled_device<
+    /*implements*/ ata_device,
+    /*by tunnelling through a*/ scsi_device
+  >
+{
+public:
+  usbjmicron_device(smart_interface * intf, scsi_device * scsidev,
+                    const char * req_type, bool ata_48bit_support, int port);
+
+  virtual ~usbjmicron_device() throw();
+
+  virtual bool open();
+
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
+
+private:
+  bool get_registers(unsigned short addr, unsigned char * buf, unsigned short size);
+
+  bool m_ata_48bit_support;
+  int m_port;
+};
+
+
+usbjmicron_device::usbjmicron_device(smart_interface * intf, scsi_device * scsidev,
+                                     const char * req_type, bool ata_48bit_support, int port)
+: smart_device(intf, scsidev->get_dev_name(), "usbjmicron", req_type),
+  tunnelled_device<ata_device, scsi_device>(scsidev),
+  m_ata_48bit_support(ata_48bit_support), m_port(port)
+{
+  set_info().info_name = strprintf("%s [USB JMicron]", scsidev->get_info_name());
+}
+
+usbjmicron_device::~usbjmicron_device() throw()
+{
+}
+
+
+bool usbjmicron_device::open()
+{
+  // Open USB first
+  if (!tunnelled_device<ata_device, scsi_device>::open())
+    return false;
+
+  // Detect port if not specified
+  if (m_port < 0) {
+    unsigned char regbuf[1] = {0};
+    if (!get_registers(0x720f, regbuf, sizeof(regbuf))) {
+      close();
+      return false;
+    }
+
+    switch (regbuf[0] & 0x44) {
+      case 0x04:
+        m_port = 0; break;
+      case 0x40:
+        m_port = 1; break;
+      case 0x44:
+        close();
+        return set_err(EINVAL, "Two devices connected, try '-d usbjmicron,[01]'");
+      default:
+        close();
+        return set_err(ENODEV, "No device connected");
+    }
+  }
+
+  return true;
+}
+
+
+bool usbjmicron_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+{
+  if (!ata_cmd_is_ok(in,
+    true,  // data_out_support
+    false, // !multi_sector_support
+    m_ata_48bit_support) // limited, see below
+  )
+    return false;
+
+  bool is_smart_status = (   in.in_regs.command  == ATA_SMART_CMD
+                          && in.in_regs.features == ATA_SMART_STATUS);
+
+  // Support output registers for SMART STATUS
+  if (in.out_needed.is_set() && !is_smart_status)
+    return set_err(ENOSYS, "ATA output registers not supported");
+
+  // Support 48-bit commands with zero high bytes
+  if (in.in_regs.is_real_48bit_cmd())
+    return set_err(ENOSYS, "48-bit ATA commands not fully supported");
+
+  if (m_port < 0)
+    return set_err(EIO, "Unknown JMicron port");
+
+  scsi_cmnd_io io_hdr;
+  memset(&io_hdr, 0, sizeof(io_hdr));
+
+  bool rwbit = true;
+  unsigned char smart_status = 0;
+
+  if (is_smart_status && in.out_needed.is_set()) {
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = 1;
+    io_hdr.dxferp = &smart_status;
+  }
+  else switch (in.direction) {
+    case ata_cmd_in::no_data:
+      io_hdr.dxfer_dir = DXFER_NONE;
+      break;
+    case ata_cmd_in::data_in:
+      io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+      io_hdr.dxfer_len = in.size;
+      io_hdr.dxferp = (unsigned char *)in.buffer;
+      memset(in.buffer, 0, in.size);
+      break;
+    case ata_cmd_in::data_out:
+      io_hdr.dxfer_dir = DXFER_TO_DEVICE;
+      io_hdr.dxfer_len = in.size;
+      io_hdr.dxferp = (unsigned char *)in.buffer;
+      rwbit = false;
+      break;
+    default:
+      return set_err(EINVAL);
+  }
+
+  // Build pass through command
+  unsigned char cdb[12];
+  cdb[ 0] = 0xdf;
+  cdb[ 1] = (rwbit ? 0x10 : 0x00);
+  cdb[ 2] = 0x00;
+  cdb[ 3] = (unsigned char)(io_hdr.dxfer_len >> 8);
+  cdb[ 4] = (unsigned char)(io_hdr.dxfer_len     );
+  cdb[ 5] = in.in_regs.features;
+  cdb[ 6] = in.in_regs.sector_count;
+  cdb[ 7] = in.in_regs.lba_low;
+  cdb[ 8] = in.in_regs.lba_mid;
+  cdb[ 9] = in.in_regs.lba_high;
+  cdb[10] = in.in_regs.device | (m_port == 0 ? 0xa0 : 0xb0);
+  cdb[11] = in.in_regs.command;
+
+  io_hdr.cmnd = cdb;
+  io_hdr.cmnd_len = sizeof(cdb);
+
+  scsi_device * scsidev = get_tunnel_dev();
+  if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+         "usbjmicron_device::ata_pass_through: "))
+    return set_err(scsidev->get_err());
+
+  if (in.out_needed.is_set()) {
+    if (is_smart_status) {
+      switch (smart_status) {
+        case 0x01: case 0xc2:
+          out.out_regs.lba_high = 0xc2;
+          out.out_regs.lba_mid = 0x4f;
+          break;
+        case 0x00: case 0x2c:
+          out.out_regs.lba_high = 0x2c;
+          out.out_regs.lba_mid = 0xf4;
+          break;
+      }
+    }
+
+#if 0 // Not needed for SMART STATUS, see also notes below
+    else {
+      // Read ATA output registers
+      // NOTE: The register addresses are not valid for some older chip revisions
+      // NOTE: There is a small race condition here!
+      unsigned char regbuf[16] = {0, };
+      if (!get_registers((m_port == 0 ? 0x8000 : 0x9000), regbuf, sizeof(regbuf)))
+        return false;
+
+      out.out_regs.sector_count = regbuf[ 0];
+      out.out_regs.lba_mid      = regbuf[ 4];
+      out.out_regs.lba_low      = regbuf[ 6];
+      out.out_regs.device       = regbuf[ 9];
+      out.out_regs.lba_high     = regbuf[10];
+      out.out_regs.error        = regbuf[13];
+      out.out_regs.status       = regbuf[14];
+    }
+#endif
+  }
+
+  return true;
+}
+
+bool usbjmicron_device::get_registers(unsigned short addr,
+                                      unsigned char * buf, unsigned short size)
+{
+  unsigned char cdb[12];
+  cdb[ 0] = 0xdf;
+  cdb[ 1] = 0x10;
+  cdb[ 2] = 0x00;
+  cdb[ 3] = (unsigned char)(size >> 8);
+  cdb[ 4] = (unsigned char)(size     );
+  cdb[ 5] = 0x00;
+  cdb[ 6] = (unsigned char)(addr >> 8);
+  cdb[ 7] = (unsigned char)(addr     );
+  cdb[ 8] = 0x00;
+  cdb[ 9] = 0x00;
+  cdb[10] = 0x00;
+  cdb[11] = 0xfd;
+
+  scsi_cmnd_io io_hdr;
+  memset(&io_hdr, 0, sizeof(io_hdr));
+  io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+  io_hdr.dxfer_len = size;
+  io_hdr.dxferp = buf;
+  io_hdr.cmnd = cdb;
+  io_hdr.cmnd_len = sizeof(cdb);
+
+  scsi_device * scsidev = get_tunnel_dev();
+  if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+         "usbjmicron_device::get_registers: "))
+    return set_err(scsidev->get_err());
+
+  return true;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+/// SunplusIT USB Bridge support.
+
+class usbsunplus_device
+: public tunnelled_device<
+    /*implements*/ ata_device,
+    /*by tunnelling through a*/ scsi_device
+  >
+{
+public:
+  usbsunplus_device(smart_interface * intf, scsi_device * scsidev,
+                    const char * req_type);
+
+  virtual ~usbsunplus_device() throw();
+
+  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
+};
+
+
+usbsunplus_device::usbsunplus_device(smart_interface * intf, scsi_device * scsidev,
+                                     const char * req_type)
+: smart_device(intf, scsidev->get_dev_name(), "usbsunplus", req_type),
+  tunnelled_device<ata_device, scsi_device>(scsidev)
+{
+  set_info().info_name = strprintf("%s [USB Sunplus]", scsidev->get_info_name());
+}
+
+usbsunplus_device::~usbsunplus_device() throw()
+{
+}
+
+bool usbsunplus_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
+{
+  if (!ata_cmd_is_ok(in,
+    true,  // data_out_support
+    false, // !multi_sector_support
+    true)  // ata_48bit_support
+  )
+    return false;
+
+  scsi_cmnd_io io_hdr;
+  unsigned char cdb[12];
+
+  if (in.in_regs.is_48bit_cmd()) {
+    // Set "previous" registers
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    io_hdr.dxfer_dir = DXFER_NONE;
+
+    cdb[ 0] = 0xf8;
+    cdb[ 1] = 0x00;
+    cdb[ 2] = 0x23; // Subcommand: Pass through presetting
+    cdb[ 3] = 0x00;
+    cdb[ 4] = 0x00;
+    cdb[ 5] = in.in_regs.prev.features;
+    cdb[ 6] = in.in_regs.prev.sector_count;
+    cdb[ 7] = in.in_regs.prev.lba_low;
+    cdb[ 8] = in.in_regs.prev.lba_mid;
+    cdb[ 9] = in.in_regs.prev.lba_high;
+    cdb[10] = 0x00;
+    cdb[11] = 0x00;
+
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+
+    scsi_device * scsidev = get_tunnel_dev();
+    if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+           "usbsunplus_device::scsi_pass_through (presetting): "))
+      return set_err(scsidev->get_err());
+  }
+
+  // Run Pass through command
+  memset(&io_hdr, 0, sizeof(io_hdr));
+  unsigned char protocol;
+  switch (in.direction) {
+    case ata_cmd_in::no_data:
+      io_hdr.dxfer_dir = DXFER_NONE;
+      protocol = 0x00;
+      break;
+    case ata_cmd_in::data_in:
+      io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+      io_hdr.dxfer_len = in.size;
+      io_hdr.dxferp = (unsigned char *)in.buffer;
+      memset(in.buffer, 0, in.size);
+      protocol = 0x10;
+      break;
+    case ata_cmd_in::data_out:
+      io_hdr.dxfer_dir = DXFER_TO_DEVICE;
+      io_hdr.dxfer_len = in.size;
+      io_hdr.dxferp = (unsigned char *)in.buffer;
+      protocol = 0x11;
+      break;
+    default:
+      return set_err(EINVAL);
+  }
+
+  cdb[ 0] = 0xf8;
+  cdb[ 1] = 0x00;
+  cdb[ 2] = 0x22; // Subcommand: Pass through
+  cdb[ 3] = protocol;
+  cdb[ 4] = (unsigned char)(io_hdr.dxfer_len >> 9);
+  cdb[ 5] = in.in_regs.features;
+  cdb[ 6] = in.in_regs.sector_count;
+  cdb[ 7] = in.in_regs.lba_low;
+  cdb[ 8] = in.in_regs.lba_mid;
+  cdb[ 9] = in.in_regs.lba_high;
+  cdb[10] = in.in_regs.device | 0xa0;
+  cdb[11] = in.in_regs.command;
+
+  io_hdr.cmnd = cdb;
+  io_hdr.cmnd_len = sizeof(cdb);
+
+  scsi_device * scsidev = get_tunnel_dev();
+  if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+         "usbsunplus_device::scsi_pass_through: "))
+    // Returns sense key 0x03 (medium error) on ATA command error
+    return set_err(scsidev->get_err());
+
+  if (in.out_needed.is_set()) {
+    // Read ATA output registers
+    unsigned char regbuf[8] = {0, };
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = sizeof(regbuf);
+    io_hdr.dxferp = regbuf;
+
+    cdb[ 0] = 0xf8;
+    cdb[ 1] = 0x00;
+    cdb[ 2] = 0x21; // Subcommand: Get status
+    memset(cdb+3, 0, sizeof(cdb)-3);
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+
+    if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+           "usbsunplus_device::scsi_pass_through (get registers): "))
+      return set_err(scsidev->get_err());
+
+    out.out_regs.error        = regbuf[1];
+    out.out_regs.sector_count = regbuf[2];
+    out.out_regs.lba_low      = regbuf[3];
+    out.out_regs.lba_mid      = regbuf[4];
+    out.out_regs.lba_high     = regbuf[5];
+    out.out_regs.device       = regbuf[6];
+    out.out_regs.status       = regbuf[7];
+  }
+
+  return true;
+}
+
+
+} // namespace
+
+using namespace sat;
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Return ATA->SCSI filter for SAT or USB.
+
+ata_device * smart_interface::get_sat_device(const char * type, scsi_device * scsidev)
+{
+  if (!strncmp(type, "sat", 3)) {
+    int ptlen = 0, n1 = -1, n2 = -1;
+    if (!(((sscanf(type, "sat%n,%d%n", &n1, &ptlen, &n2) == 1 && n2 == (int)strlen(type)) || n1 == (int)strlen(type))
+        && (ptlen == 0 || ptlen == 12 || ptlen == 16))) {
+      set_err(EINVAL, "Option '-d sat,<n>' requires <n> to be 0, 12 or 16");
+      return 0;
+    }
+    return new sat_device(this, scsidev, type, ptlen);
+  }
+
+  else if (!strncmp(type, "usbcypress", 10)) {
+    unsigned signature = 0x24; int n1 = -1, n2 = -1;
+    if (!(((sscanf(type, "usbcypress%n,0x%x%n", &n1, &signature, &n2) == 1 && n2 == (int)strlen(type)) || n1 == (int)strlen(type))
+          && signature <= 0xff)) {
+      set_err(EINVAL, "Option '-d usbcypress,<n>' requires <n> to be "
+                      "an hexadecimal number between 0x0 and 0xff");
+      return 0;
+    }
+    return new usbcypress_device(this, scsidev, type, signature);
+  }
+
+  else if (!strncmp(type, "usbjmicron", 10)) {
+    const char * t = type + 10;
+    bool ata_48bit_support = false;
+    if (!strncmp(t, ",x", 2)) {
+      t += 2;
+      ata_48bit_support = true;
+    }
+    int port = -1, n = -1;
+    if (*t && !(  (sscanf(t, ",%d%n", &port, &n) == 1
+                && n == (int)strlen(t) && 0 <= port && port <= 1))) {
+      set_err(EINVAL, "Option '-d usbmicron[,x],<n>' requires <n> to be 0 or 1");
+      return 0;
+    }
+    return new usbjmicron_device(this, scsidev, type, ata_48bit_support, port);
+  }
+
+  else if (!strcmp(type, "usbsunplus")) {
+    return new usbsunplus_device(this, scsidev, type);
+  }
+
+  else {
+    set_err(EINVAL, "Unknown USB device type '%s'", type);
+    return 0;
+  }
+}
+
+// Try to detect a SAT device behind a SCSI interface.
+
+ata_device * smart_interface::autodetect_sat_device(scsi_device * scsidev,
+  const unsigned char * inqdata, unsigned inqsize)
+{
+  if (!scsidev->is_open())
+    return 0;
+
+  ata_device * atadev = 0;
+  try {
+    // SAT ?
+    if (inqdata && inqsize >= 36 && !memcmp(inqdata + 8, "ATA     ", 8)) { // TODO: Linux-specific?
+      atadev = new sat_device(this, scsidev, "");
+      if (has_sat_pass_through(atadev))
+        return atadev; // Detected SAT
+      atadev->release(scsidev);
+      delete atadev;
+    }
+
+/* The new usbcypress_device(this, scsidev, "", 0x24) sends vendor specific comand to non-cypress devices.
+ * It's dangerous as other device may interpret such command as own valid vendor specific command.
+ * I commented it out untill problem resolved
+ */
+#if 0
+    // USB ?
+    {
+      atadev = new usbcypress_device(this, scsidev, "", 0x24);
+      if (has_usbcypress_pass_through(atadev,
+            (inqdata && inqsize >= 36 ? (const char*)inqdata  + 8 : 0),
+            (inqdata && inqsize >= 36 ? (const char*)inqdata + 16 : 0) ))
+        return atadev; // Detected USB
+      atadev->release(scsidev);
+      delete atadev;
+    }
+#endif
+  }
+  catch (...) {
+    if (atadev) {
+      atadev->release(scsidev);
+      delete atadev;
+    }
+    throw;
+  }
+
+  return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// USB device type detection
+
+struct usb_id_entry {
+  int vendor_id, product_id, version;
+  const char * type;
+};
+
+const char d_sat[]     = "sat";
+const char d_cypress[] = "usbcypress";
+const char d_jmicron[] = "usbjmicron";
+const char d_jmicron_x[] = "usbjmicron,x";
+const char d_sunplus[] = "usbsunplus";
+const char d_unsup[]   = "unsupported";
+
+// Map USB IDs -> '-d type' string
+const usb_id_entry usb_ids[] = {
+  { 0x04b4, 0x6830, 0x0001, d_unsup   }, // Cypress CY7C68300A (AT2)
+  { 0x04b4, 0x6830, 0x0240, d_cypress }, // Cypress CY7C68300B/C (AT2LP)
+//{ 0x04b4, 0x6831,     -1, d_cypress }, // Cypress CY7C68310 (ISD-300LP)
+  { 0x04fc, 0x0c15, 0xf615, d_sunplus }, // SunPlus SPDIF215
+  { 0x04fc, 0x0c25, 0x0103, d_sunplus }, // SunPlus SPDIF225 (USB+SATA->SATA)
+  { 0x059b, 0x0275, 0x0001, d_unsup   }, // Iomega MDHD500-U
+  { 0x059f, 0x0651,     -1, d_unsup   }, // LaCie hard disk (FA Porsche design)
+  { 0x059f, 0x1018,     -1, d_sat     }, // LaCie hard disk (Neil Poulton design)
+  { 0x067b, 0x3507, 0x0001, d_unsup   }, // Prolific PL3507
+  { 0x0930, 0x0b09,     -1, d_sunplus }, // Toshiba PX1396E-3T01 (similar to Dura Micro)
+  { 0x0bc2, 0x2000,     -1, d_sat     }, // Seagate FreeAgent Go
+  { 0x0bc2, 0x2100,     -1, d_sat     }, // Seagate FreeAgent Go
+  { 0x0bc2, 0x3001,     -1, d_sat     }, // Seagate FreeAgent Desk
+  { 0x0c0b, 0xb159, 0x0103, d_sunplus }, // Dura Micro (Sunplus USB-bridge)
+  { 0x0d49, 0x7310, 0x0125, d_sat     }, // Maxtor OneTouch 4
+  { 0x0d49, 0x7350, 0x0125, d_sat     }, // Maxtor OneTouch 4 Mini
+  { 0x0d49, 0x7450, 0x0122, d_sat     }, // Maxtor Basics Portable
+  { 0x1058, 0x0704, 0x0175, d_sat     }, // WD My Passport Essential
+  { 0x1058, 0x0705, 0x0175, d_sat     }, // WD My Passport Elite
+  { 0x1058, 0x0906, 0x0012, d_sat     }, // WD My Book ES
+  { 0x1058, 0x1001, 0x0104, d_sat     }, // WD Elements Desktop
+  { 0x1058, 0x1003, 0x0175, d_sat     }, // WD Elements Desktop WDE1UBK...
+  { 0x1058, 0x1010, 0x0105, d_sat     }, // WD Elements
+  { 0x1058, 0x1100, 0x0165, d_sat     }, // WD My Book Essential
+  { 0x1058, 0x1102, 0x1028, d_sat     }, // WD My Book
+  { 0x13fd, 0x1240, 0x0104, d_sat     }, // Initio ? (USB->SATA)
+  { 0x13fd, 0x1340, 0x0208, d_sat     }, // Initio ? (USB+SATA->SATA)
+  { 0x152d, 0x2329, 0x0100, d_jmicron }, // JMicron JM20329 (USB->SATA)
+  { 0x152d, 0x2336, 0x0100, d_jmicron_x},// JMicron JM20336 (USB+SATA->SATA, USB->2xSATA)
+  { 0x152d, 0x2338, 0x0100, d_jmicron }, // JMicron JM20337/8 (USB->SATA+PATA, USB+SATA->PATA)
+  { 0x152d, 0x2339, 0x0100, d_jmicron_x},// JMicron JM20339 (USB->SATA)
+  { 0x18a5, 0x0215, 0x0001, d_sat     }, // Verbatim FW/USB160 - Oxford OXUF934SSA-LQAG (USB+IEE1394->SATA)
+  { 0x1bcf, 0x0c31,     -1, d_sunplus }  // SunplusIT
+};
+
+const unsigned num_usb_ids = sizeof(usb_ids)/sizeof(usb_ids[0]);
+
+
+// Format USB ID for error messages
+static std::string format_usb_id(int vendor_id, int product_id, int version)
+{
+  if (version >= 0)
+    return strprintf("[0x%04x:0x%04x (0x%03x)]", vendor_id, product_id, version);
+  else
+    return strprintf("[0x%04x:0x%04x]", vendor_id, product_id);
+}
+
+// Get type name for USB device with known VENDOR:PRODUCT ID.
+const char * smart_interface::get_usb_dev_type_by_id(int vendor_id, int product_id,
+                                                     int version /*= -1*/)
+{
+  const usb_id_entry * entry = 0;
+  bool state = false;
+
+  for (unsigned i = 0; i < num_usb_ids; i++) {
+    const usb_id_entry & e = usb_ids[i];
+    if (!(vendor_id == e.vendor_id && product_id == e.product_id))
+      continue;
+
+    // If two entries with same vendor:product ID have different
+    // types, use version (if provided by OS) to select entry.
+    bool s = (version >= 0 && version == e.version);
+    if (entry) {
+      if (s <= state) {
+        if (s == state && e.type != entry->type) {
+          set_err(EINVAL, "USB bridge %s type is ambiguous: '%s' or '%s'",
+                  format_usb_id(vendor_id, product_id, version).c_str(),
+                  e.type, entry->type);
+          return 0;
+        }
+        continue;
+      }
+    }
+    state = s;
+    entry = &e;
+  }
+
+  if (!entry) {
+    set_err(EINVAL, "Unknown USB bridge %s",
+            format_usb_id(vendor_id, product_id, version).c_str());
+    return 0;
+  }
+  if (entry->type == d_unsup) {
+    set_err(ENOSYS, "Unsupported USB bridge %s",
+            format_usb_id(vendor_id, product_id, version).c_str());
+    return 0;
+  }
+  return entry->type;
+}
diff --git a/scsiata.h b/scsiata.h
deleted file mode 100644 (file)
index 4ff2e19..0000000
--- a/scsiata.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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
-
index cdf5b8aa372cdfb76261b2258ab7d3a176e7edf0..ff006c6a52cdba809cf4e64ec47348e8585f70be 100644 (file)
 #include "int64.h"
 #include "extern.h"
 #include "scsicmds.h"
+#include "atacmds.h" // FIXME: for smart_command_set only
+#include "dev_interface.h"
 #include "utility.h"
 
-const char *scsicmds_c_cvsid="$Id: scsicmds.cpp,v 1.96 2008/03/04 22:09:47 ballen4705 Exp $"
+const char *scsicmds_c_cvsid="$Id: scsicmds.cpp,v 1.98 2009/06/24 04:10:10 dpgilbert Exp $"
 CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 /* for passing global control variables */
@@ -121,6 +123,7 @@ static struct scsi_opcode_name opcode_name_arr[] = {
     {RECEIVE_DIAGNOSTIC, "receive diagnostic"}, /* 0x1c */
     {SEND_DIAGNOSTIC, "send diagnostic"},       /* 0x1d */
     {READ_DEFECT_10, "read defect list(10)"},   /* 0x37 */
+    {LOG_SELECT, "log select"},                 /* 0x4c */
     {LOG_SENSE, "log sense"},                   /* 0x4d */
     {MODE_SELECT_10, "mode select(10)"},        /* 0x55 */
     {MODE_SENSE_10, "mode sense(10)"},          /* 0x5a */
@@ -249,7 +252,7 @@ const char * scsiErrString(int scsiErr)
    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 scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
                  int bufLen, int known_resp_len)
 {
     struct scsi_cmnd_io io_hdr;
@@ -286,10 +289,9 @@ int scsiLogSense(int device, int pagenum, int subpagenum, UINT8 *pBuf,
         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;
+
+        if (!device->scsi_pass_through(&io_hdr))
+          return -device->get_errno();
         scsi_do_sense_disect(&io_hdr, &sinfo);
         if ((res = scsiSimpleSenseFilter(&sinfo)))
             return res;
@@ -323,9 +325,8 @@ int scsiLogSense(int device, int pagenum, int subpagenum, UINT8 *pBuf,
     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;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     status = scsiSimpleSenseFilter(&sinfo);
     if (0 != status)
@@ -338,11 +339,47 @@ int scsiLogSense(int device, int pagenum, int subpagenum, UINT8 *pBuf,
     return 0;
 }
 
+/* Sends a LOG SELECT command. Can be used to set log page values
+ * or reset one log page (or all of them) to its defaults (typically zero).
+ * Returns 0 if ok, 1 if NOT READY, 2 if command not supported, * 3 if
+ * field in command not supported, * 4 if bad parameter to command or
+ * returns negated errno. SPC-4 sections 6.5 and 7.2 (rev 20) */
+int scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum,
+                  int subpagenum, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[10];
+    UINT8 sense[32];
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_TO_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = LOG_SELECT;
+    cdb[1] = (pcr ? 2 : 0) | (sp ? 1 : 0);
+    cdb[2] = ((pc << 6) & 0xc0) | (pagenum & 0x3f);
+    cdb[3] = (subpagenum & 0xff);
+    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;
+
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
 /* 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,
+int scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,
                   UINT8 *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
@@ -368,15 +405,13 @@ int scsiModeSense(int device, int pagenum, int subpagenum, int pc,
     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;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     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;
+        if (!device->scsi_pass_through(&io_hdr))
+          return -device->get_errno();
         scsi_do_sense_disect(&io_hdr, &sinfo);
         status = scsiSimpleSenseFilter(&sinfo);
     }
@@ -399,13 +434,13 @@ int scsiModeSense(int device, int pagenum, int subpagenum, int pc,
  * 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)
+int scsiModeSelect(scsi_device * 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;
+    int pg_offset, pg_len, hdr_plus_1_pg;
 
     pg_offset = 4 + pBuf[3];
     if (pg_offset + 2 >= bufLen)
@@ -430,9 +465,8 @@ int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen)
     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;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     return scsiSimpleSenseFilter(&sinfo);
 }
@@ -441,7 +475,7 @@ int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen)
  * 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,
+int scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
                     UINT8 *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
@@ -466,15 +500,13 @@ int scsiModeSense10(int device, int pagenum, int subpagenum, int pc,
     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;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     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;
+        if (!device->scsi_pass_through(&io_hdr))
+          return -device->get_errno();
         scsi_do_sense_disect(&io_hdr, &sinfo);
         status = scsiSimpleSenseFilter(&sinfo);
     }
@@ -497,13 +529,13 @@ int scsiModeSense10(int device, int pagenum, int subpagenum, int pc,
  * 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)
+int scsiModeSelect10(scsi_device * 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;
+    int pg_offset, pg_len, hdr_plus_1_pg;
 
     pg_offset = 8 + (pBuf[6] << 8) + pBuf[7];
     if (pg_offset + 2 >= bufLen)
@@ -529,9 +561,8 @@ int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen)
     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;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     return scsiSimpleSenseFilter(&sinfo);
 }
@@ -539,13 +570,12 @@ int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen)
 /* 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)
+int scsiStdInquiry(scsi_device * 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;
@@ -562,9 +592,8 @@ int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen)
     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;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     return scsiSimpleSenseFilter(&sinfo);
 }
@@ -573,13 +602,13 @@ int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen)
  * (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)
+int scsiInquiryVpd(scsi_device * 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;
+    int res;
 
     if ((bufLen < 0) || (bufLen > 255))
         return -EINVAL;
@@ -600,9 +629,8 @@ int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen)
     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;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     if ((res = scsiSimpleSenseFilter(&sinfo)))
         return res;
@@ -619,13 +647,13 @@ int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen)
 
 /* 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)
+int scsiRequestSense(scsi_device * 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;
+    int len;
     UINT8 ecode;
 
     memset(&io_hdr, 0, sizeof(io_hdr));
@@ -641,8 +669,9 @@ int scsiRequestSense(int device, struct scsi_sense_disect * sense_info)
     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)) {
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
+    if (sense_info) {
         ecode = buff[0] & 0x7f;
         sense_info->error_code = ecode;
         sense_info->sense_key = buff[2] & 0xf;
@@ -656,19 +685,18 @@ int scsiRequestSense(int device, struct scsi_sense_disect * sense_info)
             }
         }
     }
-    return status;
+    return 0;
 }
 
 /* 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)
+int scsiSendDiagnostic(scsi_device * 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));
@@ -691,9 +719,8 @@ int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen)
     /* 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;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     return scsiSimpleSenseFilter(&sinfo);
 }
@@ -701,14 +728,13 @@ int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen)
 /* 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 scsiReceiveDiagnostic(scsi_device * 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));
@@ -726,20 +752,18 @@ int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf,
     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;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     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)
+static int _testunitready(scsi_device * 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));
@@ -753,16 +777,15 @@ static int _testunitready(int device, struct scsi_sense_disect * sinfo)
     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;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     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)
+int scsiTestUnitReady(scsi_device * device)
 {
     struct scsi_sense_disect sinfo;
     int status;
@@ -784,14 +807,13 @@ int scsiTestUnitReady(int device)
 /* 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,
+int scsiReadDefect10(scsi_device * 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));
@@ -809,9 +831,8 @@ int scsiReadDefect10(int device, int req_plist, int req_glist, int dl_format,
     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;
+    if (!device->scsi_pass_through(&io_hdr))
+      return -device->get_errno();
     scsi_do_sense_disect(&io_hdr, &sinfo);
     return scsiSimpleSenseFilter(&sinfo);
 }
@@ -860,7 +881,7 @@ int scsiModePageOffset(const UINT8 * resp, int len, int modese_len)
  * 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 scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp, int modese_len)
 {
     int err = 0;
 
@@ -950,7 +971,7 @@ int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp)
  * 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,
+int scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
                                       const struct scsi_iec_mode_page *iecp)
 {
     int k, offset, resp_len;
@@ -1025,7 +1046,7 @@ int scsiSetExceptionControlAndWarning(int device, int enabled,
     return err;
 }
 
-int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp)
+int scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp)
 {
     UINT8 tBuf[252];
     int err;
@@ -1047,7 +1068,7 @@ int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp)
  * 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,
+int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
                 UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp,
                 UINT8 *triptemp)
 {
@@ -1630,7 +1651,7 @@ const char * scsiGetIEString(UINT8 asc, UINT8 ascq)
 
 /* 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)
+int scsiSmartIBMOfflineTest(scsi_device * device)
 {       
     UINT8 tBuf[256];
     int res;
@@ -1651,7 +1672,7 @@ int scsiSmartIBMOfflineTest(int device)
     return res;
 }
 
-int scsiSmartDefaultSelfTest(int device)
+int scsiSmartDefaultSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1661,7 +1682,7 @@ int scsiSmartDefaultSelfTest(int device)
     return res;
 }
 
-int scsiSmartShortSelfTest(int device)
+int scsiSmartShortSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1671,7 +1692,7 @@ int scsiSmartShortSelfTest(int device)
     return res;
 }
 
-int scsiSmartExtendSelfTest(int device)
+int scsiSmartExtendSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1682,7 +1703,7 @@ int scsiSmartExtendSelfTest(int device)
     return res;
 }
 
-int scsiSmartShortCapSelfTest(int device)
+int scsiSmartShortCapSelfTest(scsi_device * device)
 {       
     int res;
 
@@ -1692,7 +1713,7 @@ int scsiSmartShortCapSelfTest(int device)
     return res;
 }
 
-int scsiSmartExtendCapSelfTest(int device)
+int scsiSmartExtendCapSelfTest(scsi_device * device)
 {
     int res;
 
@@ -1703,7 +1724,7 @@ int scsiSmartExtendCapSelfTest(int device)
     return res;
 }
 
-int scsiSmartSelfTestAbort(int device)
+int scsiSmartSelfTestAbort(scsi_device * device)
 {
     int res;
 
@@ -1715,7 +1736,7 @@ int scsiSmartSelfTestAbort(int device)
 
 /* 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 scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec, int modese_len)
 {
     int err, offset, res;
     UINT8 buff[64];
@@ -1875,7 +1896,7 @@ void scsiDecodeNonMediumErrPage(unsigned char *resp,
    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 scsiCountFailedSelfTests(scsi_device * fd, int noisy)
 {
     int num, k, n, err, res, fails, fail_hour;
     UINT8 * ucp;
@@ -1924,7 +1945,7 @@ int scsiCountFailedSelfTests(int fd, int noisy)
 
 /* 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 scsiSelfTestInProgress(scsi_device * fd, int * inProgress)
 {
     int num;
     UINT8 * ucp;
@@ -1951,7 +1972,7 @@ int scsiSelfTestInProgress(int fd, int * inProgress)
    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 scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current)
 {
     int err, offset;
     UINT8 buff[64];
@@ -1984,7 +2005,7 @@ int scsiFetchControlGLTSD(int device, int modese_len, int current)
    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 scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len)
 {
     int err, offset, resp_len, sp;
     UINT8 buff[64];
@@ -2053,7 +2074,7 @@ int scsiSetControlGLTSD(int device, int enabled, int modese_len)
 /* 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 scsiFetchTransportProtocol(scsi_device * device, int modese_len)
 {
     int err, offset;
     UINT8 buff[64];
index f449425b36a342af8df830845196b841083420f9..8a7211443d60c60b5063300ad85641222e7e2925 100644 (file)
@@ -32,7 +32,7 @@
 #ifndef SCSICMDS_H_
 #define SCSICMDS_H_
 
-#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.66 2008/03/04 22:09:47 ballen4705 Exp $\n"
+#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.69 2009/06/24 04:10:10 dpgilbert Exp $\n"
 
 #include <stdio.h>
 #include <stdlib.h>
 
 /* #define SCSI_DEBUG 1 */ /* Comment out to disable command debugging */
 
-/* Following conditional defines bypass inclusion of scsi/scsi.h and
- * scsi/scsi_ioctl.h . Issue will be resolved later ... */
+/* Following conditional defines just in case OS already has them defined.
+ * If they are defined we hope they are defined correctly (for SCSI). */
 #ifndef TEST_UNIT_READY
 #define TEST_UNIT_READY 0x0
 #endif
+#ifndef LOG_SELECT
+#define LOG_SELECT 0x4c
+#endif
 #ifndef LOG_SENSE
 #define LOG_SENSE 0x4d
 #endif
@@ -171,6 +174,7 @@ struct scsiNonMediumError {
 #define APPLICATION_CLIENT_LPAGE                0x0f
 #define SELFTEST_RESULTS_LPAGE                  0x10
 #define BACKGROUND_RESULTS_LPAGE                0x15   /* SBC-3 */
+#define PROTOCOL_SPECIFIC_LPAGE                 0x18
 #define IE_LPAGE                                0x2f
 
 /* Seagate vendor specific log pages. */
@@ -277,6 +281,8 @@ Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */
 
 #define LOGPAGEHDRSIZE  4
 
+class scsi_device;
+
 void scsi_do_sense_disect(const struct scsi_cmnd_io * in,
                           struct scsi_sense_disect * out);
 
@@ -285,73 +291,76 @@ int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo);
 const char * scsiErrString(int scsiErr);
 
 /* STANDARD SCSI Commands  */
-int scsiTestUnitReady(int device);
+int scsiTestUnitReady(scsi_device * device);
 
-int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen);
+int scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen);
 
-int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen);
+int scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen);
 
-int scsiLogSense(int device, int pagenum, int subpagenum, UINT8 *pBuf,
-                int bufLen, int known_resp_len);
+int scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
+                 int bufLen, int known_resp_len);
 
-int scsiModeSense(int device, int pagenum, int subpagenum, int pc,
+int scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum,
+                  int subpagenum, UINT8 *pBuf, int bufLen);
+
+int scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,
                   UINT8 *pBuf, int bufLen);
 
-int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen);
+int scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen);
 
-int scsiModeSense10(int device, int pagenum, int subpagenum, int pc,
-                   UINT8 *pBuf, int bufLen);
+int scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
+                    UINT8 *pBuf, int bufLen);
 
-int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen);
+int scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen);
 
 int scsiModePageOffset(const UINT8 * resp, int len, int modese_len);
 
-int scsiRequestSense(int device, struct scsi_sense_disect * sense_info);
+int scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info);
 
-int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen);
+int scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf, int bufLen);
 
-int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf,
+int scsiReceiveDiagnostic(scsi_device * device, int pcv, int pagenum, UINT8 *pBuf,
                       int bufLen);
 
-int scsiReadDefect10(int device, int req_plist, int req_glist, int dl_format,
+int scsiReadDefect10(scsi_device * device, int req_plist, int req_glist, int dl_format,
                      UINT8 *pBuf, int bufLen);
 
 /* SMART specific commands */
-int scsiCheckIE(int device, int hasIELogPage, int hasTempLogPage, UINT8 *asc,
+int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage, UINT8 *asc,
                 UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp);
 
-int scsiFetchIECmpage(int device, struct scsi_iec_mode_page *iecp, 
+int scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp,
                       int modese_len);
 int scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp);
 int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp);
-int scsiSetExceptionControlAndWarning(int device, int enabled,
+int scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
                             const struct scsi_iec_mode_page *iecp);
 void scsiDecodeErrCounterPage(unsigned char * resp,
                               struct scsiErrorCounter *ecp);
 void scsiDecodeNonMediumErrPage(unsigned char * resp,
                                 struct scsiNonMediumError *nmep);
-int scsiFetchExtendedSelfTestTime(int device, int * durationSec, 
+int scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec,
                                   int modese_len);
-int scsiCountFailedSelfTests(int fd, int noisy);
-int scsiSelfTestInProgress(int fd, int * inProgress);
-int scsiFetchControlGLTSD(int device, int modese_len, int current);
-int scsiSetControlGLTSD(int device, int enabled, int modese_len);
-int scsiFetchTransportProtocol(int device, int modese_len);
+int scsiCountFailedSelfTests(scsi_device * device, int noisy);
+int scsiSelfTestInProgress(scsi_device * device, int * inProgress);
+int scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current);
+int scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len);
+int scsiFetchTransportProtocol(scsi_device * device, int modese_len);
 
 /* T10 Standard IE Additional Sense Code strings taken from t10.org */
 
 const char* scsiGetIEString(UINT8 asc, UINT8 ascq);
-int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp);
+int scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp);
 
 
-int scsiSmartIBMOfflineTest(int device);
+int scsiSmartIBMOfflineTest(scsi_device * device);
 
-int scsiSmartDefaultSelfTest(int device);
-int scsiSmartShortSelfTest(int device);
-int scsiSmartExtendSelfTest(int device);
-int scsiSmartShortCapSelfTest(int device);
-int scsiSmartExtendCapSelfTest(int device);
-int scsiSmartSelfTestAbort(int device);
+int scsiSmartDefaultSelfTest(scsi_device * device);
+int scsiSmartShortSelfTest(scsi_device * device);
+int scsiSmartExtendSelfTest(scsi_device * device);
+int scsiSmartShortCapSelfTest(scsi_device * device);
+int scsiSmartExtendCapSelfTest(scsi_device * device);
+int scsiSmartSelfTestAbort(scsi_device * device);
 
 const char * scsiTapeAlertsTapeDevice(unsigned short code);
 const char * scsiTapeAlertsChangerDevice(unsigned short code);
@@ -370,7 +379,9 @@ inline void dStrHex(const unsigned char* str, int len, int no_ascii)
  * (e.g. CHECK CONDITION). If the SCSI command could not be issued
  * (e.g. device not present or not a SCSI device) or some other problem
  * arises (e.g. timeout) then returns a negative errno value. */
-int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);
+// Moved to C++ interface
+//int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);
+
 
 
 #endif
index c1e9743ddba3f2ea7e73e334499958f1415aee18..c2c894e43d69e2db9a4e3e88ea8618993dc3f421 100644 (file)
@@ -3,11 +3,11 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
  *
  * Additional SCSI work:
- * Copyright (C) 2003-8 Douglas Gilbert <dougg@torque.net>
+ * Copyright (C) 2003-9 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
 #include "int64.h"
 #include "extern.h"
 #include "scsicmds.h"
+#include "atacmds.h" // smart_command_set
+#include "dev_interface.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.121 2008/03/04 22:09:47 ballen4705 Exp $"
-CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
+const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 2861 2009-07-24 16:47:03Z chrfranke $"
+                                 SCSIPRINT_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_LONG_LEN ((62 * 256) + 252)
 #define LOG_RESP_TAPE_ALERT_LEN 0x144
 
 /* Log pages supported */
@@ -67,6 +65,7 @@ static int gVerifyECounterLPage = 0;
 static int gNonMediumELPage = 0;
 static int gLastNErrorLPage = 0;
 static int gBackgroundResultsLPage = 0;
+static int gProtocolSpecificLPage = 0;
 static int gTapeAlertsLPage = 0;
 static int gSeagateCacheLPage = 0;
 static int gSeagateFactoryLPage = 0;
@@ -81,7 +80,7 @@ static int modese_len = 0;
 // simply returns to the calling routine.
 extern void failuretest(int type, int returnvalue);
 
-static void scsiGetSupportedLogPages(int device)
+static void scsiGetSupportedLogPages(scsi_device * device)
 {
     int i, err;
 
@@ -126,6 +125,9 @@ static void scsiGetSupportedLogPages(int device)
             case BACKGROUND_RESULTS_LPAGE:
                 gBackgroundResultsLPage = 1;
                 break;
+            case PROTOCOL_SPECIFIC_LPAGE:
+                gProtocolSpecificLPage = 1;
+                break;
             case TAPE_ALERTS_LPAGE:
                 gTapeAlertsLPage = 1;
                 break;
@@ -143,7 +145,7 @@ static void scsiGetSupportedLogPages(int device)
 
 /* 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)
+static int scsiGetSmartData(scsi_device * device, bool attribs)
 {
     UINT8 asc;
     UINT8 ascq;
@@ -187,14 +189,14 @@ static int scsiGetSmartData(int device, int attribs)
 
 // Returns number of logged errors or zero if none or -1 if fetching
 // TapeAlerts fails
-static char *severities = "CWI";
+static const char * const severities = "CWI";
 
-static int scsiGetTapeAlertsData(int device, int peripheral_type)
+static int scsiGetTapeAlertsData(scsi_device * device, int peripheral_type)
 {
     unsigned short pagelength;
     unsigned short parametercode;
     int i, err;
-    char *s;
+    const char *s;
     const char *ts;
     int failures = 0;
 
@@ -237,7 +239,7 @@ static int scsiGetTapeAlertsData(int device, int peripheral_type)
     return failures;
 }
 
-static void scsiGetStartStopData(int device)
+static void scsiGetStartStopData(scsi_device * device)
 {
     UINT32 u;
     int err, len, k, extra, pc;
@@ -280,7 +282,7 @@ static void scsiGetStartStopData(int device)
             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",
+                    pout("Specified cycle count over device lifetime:  %u\n",
                          u);
             }
             break;
@@ -288,7 +290,22 @@ static void scsiGetStartStopData(int device)
             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);
+                    pout("Accumulated start-stop cycles:  %u\n", u);
+            }
+            break;
+        case 5:
+            if (extra > 7) {
+                u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
+                if (0xffffffff != u)
+                    pout("Specified load-unload count over device "
+                         "lifetime:  %u\n", u);
+            }
+            break;
+        case 6:
+            if (extra > 7) {
+                u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
+                if (0xffffffff != u)
+                    pout("Accumulated load-unload cycles:  %u\n", u);
             }
             break;
         default:
@@ -298,7 +315,7 @@ static void scsiGetStartStopData(int device)
     }
 } 
 
-static void scsiPrintGrownDefectListLen(int device)
+static void scsiPrintGrownDefectListLen(scsi_device * device)
 {
     int err, dl_format, dl_len, div;
 
@@ -347,7 +364,7 @@ static void scsiPrintGrownDefectListLen(int device)
     }
 }
 
-static void scsiPrintSeagateCacheLPage(int device)
+static void scsiPrintSeagateCacheLPage(scsi_device * device)
 {
     int k, j, num, pl, pc, err, len;
     unsigned char * ucp;
@@ -422,7 +439,7 @@ static void scsiPrintSeagateCacheLPage(int device)
     }
 }
 
-static void scsiPrintSeagateFactoryLPage(int device)
+static void scsiPrintSeagateFactoryLPage(scsi_device * device)
 {
     int k, j, num, pl, pc, len, err, good, bad;
     unsigned char * ucp;
@@ -516,7 +533,7 @@ static void scsiPrintSeagateFactoryLPage(int device)
     }
 }
 
-static void scsiPrintErrorCounterLog(int device)
+static void scsiPrintErrorCounterLog(scsi_device * device)
 {
     struct scsiErrorCounter errCounterArr[3];
     struct scsiErrorCounter * ecp;
@@ -639,8 +656,8 @@ static const char * self_test_code[] = {
 
 static const char * self_test_result[] = {
         "Completed                ",
-        "Interrupted ('-X' switch)",
-        "Interrupted (bus reset ?)",
+        "Aborted (by user command)",
+        "Aborted (device reset ?) ",
         "Unknown error, incomplete",
         "Completed, segment failed",
         "Failed in first segment  ",
@@ -660,7 +677,7 @@ static const char * self_test_result[] = {
 // 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)
+static int scsiPrintSelfTest(scsi_device * device)
 {
     int num, k, n, res, err, durationSec;
     int noheader = 1;
@@ -797,7 +814,7 @@ static int scsiPrintSelfTest(int device)
         pout("No self-tests have been logged\n");
     else
         pout("\n");
-    if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, 
+    if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
                         modese_len)) && (durationSec > 0)) {
         pout("Long (extended) Self Test duration: %d seconds "
              "[%.1f minutes]\n", durationSec, durationSec / 60.0);
@@ -814,15 +831,15 @@ static const char * bms_status[] = {
     "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 */
+    "waiting until BMS interval timer expires", /* 8 */
 };
 
 static const char * reassign_status[] = {
-    "No reassignment needed",
-    "Require Reassign or Write command",
+    "Reserved [0x0]",
+    "Require Write or Reassign Blocks command",
     "Successfully reassigned",
     "Reserved [0x3]",
-    "Failed",
+    "Reassignment by disk failed",
     "Recovered via rewrite in-place",
     "Reassigned by app, has valid data",
     "Reassigned by app, has no valid data",
@@ -833,7 +850,7 @@ static const char * reassign_status[] = {
 // 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)
+static int scsiPrintBackgroundResults(scsi_device * device)
 {
     int num, j, m, err, pc, pl, truncated;
     int noheader = 1;
@@ -895,6 +912,8 @@ static int scsiPrintBackgroundResults(int device)
                  (ucp[10] << 8) + ucp[11]);
             pout("scan progress: %.2f%%\n",
                  (double)((ucp[12] << 8) + ucp[13]) * 100.0 / 65536.0);
+            pout("    Number of background medium scans performed: %d\n",
+                 (ucp[14] << 8) + ucp[15]);
             break;
         default:
             if (noheader) {
@@ -934,6 +953,334 @@ static int scsiPrintBackgroundResults(int device)
     return retval;
 }
 
+
+static void show_sas_phy_event_info(int peis, unsigned int val,
+                                    unsigned thresh_val)
+{
+    unsigned int u;
+
+    switch (peis) {
+    case 0:
+        pout("     No event\n");
+        break;
+    case 0x1:
+        pout("     Invalid word count: %u\n", val);
+        break;
+    case 0x2:
+        pout("     Running disparity error count: %u\n", val);
+        break;
+    case 0x3:
+        pout("     Loss of dword synchronization count: %u\n", val);
+        break;
+    case 0x4:
+        pout("     Phy reset problem count: %u\n", val);
+        break;
+    case 0x5:
+        pout("     Elasticity buffer overflow count: %u\n", val);
+        break;
+    case 0x6:
+        pout("     Received ERROR  count: %u\n", val);
+        break;
+    case 0x20:
+        pout("     Received address frame error count: %u\n", val);
+        break;
+    case 0x21:
+        pout("     Transmitted abandon-class OPEN_REJECT count: %u\n", val);
+        break;
+    case 0x22:
+        pout("     Received abandon-class OPEN_REJECT count: %u\n", val);
+        break;
+    case 0x23:
+        pout("     Transmitted retry-class OPEN_REJECT count: %u\n", val);
+        break;
+    case 0x24:
+        pout("     Received retry-class OPEN_REJECT count: %u\n", val);
+        break;
+    case 0x25:
+        pout("     Received AIP (WATING ON PARTIAL) count: %u\n", val);
+        break;
+    case 0x26:
+        pout("     Received AIP (WAITING ON CONNECTION) count: %u\n", val);
+        break;
+    case 0x27:
+        pout("     Transmitted BREAK count: %u\n", val);
+        break;
+    case 0x28:
+        pout("     Received BREAK count: %u\n", val);
+        break;
+    case 0x29:
+        pout("     Break timeout count: %u\n", val);
+        break;
+    case 0x2a:
+        pout("     Connection count: %u\n", val);
+        break;
+    case 0x2b:
+        pout("     Peak transmitted pathway blocked count: %u\n",
+               val & 0xff);
+        pout("         Peak value detector threshold: %u\n",
+               thresh_val & 0xff);
+        break;
+    case 0x2c:
+        u = val & 0xffff;
+        if (u < 0x8000)
+            pout("     Peak transmitted arbitration wait time (us): "
+                   "%u\n", u);
+        else
+            pout("     Peak transmitted arbitration wait time (ms): "
+                   "%u\n", 33 + (u - 0x8000));
+        u = thresh_val & 0xffff;
+        if (u < 0x8000)
+            pout("         Peak value detector threshold (us): %u\n",
+                   u);
+        else
+            pout("         Peak value detector threshold (ms): %u\n",
+                   33 + (u - 0x8000));
+        break;
+    case 0x2d:
+        pout("     Peak arbitration time (us): %u\n", val);
+        pout("         Peak value detector threshold: %u\n", thresh_val);
+        break;
+    case 0x2e:
+        pout("     Peak connection time (us): %u\n", val);
+        pout("         Peak value detector threshold: %u\n", thresh_val);
+        break;
+    case 0x40:
+        pout("     Transmitted SSP frame count: %u\n", val);
+        break;
+    case 0x41:
+        pout("     Received SSP frame count: %u\n", val);
+        break;
+    case 0x42:
+        pout("     Transmitted SSP frame error count: %u\n", val);
+        break;
+    case 0x43:
+        pout("     Received SSP frame error count: %u\n", val);
+        break;
+    case 0x44:
+        pout("     Transmitted CREDIT_BLOCKED count: %u\n", val);
+        break;
+    case 0x45:
+        pout("     Received CREDIT_BLOCKED count: %u\n", val);
+        break;
+    case 0x50:
+        pout("     Transmitted SATA frame count: %u\n", val);
+        break;
+    case 0x51:
+        pout("     Received SATA frame count: %u\n", val);
+        break;
+    case 0x52:
+        pout("     SATA flow control buffer overflow count: %u\n", val);
+        break;
+    case 0x60:
+        pout("     Transmitted SMP frame count: %u\n", val);
+        break;
+    case 0x61:
+        pout("     Received SMP frame count: %u\n", val);
+        break;
+    case 0x63:
+        pout("     Received SMP frame error count: %u\n", val);
+        break;
+    default:
+        break;
+    }
+}
+
+static void show_sas_port_param(unsigned char * ucp, int param_len)
+{
+    int j, m, n, nphys, pcb, t, sz, spld_len;
+    unsigned char * vcp;
+    uint64_t ull;
+    unsigned int ui;
+    char s[64];
+
+    sz = sizeof(s);
+    pcb = ucp[2];
+    t = (ucp[0] << 8) | ucp[1];
+    pout("relative target port id = %d\n", t);
+    pout("  generation code = %d\n", ucp[6]);
+    nphys = ucp[7];
+    pout("  number of phys = %d\n", nphys);
+
+    for (j = 0, vcp = ucp + 8; j < (param_len - 8);
+         vcp += spld_len, j += spld_len) {
+        pout("  phy identifier = %d\n", vcp[1]);
+        spld_len = vcp[3];
+        if (spld_len < 44)
+            spld_len = 48;      /* in SAS-1 and SAS-1.1 vcp[3]==0 */
+        else
+            spld_len += 4;
+        t = ((0x70 & vcp[4]) >> 4);
+        switch (t) {
+        case 0: snprintf(s, sz, "no device attached"); break;
+        case 1: snprintf(s, sz, "end device"); break;
+        case 2: snprintf(s, sz, "expander device"); break;
+        case 3: snprintf(s, sz, "expander device (fanout)"); break;
+        default: snprintf(s, sz, "reserved [%d]", t); break;
+        }
+        pout("    attached device type: %s\n", s);
+        t = 0xf & vcp[4];
+        switch (t) {
+        case 0: snprintf(s, sz, "unknown"); break;
+        case 1: snprintf(s, sz, "power on"); break;
+        case 2: snprintf(s, sz, "hard reset"); break;
+        case 3: snprintf(s, sz, "SMP phy control function"); break;
+        case 4: snprintf(s, sz, "loss of dword synchronization"); break;
+        case 5: snprintf(s, sz, "mux mix up"); break;
+        case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
+            break;
+        case 7: snprintf(s, sz, "break timeout timer expired"); break;
+        case 8: snprintf(s, sz, "phy test function stopped"); break;
+        case 9: snprintf(s, sz, "expander device reduced functionality");
+             break;
+        default: snprintf(s, sz, "reserved [0x%x]", t); break;
+        }
+        pout("    attached reason: %s\n", s);
+        t = (vcp[5] & 0xf0) >> 4;
+        switch (t) {
+        case 0: snprintf(s, sz, "unknown"); break;
+        case 1: snprintf(s, sz, "power on"); break;
+        case 2: snprintf(s, sz, "hard reset"); break;
+        case 3: snprintf(s, sz, "SMP phy control function"); break;
+        case 4: snprintf(s, sz, "loss of dword synchronization"); break;
+        case 5: snprintf(s, sz, "mux mix up"); break;
+        case 6: snprintf(s, sz, "I_T nexus loss timeout for STP/SATA");
+            break;
+        case 7: snprintf(s, sz, "break timeout timer expired"); break;
+        case 8: snprintf(s, sz, "phy test function stopped"); break;
+        case 9: snprintf(s, sz, "expander device reduced functionality");
+             break;
+        default: snprintf(s, sz, "reserved [0x%x]", t); break;
+        }
+        pout("    reason: %s\n", s);
+        t = (0xf & vcp[5]);
+        switch (t) {
+        case 0: snprintf(s, sz, "phy enabled; unknown");
+                     break;
+        case 1: snprintf(s, sz, "phy disabled"); break;
+        case 2: snprintf(s, sz, "phy enabled; speed negotiation failed");
+                     break;
+        case 3: snprintf(s, sz, "phy enabled; SATA spinup hold state");
+                     break;
+        case 4: snprintf(s, sz, "phy enabled; port selector");
+                     break;
+        case 5: snprintf(s, sz, "phy enabled; reset in progress");
+                     break;
+        case 6: snprintf(s, sz, "phy enabled; unsupported phy attached");
+                     break;
+        case 8: snprintf(s, sz, "phy enabled; 1.5 Gbps"); break;
+        case 9: snprintf(s, sz, "phy enabled; 3 Gbps"); break;
+        case 0xa: snprintf(s, sz, "phy enabled; 6 Gbps"); break;
+        default: snprintf(s, sz, "reserved [%d]", t); break;
+        }
+        pout("    negotiated logical link rate: %s\n", s);
+        pout("    attached initiator port: ssp=%d stp=%d smp=%d\n",
+               !! (vcp[6] & 8), !! (vcp[6] & 4), !! (vcp[6] & 2));
+        pout("    attached target port: ssp=%d stp=%d smp=%d\n",
+               !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
+        for (n = 0, ull = vcp[8]; n < 8; ++n) {
+            ull <<= 8; ull |= vcp[8 + n];
+        }
+        pout("    SAS address = 0x%" PRIx64 "\n", ull);
+        for (n = 0, ull = vcp[16]; n < 8; ++n) {
+            ull <<= 8; ull |= vcp[16 + n];
+        }
+        pout("    attached SAS address = 0x%" PRIx64 "\n", ull);
+        pout("    attached phy identifier = %d\n", vcp[24]);
+        ui = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35];
+        pout("    Invalid DWORD count = %u\n", ui);
+        ui = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39];
+        pout("    Running disparity error count = %u\n", ui);
+        ui = (vcp[40] << 24) | (vcp[41] << 16) | (vcp[42] << 8) | vcp[43];
+        pout("    Loss of DWORD synchronization = %u\n", ui);
+        ui = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47];
+        pout("    Phy reset problem = %u\n", ui);
+        if (spld_len > 51) {
+            int num_ped, peis;
+            unsigned char * xcp;
+            unsigned int pvdt;
+
+            num_ped = vcp[51];
+            if (num_ped > 0)
+               pout("    Phy event descriptors:\n");
+            xcp = vcp + 52;
+            for (m = 0; m < (num_ped * 12); m += 12, xcp += 12) {
+                peis = xcp[3];
+                ui = (xcp[4] << 24) | (xcp[5] << 16) | (xcp[6] << 8) |
+                     xcp[7];
+                pvdt = (xcp[8] << 24) | (xcp[9] << 16) | (xcp[10] << 8) |
+                       xcp[11];
+                show_sas_phy_event_info(peis, ui, pvdt);
+            }
+        }
+    }
+}
+
+// Returns 1 if okay, 0 if non SAS descriptors
+static int show_protocol_specific_page(unsigned char * resp, int len)
+{
+    int k, num, param_len;
+    unsigned char * ucp;
+
+    num = len - 4;
+    for (k = 0, ucp = resp + 4; k < num; ) {
+        param_len = ucp[3] + 4;
+        if (6 != (0xf & ucp[4]))
+            return 0;   /* only decode SAS log page */
+        if (0 == k)
+            pout("Protocol Specific port log page for SAS SSP\n");
+        show_sas_port_param(ucp, param_len);
+        k += param_len;
+        ucp += param_len;
+    }
+    return 1;
+}
+
+
+// See Serial Attached SCSI (SAS-2) (e.g. revision 16) the Protocol Specific
+// log pageSerial Attached SCSI (SAS-2) (e.g. revision 16) the Protocol
+// Specific log page.
+// 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 scsiPrintSasPhy(scsi_device * device, int reset)
+{
+    int num, err;
+
+    if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf,
+                            LOG_RESP_LONG_LEN, 0))) {
+        PRINT_ON(con);
+        pout("scsiPrintSasPhy Log Sense Failed [%s]\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) {
+        PRINT_ON(con);
+        pout("Protocol specific Log Sense Failed, page mismatch\n");
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    // compute page length
+    num = (gBuf[2] << 8) + gBuf[3];
+    if (1 != show_protocol_specific_page(gBuf, num + 4)) {
+        PRINT_ON(con);
+        pout("Only support protocol specific log page on SAS devices\n");
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    if (reset) {
+        if ((err = scsiLogSelect(device, 1 /* pcr */, 0 /* sp */, 0 /* pc */,
+                                 PROTOCOL_SPECIFIC_LPAGE, 0, NULL, 0))) {
+            PRINT_ON(con);
+            pout("scsiPrintSasPhy Log Select (reset) Failed [%s]\n",
+                 scsiErrString(err));
+            PRINT_OFF(con);
+            return FAILSMART;
+        }
+    }
+    return 0;
+}
+
+
 static const char * peripheral_dt_arr[] = {
         "disk",
         "tape",
@@ -973,7 +1320,7 @@ static const char * transport_proto_arr[] = {
 };
 
 /* Returns 0 on success, 1 on general error and 2 for early, clean exit */
-static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
+static int scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
 {
     char manufacturer[9];
     char product[17];
@@ -1025,29 +1372,7 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     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 ((0 == con->controller_explicit) &&
+    if (!*device->get_req_type()/*no type requested*/ &&
                (0 == strncmp(manufacturer, "ATA", 3))) {
         pout("\nProbable ATA device behind a SAT layer\n"
              "Try an additional '-d ata' or '-d sat' argument.\n");
@@ -1154,7 +1479,7 @@ static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
     return 0;
 }
 
-static int scsiSmartEnable(int device)
+static int scsiSmartEnable(scsi_device * device)
 {
     struct scsi_iec_mode_page iec;
     int err;
@@ -1190,7 +1515,7 @@ static int scsiSmartEnable(int device)
     return 0;
 }
         
-static int scsiSmartDisable(int device)
+static int scsiSmartDisable(scsi_device * device)
 {
     struct scsi_iec_mode_page iec;
     int err;
@@ -1226,7 +1551,7 @@ static int scsiSmartDisable(int device)
     return 0;
 }
 
-static void scsiPrintTemp(int device)
+static void scsiPrintTemp(scsi_device * device)
 {
     UINT8 temp = 0;
     UINT8 trip = 0;
@@ -1245,14 +1570,14 @@ static void scsiPrintTemp(int device)
 }
 
 /* Main entry point used by smartctl command. Return 0 for success */
-int scsiPrintMain(int fd)
+int scsiPrintMain(scsi_device * device, const scsi_print_options & options)
 {
     int checkedSupportedLogPages = 0;
     UINT8 peripheral_type = 0;
     int returnval = 0;
     int res, durationSec;
 
-    res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo);
+    res = scsiGetDriveInfo(device, &peripheral_type, options.drive_info);
     if (res) {
         if (2 == res)
             return 0;
@@ -1260,45 +1585,45 @@ int scsiPrintMain(int fd)
             failuretest(MANDATORY_CMD, returnval |= FAILID);
     }
 
-    if (con->smartenable) {
-        if (scsiSmartEnable(fd))
+    if (options.smart_enable) {
+        if (scsiSmartEnable(device))
             failuretest(MANDATORY_CMD, returnval |= FAILSMART);
     }
 
-    if (con->smartdisable) {
-        if (scsiSmartDisable(fd))
+    if (options.smart_disable) {
+        if (scsiSmartDisable(device))
             failuretest(MANDATORY_CMD,returnval |= FAILSMART);
     }
     
-    if (con->smartautosaveenable) {
-      if (scsiSetControlGLTSD(fd, 0, modese_len)) {
+    if (options.smart_auto_save_enable) {
+      if (scsiSetControlGLTSD(device, 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)) {
+    if (options.smart_auto_save_disable) {
+      if (scsiSetControlGLTSD(device, 1, modese_len)) {
         pout("Disable autosave (set GLTSD bit) failed\n");
         failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
       }
     }
     
-    if (con->checksmart) {
-        scsiGetSupportedLogPages(fd);
+    if (options.smart_check_status) {
+        scsiGetSupportedLogPages(device);
         checkedSupportedLogPages = 1;
         if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
             (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
             if (gTapeAlertsLPage) {
-                if (con->driveinfo)
+                if (options.drive_info)
                     pout("TapeAlert Supported\n");
-                if (-1 == scsiGetTapeAlertsData(fd, peripheral_type))
+                if (-1 == scsiGetTapeAlertsData(device, 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 ((res = scsiGetSmartData(device, options.smart_vendor_attrib))) {
                 if (-2 == res)
                     returnval |= FAILSTATUS;
                 else
@@ -1306,38 +1631,38 @@ int scsiPrintMain(int fd)
             }
         }
     }   
-    if (con->smartvendorattrib) {
+    if (options.smart_vendor_attrib) {
         if (! checkedSupportedLogPages)
-            scsiGetSupportedLogPages(fd);
+            scsiGetSupportedLogPages(device);
         if (gTempLPage) {
-            if (con->checksmart)
+            if (options.smart_check_status)
                 pout("\n");
-            scsiPrintTemp(fd);         
+            scsiPrintTemp(device);
         }
         if (gStartStopLPage)
-            scsiGetStartStopData(fd);
+            scsiGetStartStopData(device);
         if (SCSI_PT_DIRECT_ACCESS == peripheral_type) {
-            scsiPrintGrownDefectListLen(fd);
+            scsiPrintGrownDefectListLen(device);
             if (gSeagateCacheLPage)
-                scsiPrintSeagateCacheLPage(fd);
+                scsiPrintSeagateCacheLPage(device);
             if (gSeagateFactoryLPage)
-                scsiPrintSeagateFactoryLPage(fd);
+                scsiPrintSeagateFactoryLPage(device);
         }
     }
-    if (con->smarterrorlog) {
+    if (options.smart_error_log) {
         if (! checkedSupportedLogPages)
-            scsiGetSupportedLogPages(fd);
-        scsiPrintErrorCounterLog(fd);
-        if (1 == scsiFetchControlGLTSD(fd, modese_len, 1))
+            scsiGetSupportedLogPages(device);
+        scsiPrintErrorCounterLog(device);
+        if (1 == scsiFetchControlGLTSD(device, modese_len, 1))
             pout("\n[GLTSD (Global Logging Target Save Disable) set. "
                  "Enable Save with '-S on']\n");
     }
-    if (con->smartselftestlog) {
+    if (options.smart_selftest_log) {
         if (! checkedSupportedLogPages)
-            scsiGetSupportedLogPages(fd);
+            scsiGetSupportedLogPages(device);
         res = 0;
         if (gSelfTestLPage)
-            res = scsiPrintSelfTest(fd);
+            res = scsiPrintSelfTest(device);
         else {
             pout("Device does not support Self Test logging\n");
             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
@@ -1345,12 +1670,12 @@ int scsiPrintMain(int fd)
         if (0 != res)
             failuretest(OPTIONAL_CMD, returnval|=res);
     }
-    if (con->smartbackgroundlog) {
+    if (options.smart_background_log) {
         if (! checkedSupportedLogPages)
-            scsiGetSupportedLogPages(fd);
+            scsiGetSupportedLogPages(device);
         res = 0;
         if (gBackgroundResultsLPage)
-            res = scsiPrintBackgroundResults(fd);
+            res = scsiPrintBackgroundResults(device);
         else {
             pout("Device does not support Background scan results logging\n");
             failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
@@ -1358,27 +1683,27 @@ int scsiPrintMain(int fd)
         if (0 != res)
             failuretest(OPTIONAL_CMD, returnval|=res);
     }
-    if (con->smartexeoffimmediate) {
-        if (scsiSmartDefaultSelfTest(fd))
+    if (options.smart_default_selftest) {
+        if (scsiSmartDefaultSelfTest(device))
             return returnval | FAILSMART;
         pout("Default Self Test Successful\n");
     }
-    if (con->smartshortcapselftest) {
-        if (scsiSmartShortCapSelfTest(fd))
+    if (options.smart_short_cap_selftest) {
+        if (scsiSmartShortCapSelfTest(device))
             return returnval | FAILSMART;
         pout("Short Foreground Self Test Successful\n");
     }
-    if (con->smartshortselftest ) { 
-        if (scsiSmartShortSelfTest(fd))
+    if (options.smart_short_selftest) {
+        if (scsiSmartShortSelfTest(device))
             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))
+    if (options.smart_extend_selftest) {
+        if (scsiSmartExtendSelfTest(device))
             return returnval | FAILSMART;
         pout("Extended Background Self Test has begun\n");
-        if ((0 == scsiFetchExtendedSelfTestTime(fd, &durationSec, 
+        if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
                         modese_len)) && (durationSec > 0)) {
             time_t t = time(NULL);
 
@@ -1389,15 +1714,19 @@ int scsiPrintMain(int fd)
         }
         pout("Use smartctl -X to abort test\n");        
     }
-    if (con->smartextendcapselftest) {
-        if (scsiSmartExtendCapSelfTest(fd))
+    if (options.smart_extend_cap_selftest) {
+        if (scsiSmartExtendCapSelfTest(device))
             return returnval | FAILSMART;
         pout("Extended Foreground Self Test Successful\n");
     }
-    if (con->smartselftestabort) {
-        if (scsiSmartSelfTestAbort(fd))
+    if (options.smart_selftest_abort) {
+        if (scsiSmartSelfTestAbort(device))
             return returnval | FAILSMART;
         pout("Self Test returned without error\n");
     }           
+    if (options.sasphy) {
+        if (scsiPrintSasPhy(device, options.sasphy_reset))
+            return returnval | FAILSMART;
+    }           
     return returnval;
 }
index 699bef5b6c8a787c7a4c2c688be5d3a185372f40..dcc1d64ace04f20e7518fa4cba33e4394b240eb2 100644 (file)
@@ -3,11 +3,11 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
  *
  * Additional SCSI work:
- * Copyright (C) 2003-8 Douglas Gilbert <dougg@torque.net>
+ * Copyright (C) 2003-9 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
  */
 
 
-/* scsismart version number */
 #ifndef SCSI_PRINT_H_
 #define SCSI_PRINT_H_
 
-#define SCSIPRINT_H_CVSID "$Id: scsiprint.h,v 1.21 2008/03/04 22:09:47 ballen4705 Exp $\n"
+#define SCSIPRINT_H_CVSID "$Id: scsiprint.h,v 1.24 2009/06/21 02:39:32 dpgilbert Exp $\n"
 
-int scsiPrintMain(int fd);
+// Options for scsiPrintMain
+// TODO: Move remaining options from con->* to here.
+struct scsi_print_options
+{
+  bool drive_info;
+  bool smart_check_status;
+  bool smart_vendor_attrib;
+  bool smart_error_log;
+  bool smart_selftest_log;
+  bool smart_background_log;
+
+  bool smart_disable, smart_enable;
+  bool smart_auto_save_disable, smart_auto_save_enable;
+
+  bool smart_default_selftest;
+  bool smart_short_selftest, smart_short_cap_selftest;
+  bool smart_extend_selftest, smart_extend_cap_selftest;
+  bool smart_selftest_abort;
+
+  bool sasphy, sasphy_reset;
+
+  scsi_print_options()
+    : drive_info(false),
+      smart_check_status(false),
+      smart_vendor_attrib(false),
+      smart_error_log(false),
+      smart_selftest_log(false),
+      smart_background_log(false),
+      smart_disable(false), smart_enable(false),
+      smart_auto_save_disable(false), smart_auto_save_enable(false),
+      smart_default_selftest(false),
+      smart_short_selftest(false), smart_short_cap_selftest(false),
+      smart_extend_selftest(false), smart_extend_cap_selftest(false),
+      smart_selftest_abort(false),
+      sasphy(false), sasphy_reset(false)
+    { }
+};
+
+int scsiPrintMain(scsi_device * device, const scsi_print_options & options);
 
 #endif
index 70cb0a5caacb59dcb77f66810d4e21a982e145c8..348972ea55b4cd53604d2be3825f39d1acf1ecec 100644 (file)
@@ -1,7 +1,7 @@
 .ig
- Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
- $Id: smartctl.8.in,v 1.105 2008/03/04 22:09:47 ballen4705 Exp $
+ $Id: smartctl.8.in 2855 2009-07-21 19:55:08Z chrfranke $
  
  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
@@ -54,18 +54,23 @@ ignored and/or return an error.
 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:
+the final argument to \fBsmartctl\fP. The command set used by the device
+is often derived from the device path but may need help with the \'\-d\'
+option (for more information see the section on "ATA, SCSI command sets
+and SAT" below). 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.
+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.  For disks behind Areca SATA RAID controllers,
+you need \fB"/dev/sg[2\-9]"\fP (note that smartmontools interacts with
+the Areca controllers via a SCSI generic device which is different
+than the SCSI device used for reading and writing data)!
 .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
@@ -99,7 +104,8 @@ Use one the forms \fB"/dev/tape[0\-255]"\fP, \fB"/dev/st[0\-255]"\fP,
 or \fB"/dev/nst[0\-255]"\fP for SCSI tape drives "\\\\.\\Tape[0\-255]".
 
 Alternatively, drive letters \fB"X:"\fP or \fB"X:\\"\fP may be used to
-specify the physical drive behind a mounted partition.
+specify the (\'basic\') disk behind a mounted partition.  This does
+not work with \'dynamic\' disks.
 
 For disks behind 3ware 9000 controllers use \fB"/dev/sd[a\-z],N"\fP where
 N specifies the disk number (3ware \'port\') behind the controller
@@ -137,12 +143,6 @@ 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 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
 .B \'smartctl \-h\'
 to see the available options.
@@ -154,9 +154,9 @@ to see the available options.
 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.
+Prints version, copyright, license, home page and SVN revision
+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
@@ -181,8 +181,20 @@ 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.
+Note that for ATA disks this does \fBnot\fP enable the non-SMART options
+and the SMART options which require support for 48-bit ATA commands.
+.TP
+.B \-x, \-\-xall
+Prints all SMART and non-SMART information about the device. For ATA
+devices this is equivalent to
+.nf
+\'\-H \-i \-c \-A \-l xerror,error \-l xselftest,selftest \-l selective
+\-l directory \-l scttemp \-l sataphy\'.
+.fi
+and for SCSI, this is equivalent to
+.nf
+\'\-H \-i \-A \-l error \-l selftest \-l background \-l sasphy\'.
+.fi
 
 .TP
 .B RUN\-TIME BEHAVIOR OPTIONS:
@@ -210,9 +222,17 @@ 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,
-\fIcciss,N\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.
+are \fIata\fP, \fIscsi\fP, \fIsat\fP, \fImarvell\fP, \fI3ware,N\fP,
+\fIareca,N\fP, \fIusbcypress\fP, \fIusbjmicron\fP, \fIusbsunplus\fP,
+\fIcciss,N\fP, \fIhpt,L/M\fP (or \fIhpt,L/M/N\fP), and \fItest\fP.
+
+If this option is not used then \fBsmartctl\fP will attempt to guess
+the device type from the device name or from controller type info
+provided by the operating system.
+
+If \'test\' is used as the TYPE name, \fBsmartctl\fP prints the guessed
+TYPE name, then opens the device and prints the (possibly changed) TYPE
+name and then exists without performing any further commands.
 
 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.
@@ -221,6 +241,26 @@ 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\'.
 
+The \'usbcypress\' device type is for ATA disks that are behind a Cypress
+usb-pata bridge. This will use the ATACB proprietary scsi pass through command. There is no autodetection at the moment. The best way to know if your device support it, is to check your device usb id (most Cypress usb ata bridge got vid=0x04b4, pid=0x6830) or to try it (if the usb device doesn't support ATACB, smartmontools print an error).
+The default scsi operation code is 0x24, but although it can be overridden
+with \'\-d usbcypress,0xn\', where n is the scsi operation code,
+you're running the risk of damage to the device or filesystems on it.
+
+[NEW EXPERIMENTAL SMARTCTL FEATURE] The \'usbjmicron\' device type is for
+SATA disks that are behind a JMicron USB to PATA/SATA bridge. The 48-bit
+ATA commands (required e.g. for \'\-l xerror\', see below) do not work with
+all of these bridges and are therefore disabled by default. These commands
+can be enabled by \'\-d usbjmicron,x\'. CAUTION: Specifying \',x\' for a
+device which do not support it results in I/O errors and may disconnect
+the drive. The port can be specified by \'\-d usbjmicron[,x],PORT\' where
+PORT is 0 (master) or 1 (slave). This is not necessary if only one disk is
+connected to the USB bridge. If two disks are connected, an error message
+is printed if no PORT is specified.
+
+[NEW EXPERIMENTAL SMARTCTL FEATURE] The \'usbsunplus\' device type is for
+SATA disks that are behind a SunplusIT USB to SATA bridge.
+
 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
@@ -229,6 +269,22 @@ 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).
 
+Under Linux , to look at SCSI/SAS disks behind LSI MegaRAID controllers,
+use syntax such as:
+.nf
+\fBsmartctl \-a \-d megaraid,2 /dev/sda\fP
+.fi
+.nf
+\fBsmartctl \-a \-d megaraid,0 /dev/sdb\fP
+.fi
+where in the argument \fImegaraid,N\fP, the integer N is the physical disk
+number within the MegaRAID controller.  This interface will also work for
+Dell PERC controllers.  The following /dev/XXX entry must exist:
+.fi
+For PERC2/3/4 controllers: \fB/dev/megadev0\fP
+.fi
+For PERC5/6 controllers: \fB/dev/megaraid_sas_ioctl_node\fP
+
 Under Linux and FreeBSD, to look at ATA disks behind 3ware SCSI RAID controllers,
 use syntax such as:
 .nf
@@ -242,7 +298,7 @@ use syntax such as:
 .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 31 inclusive.  The first two forms, which
+values of N are from 0 to 127 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
@@ -295,33 +351,66 @@ 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 supported under Linux, FreeBSD and Windows.
+.B Areca SATA RAID controllers are currently supported under Linux only.
+To look at SATA disks behind Areca RAID controllers, use syntax such
+as:
+.nf
+\fBsmartctl \-a \-d areca,2 /dev/sg2\fP
+.fi
+.nf
+\fBsmartctl \-a \-d areca,3 /dev/sg3\fP
+.fi
+where in the argument \fIareca,N\fP, the integer N is the disk number
+(Areca \'port\') within the Areca SATA RAID controller.  The allowed
+values of N are from 1 to 24 inclusive.  The first line above
+addresses the second disk on the first Areca RAID controller.  The
+second line addresses the third disk on the second Areca RAID
+controller.  To help identify the correct device, use the command:
+.nf
+\fBcat /proc/scsi/sg/device_hdr /proc/scsi/sg/devices\fP
+.fi
+to show the SCSI generic devices (one per line, starting with
+/dev/sg0). The correct SCSI generic devices to address for
+smartmontools are the ones with the type field equal to 3.  If the
+incorrect device is addressed, please read the warning/error messages
+carefully.  They should provide hints about what devices to use.
+
+Important: the Areca controller must have firmware version 1.46 or
+later. Lower-numbered firmware versions will give (harmless) SCSI
+error messages and no SMART information.
 
 To look at (S)ATA disks behind HighPoint RocketRAID controllers, use syntax
 such as:
 .nf
-\fBsmartctl \-a \-d hpt,1/3 /dev/sda\fP
+\fBsmartctl \-a \-d hpt,1/3 /dev/sda\fP    (under Linux)
+.fi
+.nf
+\fBsmartctl \-a \-d hpt,1/2/3 /dev/sda\fP    (under Linux)
 .fi
-or
 .nf
-\fBsmartctl \-a \-d hpt,1/2/3 /dev/sda\fP
+\fBsmartctl \-a \-d hpt,1/3 /dev/hptrr\fP    (under FreeBSD)
+.fi
+.nf
+\fBsmartctl \-a \-d hpt,1/2/3 /dev/hptrr\fP    (under FreeBSD)
 .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.
+inclusive, M are from 1 to 8 inclusive and N from 1 to 5 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.
+the disks derived from the HighPoint RocketRAID controllers under Linux and
+under FreeBSD, it is the character device which the driver registered (eg,
+/dev/hptrr, /dev/hptmv6).  And also these values are limited by the model
+of the HighPoint RocketRAID controller.
 
-.B HighPoint RocketRAID controllers are currently ONLY supported under Linux.
+.B HighPoint RocketRAID controllers are currently ONLY supported under Linux and FreeBSD.
 
 .B cciss 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. 
+[ATA only] 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
@@ -361,9 +450,9 @@ 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
+[ATA only] 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:
@@ -417,9 +506,9 @@ behaviour. This is does not work for SCSI devices yet.
 
 .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
+[ATA only] Specifies 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
@@ -454,16 +543,16 @@ the corresponding disable command.
 .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
+(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
+[ATA only] 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
@@ -568,8 +657,8 @@ this happens, use the \'\-a\' option to get more information, and
 .B get your data off the disk and to 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
+[ATA only] 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
@@ -585,8 +674,8 @@ 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
+[ATA] 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.
 
@@ -667,7 +756,7 @@ 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
+[SCSI] 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).
@@ -679,8 +768,8 @@ the Background Scan Results Log [SCSI 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
+\- [ATA] prints the Summary 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
@@ -753,13 +842,34 @@ 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.
+.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 xerror[,NUM][,error]
+\- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints the Extended
+Comprehensive SMART error log (General Purpose Log address 0x03).
+Unlike the Summary SMART error log (see \'\-l error\' above),
+it provides sufficient space to log the contents of the 48-bit
+LBA register set introduced with ATA-6.  It also supports logs
+with more than one sector.  Each sector holds up to 4 log entries.
+The actual number of log sectors is vendor specific, typical values
+for HDD are 2 (Samsung), 5 (Seagate) or 6 (WD). Some recent SSD devices
+have much larger error logs.
+
+Only the 8 most recent error log entries are printed by default.
+This number can be changed by the optional parameter NUM.
+
+If ',error' is appended and the Extended Comprehensive SMART error
+log is not supported, the Summary SMART self-test log is printed.
+
+Please note that some recent (e.g. Samsung) drives report errors only
+in the Comprehensive SMART error log. The Summary SMART error log can
+be read but is always empty.
+
 .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
+\- [ATA] 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
@@ -771,9 +881,9 @@ 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
+.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
@@ -793,9 +903,22 @@ 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
+.I xselftest[,NUM][,selftest]
+\- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints the Extended
+SMART self\-test log (General Purpose Log address 0x07). Unlike the SMART
+self\-test log (see \'\-l selftest\' above), it supports 48-bit LBA
+and logs with more than one sector.  Each sector holds up to 19 log
+entries. The actual number of log sectors is vendor specific, typical
+values are 1 (Seagate) or 2 (Samsung).
+
+Only the 25 most recent log entries are printed by default. This number
+can be changed by the optional parameter NUM.
+
+If ',selftest' is appended and the Extended SMART self-test log is not
+supported, the old SMART self-test log is printed.
+
+.I selective
+\- [ATA only] 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
@@ -809,9 +932,9 @@ delay before restarting this read\-scan if it is interrupted (see
 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
+.I directory[,gs]
+\- [ATA only] if the device supports the General Purpose Logging feature
+set (ATA\-6 and above) 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
@@ -819,16 +942,16 @@ 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.]
-
-.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
+arguments to this option.
+If your version of smartctl supports 48-bit ATA commands, both the
+General Purpose Log (GPL) and SMART Log (SL) directories are printed in
+one combined table. The output can be restricted to the GPL directory or
+SL directory by \'\-l directory,q\' or \'\-l directory,s\' respectively.
+
+.I background
+\- [SCSI only] 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
@@ -837,9 +960,9 @@ 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 ).
 
-.I scttemp, scttempsts, scttemphist [ATA]
-\- [NEW EXPERIMENTAL SMARTCTL FEATURE] prints the disk temperature
-information provided by the SMART Command Transport (SCT) commands.
+.I scttemp, scttempsts, scttemphist
+\- [ATA only] prints the disk temperature information provided by the
+SMART Command Transport (SCT) commands.
 The option \'scttempsts\' prints current temperature and temperature
 ranges returned by the SCT Status command, \'scttemphist\' prints
 temperature limits and the temperature history table returned by
@@ -850,10 +973,52 @@ configured with the \'\-t scttempint,N[,p]\' option, see below.
 The SCT commands are specified in the proposed ATA\-8 Command Set
 (ACS), and are already implemented in some recent ATA\-7 disks.
 
+.I sataphy[,reset]
+\- [SATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints values
+and descriptions of the SATA Phy Event Counters (General Purpose Log
+address 0x11). If \'\-l sataphy,reset\' is specified, all counters
+are reset after reading the values.
+
+.I sasphy[,reset]
+\- [SAS (SCSI) only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints values
+and descriptions of the SAS (SSP) Protocol Specific log page (log page
+0x18). If \'\-l sasphy,reset\' is specified, all counters
+are reset after reading the values.
+
+.I gplog,ADDR[,FIRST[\-LAST|+SIZE]]
+\- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints a hex dump
+of any log accessible via General Purpose Logging (GPL) feature.
+The log address ADDR is the hex address listed in the log directory
+(see \'\-l directory\' above). The range of log sectors (pages) can
+be specified by decimal values FIRST\-LAST or FIRST+SIZE. FIRST
+defaults to 0, SIZE defaults to 1. LAST can be set to \'max\' to
+specify the last page of the log.
+
+.I smartlog,ADDR[,FIRST[\-LAST|+SIZE]]
+\- [ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] prints a hex dump
+of any log accessible via SMART Read Log command. See
+\'\-l gplog,...\' above for parameter syntax.
+
+For example, all these commands:
+.nf
+  smartctl \-l gplog,0x80,10-15 /dev/sda
+  smartctl \-l gplog,0x80,10+6 /dev/sda
+  smartctl \-l smartlog,0x80,10-15 /dev/sda
+.fi
+print pages 10-15 of log 0x80 (first host vendor specific log).
+
+The hex dump format is compatible with the \'xxd \-r\' command.
+This command:
+.nf
+  smartctl \-l gplog,0x11 /dev/sda | grep ^0 | xxd -r >log.bin
+.fi
+writes a binary representation of the one sector log 0x11
+(SATA Phy Event Counters) to file log.bin.
+
 .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:
+[ATA only] 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,
@@ -904,6 +1069,14 @@ with RK100\-13 firmware).
 interpretation is unknown. This is primarily useful for the \-P
 (presets) option.
 
+.I 197,increasing
+\- Raw Attribute number 197 (Current Pending Sector Count) is not
+reset if uncorrectable sectors are reallocated.
+
+.I 198,increasing
+\- Raw Attribute number 198 (Offline Uncorrectable Sector Count) is not
+reset if uncorrectable sectors are reallocated.
+
 .I 198,offlinescanuncsectorct
 \- Raw Attribute number 198 is the Offline Scan UNC Sector Count.
 
@@ -943,8 +1116,8 @@ 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 or driver bug.  Except \'swapid\',
+[ATA only] Modifies the behavior of \fBsmartctl\fP to compensate for some
+known and understood device firmware or driver bug.  Except \'swapid\',
 the arguments to this option are exclusive, so that only the final
 option given is used.  The valid values are:
 
@@ -989,8 +1162,8 @@ firmware version) returned by some buggy device drivers.
 
 .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
+[ATA only] 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
@@ -1048,6 +1221,43 @@ lists all entries matching MODEL, and the command:
 .fi
 lists all entries for this MODEL and a specific FIRMWARE version.
 
+.TP
+.B \-B [+]FILE, \-\-drivedb=[+]FILE
+[ATA only] [NEW EXPERIMENTAL SMARTCTL FEATURE] Read the drive database from
+FILE.  The new database replaces the built in database by default. If \'+\' is
+specified, then the new entries prepend the built in entries.
+
+If this option is not specified, optional entries are read from the file
+\fB/usr/local/etc/smart_drivedb.h\fP (Windows: \fB./smart_drivedb.conf\fP).
+.\" BEGIN ENABLE_DRIVEDB
+If \fB/usr/local/share/smartmontools/drivedb.h\fP is present, the
+contents of this file is used instead of the built in table.
+.\" END ENABLE_DRIVEDB
+
+The database files use the same C/C++ syntax that is used to initialize
+the built in database array. C/C++ style comments are allowed.
+Example:
+
+.nf
+  /* Full entry: */
+  {
+    "Model family",    // Info about model family/series.
+    "MODEL1.*REGEX",   // Regular expression to match model of device.
+    "VERSION.*REGEX",  // Regular expression to match firmware version(s).
+    "Some warning",    // Warning message.
+    "\-v 9,minutes"     // String of preset \-v and \-F options.
+  },
+  /* Minimal entry: */
+  {
+    "",                // No model family/series info.
+    "MODEL2.*REGEX",   // Regular expression to match model of device.
+    "",                // All firmware versions.
+    "",                // No warning.
+    ""                 // No options preset.
+  },
+  /* ... */
+.fi
+
 .TP
 .B SMART RUN/ABORT OFFLINE TEST AND SELF\-TEST OPTIONS:
 .TP
@@ -1105,7 +1315,7 @@ 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
+\- [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
@@ -1113,13 +1323,12 @@ during normal system operation (unless run in captive mode \- see the
 \'\-C\' option below).
 
 .I select,N\-M, select,N+SIZE
-\- [ATA ONLY] [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. The range
-can also be specified as N+SIZE. A span at the end of a disk can
-be specified by N\-\fBmax\fP.
+\- [ATA only] 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. The range can also be specified as N+SIZE. A span at the end of
+a disk can be specified by N\-\fBmax\fP.
 
 For example the commands:
 .nf
@@ -1165,10 +1374,10 @@ The following variants of the selective self\-test command use spans based
 on the ranges from past tests already stored on the disk:
 
 .I select,redo[+SIZE]
-\- [ATA ONLY] [NEW EXPERIMENTAL SMARTCTL FEATURE] redo the last SMART
-Selective Self Test using the same LBA range. The starting LBA is identical
-to the LBA used by last test, same for ending LBA unless a new span size
-is specified by optional +SIZE argument.
+\- [ATA only] redo the last SMART Selective Self Test using the same LBA
+range. The starting LBA is identical to the LBA used by last test, same
+for ending LBA unless a new span size is specified by optional +SIZE
+argument.
 
 For example the commands:
 .nf
@@ -1184,10 +1393,10 @@ have the same effect as:
 .fi
 
 .I select,next[+SIZE]
-\- [ATA ONLY] [NEW EXPERIMENTAL SMARTCTL FEATURE] runs a SMART Selective
-Self Test on the LBA range which follows the range of the last test. The
-starting LBA is set to (ending LBA +1) of the last test. A new span size
-may be specified by the optional +SIZE argument.
+\- [ATA only] runs a SMART Selective Self Test on the LBA range which
+follows the range of the last test. The starting LBA is set to (ending
+LBA +1) of the last test. A new span size may be specified by the
+optional +SIZE argument.
 
 For example the commands:
 .nf
@@ -1208,12 +1417,12 @@ the total number of spans to check the full disk will not be changed
 by future uses of \'\-t select,next\'.
 
 .I select,cont[+SIZE]
-\- [ATA ONLY] [NEW EXPERIMENTAL SMARTCTL FEATURE] performs a \'redo\'
-(above) if the self test status reports that the last test was aborted
-by the host. Otherwise it run the \'next\' (above) test.
+\- [ATA only] performs a \'redo\' (above) if the self test status reports
+that the last test was aborted by the host. Otherwise it run the \'next\'
+(above) test.
 
 .I afterselect,on
-\- [ATA ONLY] perform an offline read scan after a Selective Self\-test
+\- [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
@@ -1224,13 +1433,13 @@ 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
+\- [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.
+\- [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
@@ -1239,13 +1448,12 @@ options above. The value of this option is preserved between selective
 self\-tests.
 
 .I scttempint,N[,p]
-\- [ATA ONLY] [NEW EXPERIMENTAL SMARTCTL FEATURE] set the time interval
-for SCT temperature logging to N minutes. If \',p\' is specified, the
-setting is preserved across power cycles. Otherwise, the setting is
-volatile and will be reverted to default (1 minute), or last
-non-volatile setting by the next hard reset. This command also clears
-the temperature history table. See \'\-l scttemp\' above for more
-information about SCT temperature logging.
+\- [ATA only] set the time interval for SCT temperature logging to N
+minutes. If \',p\' is specified, the setting is preserved across power
+cycles. Otherwise, the setting is volatile and will be reverted to
+default (1 minute), or last non-volatile setting by the next hard reset.
+This command also clears the temperature history table. See
+\'\-l scttemp\' above for more information about SCT temperature logging.
 
 .TP
 .B \-C, \-\-captive
@@ -1264,16 +1472,67 @@ 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 ATA, SCSI command sets and SAT
+In the past there has been a clear distinction between storage devices
+that used the ATA and SCSI command sets. This distinction was often
+reflected in their device naming and hardware. Now various SCSI
+transports (e.g. SAS, FC and iSCSI) can interconnect to both SCSI
+disks (e.g. FC and SAS) and ATA disks (especially SATA). USB and
+IEEE 1394 storage devices use the SCSI command set externally but
+almost always contain ATA or SATA disks (or flash). The storage
+subsystems in some operating systems have started to remove the
+distinction between ATA and SCSI in their device naming policies.
+.PP
+99% of operations that an OS performs on a disk involve the SCSI INQUIRY,
+READ CAPACITY, READ and WRITE commands, or their ATA equivalents. Since
+the SCSI commands are slightly more general than their ATA equivalents,
+many OSes are generating SCSI commands (mainly READ and WRITE) and
+letting a lower level translate them to their ATA equivalents as the
+need arises. An important note here is that "lower level" may be in
+external equipment and hence outside the control of an OS.
+.PP
+SCSI to ATA Translation (SAT) is a standard (ANSI INCITS 431-2007) that
+specifies how this translation is done. For the other 1% of operations
+that an OS performs on a disk, SAT provides two options. First is an
+optional ATA PASS-THROUGH SCSI command (there are two variants). The
+second is a translation from the closest SCSI command. Most current
+interest is in the "pass-through" option.
+.PP
+The relevance to smartmontools (and hence smartctl) is that its
+interactions with disks fall solidly into the "1%" category. So even
+if the OS can happily treat (and name) a disk as "SCSI", smartmontools
+needs to detect the native command set and act accordingly.
+As more storage manufacturers (including external SATA drives) comply
+with SAT, smartmontools is able to automatically distinguish the native
+command set of the device. In some cases the '\-d sat' option is needed
+on the command line.
+.PP
+There are also virtual disks which typically have no useful information
+to convey to smartmontools, but could conceivably in the future. An
+example of a virtual disk is the OS's view of a RAID 1 box. There are
+most likely two SATA disks inside a RAID 1 box. Addressing those SATA
+disks from a distant OS is a challenge for smartmontools. Another
+approach is running a tool like smartmontools inside the RAID 1 box (e.g.
+a Network Attached Storage (NAS) box) and fetching the logs via a
+browser. 
+.PP
 .SH EXAMPLES
 .nf
 .B smartctl \-a /dev/hda
 .fi
-Print all SMART information for drive /dev/hda (Primary Master).
+Print a large amount of SMART information for drive /dev/hda which is
+typically an ATA (IDE) or SATA disk in Linux.
+.PP
+.nf
+.B smartctl \-a /dev/sdb
+.fi
+Print a large amount of SMART information for drive /dev/sda . This may
+be a SCSI disk or an ATA (SATA) disk.
 .PP
 .nf
 .B smartctl \-s off /dev/hdd
 .fi
-Disable SMART on drive /dev/hdd (Secondary Slave).
+Disable SMART monitoring and data log collection on drive /dev/hdd .
 .PP
 .nf
 .B smartctl \-\-smart=on \-\-offlineauto=on \-\-saveauto=on /dev/hda
@@ -1346,14 +1605,22 @@ 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
+.B smartctl \-t long \-d areca,4 /dev/sg2
+.fi
+Start a long self\-test on the fourth SATA disk connected to an Areca RAID
+controller addressed by /dev/sg2.
+.PP
+.nf
+.B smartctl \-a \-d hpt,1/3 /dev/sda    (under Linux)
+.B smartctl \-a \-d hpt,1/3 /dev/hptrr    (under FreeBSD)
 .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
+.B smartctl \-t short \-d hpt,1/1/2 /dev/sda    (under Linux)
+.B smartctl \-t short \-d hpt,1/1/2 /dev/hptrr    (under FreeBSD)
 .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.
@@ -1438,18 +1705,20 @@ University of Wisconsin \- Milwaukee Physics Department
 The following have made large contributions to smartmontools:
 .nf
 \fBCasper Dik\fP (Solaris SCSI interface)
-\fBChristian Franke\fP (Windows interface and Cygwin package)
+\fBChristian Franke\fP (Windows interface, C++ redesign, USB support, ...)
 \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)
+\fBGabriele Pohl\fP (Web site and Wiki, conversion from CVS to SVN)
 \fBKeiji Sawada\fP (Solaris ATA interface)
+\fBManfred Schwarb\fP (Drive database)
 \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)
-\fBShengfeng Zhou\fP (Linux Highpoint RocketRaid interface)
+\fBShengfeng Zhou\fP (Linux/FreeBSD HighPoint RocketRAID interface)
 .fi
 Many other individuals have made smaller contributions and corrections.
 
@@ -1499,8 +1768,8 @@ these documents may be found in the References section of the
 \fBhttp://smartmontools.sourceforge.net/\fP .
 
 .SH
-CVS ID OF THIS PAGE:
-$Id: smartctl.8.in,v 1.105 2008/03/04 22:09:47 ballen4705 Exp $
+SVN ID OF THIS PAGE:
+$Id: smartctl.8.in 2855 2009-07-21 19:55:08Z chrfranke $
 .\" Local Variables:            
 .\" mode: nroff         
 .\" End:
index 779a79c1b6200ba147d8d2203a88e2b523b1918b..549dd957c9f06168f266f63096aa5cc7695db9d6 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-9 Christian Franke <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
 #include <sys/types.h>
 #include <string.h>
 #include <stdarg.h>
+#include <stdexcept>
+#include <getopt.h>
 
 #include "config.h"
-#ifdef HAVE_GETOPT_LONG
-#include <getopt.h>
-#endif
-#if defined(__FreeBSD_version) && (__FreeBSD_version < 500000)
+
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
 
-#if defined(__QNXNTO__) 
-#include <unistd.h>
+#if defined(__FreeBSD__)
+#include <sys/param.h>
 #endif
 
+#if defined(__QNXNTO__) 
+#include <new> // TODO: Why is this include necessary on QNX ?
+#endif
 
 #include "int64.h"
 #include "atacmds.h"
+#include "dev_interface.h"
 #include "ataprint.h"
 #include "extern.h"
 #include "knowndrives.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.169 2008/03/04 22:09:47 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;
+const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 2854 2009-07-21 19:23:20Z chrfranke $"
+                                  CONFIG_H_CVSID EXTERN_H_CVSID SMARTCTL_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-8 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(){
-  const 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;
+static void printslogan()
+{
+  pout("%s\n", format_version_info("smartctl").c_str());
 }
 
 void UsageSummary(){
@@ -119,12 +73,13 @@ void UsageSummary(){
   return;
 }
 
+static const char *getvalidarglist(char opt);
+
 /*  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(
+"============================================ SHOW INFORMATION OPTIONS =====\n\n"
 "  -h, --help, --usage\n"
 "         Display this help and exit\n\n"
 "  -V, --version, --copyright, --license\n"
@@ -133,22 +88,15 @@ void Usage (void){
 "         Show identity information for device\n\n"
 "  -a, --all                                                        \n"
 "         Show all SMART information for device\n\n"
+"  -x, --xall\n"
+"         Show all 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(
+"================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n"
 "  -q TYPE, --quietmode=TYPE                                           (ATA)\n"
 "         Set smartctl quiet mode to one of: errorsonly, silent, noserial\n\n"
 "  -d TYPE, --device=TYPE\n"
-"         Specify device type to one of: ata, scsi, marvell, sat, 3ware,N\n\n"
+"         Specify device type to one of: %s\n\n"
 "  -T TYPE, --tolerance=TYPE                                           (ATA)\n"
 "         Tolerance: normal, conservative, permissive, verypermissive\n\n"
 "  -b TYPE, --badsum=TYPE                                              (ATA)\n"
@@ -156,22 +104,10 @@ void Usage (void){
 "  -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"
-"                                               noserial\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
+"         No check if: never, sleep, standby, idle (see man page)\n\n",
+  getvalidarglist('d')); // TODO: Use this function also for other options ?
   printf(
+"============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n"
 "  -s VALUE, --smart=VALUE\n"
 "        Enable/disable SMART on device (on/off)\n\n"
 "  -o VALUE, --offlineauto=VALUE                                       (ATA)\n"
@@ -179,16 +115,8 @@ void Usage (void){
 "  -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(
+"======================================= READ AND DISPLAY DATA OPTIONS =====\n\n"
 "  -H, --health\n"
 "        Show device SMART health status\n\n"
 "  -c, --capabilities                                                  (ATA)\n"
@@ -196,8 +124,11 @@ void Usage (void){
 "  -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, scttemp[sts,hist]\n\n"
+"        Show device log. TYPE: error, selftest, selective, directory[,g|s],\n"
+"                               background, sasphy[,reset], sataphy[,reset],\n"
+"                               scttemp[sts,hist],\n"
+"                               gplog,N[,RANGE], smartlog,N[,RANGE],\n"
+"                               xerror[,N][,error], xselftest[,N][,selftest]\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"
@@ -205,23 +136,15 @@ void Usage (void){
 "                                     samsung3, swapid\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, scttemp[sts,hist]\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"
-"                                         samsung3, swapid\n"
-"  -P TYPE   Drive-specific presets: use, ignore, show, showall         (ATA)\n\n"
-  );
+"  -B [+]FILE, --drivedb=[+]FILE                                       (ATA)\n"
+"        Read and replace [add] drive database from FILE\n"
+#ifdef SMARTMONTOOLS_DRIVEDBDIR
+"        [default is "SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h]\n"
 #endif
-  printf("============================================ DEVICE SELF-TEST OPTIONS =====\n\n");
-#ifdef HAVE_GETOPT_LONG
+"\n"
+  );
   printf(
+"============================================ DEVICE SELF-TEST OPTIONS =====\n\n"
 "  -t TEST, --test=TEST\n"
 "        Run test. TEST: offline short long conveyance select,M-N\n"
 "                        pending,N afterselect,[on|off] scttempint,N[,p]\n\n"
@@ -230,26 +153,24 @@ void Usage (void){
 "  -X, --abort\n"
 "        Abort any non-captive test on device\n\n"
 );
-#else
-  printf(
-"  -t TEST   Run test. TEST: offline short long conveyance select,M-N\n"
-"                            pending,N afterselect,[on|off] scttempint,N[,p]\n"
-"  -C        Do test in captive mode (along with -t)\n"
-"  -X        Abort any non-captive test\n\n"
-  );
-#endif
-  print_smartctl_examples();
-  return;
+  const char * examples = smi()->get_app_examples("smartctl");
+  if (examples)
+    printf("%s\n", examples);
 }
 
 /* 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) {
+static const char *getvalidarglist(char opt)
+{
   switch (opt) {
   case 'q':
     return "errorsonly, silent, noserial";
   case 'd':
-    return "ata, scsi, marvell, sat, 3ware,N, hpt,L/M/N cciss,N";
+    { // TODO: let this function return std::string ?
+      static std::string buf = smi()->get_valid_dev_types_str();
+      buf += ", test";
+      return buf.c_str();
+    }
   case 'T':
     return "normal, conservative, permissive, verypermissive";
   case 'b':
@@ -261,7 +182,9 @@ const char *getvalidarglist(char opt) {
   case 'S':
     return "on, off";
   case 'l':
-    return "error, selftest, selective, directory, background, scttemp[sts|hist]";
+    return "error, selftest, selective, directory[,g|s], background, scttemp[sts|hist], "
+           "sasphy[,reset], sataphy[,reset], gplog,N[,RANGE], smartlog,N[,RANGE], "
+          "xerror[,N][,error], xselftest[,N][,selftest]";
   case 'P':
     return "use, ignore, show, showall";
   case 't':
@@ -279,45 +202,36 @@ const char *getvalidarglist(char opt) {
 /* 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);
+    pout("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n",
+         create_vendor_attribute_arg_list().c_str());
   }
   else {
   // getvalidarglist() might produce a multiline or single line string.  We
   // need to figure out which to get the formatting right.
+    const char * s = getvalidarglist(opt);
     char separator = strchr(s, '\n') ? '\n' : ' ';
-    pout("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, (char *)s, separator);
+    pout("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, s, separator);
   }
 
   return;
 }
 
+// Checksum error mode
+enum checksum_err_mode_t {
+  CHECKSUM_ERR_WARN, CHECKSUM_ERR_EXIT, CHECKSUM_ERR_IGNORE
+};
+
+static checksum_err_mode_t checksum_err_mode = CHECKSUM_ERR_WARN;
+
 /*      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];
+const char * parse_options(int argc, char** argv,
+                           ata_print_options & ataopts,
+                           scsi_print_options & scsiopts)
+{
   // 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;
+  const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iaxv:P:t:CXF:n:B:";
   // Please update getvalidarglist() if you edit longopts
   struct option longopts[] = {
     { "help",            no_argument,       0, 'h' },
@@ -339,6 +253,7 @@ void ParseOpts (int argc, char** argv){
     { "log",             required_argument, 0, 'l' },
     { "info",            no_argument,       0, 'i' },
     { "all",             no_argument,       0, 'a' },
+    { "xall",            no_argument,       0, 'x' },
     { "vendorattribute", required_argument, 0, 'v' },
     { "presets",         required_argument, 0, 'P' },
     { "test",            required_argument, 0, 't' },
@@ -346,188 +261,73 @@ void ParseOpts (int argc, char** argv){
     { "abort",           no_argument,       0, 'X' },
     { "firmwarebug",     required_argument, 0, 'F' },
     { "nocheck",         required_argument, 0, 'n' },
+    { "drivedb",         required_argument, 0, 'B' },
     { 0,                 0,                 0, 0   }
   };
-#endif
-  
+
+  char extraerror[256];
   memset(extraerror, 0, sizeof(extraerror));
   memset(con,0,sizeof(*con));
-  con->testcase=-1;
   opterr=optopt=0;
-  badarg = captive = FALSE;
-  
+
+  const char * type = 0; // set to -d optarg
+  bool no_defaultdb = false; // set true on '-B FILE'
+  bool badarg = false, captive = false;
+  int testcnt = 0; // number of self-tests requested
+
+  int optchar;
+  char *arg;
+
   // 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);
+      con->dont_print = false;
+      pout("%s", format_version_info("smartctl", true /*full*/).c_str());
+      EXIT(0);
       break;
     case 'q':
       if (!strcmp(optarg,"errorsonly")) {
-        con->printing_switchable     = TRUE;
-        con->dont_print = FALSE;
+        con->printing_switchable = true;
+        con->dont_print = false;
       } else if (!strcmp(optarg,"silent")) {
-        con->printing_switchable     = FALSE;
-        con->dont_print = TRUE;
+        con->printing_switchable = false;
+        con->dont_print = true;
       } else if (!strcmp(optarg,"noserial")) {
-        con->dont_print_serial = TRUE;
+        con->dont_print_serial = true;
       } else {
-        badarg = TRUE;
+        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)) {
-            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>31) {
-                 sprintf(extraerror, "Option -d 3ware,N (N=%d) must have 0 <= N <= 31\n", i);
-                 badarg = TRUE;
-            } else {
-               // NOTE: controller_port == disk number + 1
-               con->controller_type = CONTROLLER_3WARE;
-                 con->controller_port = i+1;
-            }
-           free(s);
-        } else if (!strncmp(s,"cciss,",6)) {
-             if (split_report_arg2(s, &i)) {
-                 sprintf(extraerror, "Option -d cciss,N requires N to be a non-negative integer\n");
-                 badarg = TRUE;
-             } else if (i<0 || i>127) {
-                 sprintf(extraerror, "Option -d cciss,N (N=%d) must have 0 <= N <= 127\n", i);
-                 badarg = TRUE;
-             } else {
-               // NOTE: controller_port == drive number
-               con->controller_type = CONTROLLER_CCISS;
-               con->controller_port = i+1;
-             }
-             free(s);
-        } else
-           badarg=TRUE;
-      }
+      type = optarg;
       break;
     case 'T':
       if (!strcmp(optarg,"normal")) {
-        con->conservative = FALSE;
+        con->conservative = false;
         con->permissive   = 0;
       } else if (!strcmp(optarg,"conservative")) {
-        con->conservative = TRUE;
+        con->conservative = true;
       } else if (!strcmp(optarg,"permissive")) {
         if (con->permissive<0xff)
           con->permissive++;
       } else if (!strcmp(optarg,"verypermissive")) {
-        con->permissive=0xff;
+        con->permissive = 0xff;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'b':
       if (!strcmp(optarg,"warn")) {
-        con->checksumfail   = FALSE;
-        con->checksumignore = FALSE;
+        checksum_err_mode = CHECKSUM_ERR_WARN;
       } else if (!strcmp(optarg,"exit")) {
-        con->checksumfail   = TRUE;
-        con->checksumignore = FALSE;
+        checksum_err_mode = CHECKSUM_ERR_EXIT;
       } else if (!strcmp(optarg,"ignore")) {
-        con->checksumignore = TRUE;
-        con->checksumfail   = FALSE;
+        checksum_err_mode = CHECKSUM_ERR_IGNORE;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'r':
@@ -538,13 +338,10 @@ void ParseOpts (int argc, char** argv){
         // 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);
+          throw std::bad_alloc();
         }
         if (split_report_arg(s, &i)) {
-          badarg = TRUE;
+          badarg = true;
         } else if (!strcmp(s,"ioctl")) {
           con->reportataioctl  = con->reportscsiioctl = i;
         } else if (!strcmp(s,"ataioctl")) {
@@ -552,132 +349,213 @@ void ParseOpts (int argc, char** argv){
         } else if (!strcmp(s,"scsiioctl")) {
           con->reportscsiioctl = i;
         } else {
-          badarg = TRUE;
+          badarg = true;
         }
         free(s);
       }
       break;
     case 's':
       if (!strcmp(optarg,"on")) {
-        con->smartenable  = TRUE;
-        con->smartdisable = FALSE;
+        ataopts.smart_enable  = scsiopts.smart_enable  = true;
+        ataopts.smart_disable = scsiopts.smart_disable = false;
       } else if (!strcmp(optarg,"off")) {
-        con->smartdisable = TRUE;
-        con->smartenable  = FALSE;
+        ataopts.smart_disable = scsiopts.smart_disable = true;
+        ataopts.smart_enable  = scsiopts.smart_enable  = false;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'o':
       if (!strcmp(optarg,"on")) {
-        con->smartautoofflineenable  = TRUE;
-        con->smartautoofflinedisable = FALSE;
+        ataopts.smart_auto_offl_enable  = true;
+        ataopts.smart_auto_offl_disable = false;
       } else if (!strcmp(optarg,"off")) {
-        con->smartautoofflinedisable = TRUE;
-        con->smartautoofflineenable  = FALSE;
+        ataopts.smart_auto_offl_disable = true;
+        ataopts.smart_auto_offl_enable  = false;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'S':
       if (!strcmp(optarg,"on")) {
-        con->smartautosaveenable  = TRUE;
-        con->smartautosavedisable = FALSE;
+        ataopts.smart_auto_save_enable  = scsiopts.smart_auto_save_enable  = true;
+        ataopts.smart_auto_save_disable = scsiopts.smart_auto_save_disable = false;
       } else if (!strcmp(optarg,"off")) {
-        con->smartautosavedisable = TRUE;
-        con->smartautosaveenable  = FALSE;
+        ataopts.smart_auto_save_disable = scsiopts.smart_auto_save_disable = true;
+        ataopts.smart_auto_save_enable  = scsiopts.smart_auto_save_enable  = false;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'H':
-      con->checksmart = TRUE;           
+      ataopts.smart_check_status = scsiopts.smart_check_status = true;
       break;
     case 'F':
       if (!strcmp(optarg,"none")) {
-        con->fixfirmwarebug = FIX_NONE;
+        ataopts.fix_firmwarebug = FIX_NONE;
       } else if (!strcmp(optarg,"samsung")) {
-        con->fixfirmwarebug = FIX_SAMSUNG;
+        ataopts.fix_firmwarebug = FIX_SAMSUNG;
       } else if (!strcmp(optarg,"samsung2")) {
-        con->fixfirmwarebug = FIX_SAMSUNG2;
+        ataopts.fix_firmwarebug = FIX_SAMSUNG2;
       } else if (!strcmp(optarg,"samsung3")) {
-        con->fixfirmwarebug = FIX_SAMSUNG3;
+        ataopts.fix_firmwarebug = FIX_SAMSUNG3;
       } else if (!strcmp(optarg,"swapid")) {
-        con->fixswappedid = TRUE;
+        ataopts.fix_swapped_id = true;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'c':
-      con->generalsmartvalues = TRUE;
+      ataopts.smart_general_values = true;
       break;
     case 'A':
-      con->smartvendorattrib = TRUE;
+      ataopts.smart_vendor_attrib = scsiopts.smart_vendor_attrib = true;
       break;
     case 'l':
       if (!strcmp(optarg,"error")) {
-        con->smarterrorlog = TRUE;
+        ataopts.smart_error_log = scsiopts.smart_error_log = true;
       } else if (!strcmp(optarg,"selftest")) {
-        con->smartselftestlog = TRUE;
+        ataopts.smart_selftest_log = scsiopts.smart_selftest_log = true;
       } else if (!strcmp(optarg, "selective")) {
-        con->selectivetestlog = TRUE;
+        ataopts.smart_selective_selftest_log = true;
       } else if (!strcmp(optarg,"directory")) {
-        con->smartlogdirectory = TRUE;
+        ataopts.smart_logdir = ataopts.gp_logdir = true; // SMART+GPL
+      } else if (!strcmp(optarg,"directory,s")) {
+        ataopts.smart_logdir = true; // SMART
+      } else if (!strcmp(optarg,"directory,g")) {
+        ataopts.gp_logdir = true; // GPL
+      } else if (!strcmp(optarg,"sasphy")) {
+        scsiopts.sasphy = true;
+      } else if (!strcmp(optarg,"sasphy,reset")) {
+        scsiopts.sasphy = scsiopts.sasphy_reset = true;
+      } else if (!strcmp(optarg,"sataphy")) {
+        ataopts.sataphy = true;
+      } else if (!strcmp(optarg,"sataphy,reset")) {
+        ataopts.sataphy = ataopts.sataphy_reset = true;
       } else if (!strcmp(optarg,"background")) {
-        con->smartbackgroundlog = TRUE;
+        scsiopts.smart_background_log = true;
       } else if (!strcmp(optarg,"scttemp")) {
-        con->scttempsts = con->scttemphist = TRUE;
+        ataopts.sct_temp_sts = ataopts.sct_temp_hist = true;
       } else if (!strcmp(optarg,"scttempsts")) {
-        con->scttempsts = TRUE;
+        ataopts.sct_temp_sts = true;
       } else if (!strcmp(optarg,"scttemphist")) {
-        con->scttemphist = TRUE;
+        ataopts.sct_temp_hist = true;
+
+      } else if (!strncmp(optarg, "xerror", sizeof("xerror")-1)) {
+        int n1 = -1, n2 = -1, len = strlen(optarg);
+        unsigned val = 8;
+        sscanf(optarg, "xerror%n,error%n", &n1, &n2);
+        if (!(n1 == len || n2 == len)) {
+          n1 = n2 = -1;
+          sscanf(optarg, "xerror,%u%n,error%n", &val, &n1, &n2);
+        }
+        if ((n1 == len || n2 == len) && val > 0) {
+          ataopts.smart_ext_error_log = val;
+          ataopts.retry_error_log = (n2 == len);
+        }
+        else
+          badarg = true;
+
+      } else if (!strncmp(optarg, "xselftest", sizeof("xselftest")-1)) {
+        int n1 = -1, n2 = -1, len = strlen(optarg);
+        unsigned val = 25;
+        sscanf(optarg, "xselftest%n,selftest%n", &n1, &n2);
+        if (!(n1 == len || n2 == len)) {
+          n1 = n2 = -1;
+          sscanf(optarg, "xselftest,%u%n,selftest%n", &val, &n1, &n2);
+        }
+        if ((n1 == len || n2 == len) && val > 0) {
+          ataopts.smart_ext_selftest_log = val;
+          ataopts.retry_selftest_log = (n2 == len);
+        }
+        else
+          badarg = true;
+
+      } else if (   !strncmp(optarg, "gplog,"   , sizeof("gplog,"   )-1)
+                 || !strncmp(optarg, "smartlog,", sizeof("smartlog,")-1)) {
+        unsigned logaddr = ~0U; unsigned page = 0, nsectors = 1; char sign = 0;
+        int n1 = -1, n2 = -1, n3 = -1, len = strlen(optarg);
+        sscanf(optarg, "%*[a-z],0x%x%n,%u%n%c%u%n",
+               &logaddr, &n1, &page, &n2, &sign, &nsectors, &n3);
+        if (len > n2 && n3 == -1 && !strcmp(optarg+n2, "-max")) {
+          nsectors = ~0U; sign = '+'; n3 = len;
+        }
+        bool gpl = (optarg[0] == 'g');
+        const char * erropt = (gpl ? "gplog" : "smartlog");
+        if (!(   n1 == len || n2 == len
+              || (n3 == len && (sign == '+' || sign == '-')))) {
+          sprintf(extraerror, "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] syntax error\n", erropt);
+          badarg = true;
+        }
+        else if (!(    logaddr <= 0xff && page <= (gpl ? 0xffffU : 0x00ffU)
+                   && 0 < nsectors
+                   && (nsectors <= (gpl ? 0xffffU : 0xffU) || nsectors == ~0U)
+                   && (sign != '-' || page <= nsectors)                       )) {
+          sprintf(extraerror, "Option -l %s,ADDR[,FIRST[-LAST|+SIZE]] parameter out of range\n", erropt);
+          badarg = true;
+        }
+        else {
+          ata_log_request req;
+          req.gpl = gpl; req.logaddr = logaddr; req.page = page;
+          req.nsectors = (sign == '-' ? nsectors-page+1 : nsectors);
+          ataopts.log_requests.push_back(req);
+        }
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'i':
-      con->driveinfo = TRUE;
-      break;            
+      ataopts.drive_info = scsiopts.drive_info = 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; */
+      ataopts.drive_info           = scsiopts.drive_info          = true;
+      ataopts.smart_check_status   = scsiopts.smart_check_status  = true;
+      ataopts.smart_general_values = true;
+      ataopts.smart_vendor_attrib  = scsiopts.smart_vendor_attrib = true;
+      ataopts.smart_error_log      = scsiopts.smart_error_log     = true;
+      ataopts.smart_selftest_log   = scsiopts.smart_selftest_log  = true;
+      ataopts.smart_selective_selftest_log = true;
+      /* scsiopts.smart_background_log = true; */
+      break;
+    case 'x':
+      ataopts.drive_info           = scsiopts.drive_info          = true;
+      ataopts.smart_check_status   = scsiopts.smart_check_status  = true;
+      ataopts.smart_general_values = true;
+      ataopts.smart_vendor_attrib  = scsiopts.smart_vendor_attrib = true;
+      ataopts.smart_ext_error_log  = 8;
+      ataopts.retry_error_log      = true;
+      ataopts.smart_ext_selftest_log = 25;
+      ataopts.retry_selftest_log   = true;
+      scsiopts.smart_error_log     = scsiopts.smart_selftest_log    = true;
+      ataopts.smart_selective_selftest_log = true;
+      ataopts.smart_logdir = ataopts.gp_logdir = true;
+      ataopts.sct_temp_sts = ataopts.sct_temp_hist = true;
+      ataopts.sataphy = true;
+      scsiopts.smart_background_log = true;
+      scsiopts.sasphy = true;
       break;
     case 'v':
       // parse vendor-specific definitions of attributes
       if (!strcmp(optarg,"help")) {
-        char *s;
-        con->dont_print=FALSE;
+        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);
+        pout("The valid arguments to -v are:\n\thelp\n%s\n",
+             create_vendor_attribute_arg_list().c_str());
         EXIT(0);
       }
-      charp=con->attributedefs;
-      if (!charp){
-        pout("Fatal internal error in ParseOpts()\n");
-        EXIT(FAILCMD);
-      }
-      if (parse_attribute_def(optarg, &charp))
-        badarg = TRUE;
+      if (parse_attribute_def(optarg, ataopts.attributedefs))
+        badarg = true;
       break;    
     case 'P':
       if (!strcmp(optarg, "use")) {
-        con->ignorepresets = FALSE;
+        ataopts.ignore_presets = false;
       } else if (!strcmp(optarg, "ignore")) {
-        con->ignorepresets = TRUE;
+        ataopts.ignore_presets = true;
       } else if (!strcmp(optarg, "show")) {
-        con->showpresets = TRUE;
+        ataopts.show_presets = true;
       } else if (!strcmp(optarg, "showall")) {
+        if (!no_defaultdb && !read_default_drive_databases())
+          EXIT(FAILCMD);
         if (optind < argc) { // -P showall MODEL [FIRMWARE]
           int cnt = showmatchingpresets(argv[optind], (optind+1<argc ? argv[optind+1] : NULL));
           EXIT(cnt); // report #matches
@@ -686,28 +564,31 @@ void ParseOpts (int argc, char** argv){
           EXIT(FAILCMD); // report regexp syntax error
         EXIT(0);
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 't':
       if (!strcmp(optarg,"offline")) {
-        con->smartexeoffimmediate = TRUE;
-        con->testcase             = OFFLINE_FULL_SCAN;
+        testcnt++;
+        ataopts.smart_selftest_type = OFFLINE_FULL_SCAN;
+        scsiopts.smart_default_selftest = true;
       } else if (!strcmp(optarg,"short")) {
-        con->smartshortselftest = TRUE;
-        con->testcase           = SHORT_SELF_TEST;
+        testcnt++;
+        ataopts.smart_selftest_type = SHORT_SELF_TEST;
+        scsiopts.smart_short_selftest = true;
       } else if (!strcmp(optarg,"long")) {
-        con->smartextendselftest = TRUE;
-        con->testcase            = EXTEND_SELF_TEST;
+        testcnt++;
+        ataopts.smart_selftest_type = EXTEND_SELF_TEST;
+        scsiopts.smart_extend_selftest = true;
       } else if (!strcmp(optarg,"conveyance")) {
-        con->smartconveyanceselftest = TRUE;
-        con->testcase            = CONVEYANCE_SELF_TEST;
+        testcnt++;
+        ataopts.smart_selftest_type = CONVEYANCE_SELF_TEST;
       } else if (!strcmp(optarg,"afterselect,on")) {
-       // scan remainder of disk after doing selected segments
-       con->scanafterselect=2;
+        // scan remainder of disk after doing selected segment
+        ataopts.smart_selective_args.scan_after_select = 2;
       } else if (!strcmp(optarg,"afterselect,off")) {
-       // don't scan remainder of disk after doing selected segments
-       con->scanafterselect=1;
+        // don't scan remainder of disk after doing selected segments
+        ataopts.smart_selective_args.scan_after_select = 1;
       } else if (!strncmp(optarg,"pending,",strlen("pending,"))) {
        // parse number of minutes that test should be pending
        int i;
@@ -716,21 +597,22 @@ void ParseOpts (int argc, char** argv){
        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;
+          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;
+          badarg = true;
        } else {
-         con->pendingtime=i+1;
+          ataopts.smart_selective_args.pending_time = i+1;
        }
       } else if (!strncmp(optarg,"select",strlen("select"))) {
+        testcnt++;
         // parse range of LBAs to test
         uint64_t start, stop; int mode;
         if (split_selective_arg(optarg, &start, &stop, &mode)) {
          sprintf(extraerror, "Option -t select,M-N must have non-negative integer M and N\n");
-          badarg = TRUE;
+          badarg = true;
         } else {
-          if (con->smartselectivenumspans >= 5 || start > stop) {
+          if (ataopts.smart_selective_args.num_spans >= 5 || start > stop) {
             if (start > stop) {
               sprintf(extraerror, "ERROR: Start LBA (%"PRIu64") > ending LBA (%"PRId64") in argument \"%s\"\n",
                 start, stop, optarg);
@@ -738,58 +620,69 @@ void ParseOpts (int argc, char** argv){
               sprintf(extraerror,"ERROR: No more than five selective self-test spans may be"
                 " defined\n");
             }
-           badarg = TRUE;
+            badarg = true;
           }
-          con->smartselectivespan[con->smartselectivenumspans][0] = start;
-          con->smartselectivespan[con->smartselectivenumspans][1] = stop;
-          con->smartselectivemode[con->smartselectivenumspans] = mode;
-          con->smartselectivenumspans++;
-          con->testcase            = SELECTIVE_SELF_TEST;
+          ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].start = start;
+          ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].end   = stop;
+          ataopts.smart_selective_args.span[ataopts.smart_selective_args.num_spans].mode  = mode;
+          ataopts.smart_selective_args.num_spans++;
+          ataopts.smart_selftest_type = SELECTIVE_SELF_TEST;
         }
       } else if (!strncmp(optarg, "scttempint,", sizeof("scstempint,")-1)) {
         unsigned interval = 0; int n1 = -1, n2 = -1, len = strlen(optarg);
         if (!(   sscanf(optarg,"scttempint,%u%n,p%n", &interval, &n1, &n2) == 1
               && 0 < interval && interval <= 0xffff && (n1 == len || n2 == len))) {
             strcpy(extraerror, "Option -t scttempint,N[,p] must have positive integer N\n");
-            badarg = TRUE;
+            badarg = true;
         }
-        con->scttempint = interval;
-        con->scttempintp = (n2 == len);
+        ataopts.sct_temp_int = interval;
+        ataopts.sct_temp_int_pers = (n2 == len);
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'C':
-      captive = TRUE;
+      captive = true;
       break;
     case 'X':
-      con->smartselftestabort = TRUE;
-      con->testcase           = ABORT_SELF_TEST;
+      testcnt++;
+      scsiopts.smart_selftest_abort = true;
+      ataopts.smart_selftest_type = 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
+        ataopts.powermode = 1; // do not skip, but print mode
       else if (!strcmp(optarg, "sleep"))
-        con->powermode = 2;
+        ataopts.powermode = 2;
       else if (!strcmp(optarg, "standby"))
-        con->powermode = 3;
+        ataopts.powermode = 3;
       else if (!strcmp(optarg, "idle"))
-        con->powermode = 4;
+        ataopts.powermode = 4;
       else
-        badarg = TRUE;
+        badarg = true;
+      break;
+    case 'B':
+      {
+        const char * path = optarg;
+        if (*path == '+' && path[1])
+          path++;
+        else
+          no_defaultdb = true;
+        if (!read_drive_database(path))
+          EXIT(FAILCMD);
+      }
       break;
     case 'h':
-      con->dont_print=FALSE;
+      con->dont_print = false;
       printslogan();
       Usage();
       EXIT(0);  
       break;
     case '?':
     default:
-      con->dont_print=FALSE;
+      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.
@@ -805,7 +698,6 @@ void ParseOpts (int argc, char** argv){
         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
@@ -842,12 +734,11 @@ void ParseOpts (int argc, char** argv){
   // print output is switchable, then start with the print output
   // turned off
   if (con->printing_switchable)
-    con->dont_print=TRUE;
+    con->dont_print = false;
 
   // 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;
+  if (testcnt > 1) {
+    con->dont_print = false;
     printslogan();
     pout("\nERROR: smartctl can only run a single test type (or abort) at a time.\n");
     UsageSummary();
@@ -856,10 +747,11 @@ void ParseOpts (int argc, char** argv){
 
   // 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;
+  if (   (ataopts.smart_selective_args.pending_time || ataopts.smart_selective_args.scan_after_select)
+      && !ataopts.smart_selective_args.num_spans) {
+    con->dont_print = false;
     printslogan();
-    if (con->pendingtime)
+    if (ataopts.smart_selective_args.pending_time)
       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");
@@ -868,26 +760,26 @@ void ParseOpts (int argc, char** argv){
   }
 
   // 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;
-  }
+  if (captive)
+    switch (ataopts.smart_selftest_type) {
+      case SHORT_SELF_TEST:
+        ataopts.smart_selftest_type = SHORT_CAPTIVE_SELF_TEST;
+        scsiopts.smart_short_selftest     = false;
+        scsiopts.smart_short_cap_selftest = true;
+        break;
+      case EXTEND_SELF_TEST:
+        ataopts.smart_selftest_type = EXTEND_CAPTIVE_SELF_TEST;
+        scsiopts.smart_extend_selftest     = false;
+        scsiopts.smart_extend_cap_selftest = true;
+        break;
+      case CONVEYANCE_SELF_TEST:
+        ataopts.smart_selftest_type = CONVEYANCE_CAPTIVE_SELF_TEST;
+        break;
+      case SELECTIVE_SELF_TEST:
+        ataopts.smart_selftest_type = SELECTIVE_CAPTIVE_SELF_TEST;
+        break;
+    }
+
   // From here on, normal operations...
   printslogan();
   
@@ -907,7 +799,13 @@ void ParseOpts (int argc, char** argv){
       pout("%s\n",argv[optind+i]);
     UsageSummary();
     EXIT(FAILCMD);
-  }  
+  }
+
+  // Read or init drive database
+  if (!no_defaultdb && !read_default_drive_databases())
+    EXIT(FAILCMD);
+
+  return type;
 }
 
 // Printing function (controlled by global con->dont_print) 
@@ -949,113 +847,152 @@ void PrintOut(int priority, const char *fmt, ...) {
   return;
 }
 
+// Used to warn users about invalid checksums. Called from atacmds.cpp.
+// Action to be taken may be altered by the user.
+void checksumwarning(const char * string)
+{
+  // user has asked us to ignore checksum errors
+  if (checksum_err_mode == CHECKSUM_ERR_IGNORE)
+    return;
 
-/* Main Program */
-int main (int argc, char **argv){
-  int fd,retval=0;
-  char *device;
-  smartmonctrl control;
-  char *mode=NULL;
+  pout("Warning! %s error: invalid SMART checksum.\n", string);
 
-  // define control block for external functions
-  con=&control;
+  // user has asked us to fail on checksum errors
+  if (checksum_err_mode == CHECKSUM_ERR_EXIT)
+    EXIT(FAILSMART);
+}
 
-  // Part input arguments
-  ParseOpts(argc,argv);
+// Return info string about device protocol
+static const char * get_protocol_info(const smart_device * dev)
+{
+  switch ((int)dev->is_ata() | ((int)dev->is_scsi() << 1)) {
+    case 0x1: return "ATA";
+    case 0x2: return "SCSI";
+    case 0x3: return "ATA+SCSI";
+    default:  return "Unknown";
+  }
+}
 
-  device = argv[argc-1];
+// Main program without exception handling
+int main_worker(int argc, char **argv)
+{
+  // Initialize interface
+  smart_interface::init();
+  if (!smi())
+    return 1;
+
+  int retval = 0;
+
+  smart_device * dev = 0;
+  try {
+    // define control block for external functions
+    smartmonctrl control;
+    con=&control;
+
+    // Parse input arguments
+    ata_print_options ataopts;
+    scsi_print_options scsiopts;
+    const char * type = parse_options(argc, argv, ataopts, scsiopts);
+
+    // '-d test' -> Report result of autodetection
+    bool print_type_only = (type && !strcmp(type, "test"));
+    if (print_type_only)
+      type = 0;
+
+    const char * name = argv[argc-1];
+
+    if (!strcmp(name,"-")) {
+      // Parse "smartctl -r ataioctl,2 ..." output from stdin
+      if (type || print_type_only) {
+        pout("Smartctl: -d option is not allowed in conjunction with device name \"-\".\n");
+        UsageSummary();
+        return FAILCMD;
+      }
+      dev = get_parsed_ata_device(smi(), name);
+    }
+    else
+      // get device of appropriate type
+      dev = smi()->get_smart_device(name, type);
 
-  // Device name "-": Parse "smartctl -r ataioctl,2 ..." output
-  if (!strcmp(device,"-")) {
-    if (con->controller_type != CONTROLLER_UNKNOWN) {
-      pout("Smartctl: -d option is not allowed in conjunction with device name \"-\".\n");
+    if (!dev) {
+      pout("%s: %s\n", name, smi()->get_errmsg());
+      if (type)
+        printvalidarglistmessage('d');
+      else
+        pout("Smartctl: please specify device type with the -d option.\n");
       UsageSummary();
       return FAILCMD;
     }
-    con->controller_type = CONTROLLER_PARSEDEV;
-  }
 
-  // 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 (print_type_only)
+      // Report result of first autodetection
+      pout("%s: Device of type '%s' [%s] detected\n",
+           dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev));
+
+    // Open device
+    {
+      // Save old info
+      smart_device::device_info oldinfo = dev->get_info();
+
+      // Open with autodetect support, may return 'better' device
+      dev = dev->autodetect_open();
+
+      // Report if type has changed
+      if ((type || print_type_only) && oldinfo.dev_type != dev->get_dev_type())
+        pout("%s: Device open changed type from '%s' to '%s'\n",
+          dev->get_info_name(), oldinfo.dev_type.c_str(), dev->get_dev_type());
+    }
+    if (!dev->is_open()) {
+      pout("Smartctl open device: %s failed: %s\n", dev->get_info_name(), dev->get_errmsg());
+      delete dev;
+      return FAILDEV;
+    }
+
+    // now call appropriate ATA or SCSI routine
+    if (print_type_only)
+      pout("%s: Device of type '%s' [%s] opened\n",
+           dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev));
+    else if (dev->is_ata())
+      retval = ataPrintMain(dev->to_ata(), ataopts);
+    else if (dev->is_scsi())
+      retval = scsiPrintMain(dev->to_scsi(), scsiopts);
+    else
+      // we should never fall into this branch!
+      pout("%s: Neither ATA nor SCSI device\n", dev->get_info_name());
+
+    dev->close();
+    delete dev;
+  }
+  catch (...) {
+    delete dev;
+    throw;
   }
+  return retval;
+}
 
-  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;
+
+// Main program
+int main(int argc, char **argv)
+{
+  int status;
+  try {
+    // Do the real work ...
+    status = main_worker(argc, argv);
   }
-  
-  // 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;
-  case CONTROLLER_CCISS:
-    mode="CCISS";
-    break;
-  default:
-    mode="ATA";
-    break;
+  catch (int ex) {
+    // EXIT(status) arrives here
+    status = ex;
   }
-  
-  // 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).
-  if (con->controller_type != CONTROLLER_PARSEDEV)
-    fd = deviceopen(device, mode);
-  else
-    fd = parsedev_open(device);
-  if (fd<0) {
-    char errmsg[256];
-    snprintf(errmsg,256,"Smartctl open device: %s failed",argv[argc-1]);
-    errmsg[255]='\0';
-    syserror(errmsg);
-    return FAILDEV;
+  catch (const std::bad_alloc & /*ex*/) {
+    // Memory allocation failed (also thrown by std::operator new)
+    pout("Smartctl: Out of memory\n");
+    status = FAILCMD;
   }
-
-  // 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;
-  case CONTROLLER_CCISS:
-    // route the cciss command through scsiPrintMain. 
-    // cciss pass-throughs will separeate from the SCSI data-path.
-    retval = scsiPrintMain(fd);
-    break;
-  default:
-    retval = ataPrintMain(fd);
-    break;
+  catch (const std::exception & ex) {
+    // Other fatal errors
+    pout("Smartctl: Exception: %s\n", ex.what());
+    status = FAILCMD;
   }
-  
-  if (con->controller_type != CONTROLLER_PARSEDEV)
-    deviceclose(fd);
-  else
-    parsedev_close(fd);
-
-  return retval;
+  return status;
 }
+
index 781062a499c32d00077d94e5666090f222e88122..51686e2fd28a3762dfb78056b5b92f28271457f5 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 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
 #ifndef SMARTCTL_H_
 #define SMARTCTL_H_
 
-#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.25 2008/03/04 22:09:47 ballen4705 Exp $\n"
-
-/* Boolean Values */
-#define TRUE 0x01
-#define FALSE 0x00
+#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.27 2009/06/20 19:11:04 chrfranke Exp $\n"
 
 // Return codes (bitmask)
 
@@ -72,6 +68,7 @@
 #define OPTIONAL_CMD 1
 #define MANDATORY_CMD 2
 
-void print_smartctl_examples();
+// Moved to C++ interface
+//void print_smartctl_examples();
 
 #endif
index b59db84ee40a9797348c4946bab6c53b1bdaae08..011bff98f87a1d8e96ca2170cd6778df4a6d41ec 100644 (file)
@@ -1,7 +1,7 @@
 .ig
 Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
  
-$Id: smartd.8.in,v 1.121 2008/03/04 22:09:47 ballen4705 Exp $
+$Id: smartd.8.in 2870 2009-08-02 20:38:30Z manfred99 $
 
 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
@@ -128,9 +128,40 @@ below).
 OPTIONS
 Long options are not supported on all systems.  Use \fB\'smartd
 \-h\'\fP to see the available options.
+
 .TP
-.B \-c FILE, \-\-configfile=FILE
+.B \-A PREFIX, \-\-attributelog=PREFIX
+[NEW EXPERIMENTAL SMARTD FEATURE] [ATA ONLY]
+Writes \fBsmartd\fP attribute information (normalized and raw attribute values)
+to files \'PREFIX\'\'MODEL\-SERIAL.ata.csv\'. At each check cycle attributes
+are logged as a line of semicolon separated triplets of the form
+"attribute-ID;attribute-norm-value;attribute-raw-value;". Each line is
+led by a date string of the form "yyyy-mm-dd HH:MM:SS" (in UTC).
+
+.\" BEGIN ENABLE_ATTRIBUTELOG
+If this option is not specified, attribute information is written to files
+\'/usr/local/var/lib/smartmontools/attrlog.MODEL\-SERIAL.ata.csv\'.
+To disable attribute log files, specify this option with an empty string
+argument: \'-A ""\'.
+.\" END ENABLE_ATTRIBUTELOG
+MODEL and SERIAL are build from drive identify information, invalid
+characters are replaced by underline.
+
+If the PREFIX has the form \'/path/dir/\' (e.g. \'/var/lib/smartd/\'), then
+files \'MODEL\-SERIAL.ata.csv\' are created in directory \'/path/dir\'.
+If the PREFIX has the form \'/path/name\' (e.g. \'/var/lib/misc/attrlog\-\'),
+then files 'nameMODEL\-SERIAL.ata.csv' are created in directory '/path/'.
+The path must be absolute, except if debug mode is enabled.
 
+.TP
+.B \-B [+]FILE, \-\-drivedb=[+]FILE
+[NEW EXPERIMENTAL SMARTD FEATURE] Read the drive database from FILE.
+The new database replaces the built in database by default. If \'+\' is
+specified, then the new entries prepend the built in entries.
+Please see the \fBsmartctl\fP man page for further details.
+
+.TP
+.B \-c FILE, \-\-configfile=FILE
 Read \fBsmartd\fP configuration Directives from FILE, instead of from
 the default location \fB/usr/local/etc/smartd.conf\fP (Windows: \fB./smartd.conf\fP).
 If FILE does \fBnot\fP exist, then \fBsmartd\fP will print an error
@@ -168,6 +199,7 @@ appear in the configuration file following the device name.
 .TP
 .B \-h, \-\-help, \-\-usage
 Prints usage message to STDOUT and exits.
+
 .TP
 .B \-i N, \-\-interval=N
 Sets the interval between disk checks to \fIN\fP seconds, where
@@ -353,6 +385,36 @@ comma then the integer with no spaces.  For example, \fIataioctl,2\fP
 The default level is 1, so \'\-r ataioctl,1\' and \'\-r ataioctl\' are
 equivalent.
 
+.TP
+.B \-s PREFIX, \-\-savestates=PREFIX
+[NEW EXPERIMENTAL SMARTD FEATURE] [ATA ONLY]
+Reads/writes \fBsmartd\fP state information from/to files
+\'PREFIX\'\'MODEL\-SERIAL.ata.state\'. This preserves SMART attributes, drive
+min and max temperatures (\-W directive), info about last sent warning email
+(\-m directive), and the time of next check of the self-test REGEXP
+(\-s directive) across boot cycles.
+
+.\" BEGIN ENABLE_SAVESTATES
+If this option is not specified, state information is maintained in files
+\'/usr/local/var/lib/smartmontools/smartd.MODEL\-SERIAL.ata.state\'.
+To disable state files, specify this option with an empty string
+argument: \'-s ""\'.
+.\" END ENABLE_SAVESTATES
+MODEL and SERIAL are build from drive identify information, invalid
+characters are replaced by underline.
+
+If the PREFIX has the form \'/path/dir/\' (e.g. \'/var/lib/smartd/\'), then
+files \'MODEL\-SERIAL.ata.state\' are created in directory \'/path/dir\'.
+If the PREFIX has the form \'/path/name\' (e.g. \'/var/lib/misc/smartd\-\'),
+then files 'nameMODEL\-SERIAL.ata.state' are created in directory '/path/'.
+The path must be absolute, except if debug mode is enabled.
+
+The state information files are read on smartd startup. The files are
+always (re)written after reading the configuration file, before rereading
+the configuration file (SIGHUP), before smartd shutdown, and after a check
+forced by SIGUSR1. After a normal check cycle, a file is only rewritten if
+an important change (which usually results in a SYSLOG output) occurred.
+
 .TP
 .B \-\-service
 Cygwin and Windows only: Enables \fBsmartd\fP to run as a Windows service.
@@ -367,10 +429,9 @@ See NOTES below for details.
 
 .TP
 .B \-V, \-\-version, \-\-license, \-\-copyright
-Prints license, copyright, and CVS version information onto
-STDOUT and then exits. Please include this information if you are
-reporting bugs, or have specific questions about the behavior of
-\fBsmartd\fP.
+Prints version, copyright, license, home page and SVN revision
+information for your copy of \fBsmartd\fP to STDOUT and then exits.
+Please include this information if you are reporting bugs or problems.
 
 .SH EXAMPLES
 
@@ -497,10 +558,11 @@ Section below!
 .B # /usr/local/etc/smartd.conf for monitoring three
 .B # ATA disks, three SCSI disks, six ATA disks
 .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 # directly connected to the HighPoint Rocket-
+.B # RAID controller, two SATA disks connected to
+.B # the HighPoint RocketRAID controller via a pmport
+.B # device, four SATA disks connected to an Areca
+.B # RAID controller, and one SATA disk.
 .B #
 .nf
 .B # First ATA disk on two different interfaces. On
@@ -532,6 +594,15 @@ Section below!
 .B \ \ /dev/sda -a -d sat
 .B #
 .nf
+.B # Three disks connected to a MegaRAID controller
+.B # Start short self-tests daily between 1-2, 2-3, and
+.B # 3-4 am.
+.B \ \ /dev/sda -d megaraid,0 -a -s S/../.././01
+.B \ \ /dev/sda -d megaraid,1 -a -s S/../.././02
+.B \ \ /dev/sda -d megaraid,2 -a -s S/../.././03
+.B
+.B #
+.nf
 .B # Four ATA disks on a 3ware 6/7/8000 controller.
 .B # Start short self-tests daily between midnight and 1am,
 .B # 1-2, 2-3, and 3-4 am.  Starting with the Linux 2.6
@@ -551,19 +622,36 @@ 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 # 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 # under Linux
 .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 # or under FreeBSD
+.B # /dev/hptrr -d hpt,1/1 -a -s S/../.././01
+.B # /dev/hptrr -d hpt,1/2 -a -s S/../.././02
+.B # /dev/hptrr -d hpt,1/3 -a -s S/../.././03
 .B #
 .nf
-.B # Two SATA disks connected to a highpoint rocketraid 
+.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 # under Linux
 .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 # or under FreeBSD
+.B # /dev/hptrr -d hpt,1/4/1 -a -s L/../../7/00
+.B # /dev/hptrr -d hpt,1/4/2 -a -s L/../../7/02
+.B #
+.nf
+.B # Three SATA disks connected to an Areca
+.B # RAID controller. Start long self-tests Sundays
+.B # between midnight and 3 am.
+.B \ \ /dev/sg2 -d areca,1 -a -s L/../../7/00
+.B \ \ /dev/sg2 -d areca,2 -a -s L/../../7/01
+.B \ \ /dev/sg2 -d areca,3 -a -s L/../../7/02
 .B #
 .nf
 .B # The following line enables monitoring of the 
@@ -630,12 +718,22 @@ or /dev/twa?) must be listed, along with the \'\-d 3ware,N\' Directive
 appear to \fBsmartd\fP as normal ATA devices.  Hence all the ATA
 directives can be used for these disks (but see note below).
 
+.B If an Areca controller is used
+then the corresponding SCSI generic device (/dev/sg?)  must be listed,
+along with the \'\-d areca,N\' Directive (see below).  The individual
+SATA disks hosted by the Areca controller appear to \fBsmartd\fP as
+normal ATA devices.  Hence all the ATA directives can be used for
+these disks.  Areca firmware version 1.46 or later which supports
+smartmontools must be used; Please see the \fBsmartctl\fP man page for
+further details.
+
 .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, \fIsat\fP,
-\fImarvell\fP, \fIcciss,N\fP and \fI3ware,N\fP are mutually-exclusive. If more
-than one is given then \fBsmartd\fP will use the last one which appears.
+times for one device, but the arguments \fIata\fP, \fIscsi\fP,
+\fIsat\fP, \fImarvell\fP, \fIcciss,N\fP, \fIareca,N\fP, \fImegaraid,N\fP
+and \fI3ware,N\fP are mutually-exclusive. If more than one is given then
+\fBsmartd\fP will use the last one which appears.
 
 If none of these three arguments is given, then \fBsmartd\fP will
 first attempt to guess the device type by looking at whether the sixth
@@ -674,12 +772,19 @@ be overridden with this syntax: \'\-d sat,12\' or \'\-d sat,16\'.
 \- Under Linux, interact with SATA disks behind Marvell chip-set
 controllers (using the Marvell rather than libata driver).
 
+.I megaraid,N
+\- the device consists of one or more SCSI/SAS/SATA disks connected
+to a MegaRAID controller.  The non-negative integer N (in the range
+of 0 to 127 inclusive) denotes which disk on the controller is monitored.
+In log files and email messages this disk will be identified as
+megaraid_disk_XXX with XXX in the range from 000 to 127 inclusive.
+
 .I 3ware,N
 \- the device consists of one or more ATA disks connected to a 3ware
-RAID controller. The non-negative integer N (in the range from 0 to 31
+RAID controller. The non-negative integer N (in the range from 0 to 127 
 inclusive) denotes which disk on the controller is monitored.  In log
-files and email messages this disk will be identified as 3ware_disk_XX
-with XX in the range from 00 to 31 inclusive.
+files and email messages this disk will be identified as 3ware_disk_XXX
+with XXX in the range from 000 to 127 inclusive.
 
 This Directive may at first appear confusing, because the 3ware
 controller is a SCSI device (such as /dev/sda) and should be listed as
@@ -714,6 +819,13 @@ Alternatively use the character device interfaces /dev/twe0-15 (3ware
 6/7/8000 series controllers) or /dev/twa0-15 (3ware 9000 series
 controllers).
 
+.I areca,N
+\- the device consists of one or more SATA disks connected to an Areca
+SATA RAID controller.  The positive integer N (in the range from 1 to
+24 inclusive) denotes which disk on the controller is monitored.  In
+log files and email messages this disk will be identifed as
+areca_disk_XX with XX in the range from 01 to 24 inclusive.
+
 .I cciss,N
 \- the device consists of one or more SCSI disks connected to a cciss
 RAID controller. The non-negative integer N (in the range from 0 to 15
@@ -721,7 +833,7 @@ inclusive) denotes which disk on the controller is monitored.  In log
 files and email messages this disk will be identified as cciss_disk_XX
 with XX in the range from 00 to 15 inclusive.
 
-.B 3ware and cciss controllers are currently ONLY supported under Linux.
+.B 3ware, MegaRAID, Areca and cciss 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
@@ -734,7 +846,7 @@ 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.
+.B HighPoint RocketRAID controllers are currently ONLY supported under Linux and FreeBSD.
 
 .I removable
 \- the device or its media is removable.  This indicates to
@@ -745,7 +857,7 @@ behavior) if the device does not appear to be present when
 with the other \'\-d\' Directives.
 
 .TP
-.B \-n POWERMODE[,q]
+.B \-n POWERMODE[,N][,q]
 This \'nocheck\' Directive is used to prevent a disk from being
 spun-up when it is periodically polled by \fBsmartd\fP.
 
@@ -790,14 +902,18 @@ 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.
+Maximum number of skipped checks (in a row) can be specified by
+appending positive number \',N\' to POWERMODE (like \'\-n standby,15\').
+After N checks are skipped in a row, powermode is ignored and the
+check is performed anyway.
 
 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\').
 This prevents a laptop disk from spinning up due to this message.
 
+Both \',N\' and \',q\' can be specified together.
+
 .TP
 .B \-T TYPE
 Specifies how tolerant
@@ -886,6 +1002,15 @@ match (in turn) are: \'L\' for a \fBL\fPong Self-Test, \'S\' for a
 only), and \'O\' for an \fBO\fPffline Immediate Test (ATA only).  As
 soon as a match is found, the test will be started and no additional
 matches will be sought for that device and that polling cycle.
+
+[NEW EXPERIMENTAL SMARTD FEATURE] To run scheduled Selective
+Self-Tests, use \'n\' for \fBn\fPext span, \'r\' to \fBr\fPedo last
+span, or \'c\' to \fBc\fPontinue with next span or redo last span
+based on status of last test. The LBA range is based on the first
+span from the last test.
+See the \fBsmartctl \-t select,[next|redo|cont]\fP options for
+further info.
+
 .IP \fBMM\fP 4
 is the month of the year, expressed with two decimal digits.  The
 range is from 01 (January) to 12 (December) inclusive.  Do \fBnot\fP
@@ -931,6 +1056,19 @@ Self-Test every Saturday at 3-4am, use:
 .nf
 \fB \-s (O/../.././(00|06|12|18)|S/../.././01|L/../../6/03)\fP
 .fi
+If Long Self-Tests of a large disks take longer than the system uptime,
+a full disk test can be performed by several Selective Self-Tests.
+To setup a full test of a 1TB disk within 20 days (one 50GB span
+each day), run this command once:
+.nf
+  smartctl -t select,0-99999999 /dev/sda
+.fi
+To run the next test spans on Monday-Friday between 12-13am, run smartd
+with this directive:
+.nf
+\fB \-s n/../../[1-5]/12\fP
+.fi
+
 
 Scheduled tests are run immediately following the regularly-scheduled
 device polling, if the current local date, time, and test type, match
@@ -938,8 +1076,8 @@ device polling, if the current local date, time, and test type, match
 occurs every thirty minutes after starting \fBsmartd\fP.  Take caution
 if you use the \'\-i\' option to make this polling interval more than
 sixty minutes: the poll times may fail to coincide with any of the
-testing times that you have specified with \fBREGEXP\fP, and so the
-self tests may not take place as you wish.
+testing times that you have specified with \fBREGEXP\fP.  In this case
+the test will be run following the next device polling.
 
 Before running an offline or self-test, \fBsmartd\fP checks to be sure
 that a self-test is not already running.  If a self-test \fBis\fP
@@ -960,6 +1098,16 @@ that you constructed \fBREGEXP\fP correctly.  The matching order
 if multiple test types are all scheduled for the same hour, the
 longer test type has precedence.  This is usually the desired behavior.
 
+If the scheduled tests are used in conjunction with state persistence
+(\'\-s\' option), smartd will also try to match the hours since last
+shutdown (or 90 days at most). If any test would have been started
+during downtime, the longest (see above) of these tests is run after
+second device polling.
+
+If the \'\-n\' directive is used and any test would have been started
+during disk standby time, the longest of these tests is run when the
+disk is active again.
+
 Unix users: please beware that the rules for extended regular
 expressions [regex(7)] are \fBnot\fP the same as the rules for
 file\-name pattern matching by the shell [glob(7)].  \fBsmartd\fP will
@@ -1110,17 +1258,19 @@ 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, hpt,L/M/N).
-Here N=0,...,23 denotes the ATA disk behind a 3ware RAID controller and
-L/M/N denotes the SATA disk behind a HighPoint RocketRAID controller.
+is set to the device type (possible values: ata, scsi, 3ware,N,
+areca,N, hpt,L/M/N).  Here N=0,...,127 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]\'.  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.
+the form used is \'/dev/sdc [3ware_disk_01]\'.  For HighPoint
+RocketRAID controller, the form is \'/dev/sdd [hpt_1/1/1]\' under Linux
+or \'/dev/hptrr [hpt_1/1/1]\' under FreeBSD.  For Areca controllers, the
+form is \'/dev/sg2 [areca_disk_09]\'.  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
 gives the reason for the warning or message email.  The possible values that
 it takes and their meanings are:
@@ -1289,7 +1439,7 @@ temperature (usually Attribute 194 or 231). It\'s annoying to get reports
 each time the temperature changes.  This Directive may appear multiple
 times for a single device, if you want to ignore multiple Attributes.
 .TP
-.B \-r ID
+.B \-r ID[!]
 When tracking, report the \fIRaw\fP value of Attribute \fBID\fP along
 with its (normally reported) \fINormalized\fP value.  \fBID\fP must be
 a decimal integer in the range from 1 to 255.  This Directive modifies
@@ -1300,8 +1450,12 @@ multiple times.
 A common use of this Directive is to track the device Temperature
 (often ID=194 or 231).
 
+If the optional flag \'!\' is appended, a change of the Normalized
+value is considered critical.  The report will be logged as LOG_CRIT
+and a warning email will be sent if \'-m\' is specified.
+
 .TP
-.B \-R ID
+.B \-R ID[!]
 When tracking, report whenever the \fIRaw\fP value of Attribute
 \fBID\fP changes.  (Normally \fBsmartd\fP only tracks/reports changes
 of the \fINormalized\fP Attribute values.)  \fBID\fP must be a decimal
@@ -1319,8 +1473,13 @@ A common use of this Directive is to track the device Temperature
 different types of system behavior affects the values of certain
 Attributes.
 
+If the optional flag \'!\' is appended, a change of the Raw
+value is considered critical.  The report will be logged as
+LOG_CRIT and a warning email will be sent if \'-m\' is specified.
+An example is \'-R 5!\' to warn when new sectors are reallocated.
+
 .TP
-.B \-C ID
+.B \-C ID[+]
 [ATA only] Report if the current number of pending sectors is
 non-zero.  Here \fBID\fP is the id number of the Attribute whose raw
 value is the Current Pending Sector count.  The allowed range of
@@ -1329,6 +1488,11 @@ ID\ =\ 0.  If the \fB\-C ID\fP option is not given, then it defaults to
 \fB\-C 197\fP (since Attribute 197 is generally used to monitor
 pending sectors).
 
+If \'+\' is specified, a report is only printed if the number of sectors
+has increased between two check cycles. Some disks do not reset this
+attribute when a bad sector is reallocated.
+See also \'\-v 197,increasing\' below.
+
 A pending sector is a disk sector (containing 512 bytes of your data)
 which the device would like to mark as ``bad" and reallocate.
 Typically this is because your computer tried to read that sector, and
@@ -1342,7 +1506,7 @@ device substitute a spare good sector for the bad one) but at the
 price of losing the 512 bytes of data stored there.
 
 .TP
-.B \-U ID
+.B \-U ID[+]
 [ATA only] Report if the number of offline uncorrectable sectors is
 non-zero.  Here \fBID\fP is the id number of the Attribute whose raw
 value is the Offline Uncorrectable Sector count.  The allowed range of
@@ -1351,6 +1515,10 @@ ID\ =\ 0.  If the \fB\-U ID\fP option is not given, then it defaults to
 \fB\-U 198\fP (since Attribute 198 is generally used to monitor
 offline uncorrectable sectors).
 
+If \'+\' is specified, a report is only printed if the number of sectors
+has increased since the last check cycle. Some disks do not reset this
+attribute when a bad sector is reallocated.
+See also \'\-v 198,increasing\' below.
 
 An offline uncorrectable sector is a disk sector which was not
 readable during an off\-line scan or a self\-test.  This is important
@@ -1361,13 +1529,19 @@ 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
+degrees since last report, or if new min or max temperature is detected.
+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.
 
+If this directive is used in conjunction with state persistence
+(\'\-s\' option), the min and max temperature values are preserved
+across boot cycles. The minimum temperature value is not updated
+during the first 30 minutes after startup.
+
 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\').
@@ -1486,6 +1660,16 @@ with RK100-13 firmware).
 interpretation is unknown. This is primarily useful for the -P
 (presets) Directive.
 
+.I 197,increasing
+\- Raw Attribute number 197 (Current Pending Sector Count) is not
+reset if uncorrectable sectors are reallocated.  This also sets
+\'-C 197+\' if no other \'-C\' directive is specified.
+
+.I 198,increasing
+\- Raw Attribute number 198 (Offline Uncorrectable Sector Count) is not
+reset if uncorrectable sector are reallocated.  This also sets
+\'-U 198+\' if no other \'-U\' directive is specified.
+
 .I 198,offlinescanuncsectorct
 \- Raw Attribute number 198 is the Offline Scan UNC Sector Count.
 
@@ -1904,17 +2088,19 @@ University of Wisconsin \- Milwaukee Physics Department
 The following have made large contributions to smartmontools:
 .nf
 \fBCasper Dik\fP (Solaris SCSI interface)
-\fBChristian Franke\fP (Windows interface and Cygwin package)
+\fBChristian Franke\fP (Windows interface, C++ redesign, USB support, ...)
 \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)
+\fBGabriele Pohl\fP (Web site and Wiki, conversion from CVS to SVN)
 \fBKeiji Sawada\fP (Solaris ATA interface)
+\fBManfred Schwarb\fP (Drive database)
 \fBSergey Svishchev\fP (NetBSD interface)
 \fBDavid Snyder and Sergey Svishchev\fP (OpenBSD interface)
 \fBPhil Williams\fP (User interface and drive database)
-\fBShengfeng Zhou\fP (Linux Highpoint RocketRaid interface)
+\fBShengfeng Zhou\fP (Linux/FreeBSD HighPoint RocketRAID interface)
 .fi
 Many other individuals have made smaller contributions and corrections.
 
@@ -1964,5 +2150,5 @@ these documents may be found in the References section of the
 smartmontools home page at \fBhttp://smartmontools.sourceforge.net/#references\fP .
 
 .SH
-CVS ID OF THIS PAGE:
-$Id: smartd.8.in,v 1.121 2008/03/04 22:09:47 ballen4705 Exp $
+SVN ID OF THIS PAGE:
+$Id: smartd.8.in 2870 2009-08-02 20:38:30Z manfred99 $
index 3b2db2d3c76fea474c7bd308165350d6dfe7ab82..6882507ecea7bf73f646c802c3babe6af0de0ffb 100644 (file)
@@ -1,7 +1,7 @@
 .ig
 Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
-$Id: smartd.conf.5.in,v 1.87 2008/03/04 22:09:47 ballen4705 Exp $
+$Id: smartd.conf.5.in 2847 2009-07-18 14:58:44Z chrfranke $
 
 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
@@ -119,10 +119,11 @@ Section below!
 .B # /usr/local/etc/smartd.conf for monitoring three
 .B # ATA disks, three SCSI disks, six ATA disks
 .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 # directly connected to the HighPoint Rocket-
+.B # RAID controller, two SATA disks connected to
+.B # the HighPoint RocketRAID controller via a pmport
+.B # device, four SATA disks connected to an Areca
+.B # RAID controller, and one SATA disk.
 .B #
 .nf
 .B # First ATA disk on two different interfaces. On
@@ -154,6 +155,15 @@ Section below!
 .B \ \ /dev/sda -a -d sat
 .B #
 .nf
+.B # Three disks connected to a MegaRAID controller
+.B # Start short self-tests daily between 1-2, 2-3, and
+.B # 3-4 am.
+.B \ \ /dev/sda -d megaraid,0 -a -s S/../.././01
+.B \ \ /dev/sda -d megaraid,1 -a -s S/../.././02
+.B \ \ /dev/sda -d megaraid,2 -a -s S/../.././03
+.B
+.B #
+.nf
 .B # Four ATA disks on a 3ware 6/7/8000 controller.
 .B # Start short self-tests daily between midnight and 1am,
 .B # 1-2, 2-3, and 3-4 am.  Starting with the Linux 2.6
@@ -173,19 +183,36 @@ 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 # 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 # under Linux
 .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 # or under FreeBSD
+.B # /dev/hptrr -d hpt,1/1 -a -s S/../.././01
+.B # /dev/hptrr -d hpt,1/2 -a -s S/../.././02
+.B # /dev/hptrr -d hpt,1/3 -a -s S/../.././03
 .B #
 .nf
-.B # Two SATA disks connected to a highpoint rocketraid 
+.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 # under Linux
 .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 # or under FreeBSD
+.B # /dev/hptrr -d hpt,1/4/1 -a -s L/../../7/00
+.B # /dev/hptrr -d hpt,1/4/2 -a -s L/../../7/02
+.B #
+.nf
+.B # Three SATA disks connected to an Areca
+.B # RAID controller. Start long self-tests Sundays
+.B # between midnight and 3 am.
+.B \ \ /dev/sg2 -d areca,1 -a -s L/../../7/00
+.B \ \ /dev/sg2 -d areca,2 -a -s L/../../7/01
+.B \ \ /dev/sg2 -d areca,3 -a -s L/../../7/02
 .B #
 .nf
 .B # The following line enables monitoring of the 
@@ -252,12 +279,22 @@ or /dev/twa?) must be listed, along with the \'\-d 3ware,N\' Directive
 appear to \fBsmartd\fP as normal ATA devices.  Hence all the ATA
 directives can be used for these disks (but see note below).
 
+.B If an Areca controller is used
+then the corresponding SCSI generic device (/dev/sg?)  must be listed,
+along with the \'\-d areca,N\' Directive (see below).  The individual
+SATA disks hosted by the Areca controller appear to \fBsmartd\fP as
+normal ATA devices.  Hence all the ATA directives can be used for
+these disks.  Areca firmware version 1.46 or later which supports
+smartmontools must be used; Please see the \fBsmartctl\fP man page for
+further details.
+
 .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, \fIsat\fP,
-\fImarvell\fP, \fIcciss,N\fP and \fI3ware,N\fP are mutually-exclusive. If more
-than one is given then \fBsmartd\fP will use the last one which appears.
+times for one device, but the arguments \fIata\fP, \fIscsi\fP,
+\fIsat\fP, \fImarvell\fP, \fIcciss,N\fP, \fIareca,N\fP, \fImegaraid,N\fP
+and \fI3ware,N\fP are mutually-exclusive. If more than one is given then
+\fBsmartd\fP will use the last one which appears.
 
 If none of these three arguments is given, then \fBsmartd\fP will
 first attempt to guess the device type by looking at whether the sixth
@@ -296,12 +333,19 @@ be overridden with this syntax: \'\-d sat,12\' or \'\-d sat,16\'.
 \- Under Linux, interact with SATA disks behind Marvell chip-set
 controllers (using the Marvell rather than libata driver).
 
+.I megaraid,N
+\- the device consists of one or more SCSI/SAS/SATA disks connected
+to a MegaRAID controller.  The non-negative integer N (in the range
+of 0 to 127 inclusive) denotes which disk on the controller is monitored.
+In log files and email messages this disk will be identified as
+megaraid_disk_XXX with XXX in the range from 000 to 127 inclusive.
+
 .I 3ware,N
 \- the device consists of one or more ATA disks connected to a 3ware
-RAID controller. The non-negative integer N (in the range from 0 to 31
+RAID controller. The non-negative integer N (in the range from 0 to 127 
 inclusive) denotes which disk on the controller is monitored.  In log
-files and email messages this disk will be identified as 3ware_disk_XX
-with XX in the range from 00 to 31 inclusive.
+files and email messages this disk will be identified as 3ware_disk_XXX
+with XXX in the range from 000 to 127 inclusive.
 
 This Directive may at first appear confusing, because the 3ware
 controller is a SCSI device (such as /dev/sda) and should be listed as
@@ -336,6 +380,13 @@ Alternatively use the character device interfaces /dev/twe0-15 (3ware
 6/7/8000 series controllers) or /dev/twa0-15 (3ware 9000 series
 controllers).
 
+.I areca,N
+\- the device consists of one or more SATA disks connected to an Areca
+SATA RAID controller.  The positive integer N (in the range from 1 to
+24 inclusive) denotes which disk on the controller is monitored.  In
+log files and email messages this disk will be identifed as
+areca_disk_XX with XX in the range from 01 to 24 inclusive.
+
 .I cciss,N
 \- the device consists of one or more SCSI disks connected to a cciss
 RAID controller. The non-negative integer N (in the range from 0 to 15
@@ -343,7 +394,7 @@ inclusive) denotes which disk on the controller is monitored.  In log
 files and email messages this disk will be identified as cciss_disk_XX
 with XX in the range from 00 to 15 inclusive.
 
-.B 3ware and cciss controllers are currently ONLY supported under Linux.
+.B 3ware, MegaRAID, Areca and cciss 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
@@ -356,7 +407,7 @@ 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.
+.B HighPoint RocketRAID controllers are currently ONLY supported under Linux and FreeBSD.
 
 .I removable
 \- the device or its media is removable.  This indicates to
@@ -367,7 +418,7 @@ behavior) if the device does not appear to be present when
 with the other \'\-d\' Directives.
 
 .TP
-.B \-n POWERMODE[,q]
+.B \-n POWERMODE[,N][,q]
 This \'nocheck\' Directive is used to prevent a disk from being
 spun-up when it is periodically polled by \fBsmartd\fP.
 
@@ -412,14 +463,18 @@ 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.
+Maximum number of skipped checks (in a row) can be specified by
+appending positive number \',N\' to POWERMODE (like \'\-n standby,15\').
+After N checks are skipped in a row, powermode is ignored and the
+check is performed anyway.
 
 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\').
 This prevents a laptop disk from spinning up due to this message.
 
+Both \',N\' and \',q\' can be specified together.
+
 .TP
 .B \-T TYPE
 Specifies how tolerant
@@ -508,6 +563,15 @@ match (in turn) are: \'L\' for a \fBL\fPong Self-Test, \'S\' for a
 only), and \'O\' for an \fBO\fPffline Immediate Test (ATA only).  As
 soon as a match is found, the test will be started and no additional
 matches will be sought for that device and that polling cycle.
+
+[NEW EXPERIMENTAL SMARTD FEATURE] To run scheduled Selective
+Self-Tests, use \'n\' for \fBn\fPext span, \'r\' to \fBr\fPedo last
+span, or \'c\' to \fBc\fPontinue with next span or redo last span
+based on status of last test. The LBA range is based on the first
+span from the last test.
+See the \fBsmartctl \-t select,[next|redo|cont]\fP options for
+further info.
+
 .IP \fBMM\fP 4
 is the month of the year, expressed with two decimal digits.  The
 range is from 01 (January) to 12 (December) inclusive.  Do \fBnot\fP
@@ -553,6 +617,19 @@ Self-Test every Saturday at 3-4am, use:
 .nf
 \fB \-s (O/../.././(00|06|12|18)|S/../.././01|L/../../6/03)\fP
 .fi
+If Long Self-Tests of a large disks take longer than the system uptime,
+a full disk test can be performed by several Selective Self-Tests.
+To setup a full test of a 1TB disk within 20 days (one 50GB span
+each day), run this command once:
+.nf
+  smartctl -t select,0-99999999 /dev/sda
+.fi
+To run the next test spans on Monday-Friday between 12-13am, run smartd
+with this directive:
+.nf
+\fB \-s n/../../[1-5]/12\fP
+.fi
+
 
 Scheduled tests are run immediately following the regularly-scheduled
 device polling, if the current local date, time, and test type, match
@@ -560,8 +637,8 @@ device polling, if the current local date, time, and test type, match
 occurs every thirty minutes after starting \fBsmartd\fP.  Take caution
 if you use the \'\-i\' option to make this polling interval more than
 sixty minutes: the poll times may fail to coincide with any of the
-testing times that you have specified with \fBREGEXP\fP, and so the
-self tests may not take place as you wish.
+testing times that you have specified with \fBREGEXP\fP.  In this case
+the test will be run following the next device polling.
 
 Before running an offline or self-test, \fBsmartd\fP checks to be sure
 that a self-test is not already running.  If a self-test \fBis\fP
@@ -582,6 +659,16 @@ that you constructed \fBREGEXP\fP correctly.  The matching order
 if multiple test types are all scheduled for the same hour, the
 longer test type has precedence.  This is usually the desired behavior.
 
+If the scheduled tests are used in conjunction with state persistence
+(\'\-s\' option), smartd will also try to match the hours since last
+shutdown (or 90 days at most). If any test would have been started
+during downtime, the longest (see above) of these tests is run after
+second device polling.
+
+If the \'\-n\' directive is used and any test would have been started
+during disk standby time, the longest of these tests is run when the
+disk is active again.
+
 Unix users: please beware that the rules for extended regular
 expressions [regex(7)] are \fBnot\fP the same as the rules for
 file\-name pattern matching by the shell [glob(7)].  \fBsmartd\fP will
@@ -732,17 +819,19 @@ 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, hpt,L/M/N).
-Here N=0,...,23 denotes the ATA disk behind a 3ware RAID controller and
-L/M/N denotes the SATA disk behind a HighPoint RocketRAID controller.
+is set to the device type (possible values: ata, scsi, 3ware,N,
+areca,N, hpt,L/M/N).  Here N=0,...,127 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]\'.  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.
+the form used is \'/dev/sdc [3ware_disk_01]\'.  For HighPoint
+RocketRAID controller, the form is \'/dev/sdd [hpt_1/1/1]\' under Linux
+or \'/dev/hptrr [hpt_1/1/1]\' under FreeBSD.  For Areca controllers, the
+form is \'/dev/sg2 [areca_disk_09]\'.  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
 gives the reason for the warning or message email.  The possible values that
 it takes and their meanings are:
@@ -911,7 +1000,7 @@ temperature (usually Attribute 194 or 231). It\'s annoying to get reports
 each time the temperature changes.  This Directive may appear multiple
 times for a single device, if you want to ignore multiple Attributes.
 .TP
-.B \-r ID
+.B \-r ID[!]
 When tracking, report the \fIRaw\fP value of Attribute \fBID\fP along
 with its (normally reported) \fINormalized\fP value.  \fBID\fP must be
 a decimal integer in the range from 1 to 255.  This Directive modifies
@@ -922,8 +1011,12 @@ multiple times.
 A common use of this Directive is to track the device Temperature
 (often ID=194 or 231).
 
+If the optional flag \'!\' is appended, a change of the Normalized
+value is considered critical.  The report will be logged as LOG_CRIT
+and a warning email will be sent if \'-m\' is specified.
+
 .TP
-.B \-R ID
+.B \-R ID[!]
 When tracking, report whenever the \fIRaw\fP value of Attribute
 \fBID\fP changes.  (Normally \fBsmartd\fP only tracks/reports changes
 of the \fINormalized\fP Attribute values.)  \fBID\fP must be a decimal
@@ -941,8 +1034,13 @@ A common use of this Directive is to track the device Temperature
 different types of system behavior affects the values of certain
 Attributes.
 
+If the optional flag \'!\' is appended, a change of the Raw
+value is considered critical.  The report will be logged as
+LOG_CRIT and a warning email will be sent if \'-m\' is specified.
+An example is \'-R 5!\' to warn when new sectors are reallocated.
+
 .TP
-.B \-C ID
+.B \-C ID[+]
 [ATA only] Report if the current number of pending sectors is
 non-zero.  Here \fBID\fP is the id number of the Attribute whose raw
 value is the Current Pending Sector count.  The allowed range of
@@ -951,6 +1049,11 @@ ID\ =\ 0.  If the \fB\-C ID\fP option is not given, then it defaults to
 \fB\-C 197\fP (since Attribute 197 is generally used to monitor
 pending sectors).
 
+If \'+\' is specified, a report is only printed if the number of sectors
+has increased between two check cycles. Some disks do not reset this
+attribute when a bad sector is reallocated.
+See also \'\-v 197,increasing\' below.
+
 A pending sector is a disk sector (containing 512 bytes of your data)
 which the device would like to mark as ``bad" and reallocate.
 Typically this is because your computer tried to read that sector, and
@@ -964,7 +1067,7 @@ device substitute a spare good sector for the bad one) but at the
 price of losing the 512 bytes of data stored there.
 
 .TP
-.B \-U ID
+.B \-U ID[+]
 [ATA only] Report if the number of offline uncorrectable sectors is
 non-zero.  Here \fBID\fP is the id number of the Attribute whose raw
 value is the Offline Uncorrectable Sector count.  The allowed range of
@@ -973,6 +1076,10 @@ ID\ =\ 0.  If the \fB\-U ID\fP option is not given, then it defaults to
 \fB\-U 198\fP (since Attribute 198 is generally used to monitor
 offline uncorrectable sectors).
 
+If \'+\' is specified, a report is only printed if the number of sectors
+has increased since the last check cycle. Some disks do not reset this
+attribute when a bad sector is reallocated.
+See also \'\-v 198,increasing\' below.
 
 An offline uncorrectable sector is a disk sector which was not
 readable during an off\-line scan or a self\-test.  This is important
@@ -983,13 +1090,19 @@ 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
+degrees since last report, or if new min or max temperature is detected.
+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.
 
+If this directive is used in conjunction with state persistence
+(\'\-s\' option), the min and max temperature values are preserved
+across boot cycles. The minimum temperature value is not updated
+during the first 30 minutes after startup.
+
 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\').
@@ -1108,6 +1221,16 @@ with RK100-13 firmware).
 interpretation is unknown. This is primarily useful for the -P
 (presets) Directive.
 
+.I 197,increasing
+\- Raw Attribute number 197 (Current Pending Sector Count) is not
+reset if uncorrectable sectors are reallocated.  This also sets
+\'-C 197+\' if no other \'-C\' directive is specified.
+
+.I 198,increasing
+\- Raw Attribute number 198 (Offline Uncorrectable Sector Count) is not
+reset if uncorrectable sector are reallocated.  This also sets
+\'-U 198+\' if no other \'-U\' directive is specified.
+
 .I 198,offlinescanuncsectorct
 \- Raw Attribute number 198 is the Offline Scan UNC Sector Count.
 
@@ -1317,17 +1440,19 @@ University of Wisconsin \- Milwaukee Physics Department
 The following have made large contributions to smartmontools:
 .nf
 \fBCasper Dik\fP (Solaris SCSI interface)
-\fBChristian Franke\fP (Windows interface and Cygwin package)
+\fBChristian Franke\fP (Windows interface, C++ redesign, USB support, ...)
 \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)
+\fBGabriele Pohl\fP (Web site and Wiki, conversion from CVS to SVN)
 \fBKeiji Sawada\fP (Solaris ATA interface)
+\fBManfred Schwarb\fP (Drive database)
 \fBSergey Svishchev\fP (NetBSD interface)
 \fBDavid Snyder and Sergey Svishchev\fP (OpenBSD interface)
 \fBPhil Williams\fP (User interface and drive database)
-\fBShengfeng Zhou\fP (Linux Highpoint RocketRaid interface)
+\fBShengfeng Zhou\fP (Linux/FreeBSD HighPoint RocketRAID interface)
 .fi
 Many other individuals have made smaller contributions and corrections.
 
@@ -1356,5 +1481,5 @@ SEE ALSO:
 \fBsyslog.conf\fP(5), \fBbadblocks\fP(8), \fBide\-smart\fP(8), \fBregex\fP(7).
 
 .SH
-CVS ID OF THIS PAGE:
-$Id: smartd.conf.5.in,v 1.87 2008/03/04 22:09:47 ballen4705 Exp $
+SVN ID OF THIS PAGE:
+$Id: smartd.conf.5.in 2847 2009-07-18 14:58:44Z chrfranke $
index 24db34a0d36b73b87d315e4c150ab8d9669747d5..1e63dc9226391a94502fd249fe037cd6756ca41a 100644 (file)
@@ -1,8 +1,10 @@
 /*
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
- * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000   Michael Cornwell <cornwell@acm.org>
+ * Copyright (C) 2008   Oliver Bock <brevilo@users.sourceforge.net>
+ * Copyright (C) 2008-9 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
@@ -10,8 +12,7 @@
  * 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.
+ * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
  *
  * This code was originally developed as a Senior Thesis by Michael Cornwell
  * at the Concurrent Systems Laboratory (now part of the Storage Systems
@@ -21,6 +22,7 @@
  */
 
 #ifndef _GNU_SOURCE
+// TODO: Why is this define necessary?
 #define _GNU_SOURCE
 #endif
 
 #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 <errno.h>
 #include <time.h>
 #include <limits.h>
+#include <getopt.h>
 
-#if SCSITIMEOUT
-#include <setjmp.h>
-#endif
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <algorithm> // std::replace()
 
 // see which system files to conditionally include
 #include "config.h"
 
 // conditionally included files
-#ifdef HAVE_GETOPT_LONG
-#include <getopt.h>
+#ifndef _WIN32
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
 #endif
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
@@ -77,14 +80,20 @@ extern "C" int __stdcall FreeConsole(void);
 // locally included files
 #include "int64.h"
 #include "atacmds.h"
-#include "ataprint.h"
+#include "dev_interface.h"
 #include "extern.h"
 #include "knowndrives.h"
 #include "scsicmds.h"
-#include "scsiata.h"
-#include "smartd.h"
 #include "utility.h"
 
+// This is for solaris, where signal() resets the handler to SIG_DFL
+// after the first signal is caught.
+#ifdef HAVE_SIGSET
+#define SIGNALFN sigset
+#else
+#define SIGNALFN signal
+#endif
+
 #ifdef _WIN32
 #include "hostname_win32.h" // gethost/domainname()
 #define HAVE_GETHOSTNAME   1
@@ -95,9 +104,7 @@ extern "C" int __stdcall FreeConsole(void);
 #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.
+// SIGQUIT does not exist, CONTROL-Break signals SIGBREAK.
 #define SIGQUIT SIGBREAK
 #define SIGQUIT_KEYNAME "CONTROL-Break"
 #else  // _WIN32
@@ -115,58 +122,62 @@ extern "C" int getdomainname(char *, int); // no declaration in header files!
 
 #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.397 2008/03/04 22:09:47 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.cpp,v 1.397 2008/03/04 22:09:47 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;
+const char * smartd_cpp_cvsid = "$Id: smartd.cpp 2870 2009-08-02 20:38:30Z manfred99 $"
+                                CONFIG_H_CVSID EXTERN_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;
 
+// smartd exit codes
+#define EXIT_BADCMD    1   // command line did not parse
+#define EXIT_BADCONF   2   // syntax error in config file
+#define EXIT_STARTUP   3   // problem forking daemon
+#define EXIT_PID       4   // problem creating pid file
+#define EXIT_NOCONF    5   // config file does not exist
+#define EXIT_READCONF  6   // config file exists but cannot be read
+
+#define EXIT_NOMEM     8   // out of memory
+#define EXIT_BADCODE   10  // internal error - should NEVER happen
+
+#define EXIT_BADDEV    16  // we can't monitor this device
+#define EXIT_NODEV     17  // no devices to monitor
+
+#define EXIT_SIGNAL    254 // abort on signal
+
 // command-line: how long to sleep between checks
+#define CHECKTIME 1800
 static int checktime=CHECKTIME;
 
-// command-line: name of PID file (NULL for no pid file)
-static char* pid_file=NULL;
+// command-line: name of PID file (empty for no pid file)
+static std::string pid_file;
+
+// command-line: path prefix of persistent state file, empty if no persistence.
+static std::string state_path_prefix
+#ifdef SMARTMONTOOLS_SAVESTATES
+          = SMARTMONTOOLS_SAVESTATES
+#endif
+                                    ;
+
+// command-line: path prefix of attribute log file, empty if no logs.
+static std::string attrlog_path_prefix
+#ifdef SMARTMONTOOLS_ATTRIBUTELOG
+          = SMARTMONTOOLS_ATTRIBUTELOG
+#endif
+                                    ;
 
 // configuration file name
+#define CONFIGFILENAME "smartd.conf"
+
 #ifndef _WIN32
-static char* configfile = SMARTMONTOOLS_SYSCONFDIR "/" CONFIGFILENAME ;
+static const char *configfile = SMARTMONTOOLS_SYSCONFDIR "/" CONFIGFILENAME ;
 #else
-static char* configfile = "./" CONFIGFILENAME ;
+static const 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;
+static const char * const configfile_stdin = "<stdin>";
+// path of alternate configuration file
+static std::string configfile_alt;
 
 // command-line: when should we exit?
 static int quit=0;
@@ -182,23 +193,6 @@ static bool do_fork=true;
 // 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 **ATAandSCSIdevlist=NULL;
-int ATAandSCSIdevlist_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;
 
@@ -214,204 +208,532 @@ 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
+// Attribute monitoring flags.
+// See monitor_attr_flags below.
+enum {
+  MONITOR_IGN_FAILUSE = 0x01,
+  MONITOR_IGNORE      = 0x02,
+  MONITOR_RAW_PRINT   = 0x04,
+  MONITOR_RAW         = 0x08,
+  MONITOR_AS_CRIT     = 0x10,
+  MONITOR_RAW_AS_CRIT = 0x20,
+};
+
+// Array of flags for each attribute.
+class attribute_flags
+{
+public:
+  attribute_flags()
+    { memset(m_flags, 0, sizeof(m_flags)); }
 
-// tranlate cfg->pending into the correct Attribute numbers
-void TranslatePending(unsigned short pending, unsigned char *current, unsigned char *offline) {
+  bool is_set(int id, unsigned char flag) const
+    { return (0 < id && id < (int)sizeof(m_flags) && (m_flags[id] & flag)); }
 
-  unsigned char curr = CURR_PEND(pending);
-  unsigned char off =  OFF_PEND(pending);
+  void set(int id, unsigned char flags)
+    {
+      if (0 < id && id < (int)sizeof(m_flags))
+        m_flags[id] |= flags;
+    }
 
-  // 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;
+private:
+  unsigned char m_flags[256];
+};
 
-  *current=curr;
-  *offline=off;
 
-  return;
+/// Configuration data for a device. Read from smartd.conf.
+/// Supports copy & assignment and is compatible with STL containers.
+struct dev_config
+{
+  int lineno;                             // Line number of entry in file
+  std::string name;                       // Device name
+  std::string dev_type;                   // Device type argument from -d directive, empty if none
+  std::string state_file;                 // Path of the persistent state file, empty if none
+  std::string attrlog_file;               // Path of the persistent attrlog file, empty if none
+  bool smartcheck;                        // Check SMART status
+  bool usagefailed;                       // Check for failed Usage Attributes
+  bool prefail;                           // Track changes in Prefail Attributes
+  bool usage;                             // Track changes in Usage Attributes
+  bool selftest;                          // Monitor number of selftest errors
+  bool errorlog;                          // Monitor number of ATA errors
+  bool permissive;                        // Ignore failed SMART commands
+  char autosave;                          // 1=disable, 2=enable Autosave Attributes
+  char autoofflinetest;                   // 1=disable, 2=enable Auto Offline Test
+  unsigned char fix_firmwarebug;          // FIX_*, see atacmds.h
+  bool ignorepresets;                     // Ignore database of -v options
+  bool showpresets;                       // Show database entry for this device
+  bool removable;                         // Device may disappear (not be present)
+  char powermode;                         // skip check, if disk in idle or standby mode
+  bool powerquiet;                        // skip powermode 'skipping checks' message
+  int powerskipmax;                       // how many times can be check skipped
+  unsigned char tempdiff;                 // Track Temperature changes >= this limit
+  unsigned char tempinfo, tempcrit;       // Track Temperatures >= these limits as LOG_INFO, LOG_CRIT+mail
+  regular_expression test_regex;          // Regex for scheduled testing
+
+  // Configuration of email warning messages
+  std::string emailcmdline;               // script to execute, empty if no messages
+  std::string emailaddress;               // email address, or empty
+  unsigned char emailfreq;                // Emails once (1) daily (2) diminishing (3)
+  bool emailtest;                         // Send test email?
+
+  // ATA ONLY
+  unsigned char curr_pending_id;          // ID of current pending sector count, 0 if none
+  unsigned char offl_pending_id;          // ID of offline uncorrectable sector count, 0 if none
+  bool curr_pending_incr, offl_pending_incr; // True if current/offline pending values increase
+  bool curr_pending_set,  offl_pending_set;  // True if '-C', '-U' set in smartd.conf
+
+  attribute_flags monitor_attr_flags;     // MONITOR_* flags for each attribute
+
+  // TODO: Encapsulate, add get/set functions
+  unsigned char attributedefs[256];       // -v options, see end of extern.h for def
+
+  dev_config();
+};
+
+dev_config::dev_config()
+: lineno(0),
+  smartcheck(false),
+  usagefailed(false),
+  prefail(false),
+  usage(false),
+  selftest(false),
+  errorlog(false),
+  permissive(false),
+  autosave(0),
+  autoofflinetest(0),
+  fix_firmwarebug(FIX_NOTSPECIFIED),
+  ignorepresets(false),
+  showpresets(false),
+  removable(false),
+  powermode(0),
+  powerquiet(false),
+  powerskipmax(0),
+  tempdiff(0),
+  tempinfo(0), tempcrit(0),
+  emailfreq(0),
+  emailtest(false),
+  curr_pending_id(0), offl_pending_id(0),
+  curr_pending_incr(false), offl_pending_incr(false),
+  curr_pending_set(false),  offl_pending_set(false)
+{
+  memset(attributedefs, 0, sizeof(attributedefs));
 }
 
 
-// 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));
+// Number of allowed mail message types
+const int SMARTD_NMAIL = 13;
+// Type for '-M test' mails (state not persistent)
+const int MAILTYPE_TEST = 0;
+// TODO: Add const or enum for all mail types.
+
+struct mailinfo {
+  int logged;// number of times an email has been sent
+  time_t firstsent;// time first email was sent, as defined by time(2)
+  time_t lastsent; // time last email was sent, as defined by time(2)
 
-  // 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));
+  mailinfo()
+    : logged(0), firstsent(0), lastsent(0) { }
+};
 
-  // free remaining memory space
-  data=FreeNonZero(data, sizeof(testinfo), __LINE__, filenameandversion);
+/// Persistent state data for a device.
+struct persistent_dev_state
+{
+  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
+
+  time_t scheduled_test_next_check;       // Time of next check for scheduled self-tests
+
+  mailinfo maillog[SMARTD_NMAIL];         // log info on when mail sent
+
+  // ATA ONLY
+  int ataerrorcount;                      // Total number of ATA errors
+
+  // Persistent part of ata_smart_values:
+  struct ata_attribute {
+    unsigned char id;
+    unsigned char val;
+    uint64_t raw;
+
+    ata_attribute() : id(0), val(0), raw(0) { }
+  };
+  ata_attribute ata_attributes[NUMBER_ATA_SMART_ATTRIBUTES];
 
-  return NULL;
+  persistent_dev_state();
+};
+
+persistent_dev_state::persistent_dev_state()
+: tempmin(0), tempmax(0),
+  selflogcount(0),
+  selfloghour(0),
+  scheduled_test_next_check(0),
+  ataerrorcount(0)
+{
 }
 
-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) {
+/// Non-persistent state data for a device.
+struct temp_dev_state
+{
+  bool must_write;                        // true if persistent part should be written
+
+  bool not_cap_offline;                   // true == not capable of offline testing
+  bool not_cap_conveyance;
+  bool not_cap_short;
+  bool not_cap_long;
+  bool not_cap_selective;
+
+  unsigned char temperature;              // last recorded Temperature (in Celsius)
+  time_t tempmin_delay;                   // time where Min Temperature tracking will start
+
+  bool powermodefail;                     // true if power mode check failed
+  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 SuppressReport;           // minimize nuisance reports
+  unsigned char modese_len;               // mode sense/select cmd len: 0 (don't
+                                          // know yet) 6 or 10
+
+  // ATA ONLY
+  uint64_t num_sectors;                   // Number of sectors (for selective self-test only)
+  ata_smart_values smartval;              // SMART data
+  ata_smart_thresholds_pvt smartthres;    // SMART thresholds
+
+  temp_dev_state();
+};
+
+temp_dev_state::temp_dev_state()
+: must_write(false),
+  not_cap_offline(false),
+  not_cap_conveyance(false),
+  not_cap_short(false),
+  not_cap_long(false),
+  not_cap_selective(false),
+  temperature(0),
+  tempmin_delay(0),
+  powermodefail(false),
+  powerskipcnt(0),
+  SmartPageSupported(false),
+  TempPageSupported(false),
+  SuppressReport(false),
+  modese_len(0),
+  num_sectors(0)
+{
+  memset(&smartval, 0, sizeof(smartval));
+  memset(&smartthres, 0, sizeof(smartthres));
+}
 
-    // 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
+/// Runtime state data for a device.
+struct dev_state
+: public persistent_dev_state,
+  public temp_dev_state
+{
+  void update_persistent_state();
+  void update_temp_state();
+};
 
-    return newptr;
+/// Container for configuration info for each device.
+typedef std::vector<dev_config> dev_config_vector;
+
+/// Container for state info for each device.
+typedef std::vector<dev_state> dev_state_vector;
+
+// Copy ATA attributes to persistent state.
+void dev_state::update_persistent_state()
+{
+  for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+    const ata_smart_attribute & ta = smartval.vendor_attributes[i];
+    ata_attribute & pa = ata_attributes[i];
+    pa.id = ta.id;
+    if (ta.id == 0) {
+      pa.val = 0; pa.raw = 0;
+      continue;
+    }
+    pa.val = ta.current;
+    pa.raw =            ta.raw[0]
+           | (          ta.raw[1] <<  8)
+           | (          ta.raw[2] << 16)
+           | ((uint64_t)ta.raw[3] << 24)
+           | ((uint64_t)ta.raw[4] << 32)
+           | ((uint64_t)ta.raw[5] << 40);
   }
-  
-  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;
+// Copy ATA from persistent to temp state.
+void dev_state::update_temp_state()
+{
+  for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+    const ata_attribute & pa = ata_attributes[i];
+    ata_smart_attribute & ta = smartval.vendor_attributes[i];
+    ta.id = pa.id;
+    if (pa.id == 0) {
+      ta.current = 0; memset(ta.raw, 0, sizeof(ta.raw));
+      continue;
+    }
+    ta.current = pa.val;
+    ta.raw[0] = (unsigned char) pa.raw;
+    ta.raw[1] = (unsigned char)(pa.raw >>  8);
+    ta.raw[2] = (unsigned char)(pa.raw >> 16);
+    ta.raw[3] = (unsigned char)(pa.raw >> 24);
+    ta.raw[4] = (unsigned char)(pa.raw >> 32);
+    ta.raw[5] = (unsigned char)(pa.raw >> 40);
+  }
 }
 
-// prints CVS identity information for the executable
-void PrintCVS(void){
-  const char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]";
+// Parse a line from a state file.
+static bool parse_dev_state_line(const char * line, persistent_dev_state & state)
+{
+  static regular_expression regex(
+    "^ *"
+     "((temperature-min)" // (1 (2)
+     "|(temperature-max)" // (3)
+     "|(self-test-errors)" // (4)
+     "|(self-test-last-err-hour)" // (5)
+     "|(scheduled-test-next-check)" // (6)
+     "|(ata-error-count)"  // (7)
+     "|(mail\\.([0-9]+)\\." // (8 (9)
+       "((count)" // (10 (11)
+       "|(first-sent-time)" // (12)
+       "|(last-sent-time)" // (13)
+       ")" // 14)
+      ")" // 8)
+     "|(ata-smart-attribute\\.([0-9]+)\\." // (14 (15)
+       "((id)" // (16)
+       "|(val)" // (17)
+       "|(raw)" // (18)
+       ")" // 19)
+      ")" // 14)
+     ")" // 1)
+     " *= *([0-9]+)[ \n]*$", // (20)
+    REG_EXTENDED
+  );
+  if (regex.empty())
+    throw std::logic_error("parse_dev_state_line: invalid regex");
+
+  const int nmatch = 1+20;
+  regmatch_t match[nmatch];
+  if (!regex.execute(line, nmatch, match))
+    return false;
+  if (match[nmatch-1].rm_so < 0)
+    return false;
 
-  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;
+  uint64_t val = strtoull(line + match[nmatch-1].rm_so, (char **)0, 10);
+
+  int m = 1;
+  if (match[++m].rm_so >= 0)
+    state.tempmin = (unsigned char)val;
+  else if (match[++m].rm_so >= 0)
+    state.tempmax = (unsigned char)val;
+  else if (match[++m].rm_so >= 0)
+    state.selflogcount = (unsigned char)val;
+  else if (match[++m].rm_so >= 0)
+    state.selfloghour = (unsigned short)val;
+  else if (match[++m].rm_so >= 0)
+    state.scheduled_test_next_check = (time_t)val;
+  else if (match[++m].rm_so >= 0)
+    state.ataerrorcount = (int)val;
+  else if (match[m+=2].rm_so >= 0) {
+    int i = atoi(line+match[m].rm_so);
+    if (!(0 <= i && i < SMARTD_NMAIL))
+      return false;
+    if (i == MAILTYPE_TEST) // Don't suppress test mails
+      return true;
+    if (match[m+=2].rm_so >= 0)
+      state.maillog[i].logged = (int)val;
+    else if (match[++m].rm_so >= 0)
+      state.maillog[i].firstsent = (time_t)val;
+    else if (match[++m].rm_so >= 0)
+      state.maillog[i].lastsent = (time_t)val;
+    else
+      return false;
+  }
+  else if (match[m+=5+1].rm_so >= 0) {
+    int i = atoi(line+match[m].rm_so);
+    if (!(0 <= i && i < NUMBER_ATA_SMART_ATTRIBUTES))
+      return false;
+    if (match[m+=2].rm_so >= 0)
+      state.ata_attributes[i].id = (unsigned char)val;
+    else if (match[++m].rm_so >= 0)
+      state.ata_attributes[i].val = (unsigned char)val;
+    else if (match[++m].rm_so >= 0)
+      state.ata_attributes[i].raw = val;
+    else
+      return false;
+  }
+  else
+    return false;
+  return true;
 }
 
-// Removes config file entry, freeing all memory
-void RmConfigEntry(cfgfile **anentry, int whatline){
-  
-  cfgfile *cfg;
+// Read a state file.
+static bool read_dev_state(const char * path, persistent_dev_state & state)
+{
+  stdio_file f(path, "r");
+  if (!f) {
+    if (errno != ENOENT)
+      pout("Cannot read state file \"%s\"\n", path);
+    return false;
+  }
+#ifdef __CYGWIN__
+  setmode(fileno(f), O_TEXT); // Allow files with \r\n
+#endif
 
-  // 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);
+  int good = 0, bad = 0;
+  char line[256];
+  while (fgets(line, sizeof(line), f)) {
+    const char * s = line + strspn(line, " \t");
+    if (!*s || *s == '#')
+      continue;
+    if (!parse_dev_state_line(line, state))
+      bad++;
+    else
+      good++;
   }
-  
-  // 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);
+  if (bad) {
+    if (!good) {
+      pout("%s: format error\n", path);
+      return false;
+    }
+    pout("%s: %d invalid line(s) ignored\n", path, bad);
   }
-  cfg->testdata        = FreeTestData(cfg->testdata);
-  *anentry             = FreeNonZero(cfg,                  sizeof(cfgfile),__LINE__,filenameandversion);
+  return true;
+}
 
-  return;
+static void write_dev_state_line(FILE * f, const char * name, uint64_t val)
+{
+  if (val)
+    fprintf(f, "%s = %"PRIu64"\n", name, val);
+}
+
+static void write_dev_state_line(FILE * f, const char * name1, int id, const char * name2, uint64_t val)
+{
+  if (val)
+    fprintf(f, "%s.%d.%s = %"PRIu64"\n", name1, id, name2, val);
 }
 
-// deallocates all memory associated with cfgentries list
-void RmAllConfigEntries(){
+// Write a state file
+static bool write_dev_state(const char * path, const persistent_dev_state & state)
+{
+  // Rename old "file" to "file~"
+  std::string pathbak = path; pathbak += '~';
+  unlink(pathbak.c_str());
+  rename(path, pathbak.c_str());
+
+  stdio_file f(path, "w");
+  if (!f) {
+    pout("Cannot create state file \"%s\"\n", path);
+    return false;
+  }
+
+  fprintf(f, "# smartd state file\n");
+  write_dev_state_line(f, "temperature-min", state.tempmin);
+  write_dev_state_line(f, "temperature-max", state.tempmax);
+  write_dev_state_line(f, "self-test-errors", state.selflogcount);
+  write_dev_state_line(f, "self-test-last-err-hour", state.selfloghour);
+  write_dev_state_line(f, "scheduled-test-next-check", state.scheduled_test_next_check);
+
   int i;
+  for (i = 0; i < SMARTD_NMAIL; i++) {
+    if (i == MAILTYPE_TEST) // Don't suppress test mails
+      continue;
+    const mailinfo & mi = state.maillog[i];
+    if (!mi.logged)
+      continue;
+    write_dev_state_line(f, "mail", i, "count", mi.logged);
+    write_dev_state_line(f, "mail", i, "first-sent-time", mi.firstsent);
+    write_dev_state_line(f, "mail", i, "last-sent-time", mi.lastsent);
+  }
 
-  for (i=0; i<cfgentries_max; i++)
-    RmConfigEntry(cfgentries+i, __LINE__);
+  // ATA ONLY
+  write_dev_state_line(f, "ata-error-count", state.ataerrorcount);
 
-  cfgentries=FreeNonZero(cfgentries, sizeof(cfgfile *)*cfgentries_max, __LINE__, filenameandversion);
-  cfgentries_max=0;
+  for (i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+    const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i];
+    if (!pa.id)
+      continue;
+    write_dev_state_line(f, "ata-smart-attribute", i, "id", pa.id);
+    write_dev_state_line(f, "ata-smart-attribute", i, "val", pa.val);
+    write_dev_state_line(f, "ata-smart-attribute", i, "raw", pa.raw);
+  }
 
-  return;
+  return true;
 }
 
-// deallocates all memory associated with ATA/SCSI device lists
-void RmAllDevEntries(){
-  int i;
-  
-  for (i=0; i<ATAandSCSIdevlist_max; i++)
-      RmConfigEntry(ATAandSCSIdevlist+i, __LINE__);
-  
-  ATAandSCSIdevlist=FreeNonZero(ATAandSCSIdevlist, sizeof(cfgfile *)*ATAandSCSIdevlist_max, __LINE__, filenameandversion);
-  ATAandSCSIdevlist_max=0;
+// Write to the attrlog file
+static bool write_dev_attrlog(const char * path, const persistent_dev_state & state)
+{
+  stdio_file f(path, "a");
+  if (!f) {
+    pout("Cannot create attribute log file \"%s\"\n", path);
+    return false;
+  }
 
-  return;
+  // ATA ONLY
+  time_t now = time(0);
+  struct tm * tms = gmtime(&now);
+  fprintf(f, "%d-%02d-%02d %02d:%02d:%02d;",
+             1900+tms->tm_year, 1+tms->tm_mon, tms->tm_mday,
+             tms->tm_hour, tms->tm_min, tms->tm_sec);
+  for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+    const persistent_dev_state::ata_attribute & pa = state.ata_attributes[i];
+    if (!pa.id)
+      continue;
+    fprintf(f, "\t%d;%d;%"PRIu64";", pa.id, pa.val, pa.raw);
+  }
+  fprintf(f, "\n");
+
+  return true;
+}
+
+// Write all state files. If write_always is false, don't write
+// unless must_write is set.
+static void write_all_dev_states(const dev_config_vector & configs,
+                                 dev_state_vector & states,
+                                 bool write_always = true)
+{
+  for (unsigned i = 0; i < states.size(); i++) {
+    const dev_config & cfg = configs.at(i);
+    if (cfg.state_file.empty())
+      continue;
+    dev_state & state = states[i];
+    if (!write_always && !state.must_write)
+      continue;
+    if (!write_dev_state(cfg.state_file.c_str(), state))
+      continue;
+    state.must_write = false;
+    if (write_always || debugmode)
+      PrintOut(LOG_INFO, "Device: %s, state written to %s\n",
+               cfg.name.c_str(), cfg.state_file.c_str());
+  }
+}
+
+// Write to all attrlog files
+static void write_all_dev_attrlogs(const dev_config_vector & configs,
+                                   dev_state_vector & states)
+{
+  for (unsigned i = 0; i < states.size(); i++) {
+    const dev_config & cfg = configs.at(i);
+    if (cfg.attrlog_file.empty())
+      continue;
+    dev_state & state = states[i];
+    write_dev_attrlog(cfg.attrlog_file.c_str(), state);
+  }
 }
 
 // remove the PID file
 void RemovePidFile(){
-  if (pid_file) {
-    if ( -1==unlink(pid_file) )
+  if (!pid_file.empty()) {
+    if (unlink(pid_file.c_str()))
       PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n", 
-               pid_file, strerror(errno));
-    pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion);
+               pid_file.c_str(), strerror(errno));
+    pid_file.clear();
   }
   return;
 }
 
+extern "C" { // signal handlers require C-linkage
 
 //  Note if we catch a SIGUSR1
 void USR1handler(int sig){
@@ -445,35 +767,22 @@ void sighandler(int sig){
   return;
 }
 
+} // extern "C"
 
-// signal handler that prints Goodbye message and removes pidfile
-void Goodbye(void){
-  
-  // clean up memory -- useful for debugging
-  RmAllConfigEntries();
-  RmAllDevEntries();
-
+// Cleanup, print Goodbye message and remove pidfile
+static int Goodbye(int status)
+{
   // 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))
+  if (status==EXIT_BADCODE)
         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);
+  PrintOut(status?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", status);
 
-  return;
+  return status;
 }
 
 #define ENVLENGTH 1024
@@ -492,9 +801,28 @@ int exportenv(char* stackspace, const char *name, const char *value){
 
 char* dnsdomain(const char* hostname) {
   char *p = NULL;
-#ifdef HAVE_GETHOSTBYNAME
+#ifdef HAVE_GETADDRINFO
+  static char canon_name[NI_MAXHOST];
+  struct addrinfo *info = NULL;
+  struct addrinfo hints;
+  int err;
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_flags = AI_CANONNAME;
+  if ((err = getaddrinfo(hostname, NULL, &hints, &info)) || (!info)) {
+    PrintOut(LOG_CRIT, "Error retrieving getaddrinfo(%s): %s\n", hostname, gai_strerror(err));
+    return NULL;
+  }
+  if (info->ai_canonname) {
+    strncpy(canon_name, info->ai_canonname, sizeof(canon_name));
+    canon_name[NI_MAXHOST - 1] = '\0';
+    p = canon_name;
+    if ((p = strchr(canon_name, '.')))
+      p++;
+  }
+  freeaddrinfo(info);
+#elif HAVE_GETHOSTBYNAME
   struct hostent *hp;
-  
   if ((hp = gethostbyname(hostname))) {
     // Does this work if gethostbyname() returns an IPv6 name in
     // colon/dot notation?  [BA]
@@ -509,9 +837,12 @@ char* dnsdomain(const char* hostname) {
 
 #define EBUFLEN 1024
 
+static void MailWarning(const dev_config & cfg, dev_state & state, int which, const char *fmt, ...)
+                        __attribute__ ((format (printf, 4, 5)));
+
 // 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, ...){
+static void MailWarning(const dev_config & cfg, dev_state & state, int which, const 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];
@@ -519,7 +850,7 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
   va_list ap;
   const int day=24*3600;
   int days=0;
-  char *whichfail[]={
+  const char * const whichfail[]={
     "EmailTest",                  // 0
     "Health",                     // 1
     "Usage",                      // 2
@@ -535,33 +866,21 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
     "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)
+  if (cfg.emailaddress.empty() && cfg.emailcmdline.empty())
     return;
-  
-  address=data->address;
-  executable=data->emailcmdline;
-  
-  if (!address && !executable)
-    return;
-  
+
+  std::string address = cfg.emailaddress;
+  const char * executable = cfg.emailcmdline.c_str();
+
   // which type of mail are we sending?
-  mail=(data->maillog)+which;
-  
+  mailinfo * mail=(state.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);
+  if (cfg.emailfreq<1 || cfg.emailfreq>3) {
+    PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg.mailwarn->emailfreq=%d\n",cfg.emailfreq);
     return;
   }
   if (which<0 || which>=SMARTD_NMAIL || sizeof(whichfail)!=SMARTD_NMAIL*sizeof(char *)) {
@@ -571,7 +890,7 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
   }
   
   // Return if a single warning mail has been sent.
-  if ((data->emailfreq==1) && mail->logged)
+  if ((cfg.emailfreq==1) && mail->logged)
     return;
 
   // Return if this is an email test and one has already been sent.
@@ -582,11 +901,11 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
   epoch=time(NULL);
 
   // Return if less than one day has gone by
-  if (data->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day))
+  if (cfg.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){
+  if (cfg.emailfreq==3 && mail->logged) {
     days=0x01<<(mail->logged-1);
     days*=day;
     if  (epoch<(mail->lastsent+days))
@@ -636,7 +955,7 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
   if (which) {
     sprintf(further,"You can also use the smartctl utility for further investigation.\n");
 
-    switch (data->emailfreq){
+    switch (cfg.emailfreq) {
     case 1:
       sprintf(additional,"No additional email messages about this problem will be sent.\n");
       break;
@@ -648,7 +967,7 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
               (0x01)<<mail->logged);
       break;
     }
-    if (data->emailfreq>1 && mail->logged){
+    if (cfg.emailfreq>1 && mail->logged) {
       dateandtimezoneepoch(dates, mail->firstsent);
       sprintf(original,"The original email about this issue was sent at %s\n", dates);
     }
@@ -656,8 +975,8 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
   
   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)
+  // If the user has set cfg.emailcmdline, use that as mailer, else "mail" or "mailx".
+  if (!*executable)
 #ifdef DEFAULT_MAILER
     executable = DEFAULT_MAILER ;
 #else
@@ -668,19 +987,10 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
 #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=' ';
-    }
+  // replace commas by spaces to separate recipients
+  std::replace(address.begin(), address.end(), ',', ' ');
 #endif
-  }
-
   // Export information in environment variables that will be useful
   // for user scripts
   exportenv(environ_strings[0], "SMARTD_MAILER", executable);
@@ -691,67 +1001,12 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
   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_CCISS:
-    {
-      char *s,devicetype[16];
-      sprintf(devicetype, "cciss,%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);
-    break;
-  case CONTROLLER_SAT:
-    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "sat");
-    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
-    break;
-  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=' ';
-    }
-    break;
-  }
+  if (!address.empty())
+    exportenv(environ_strings[6], "SMARTD_ADDRESS", address.c_str());
+  exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg.name.c_str());
+
+  exportenv(environ_strings[8], "SMARTD_DEVICETYPE", cfg.dev_type.c_str());
+  exportenv(environ_strings[9], "SMARTD_DEVICE", cfg.name.c_str());
 
   snprintf(fullmessage, 1024,
              "This email was generated by the smartd daemon running on:\n\n"
@@ -767,22 +1022,23 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
 
   // now construct a command to send this as EMAIL
 #ifndef _WIN32
-  if (address)
+  if (!address.empty())
     snprintf(command, 2048, 
              "$SMARTD_MAILER -s '%s' %s 2>&1 << \"ENDMAIL\"\n"
-            "%sENDMAIL\n", subject, address, fullmessage);
+            "%sENDMAIL\n", subject, address.c_str(), 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";
+  const char * newadd = (!address.empty()? address.c_str() : "<nomailer>");
+  const char * 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;
+  FILE * pfp;
   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", 
@@ -852,28 +1108,33 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
 #else // _WIN32
 
   // No "here-documents" on Windows, so must use separate commandline and stdin
+  char stdinbuf[1024];
   command[0] = stdinbuf[0] = 0;
-  boxtype = -1; boxmsgoffs = 0;
-  newadd = "<nomailer>";
-  if (address) {
+  int boxtype = -1, boxmsgoffs = 0;
+  const char * newadd = "<nomailer>";
+  if (!address.empty()) {
     // 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++;
+    char addr1[9+1+13] = ""; int n1 = -1, n2 = -1;
+    if (sscanf(address.c_str(), "%9[a-z]%n,%n", addr1, &n1, &n2) == 1 && (n1 == (int)address.size() || n2 > 0)) {
+      if (!strcmp(addr1, "msgbox"))
+        boxtype = 0;
+      else if (!strcmp(addr1, "sysmsgbox"))
+        boxtype = 1;
+      if (boxtype >= 0)
+        address.erase(0, (n2 > n1 ? n2 : n1));
     }
-    else
-      addroffs = 0;
 
-    if (address[addroffs]) {
+    if (!address.empty()) {
       // 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;
+               executable, subject, address.c_str());
+      newadd = address.c_str();
     }
+
+#ifdef _MSC_VER
+    _set_printf_count_output(1); // "%n" disabled by default
+#endif
     // Message for mail [0...] and messagebox [boxmsgoffs...]
     snprintf(stdinbuf, sizeof(stdinbuf),
              "This email was generated by the smartd daemon running on:\n\n"
@@ -891,7 +1152,7 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
   else
     snprintf(command, sizeof(command), "%s", executable);
 
-  newwarn=which?"Warning via":"Test of";
+  const char * newwarn = (which ? "Warning via" : "Test of");
   if (boxtype >= 0) {
     // show message box
     daemon_messagebox(boxtype, subject, stdinbuf+boxmsgoffs);
@@ -918,11 +1179,6 @@ void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
 
   // 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
@@ -948,7 +1204,7 @@ void pout(const char *fmt, ...){
 #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) {
+  else if (debugmode==2 || con->reportataioctl || con->reportscsiioctl /*|| con->controller_port???*/) {
     openlog("smartd", LOG_PID, facility);
     vsyslog(LOG_INFO, fmt, ap);
     closelog();
@@ -983,6 +1239,11 @@ void PrintOut(int priority, const char *fmt, ...){
   return;
 }
 
+// Used to warn users about invalid checksums. Called from atacmds.cpp.
+void checksumwarning(const char * string)
+{
+  pout("Warning! %s error: invalid SMART checksum.\n", string);
+}
 
 // Wait for the pid file to show up, this makes sure a calling program knows
 // that the daemon is really up and running and has a pid to kill it
@@ -991,11 +1252,11 @@ bool WaitForPidFile()
     int waited, max_wait = 10;
     struct stat stat_buf;
 
-    if(!pid_file || debugmode)
+    if (pid_file.empty() || debugmode)
        return true;
 
     for(waited = 0; waited < max_wait; ++waited) {
-       if(stat(pid_file, &stat_buf) == 0) {
+       if (!stat(pid_file.c_str(), &stat_buf)) {
                return true;
        } else
                sleep(1);
@@ -1006,7 +1267,7 @@ bool WaitForPidFile()
 
 // 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
+// http://www.linuxjournal.com/article/2335
 // for a good description of why we do things this way.
 void DaemonInit(){
 #ifndef _WIN32
@@ -1023,13 +1284,14 @@ void DaemonInit(){
       PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
       EXIT(EXIT_STARTUP);
     }
-    else if (pid)
+    else if (pid) {
       // we are the parent process, wait for pid file, then exit cleanly
       if(!WaitForPidFile()) {
-        PrintOut(LOG_CRIT,"PID file %s didn't show up!\n", pid_file);
+        PrintOut(LOG_CRIT,"PID file %s didn't show up!\n", pid_file.c_str());
        EXIT(EXIT_STARTUP);
       } else
         EXIT(0);
+    }
   
     // from here on, we are the child process.
     setsid();
@@ -1056,14 +1318,18 @@ void DaemonInit(){
   FreeConsole();
 #endif // __CYGWIN__
 
+#define NO_warn_unused_result(cmd) { if (cmd) {} ; }
+
   // redirect any IO attempts to /dev/null for stdin
   i=open("/dev/null",O_RDWR);
-  // stdout
-  dup(i);
-  // stderr
-  dup(i);
-  umask(0);
-  chdir("/");
+  if (i>=0) {
+    // stdout
+    NO_warn_unused_result(dup(i));
+    // stderr
+    NO_warn_unused_result(dup(i));
+  };
+  umask(0022);
+  NO_warn_unused_result(chdir("/"));
 
   if (do_fork)
     PrintOut(LOG_INFO, "smartd has fork()ed into background mode. New PID=%d.\n", (int)getpid());
@@ -1084,58 +1350,43 @@ void DaemonInit(){
 }
 
 // create a PID file containing the current process id
-void WritePidFile() {
-  if (pid_file) {
-    int error = 0;
+static void WritePidFile()
+{
+  if (!pid_file.empty()) {
     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");
+
+    stdio_file f(pid_file.c_str(), "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);
+    if (!(f && fprintf(f, "%d\n", (int)pid) > 0 && f.close())) {
+      PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file.c_str());
       EXIT(EXIT_PID);
     }
-    PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file, (int)pid);
+    PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file.c_str(), (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-8 Bruce Allen\n", PACKAGE_VERSION, ver);
-  PrintOut(LOG_INFO,"Home page is " PACKAGE_HOMEPAGE "\n\n");
-  return;
+static void PrintHead()
+{
+  PrintOut(LOG_INFO, "%s\n", format_version_info("smartd").c_str());
 }
 
 // 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, cciss,N\n"
+           "  -d TYPE Set the device type: %s\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"
+           "  -n MODE No check if: never, sleep[,N][,q], standby[,N][,q], idle[,N][,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"
@@ -1149,8 +1400,8 @@ void Directives() {
            "  -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"
+           "  -C ID[+] Monitor [increases of] Current Pending Sectors in Attribute ID\n"
+           "  -U ID[+] Monitor [increases of] 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"
@@ -1161,7 +1412,7 @@ void Directives() {
            "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);
+           configfile, smi()->get_valid_dev_types_str());
   return;
 }
 
@@ -1191,7 +1442,6 @@ const char *GetValidArgList(char opt) {
 /* 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");
@@ -1218,81 +1468,54 @@ void Usage (void){
   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'));
+  PrintOut(LOG_INFO,"  -s PREFIX, --savestates=PREFIX\n");
+  PrintOut(LOG_INFO,"        Save disk states to {PREFIX}MODEL-SERIAL.TYPE.state\n");
+#ifdef SMARTMONTOOLS_SAVESTATES
+  PrintOut(LOG_INFO,"        [default is "SMARTMONTOOLS_SAVESTATES"MODEL-SERIAL.TYPE.state]\n");
+#endif
+  PrintOut(LOG_INFO,"\n");
+  PrintOut(LOG_INFO,"  -B [+]FILE, --drivedb=[+]FILE\n");
+  PrintOut(LOG_INFO,"        Read and replace [add] drive database from FILE\n");
+#ifdef SMARTMONTOOLS_DRIVEDBDIR
+  PrintOut(LOG_INFO,"        [default is "SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h]\n");
+#endif
+  PrintOut(LOG_INFO,"\n");
 #ifdef _WIN32
   PrintOut(LOG_INFO,"  --service\n");
   PrintOut(LOG_INFO,"        Running as windows service (see man page), install with:\n");
   PrintOut(LOG_INFO,"          smartd install [options]\n");
   PrintOut(LOG_INFO,"        Remove service with:\n");
   PrintOut(LOG_INFO,"          smartd remove\n\n");
-#else
-#endif // _WIN32 || __CYGWIN__
+#endif // _WIN32
   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,"  -n         Do not fork into background\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);
+static int CloseDevice(smart_device * device, const char * name)
+{
+  if (!device->close()){
+    PrintOut(LOG_INFO,"Device: %s, %s, close() failed\n", name, device->get_errmsg());
     return 1;
   }
   // device sucessfully closed
   return 0;
 }
 
+// return true if a char is not allowed in a state file name
+static bool not_allowed_in_filename(char c)
+{
+  return !(   ('0' <= c && c <= '9')
+           || ('A' <= c && c <= 'Z')
+           || ('a' <= c && c <= 'z'));
+}
+
 // returns <0 on failure
-int ATAErrorCount(int fd, char *name){
+static int ATAErrorCount(ata_device * device, const char * name,
+                         unsigned char fix_firmwarebug)
+{
   struct ata_smart_errorlog log;
   
-  if (-1==ataReadErrorLog(fd,&log)){
+  if (ataReadErrorLog(device, &log, fix_firmwarebug)){
     PrintOut(LOG_INFO,"Device: %s, Read SMART Error Log Failed\n",name);
     return -1;
   }
@@ -1303,102 +1526,105 @@ int ATAErrorCount(int fd, char *name){
 
 // 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){
+static int SelfTestErrorCount(ata_device * device, const char * name,
+                              unsigned char fix_firmwarebug)
+{
   struct ata_smart_selftestlog log;
 
-  if (-1==ataReadSelfTestLog(fd,&log)){
+  if (ataReadSelfTestLog(device, &log, fix_firmwarebug)){
     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);
+  return ataPrintSmartSelfTestlog(&log, false, fix_firmwarebug);
+}
+
+#define SELFTEST_ERRORCOUNT(x) (x & 0xff)
+#define SELFTEST_ERRORHOURS(x) ((x >> 8) & 0xffff)
+
+// Log self-test execution status
+static void log_self_test_exec_status(const char * name, unsigned char status)
+{
+  const char * msg;
+  switch (status >> 4) {
+    case 0x0: msg = "completed without error"; break;
+    case 0x1: msg = "was aborted by the host"; break;
+    case 0x2: msg = "was interrupted by the host with a reset"; break;
+    case 0x3: msg = "could not complete due to a fatal or unknown error"; break;
+    case 0x4: msg = "completed with error (unknown test element)"; break;
+    case 0x5: msg = "completed with error (electrical test element)"; break;
+    case 0x6: msg = "completed with error (servo/seek test element)"; break;
+    case 0x7: msg = "completed with error (read test element)"; break;
+    case 0x8: msg = "completed with error (handling damage?)"; break;
+    default:  msg = 0;
+  }
+
+  if (msg)
+    PrintOut(((status >> 4) >= 0x4 ? LOG_CRIT : LOG_INFO),
+             "Device: %s, previous self-test %s\n", name, msg);
+  else if ((status >> 4) == 0xf)
+    PrintOut(LOG_INFO, "Device: %s, self-test in progress, %u0%% remaining\n",
+             name, status & 0x0f);
+  else
+    PrintOut(LOG_INFO, "Device: %s, unknown self-test status 0x%02x\n",
+             name, status);
 }
 
+
+// TODO: Add '-F swapid' directive
+const bool fix_swapped_id = false;
+
 // scan to see what ata devices there are, and if they support SMART
-int ATADeviceScan(cfgfile *cfg, int scanning){
-  int fd, supported=0;
+static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atadev)
+{
+  int supported=0;
   struct ata_identify_device drive;
-  char *name=cfg->name;
-  int retainsmartdata=0;
+  const char *name = cfg.name.c_str();
   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;
-  
+
+  // Device must be open
+
   // Get drive identity structure
-  if ((retid=ataReadHDIdentity (fd,&drive))){
+  if ((retid=ataReadHDIdentity (atadev, &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);
+    CloseDevice(atadev, name);
     return 2; 
   }
+  // Store drive size (for selective self-test only)
+  state.num_sectors = get_num_sectors(&drive);
 
   // Show if device in database, and use preset vendor attribute
   // options unless user has requested otherwise.
-  if (cfg->ignorepresets)
+  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)
+    // do whatever applypresets decides to do.
+    if (!apply_presets(&drive, cfg.attributedefs, cfg.fix_firmwarebug, fix_swapped_id))
       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;
   }
-  
+
+  // Set default '-C 197[+]' if no '-C ID' is specified.
+  if (!cfg.curr_pending_set)
+    cfg.curr_pending_id = get_unc_attr_id(false, cfg.attributedefs, cfg.curr_pending_incr);
+  // Set default '-U 198[+]' if no '-U ID' is specified.
+  if (!cfg.offl_pending_set)
+    cfg.offl_pending_id = get_unc_attr_id(true, cfg.attributedefs, cfg.offl_pending_incr);
+
   // If requested, show which presets would be used for this drive
-  if (cfg->showpresets) {
+  if (cfg.showpresets) {
     int savedebugmode=debugmode;
     PrintOut(LOG_INFO, "Device %s: presets are:\n", name);
     if (!debugmode)
       debugmode=2;
-    showpresets(&drive);
+    show_presets(&drive, false);
     debugmode=savedebugmode;
   }
 
@@ -1413,43 +1639,43 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
       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){
+    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);
+      CloseDevice(atadev, name);
       return 2;
     }
   }
   
-  if (ataEnableSmart(fd)){
+  if (ataEnableSmart(atadev)) {
     // Enable SMART command has failed
     PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name);
-    CloseDevice(fd, name);
+    CloseDevice(atadev, name);
     return 2; 
   }
   
   // disable device attribute autosave...
-  if (cfg->autosave==1){
-    if (ataDisableAutoSave(fd))
+  if (cfg.autosave==1) {
+    if (ataDisableAutoSave(atadev))
       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))
+  if (cfg.autosave==2) {
+    if (ataEnableAutoSave(atadev))
       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){
+  if (cfg.smartcheck && ataSmartStatus2(atadev) == -1) {
     PrintOut(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",name);
-    cfg->smartcheck=0;
+    cfg.smartcheck = false;
   }
   
   // capability check: Read smart values and thresholds.  Note that
@@ -1459,66 +1685,56 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
   // 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;
+  bool 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;
+  bool smart_val_ok = false;
+  if (   retainsmartdata || cfg.autoofflinetest || cfg.selftest || cfg.errorlog
+      || cfg.curr_pending_id || cfg.offl_pending_id                            ) {
 
-    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(atadev, &state.smartval) ||
+        ataReadSmartThresholds (atadev, &state.smartthres)) {
+      PrintOut(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",name);
+      retainsmartdata = cfg.usagefailed = cfg.prefail = cfg.usage = false;
+      cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0;
+      cfg.curr_pending_id = cfg.offl_pending_id = 0;
     }
-    
-    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;
-    }
-    
+    else
+      smart_val_ok = true;
+
     // 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) {
+    if (cfg.curr_pending_id && ATAReturnAttributeRawValue(cfg.curr_pending_id, &state.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;
+               name, cfg.curr_pending_id);
+      cfg.curr_pending_id = 0;
     }
     
-    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.offl_pending_id && ATAReturnAttributeRawValue(cfg.offl_pending_id, &state.smartval) < 0) {
+      PrintOut(LOG_INFO,"Device: %s, can't monitor Offline Uncorrectable Sector count - no Attribute %d\n",
+               name, cfg.offl_pending_id);
+      cfg.offl_pending_id = 0;
     }
 
-    if (   (cfg->tempdiff || cfg->tempinfo || cfg->tempcrit)
-        && !ATAReturnTemperatureValue(cfg->smartval, cfg->attributedefs)) {
+    if (   (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)
+        && !ATAReturnTemperatureValue(&state.smartval, cfg.attributedefs)) {
       PrintOut(LOG_CRIT, "Device: %s, can't monitor Temperature, ignoring -W Directive\n", name);
-      cfg->tempdiff = cfg->tempinfo = cfg->tempcrit = 0;
+      cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0;
     }
   }
   
   // enable/disable automatic on-line testing
-  if (cfg->autoofflinetest){
+  if (cfg.autoofflinetest) {
     // is this an enable or disable request?
-    const char *what=(cfg->autoofflinetest==1)?"disable":"enable";
-    if (!cfg->smartval)
+    const char *what=(cfg.autoofflinetest==1)?"disable":"enable";
+    if (!smart_val_ok)
       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))
+      if (!isSupportAutomaticTimer(&state.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))
+      if ((cfg.autoofflinetest==1)?ataDisableAutoOffline(atadev):ataEnableAutoOffline(atadev))
         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);
@@ -1526,191 +1742,121 @@ int ATADeviceScan(cfgfile *cfg, int scanning){
   }
   
   // capability check: self-test-log
-  if (cfg->selftest){
+  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;
+    cfg.selftest = false;
+    state.selflogcount = 0;
+    state.selfloghour = 0;
 
-    if (!cfg->smartval)
+    if (!smart_val_ok)
       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))
+    else if (!cfg.permissive && !isSmartTestLogCapable(&state.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)
+    else if ((retval = SelfTestErrorCount(atadev, name, cfg.fix_firmwarebug)) < 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);
+      cfg.selftest = true;
+      state.selflogcount=SELFTEST_ERRORCOUNT(retval);
+      state.selfloghour =SELFTEST_ERRORHOURS(retval);
     }
   }
   
   // capability check: ATA error log
-  if (cfg->errorlog){
+  if (cfg.errorlog) {
     int val;
 
     // start with service disabled, and re-enable it if all works OK
-    cfg->errorlog=0;
-    cfg->ataerrorcount=0;
+    cfg.errorlog = false;
+    state.ataerrorcount=0;
 
-    if (!cfg->smartval)
+    if (!smart_val_ok)
       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))
+    else if (!cfg.permissive && !isSmartErrorLogCapable(&state.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)
+    else if ((val = ATAErrorCount(atadev, name, cfg.fix_firmwarebug)) < 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;
+        cfg.errorlog = true;
+        state.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 (cfg.powermode) {
+    int powermode = ataCheckPowerMode(atadev);
     
     if (-1 == powermode) {
       PrintOut(LOG_CRIT, "Device: %s, no ATA CHECK POWER STATUS support, ignoring -n Directive\n", name);
-      cfg->powermode=0;
+      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;
+      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);
+  if (!(cfg.errorlog    || cfg.selftest || cfg.smartcheck ||
+        cfg.usagefailed || cfg.prefail  || cfg.usage      ||
+        cfg.tempdiff    || cfg.tempinfo || cfg.tempcrit     )) {
+    CloseDevice(atadev, name);
     return 3;
   }
   
-  // Do we still have entries available?
-  while (numdevata+numdevscsi>=ATAandSCSIdevlist_max)
-    ATAandSCSIdevlist=AllocateMoreSpace(ATAandSCSIdevlist, &ATAandSCSIdevlist_max, "ATA and SCSI devices");
-  
-  // register device
+  // tell user we are registering 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;
+  // record number of device, type of device, increment device count
+  if (cfg.dev_type.empty())
+    cfg.dev_type = "ata";
   
   // close file descriptor
-  CloseDevice(fd, name);
-  return 0;
-}
-
-// Returns 0 if normal SCSI device.
-// Returns -1 if INQUIRY fails.
-// Returns 2 if ATA device detected behind SAT layer.
-// Returns 3 if ATA device detected behind Marvell controller.
-// Returns 1 if other device detected that we don't want to treat
-//     as a normal SCSI device.
-static int SCSIFilterKnown(int fd, char * device)
-{
-  char req_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 SCSIFK_FAILED;
-    }
-  }
-  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 SCSIFK_3WARE;
-    } else if ((len >= 42) && (0 == strncmp(req_buff + 36, "MVSATA", 6))) {
-      PrintOut(LOG_INFO, "Device %s: using '-d marvell' for ATA disk with Marvell driver\n", device);
-      return SCSIFK_MARVELL;
-    } else if ((avail_len >= 36) &&
-               (0 == strncmp(req_buff + 8, "ATA     ", 8)) &&
-              has_sat_pass_through(fd, 0 /* non-packet dev */)) {
-      PrintOut(LOG_INFO, "Device %s: using '-d sat' for ATA disk behind SAT layer.\n",
-               device);
-      return SCSIFK_SAT;
+  CloseDevice(atadev, name);
+
+  if (!state_path_prefix.empty() || !attrlog_path_prefix.empty()) {
+    // Build file name for state file
+    char model[40+1], serial[20+1];
+    format_ata_string(model, drive.model, sizeof(model)-1, fix_swapped_id);
+    format_ata_string(serial, drive.serial_no, sizeof(serial)-1, fix_swapped_id);
+    std::replace_if(model, model+strlen(model), not_allowed_in_filename, '_');
+    std::replace_if(serial, serial+strlen(serial), not_allowed_in_filename, '_');
+    if (!state_path_prefix.empty()) {
+      cfg.state_file = strprintf("%s%s-%s.ata.state", state_path_prefix.c_str(), model, serial);
+      // Read previous state
+      if (read_dev_state(cfg.state_file.c_str(), state)) {
+        PrintOut(LOG_INFO, "Device: %s, state read from %s\n", name, cfg.state_file.c_str());
+        // Copy ATA attribute values to temp state
+        state.update_temp_state();
+      }
     }
+    if (!attrlog_path_prefix.empty())
+      cfg.attrlog_file = strprintf("%s%s-%s.ata.csv", attrlog_path_prefix.c_str(), model, serial);
   }
-  return SCSIFK_NORMAL;
+
+  // Start self-test regex check now if time was not read from state file
+  if (!cfg.test_regex.empty() && !state.scheduled_test_next_check)
+    state.scheduled_test_next_check = time(0);
+
+  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, retval; 
-  char *device = cfg->name;
+static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scsidev)
+{
+  int k, err;
+  const char *device = cfg.name.c_str();
   struct scsi_iec_mode_page iec;
   UINT8  tBuf[64];
-  char *mode=NULL;
-  
-  // should we try to register this as a SCSI device?
-  switch (cfg->controller_type) {
-  case CONTROLLER_SCSI:
-  case CONTROLLER_UNKNOWN:
-    mode="SCSI";
-    break;
-  case CONTROLLER_CCISS:
-    mode="CCISS";
-    break;
-  default:
-    return 1;
-  }
-  // pass user settings on to low-level SCSI commands
-  con->controller_port=cfg->controller_port;
-  con->controller_type=cfg->controller_type;
-  
-  // open the device
-  if ((fd = OpenDevice(device, mode, 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 ((retval = SCSIFilterKnown(fd, device))) {
-    CloseDevice(fd, device);
+  // Device must be open
 
-    if (retval==SCSIFK_SAT)
-       // SATA Device behind SAT layer
-       return SCSIFK_SAT;
-
-    if (retval==SCSIFK_MARVELL)
-        // ATA/SATA device behind Marvell driver
-       return SCSIFK_MARVELL;
-    
-    return 2;
-  }
-    
   // check that device is ready for commands. IE stores its stuff on
   // the media.
-  if ((err = scsiTestUnitReady(fd))) {
+  if ((err = scsiTestUnitReady(scsidev))) {
     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)
@@ -1719,7 +1865,7 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
       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);
+    CloseDevice(scsidev, device);
     return 2; 
   }
   
@@ -1729,15 +1875,15 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
   // 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;
+  if (!(err = scsiFetchIECmpage(scsidev, &iec, state.modese_len)))
+    state.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);
+    CloseDevice(scsidev, device);
     return 3;
   }
   
@@ -1747,24 +1893,20 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
     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);
+    CloseDevice(scsidev, device);
     return 3;
   }
   
-  // Device exists, and does SMART.  Add to list (allocating more space if needed)
-  while (numdevscsi+numdevata >= ATAandSCSIdevlist_max)
-    ATAandSCSIdevlist=AllocateMoreSpace(ATAandSCSIdevlist, &ATAandSCSIdevlist_max, "ATA and SCSI devices");
-  
   // 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)) {
+  if (0 == scsiLogSense(scsidev, SUPPORTED_LPAGES, 0, tBuf, sizeof(tBuf), 0)) {
     for (k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) {
       switch (tBuf[k]) { 
       case TEMPERATURE_LPAGE:
-        cfg->TempPageSupported = 1;
+        state.TempPageSupported = 1;
         break;
       case IE_LPAGE:
-        cfg->SmartPageSupported = 1;
+        state.SmartPageSupported = 1;
         break;
       default:
         break;
@@ -1773,16 +1915,8 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
   }
   
   // record type of device
-  if (cfg->controller_type == CONTROLLER_UNKNOWN)
-          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);
+  if (cfg.dev_type.empty())
+    cfg.dev_type = "scsi";
   
   // Check if scsiCheckIE() is going to work
   {
@@ -1791,45 +1925,45 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
     UINT8 currenttemp = 0;
     UINT8 triptemp = 0;
     
-    if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
+    if (scsiCheckIE(scsidev, state.SmartPageSupported, state.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) {
+      state.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;
+        cfg.tempdiff = cfg.tempinfo = cfg.tempcrit = 0;
       }
     }
   }
   
   // capability check: self-test-log
-  if (cfg->selftest){
-    int retval=scsiCountFailedSelfTests(fd, 0);
+  if (cfg.selftest){
+    int retval = scsiCountFailedSelfTests(scsidev, 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;
+      cfg.selftest = false;
+      state.selflogcount = 0;
+      state.selfloghour = 0;
     }
     else {
       // register starting values to watch for changes
-      cfg->selflogcount=SELFTEST_ERRORCOUNT(retval);
-      cfg->selfloghour =SELFTEST_ERRORHOURS(retval);
+      state.selflogcount=SELFTEST_ERRORCOUNT(retval);
+      state.selfloghour =SELFTEST_ERRORHOURS(retval);
     }
   }
   
   // disable autosave (set GLTSD bit)
-  if (cfg->autosave==1){
-    if (scsiSetControlGLTSD(fd, 1, cfg->modese_len))
+  if (cfg.autosave==1){
+    if (scsiSetControlGLTSD(scsidev, 1, state.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))
+  if (cfg.autosave==2){
+    if (scsiSetControlGLTSD(scsidev, 0, state.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);
@@ -1837,43 +1971,44 @@ static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
   
   // 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;
-}
 
-// modified treatment of SCSI device behind SAT layer
-static int SCSIandSATDeviceScan(cfgfile *cfg, int scanning) {
-  int retval = SCSIDeviceScan(cfg, scanning);
-  cfg->WhichCheckDevice=1; // default SCSI device
-  
-  if (retval==SCSIFK_SAT) {
-    // found SATA device behind SAT translation layer  
-    cfg->controller_type=CONTROLLER_SAT;
-    cfg->WhichCheckDevice=0; // actually SATA device!
-    return ATADeviceScan(cfg, scanning);
+  // TODO: Build file name for state file
+  if (!state_path_prefix.empty()) {
+    PrintOut(LOG_INFO, "Device: %s, persistence not yet supported for SCSI; ignoring -s option.\n", device);
   }
-
-  if (retval==SCSIFK_MARVELL) {
-    // found SATA device behind Marvell controller
-    cfg->controller_type=CONTROLLER_MARVELL_SATA;
-    cfg->WhichCheckDevice=0; // actually SATA device!
-    return ATADeviceScan(cfg, scanning);
+  // TODO: Build file name for attribute log file
+  if (!attrlog_path_prefix.empty()) {
+    PrintOut(LOG_INFO, "Device: %s, attribute log not yet supported for SCSI; ignoring -A option.\n", device);
   }
 
-  return retval; 
+  // close file descriptor
+  CloseDevice(scsidev, device);
+
+  // Start self-test regex check now if time was not read from state file
+  if (!cfg.test_regex.empty() && !state.scheduled_test_next_check)
+    state.scheduled_test_next_check = time(0);
+
+  return 0;
 }
 
 
+struct changedattribute_t {
+  unsigned char newval;
+  unsigned char oldval;
+  unsigned char id;
+  unsigned char prefail;
+  unsigned char sameraw;
+};
+
 // 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,
+static 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){
+                            int n, const char * name)
+{
   struct ata_smart_attribute *now,*was;
   struct ata_smart_threshold_entry *thre;
   unsigned char oldval,newval;
@@ -1925,74 +2060,31 @@ int  ATACompareValues(changedattribute_t *delta,
   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;
+static void CheckSelfTestLogs(const dev_config & cfg, dev_state & state, int newi)
+{
+  const char * name = cfg.name.c_str();
 
   if (newi<0)
     // command failed
-    MailWarning(cfg, 8, "Device: %s, Read SMART Self-Test Log Failed", name);
+    MailWarning(cfg, state, 8, "Device: %s, Read SMART Self-Test Log Failed", name);
   else {      
     // old and new error counts
-    int oldc=cfg->selflogcount;
+    int oldc=state.selflogcount;
     int newc=SELFTEST_ERRORCOUNT(newi);
     
     // old and new error timestamps in hours
-    int oldh=cfg->selfloghour;
+    int oldh=state.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",
+      MailWarning(cfg, state, 3, "Device: %s, Self-Test Log error count increased from %d to %d",
                    name, oldc, newc);
+      state.must_write = true;
     } else if (oldh!=newh) {
       // more recent error
       // a 'more recent' error might actually be a smaller hour number,
@@ -2003,110 +2095,141 @@ void CheckSelfTestLogs(cfgfile *cfg, int newi){
       // 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",
+      MailWarning(cfg, state, 3, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
                    name, newh);
+      state.must_write = true;
     }
     
     // Needed since self-test error count may DECREASE.  Hour might
     // also have changed.
-    cfg->selflogcount= newc;
-    cfg->selfloghour = newh;
+    state.selflogcount= newc;
+    state.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;
+// Test types, ordered by priority.
+static const char test_type_chars[] = "LncrSCO";
+const unsigned num_test_types = sizeof(test_type_chars)-1;
 
+// returns test type if time to do test of type testtype,
+// 0 if not time to do test.
+static char next_scheduled_test(const dev_config & cfg, dev_state & state, bool scsi, time_t usetime = 0)
+{
   // check that self-testing has been requested
-  if (!dat)
+  if (cfg.test_regex.empty())
     return 0;
-  
+
+  // Exit if drive not capable of any test
+  if ( state.not_cap_long && state.not_cap_short &&
+      (scsi || (state.not_cap_conveyance && state.not_cap_offline)))
+    return 0;
+
   // since we are about to call localtime(), be sure glibc is informed
   // of any timezone changes we make.
-  if (!testtime)
+  if (!usetime)
     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);
+  // Is it time for next check?
+  time_t now = (!usetime ? time(0) : usetime);
+  if (now < state.scheduled_test_next_check)
     return 0;
+
+  // Limit time check interval to 90 days
+  if (state.scheduled_test_next_check + (3600L*24*90) < now)
+    state.scheduled_test_next_check = now - (3600L*24*90);
+
+  // Check interval [state.scheduled_test_next_check, now] for scheduled tests
+  char testtype = 0;
+  time_t testtime = 0; int testhour = 0;
+  int maxtest = num_test_types-1;
+
+  for (time_t t = state.scheduled_test_next_check; ; ) {
+    struct tm * tms = localtime(&t);
+    // tm_wday is 0 (Sunday) to 6 (Saturday).  We use 1 (Monday) to 7 (Sunday).
+    int weekday = (tms->tm_wday ? tms->tm_wday : 7);
+    for (int i = 0; i <= maxtest; i++) {
+      // Skip if drive not capable of this test
+      switch (test_type_chars[i]) {
+        case 'L': if (state.not_cap_long)       continue; break;
+        case 'S': if (state.not_cap_short)      continue; break;
+        case 'C': if (scsi || state.not_cap_conveyance) continue; break;
+        case 'O': if (scsi || state.not_cap_offline)    continue; break;
+        case 'c': case 'n':
+        case 'r': if (scsi || state.not_cap_selective)  continue; break;
+        default: continue;
+      }
+      // Try match of "T/MM/DD/d/HH"
+      char pattern[16];
+      snprintf(pattern, sizeof(pattern), "%c/%02d/%02d/%1d/%02d",
+        test_type_chars[i], tms->tm_mon+1, tms->tm_mday, weekday, tms->tm_hour);
+      if (cfg.test_regex.full_match(pattern)) {
+        // Test found
+        testtype = pattern[0];
+        testtime = t; testhour = tms->tm_hour;
+        // Limit further matches to higher priority self-tests
+        maxtest = i-1;
+        break;
+      }
+    }
+    // Exit if no tests left or current time reached
+    if (maxtest < 0)
+      break;
+    if (t >= now)
+      break;
+    // Check next hour
+    if ((t += 3600) > now)
+      t = now;
+  }
+  
+  // Do next check not before next hour.
+  struct tm * tmnow = localtime(&now);
+  state.scheduled_test_next_check = now + (3600 - tmnow->tm_min*60 - tmnow->tm_sec);
+
+  if (testtype) {
+    state.must_write = true;
+    // Tell user if an old test was found.
+    if (!usetime && !(testhour == tmnow->tm_hour && testtime + 3600 > now)) {
+      char datebuf[DATEANDEPOCHLEN]; dateandtimezoneepoch(datebuf, testtime);
+      PrintOut(LOG_INFO, "Device: %s, old test of type %c not run at %s, starting now.\n",
+        cfg.name.c_str(), testtype, datebuf);
+    }
   }
-  
-  // save time and type of the current test; we are ready to do a test
-  dat->hour=hours;
-  dat->testtype=testtype;
-  return 1;
+
+  return testtype;
 }
 
 // Print a list of future tests.
-void PrintTestSchedule(cfgfile **ATAandSCSIdevices){
-  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)
+static void PrintTestSchedule(const dev_config_vector & configs, dev_state_vector & states, const smart_device_list & devices)
+{
+  unsigned numdev = configs.size();
+  if (!numdev)
     return;
+  std::vector<int> testcnts(numdev * num_test_types, 0);
 
   PrintOut(LOG_INFO, "\nNext scheduled self tests (at most 5 of each type per device):\n");
 
   // FixGlibcTimeZoneBug(); // done in PrintOut()
-  now=time(NULL);
+  time_t now = time(0);
+  char datenow[DATEANDEPOCHLEN], date[DATEANDEPOCHLEN];
   dateandtimezoneepoch(datenow, now);
+
+  long seconds;
   for (seconds=checktime; 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 = ATAandSCSIdevices[i];
-      for (t=0; t<(cfg->WhichCheckDevice==0?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);
-          }
+    for (unsigned i = 0; i < numdev; i++) {
+      const dev_config & cfg = configs.at(i);
+      dev_state & state = states.at(i);
+      const char * p;
+      char testtype = next_scheduled_test(cfg, state, devices.at(i)->is_scsi(), testtime);
+      if (testtype && (p = strchr(test_type_chars, testtype))) {
+        unsigned t = (p - test_type_chars);
+        // Report at most 5 tests of each type
+        if (++testcnts[i*num_test_types + t] <= 5) {
+          dateandtimezoneepoch(date, testtime);
+          PrintOut(LOG_INFO, "Device: %s, will do test %d of type %c at %s\n", cfg.name.c_str(),
+            testcnts[i*num_test_types + t], testtype, date);
         }
       }
     }
@@ -2115,28 +2238,32 @@ void PrintTestSchedule(cfgfile **ATAandSCSIdevices){
   // Report totals
   dateandtimezoneepoch(date, now+seconds);
   PrintOut(LOG_INFO, "\nTotals [%s - %s]:\n", datenow, date);
-  for (i=0; i<numdev; i++) {
-      cfg = ATAandSCSIdevices[i];
-    for (t=0; t<(cfg->WhichCheckDevice==0?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]);
+  for (unsigned i = 0; i < numdev; i++) {
+    const dev_config & cfg = configs.at(i);
+    bool scsi = devices.at(i)->is_scsi();
+    for (unsigned t = 0; t < num_test_types; t++) {
+      int cnt = testcnts[i*num_test_types + t];
+      if (cnt == 0 && !strchr((scsi ? "LS" : "LSCO"), test_type_chars[t]))
+        continue;
+      PrintOut(LOG_INFO, "Device: %s, will do %3d test%s of type %c\n", cfg.name.c_str(),
+        cnt, (cnt==1?"":"s"), test_type_chars[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) {
+static int DoSCSISelfTest(const dev_config & cfg, dev_state & state, scsi_device * device, char testtype)
+{
   int retval = 0;
-  char *testname = NULL;
-  char *name = cfg->name;
+  const char *testname = 0;
+  const char *name = cfg.name.c_str();
   int inProgress;
 
-  if (scsiSelfTestInProgress(fd, &inProgress)) {
+  if (scsiSelfTestInProgress(device, &inProgress)) {
     PrintOut(LOG_CRIT, "Device: %s, does not support Self-Tests\n", name);
-    cfg->testdata->not_cap_short=cfg->testdata->not_cap_long=1;
+    state.not_cap_short = state.not_cap_long = true;
     return 1;
   }
 
@@ -2149,11 +2276,11 @@ int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) {
   switch (testtype) {
   case 'S':
     testname = "Short Self";
-    retval = scsiSmartShortSelfTest(fd);
+    retval = scsiSmartShortSelfTest(device);
     break;
   case 'L':
     testname = "Long Self";
-    retval = scsiSmartExtendSelfTest(fd);
+    retval = scsiSmartExtendSelfTest(device);
     break;
   }
   // If we can't do the test, exit
@@ -2168,9 +2295,9 @@ int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) {
       PrintOut(LOG_CRIT, "Device: %s, not capable of %s-Test\n", name, 
                testname);
       if ('L'==testtype)
-        cfg->testdata->not_cap_long=1;
+        state.not_cap_long = true;
       else
-        cfg->testdata->not_cap_short=1;
+        state.not_cap_short = true;
      
       return 1;
     }
@@ -2186,48 +2313,62 @@ int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) {
 
 // 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;
-  
+static int DoATASelfTest(const dev_config & cfg, dev_state & state, ata_device * device, char testtype)
+{
+  const char *name = cfg.name.c_str();
+
   // Read current smart data and check status/capability
-  if (ataReadSmartValues(fd, &data) || !(data.offline_data_collection_capability)) {
+  struct ata_smart_values data;
+  if (ataReadSmartValues(device, &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
+  int dotest = -1, mode = 0;
+  const char *testname = 0;
   switch (testtype) {
   case 'O':
     testname="Offline Immediate ";
     if (isSupportExecuteOfflineImmediate(&data))
       dotest=OFFLINE_FULL_SCAN;
     else
-      cfg->testdata->not_cap_offline=1;
+      state.not_cap_offline = true;
     break;
   case 'C':
     testname="Conveyance Self-";
     if (isSupportConveyanceSelfTest(&data))
       dotest=CONVEYANCE_SELF_TEST;
     else
-      cfg->testdata->not_cap_conveyance=1;
+      state.not_cap_conveyance = true;
     break;
   case 'S':
     testname="Short Self-";
     if (isSupportSelfTest(&data))
       dotest=SHORT_SELF_TEST;
     else
-      cfg->testdata->not_cap_short=1;
+      state.not_cap_short = true;
     break;
   case 'L':
     testname="Long Self-";
     if (isSupportSelfTest(&data))
       dotest=EXTEND_SELF_TEST;
     else
-      cfg->testdata->not_cap_long=1;
+      state.not_cap_long = true;
+    break;
+
+  case 'c': case 'n': case 'r':
+    testname = "Selective Self-";
+    if (isSupportSelectiveSelfTest(&data)) {
+      dotest = SELECTIVE_SELF_TEST;
+      switch (testtype) {
+        case 'c': mode = SEL_CONT; break;
+        case 'n': mode = SEL_NEXT; break;
+        case 'r': mode = SEL_REDO; break;
+      }
+    }
+    else
+      state.not_cap_selective = true;
     break;
   }
   
@@ -2239,7 +2380,7 @@ int DoATASelfTest(int fd, cfgfile *cfg, char testtype) {
   
   // If currently running a self-test, do not interrupt it to start another.
   if (15==(data.self_test_exec_status >> 4)) {
-    if (cfg->fixfirmwarebug == FIX_SAMSUNG3 && data.self_test_exec_status == 0xf0) {
+    if (cfg.fix_firmwarebug == FIX_SAMSUNG3 && data.self_test_exec_status == 0xf0) {
       PrintOut(LOG_INFO, "Device: %s, will not skip scheduled %sTest "
                "despite unclear Self-Test byte (SAMSUNG Firmware bug).\n", name, testname);
     } else {
@@ -2249,132 +2390,173 @@ int DoATASelfTest(int fd, cfgfile *cfg, char testtype) {
     }
   }
 
-  // else execute the test, and return status
-  if ((retval=smartcommandhandler(fd, IMMEDIATE_OFFLINE, dotest, NULL)))
+  if (dotest == SELECTIVE_SELF_TEST) {
+    // Set test span
+    ata_selective_selftest_args selargs;
+    selargs.num_spans = 1;
+    selargs.span[0].mode = mode;
+    if (ataWriteSelectiveSelfTestLog(device, selargs, &data, state.num_sectors)) {
+      PrintOut(LOG_CRIT, "Device: %s, prepare %sTest failed\n", name, testname);
+      return 1;
+    }
+    uint64_t start = selargs.span[0].start, end = selargs.span[0].end;
+    PrintOut(LOG_INFO, "Device: %s, %s test span at LBA %"PRIu64" - %"PRIu64" (%"PRIu64" sectors, %u%% - %u%% of disk).\n",
+      name, (selargs.span[0].mode == SEL_NEXT ? "next" : "redo"),
+      start, end, end - start + 1,
+      (unsigned)((100 * start + state.num_sectors/2) / state.num_sectors),
+      (unsigned)((100 * end   + state.num_sectors/2) / state.num_sectors));
+  }
+
+  // execute the test, and return status
+  int retval = smartcommandhandler(device, IMMEDIATE_OFFLINE, dotest, NULL);
+  if (retval) {
     PrintOut(LOG_CRIT, "Device: %s, execute %sTest failed.\n", name, testname);
+    return retval;
+  }
+
+  if (testtype != 'O')
+    // Log next self-test execution status
+    state.smartval.self_test_exec_status = 0xff;
+
+  PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname);
+  return 0;
+}
+
+// Check pending sector count attribute values (-C, -U directives).
+static void check_pending(const dev_config & cfg, dev_state & state,
+                          unsigned char id, bool increase_only,
+                          const ata_smart_values & smartval,
+                          int mailtype, const char * msg)
+{
+  // No report if no sectors pending.
+  int64_t rawval = ATAReturnAttributeRawValue(id, &smartval);
+  if (rawval <= 0)
+    return;
+
+  // If attribute is not reset, report only sector count increases.
+  int64_t prev_rawval = ATAReturnAttributeRawValue(id, &state.smartval);
+  if (!(!increase_only || prev_rawval < rawval))
+    return;
+
+  // Format message.
+  std::string s = strprintf("Device: %s, %"PRId64" %s", cfg.name.c_str(), rawval, msg);
+  if (prev_rawval > 0 && rawval != prev_rawval)
+    s += strprintf(" (changed %+"PRId64")", rawval - prev_rawval);
+
+  PrintOut(LOG_CRIT, "%s\n", s.c_str());
+  MailWarning(cfg, state, mailtype, "%s\n", s.c_str());
+  state.must_write = true;
+}
+
+// Format Temperature value
+static const char * fmt_temp(unsigned char x, char * buf)
+{
+  if (!x) // unset
+    strcpy(buf, "??");
   else
-    PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname);
-  
-  return retval;
+    sprintf(buf, "%u", x);
+  return buf;
 }
 
 // Check Temperature limits
-static void CheckTemperature(cfgfile * cfg, unsigned char currtemp, unsigned char triptemp)
+static void CheckTemperature(const dev_config & cfg, dev_state & state, 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);
+    PrintOut(LOG_INFO, "Device: %s, failed to read Temperature\n", cfg.name.c_str());
     return;
   }
 
-  if (!cfg->temperature) {
-    PrintOut(LOG_INFO, "Device: %s, initial Temperature is %d Celsius\n",
-      cfg->name, (int)currtemp);
+  // Update Max Temperature
+  const char * minchg = "", * maxchg = "";
+  if (currtemp > state.tempmax) {
+    if (state.tempmax)
+      maxchg = "!";
+    state.tempmax = currtemp;
+    state.must_write = true;
+  }
+
+  char buf[20];
+  if (!state.temperature) {
+    // First check
+    if (!state.tempmin || currtemp < state.tempmin)
+        // Delay Min Temperature update by ~ 30 minutes.
+        state.tempmin_delay = time(0) + CHECKTIME - 60;
+    PrintOut(LOG_INFO, "Device: %s, initial Temperature is %d Celsius (Min/Max %s/%u%s)\n",
+      cfg.name.c_str(), (int)currtemp, fmt_temp(state.tempmin, buf), state.tempmax, maxchg);
     if (triptemp)
       PrintOut(LOG_INFO, "    [trip Temperature is %d Celsius]\n", (int)triptemp);
-    cfg->temperature = cfg->tempmin = cfg->tempmax = currtemp;
+    state.temperature = 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 (state.tempmin_delay) {
+      // End Min Temperature update delay if ...
+      if (   (state.tempmin && currtemp > state.tempmin) // current temp exceeds recorded min,
+          || (state.tempmin_delay <= time(0))) {         // or delay time is over.
+        state.tempmin_delay = 0;
+        if (!state.tempmin)
+          state.tempmin = 255;
+      }
     }
-    if (currtemp > cfg->tempmax) {
-      cfg->tempmax = currtemp; maxchg = "!";
+
+    // Update Min Temperature
+    if (!state.tempmin_delay && currtemp < state.tempmin) {
+      state.tempmin = currtemp;
+      state.must_write = true;
+      if (currtemp != state.temperature)
+        minchg = "!";
     }
 
     // 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;
+    if (cfg.tempdiff && (*minchg || *maxchg || abs((int)currtemp - (int)state.temperature) >= cfg.tempdiff)) {
+      PrintOut(LOG_INFO, "Device: %s, Temperature changed %+d Celsius to %u Celsius (Min/Max %s%s/%u%s)\n",
+        cfg.name.c_str(), (int)currtemp-(int)state.temperature, currtemp, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg);
+      state.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);
+  if (cfg.tempcrit && currtemp >= cfg.tempcrit) {
+    PrintOut(LOG_CRIT, "Device: %s, Temperature %u Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)\n",
+      cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg);
+    MailWarning(cfg, state, 12, "Device: %s, Temperature %d Celsius reached critical limit of %u Celsius (Min/Max %s%s/%u%s)\n",
+      cfg.name.c_str(), currtemp, cfg.tempcrit, fmt_temp(state.tempmin, buf), minchg, state.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);
+  else if (cfg.tempinfo && currtemp >= cfg.tempinfo) {
+    PrintOut(LOG_INFO, "Device: %s, Temperature %u Celsius reached limit of %u Celsius (Min/Max %s%s/%u%s)\n",
+      cfg.name.c_str(), currtemp, cfg.tempinfo, fmt_temp(state.tempmin, buf), minchg, state.tempmax, maxchg);
   }
 }
 
-int ATACheckDevice(cfgfile *cfg, bool allow_selftests){
-  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;
-  // Highpoint-specific data
-  con->hpt_data[0]=cfg->hpt_data[0];
-  con->hpt_data[1]=cfg->hpt_data[1];
-  con->hpt_data[2]=cfg->hpt_data[2];
+static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device * atadev, bool allow_selftests)
+{
+  const char * name = cfg.name.c_str();
 
   // 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 (cfg.emailtest)
+    MailWarning(cfg, state, 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.  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);
+  if (!atadev->open()) {
+    PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", name, atadev->get_errmsg());
+    MailWarning(cfg, state, 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 (allow_selftests && 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';
-  }
+  } else if (debugmode)
+    PrintOut(LOG_INFO,"Device: %s, opened ATA device\n", name);
 
   // 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 (cfg.powermode && !state.powermodefail) {
+    int dontcheck=0, powermode=ataCheckPowerMode(atadev);
+    const char * mode = 0;
     if (0 <= powermode && powermode < 0xff) {
       // wait for possible spin up and check again
       int powermode2;
       sleep(5);
-      powermode2 = ataCheckPowerMode(fd);
+      powermode2 = ataCheckPowerMode(atadev);
       if (powermode2 > powermode)
         PrintOut(LOG_INFO, "Device: %s, CHECK POWER STATUS spins up disk (0x%02x -> 0x%02x)\n", name, powermode, powermode2);
       powermode = powermode2;
@@ -2384,19 +2566,19 @@ int ATACheckDevice(cfgfile *cfg, bool allow_selftests){
     case -1:
       // SLEEP
       mode="SLEEP";
-      if (cfg->powermode>=1)
+      if (cfg.powermode>=1)
         dontcheck=1;
       break;
     case 0:
       // STANDBY
       mode="STANDBY";
-      if (cfg->powermode>=2)
+      if (cfg.powermode>=2)
         dontcheck=1;
       break;
     case 0x80:
       // IDLE
       mode="IDLE";
-      if (cfg->powermode>=3)
+      if (cfg.powermode>=3)
         dontcheck=1;
       break;
     case 0xff:
@@ -2407,253 +2589,264 @@ int ATACheckDevice(cfgfile *cfg, bool allow_selftests){
       // UNKNOWN
       PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
         name, powermode);
-      cfg->powermode=0;
+      state.powermodefail = true;
       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
+      // skip at most powerskipmax checks
+      if (!cfg.powerskipmax || state.powerskipcnt<cfg.powerskipmax) {
+        CloseDevice(atadev, name);
+        if (!state.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++;
+        state.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 {
+        PrintOut(LOG_INFO, "Device: %s, %s mode ignored due to reached limit of skipped checks (%d check%s skipped)\n",
+          name, mode, state.powerskipcnt, (state.powerskipcnt==1?"":"s"));
+      }
+      state.powerskipcnt = 0;
+      state.tempmin_delay = time(0) + CHECKTIME - 60; // Delay Min Temperature update
     }
-    else if (cfg->powerskipcnt) {
+    else if (state.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;
+        name, mode, state.powerskipcnt, (state.powerskipcnt==1?"":"s"));
+      state.powerskipcnt = 0;
+      state.tempmin_delay = time(0) + CHECKTIME - 60; // Delay Min Temperature update
     }
   }
 
   // check smart status
-  if (cfg->smartcheck){
-    int status=ataSmartStatus2(fd);
+  if (cfg.smartcheck) {
+    int status=ataSmartStatus2(atadev);
     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);
+      MailWarning(cfg, state, 5, "Device: %s, not capable of SMART self-check", name);
+      state.must_write = true;
     }
     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);
+      MailWarning(cfg, state, 1, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!", name);
+      state.must_write = true;
     }
   }
   
   // 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                                 ){
+  if (   cfg.usagefailed || cfg.prefail || cfg.usage
+      || cfg.curr_pending_id || cfg.offl_pending_id
+      || cfg.tempdiff || cfg.tempinfo || cfg.tempcrit || cfg.selftest) {
     struct ata_smart_values     curval;
-    struct ata_smart_thresholds_pvt *thresh=cfg->smartthres;
-    
+    struct ata_smart_thresholds_pvt * thresh = &state.smartthres;
+
     // Read current attribute values. *drive contains old values and thresholds
-    if (ataReadSmartValues(fd,&curval)){
+    if (ataReadSmartValues(atadev, &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);
+      MailWarning(cfg, state, 6, "Device: %s, failed to read SMART Attribute Data", name);
+      state.must_write = true;
     }
     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.curr_pending_id)
+        check_pending(cfg, state, cfg.curr_pending_id, cfg.curr_pending_incr, curval, 10,
+                      (!cfg.curr_pending_incr ? "Currently unreadable (pending) sectors"
+                                              : "Total unreadable (pending) sectors"    ));
+
+      if (cfg.offl_pending_id)
+        check_pending(cfg, state, cfg.offl_pending_id, cfg.offl_pending_incr, curval, 11,
+                      (!cfg.offl_pending_incr ? "Offline uncorrectable sectors"
+                                              : "Total offline uncorrectable sectors"));
 
       // check temperature limits
-      if (cfg->tempdiff || cfg->tempinfo || cfg->tempcrit)
-        CheckTemperature(cfg, ATAReturnTemperatureValue(&curval, cfg->attributedefs), 0);
+      if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)
+        CheckTemperature(cfg, state, ATAReturnTemperatureValue(&curval, cfg.attributedefs), 0);
 
-      if (cfg->usagefailed || cfg->prefail || cfg->usage) {
+      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;
-         
+        for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+
          // 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)){
+          int att;
+         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__)){
+            if (!cfg.monitor_attr_flags.is_set(att, MONITOR_IGN_FAILUSE)) {
              char attname[64], *loc=attname;
              
              // get attribute name & skip white space
-             ataPrintSmartAttribName(loc, att, cfg->attributedefs);
+             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);
+             MailWarning(cfg, state, 2, "Device: %s, Failed SMART usage Attribute: %s.", name, loc);
+              state.must_write = true;
            }
          }
          
          // 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;
-           
+          changedattribute_t delta;
+         if ((cfg.usage || cfg.prefail) && ATACompareValues(&delta, &curval, &state.smartval, thresh, i, name)){
+
+            // Continue if we're not tracking this type of attribute
+            if (!(   ( delta.prefail && cfg.prefail)
+                  || (!delta.prefail && cfg.usage   )))
+              continue;
+
+            // Continue if '-I ID' was specified
+            unsigned char id = delta.id;
+            if (cfg.monitor_attr_flags.is_set(id, MONITOR_IGNORE))
+              continue;
+
            // 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__))
+            if (   !delta.sameraw && delta.newval == delta.oldval
+                && !cfg.monitor_attr_flags.is_set(id, MONITOR_RAW))
              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);
-           }
+
+            // get attribute name, skip spaces
+            char attname[64], *loc = attname;
+            ataPrintSmartAttribName(loc, id, cfg.attributedefs);
+            while (*loc && *loc==' ')
+              loc++;
+
+            // has the user asked for us to print raw values?
+            char newrawstring[64], oldrawstring[64];
+            if (cfg.monitor_attr_flags.is_set(id, MONITOR_RAW_PRINT)) {
+              // 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, state.smartval.vendor_attributes+i, cfg.attributedefs);
+              sprintf(oldrawstring, " [Raw %s]", rawstring);
+            }
+            else
+              newrawstring[0]=oldrawstring[0]='\0';
+
+            // Format message
+            std::string msg = strprintf("Device: %s, SMART %s Attribute: %s changed from %d%s to %d%s",
+                                        name, (delta.prefail ? "Prefailure" : "Usage"), loc,
+                                        delta.oldval, oldrawstring, delta.newval, newrawstring);
+
+            // Report this change as critical ?
+            if (   (delta.newval != delta.oldval && cfg.monitor_attr_flags.is_set(id, MONITOR_AS_CRIT))
+                || (!delta.sameraw               && cfg.monitor_attr_flags.is_set(id, MONITOR_RAW_AS_CRIT))) {
+              PrintOut(LOG_CRIT, "%s\n", msg.c_str());
+              MailWarning(cfg, state, 2, "%s", msg.c_str());
+            }
+            else {
+              PrintOut(LOG_INFO, "%s\n", msg.c_str());
+            }
+            state.must_write = true;
          } // endof block tracking usage or prefailure
        } // end of loop over attributes
        
+        if (cfg.selftest) {
+          // Log changes of self-test executions status
+          if (   curval.self_test_exec_status != state.smartval.self_test_exec_status
+              || (!allow_selftests && curval.self_test_exec_status != 0x00)          )
+            log_self_test_exec_status(name, curval.self_test_exec_status);
+        }
+
        // Save the new values into *drive for the next time around
-       *(cfg->smartval)=curval;
+       state.smartval = curval;
       }
     }
   }
   
   // check if number of selftest errors has increased (note: may also DECREASE)
-  if (cfg->selftest)
-    CheckSelfTestLogs(cfg, SelfTestErrorCount(fd, name));
-  
+  if (cfg.selftest)
+    CheckSelfTestLogs(cfg, state, SelfTestErrorCount(atadev, name, cfg.fix_firmwarebug));
+
   // check if number of ATA errors has increased
-  if (cfg->errorlog){
+  if (cfg.errorlog) {
 
-    int newc,oldc=cfg->ataerrorcount;
+    int newc, oldc= state.ataerrorcount;
 
     // new number of errors
-    newc=ATAErrorCount(fd, name);
+    newc = ATAErrorCount(atadev, name, cfg.fix_firmwarebug);
 
     // did command fail?
     if (newc<0)
       // lack of PrintOut here is INTENTIONAL
-      MailWarning(cfg, 7, "Device: %s, Read SMART Error Log Failed", name);
+      MailWarning(cfg, state, 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",
+      MailWarning(cfg, state, 4, "Device: %s, ATA error count increased from %d to %d",
                    name, oldc, newc);
+      state.must_write = true;
     }
     
     // this last line is probably not needed, count always increases
     if (newc>=0)
-      cfg->ataerrorcount=newc;
+      state.ataerrorcount=newc;
   }
-  
-  // carry out scheduled self-test
-  if (testtype)
-    DoATASelfTest(fd, cfg, testtype);
-  
+
+  // if the user has asked, and device is capable (or we're not yet
+  // sure) check whether a self test should be done now.
+  if (allow_selftests && !cfg.test_regex.empty()) {
+    char testtype = next_scheduled_test(cfg, state, false/*!scsi*/);
+    if (testtype)
+      DoATASelfTest(cfg, state, atadev, testtype);
+  }
+
   // Don't leave device open -- the OS/user may want to access it
   // before the next smartd cycle!
-  CloseDevice(fd, name);
+  CloseDevice(atadev, name);
+
+  // Copy ATA attribute values to persistent state
+  state.update_persistent_state();
+
   return 0;
 }
 
-int SCSICheckDevice(cfgfile *cfg, bool allow_selftests)
+static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_device * scsidev, bool allow_selftests)
 {
     UINT8 asc, ascq;
     UINT8 currenttemp;
     UINT8 triptemp;
-    int fd;
-    char *name=cfg->name;
+    const char * name = cfg.name.c_str();
     const char *cp;
-    char *mode=NULL;
-
-    // should we try to register this as a SCSI device?
-    switch (cfg->controller_type) {
-    case CONTROLLER_CCISS:
-      mode="CCISS";
-      break;
-    case CONTROLLER_SCSI:
-    case CONTROLLER_UNKNOWN:
-      mode="SCSI";
-      break;
-    default:
-      return 1;
-    }
-
-    // pass user settings on to low-level SCSI commands
-    con->controller_port=cfg->controller_port;
-    con->controller_type=cfg->controller_type;
 
     // 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 (cfg.emailtest)
+      MailWarning(cfg, state, 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, mode, 0))<0) {
-      // Lack of PrintOut() here is intentional!
-      MailWarning(cfg, 9, "Device: %s, unable to open device", name);
+    if (!scsidev->open()) {
+      PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", name, scsidev->get_errmsg());
+      MailWarning(cfg, state, 9, "Device: %s, unable to open device", name);
       return 1;
     } else if (debugmode)
         PrintOut(LOG_INFO,"Device: %s, opened SCSI device\n", name);
     currenttemp = 0;
     asc = 0;
     ascq = 0;
-    if (! cfg->SuppressReport) {
-        if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
+    if (!state.SuppressReport) {
+        if (scsiCheckIE(scsidev, state.SmartPageSupported, state.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;
+            MailWarning(cfg, state, 6, "Device: %s, failed to read SMART values", name);
+            state.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); 
+            MailWarning(cfg, state, 1,"Device: %s, SMART Failure: %s", name, cp);
         } else if (debugmode)
             PrintOut(LOG_INFO,"Device: %s, non-SMART asc,ascq: %d,%d\n",
                      name, (int)asc, (int)ascq);  
@@ -2661,53 +2854,47 @@ int SCSICheckDevice(cfgfile *cfg, bool allow_selftests)
         PrintOut(LOG_INFO,"Device: %s, SMART health: passed\n", name);  
 
     // check temperature limits
-    if (cfg->tempdiff || cfg->tempinfo || cfg->tempcrit)
-      CheckTemperature(cfg, currenttemp, triptemp);
+    if (cfg.tempdiff || cfg.tempinfo || cfg.tempcrit)
+      CheckTemperature(cfg, state, currenttemp, triptemp);
 
     // check if number of selftest errors has increased (note: may also DECREASE)
-    if (cfg->selftest)
-      CheckSelfTestLogs(cfg, scsiCountFailedSelfTests(fd, 0));
+    if (cfg.selftest)
+      CheckSelfTestLogs(cfg, state, scsiCountFailedSelfTests(scsidev, 0));
     
-    if (allow_selftests && 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');
+    if (allow_selftests && !cfg.test_regex.empty()) {
+      char testtype = next_scheduled_test(cfg, state, true/*scsi*/);
+      if (testtype)
+        DoSCSISelfTest(cfg, state, scsidev, testtype);
     }
-    CloseDevice(fd, name);
+    CloseDevice(scsidev, name);
     return 0;
 }
 
 // Checks the SMART status of all ATA and SCSI devices
-void CheckDevicesOnce(cfgfile **ATAandSCSIdevices, bool allow_selftests){
-  int i;
-  
-  for (i=0; i<numdevata+numdevscsi; i++) {
-      if (ATAandSCSIdevices[i]->WhichCheckDevice==1)
-         SCSICheckDevice(ATAandSCSIdevices[i], allow_selftests);
-      else
-         ATACheckDevice(ATAandSCSIdevices[i], allow_selftests);
+static void CheckDevicesOnce(const dev_config_vector & configs, dev_state_vector & states,
+                             smart_device_list & devices, bool allow_selftests)
+{
+  for (unsigned i = 0; i < configs.size(); i++) {
+    const dev_config & cfg = configs.at(i);
+    dev_state & state = states.at(i);
+    smart_device * dev = devices.at(i);
+    if (dev->is_ata())
+      ATACheckDevice(cfg, state, dev->to_ata(), allow_selftests);
+    else if (dev->is_scsi())
+      SCSICheckDevice(cfg, state, dev->to_scsi(), allow_selftests);
   }
-
-  return;
 }
 
-#if SCSITIMEOUT
-// This alarm means that a SCSI USB device was hanging
-void AlarmHandler(int signal) {
-  longjmp(registerscsienv, 1);
-}
-#endif
+// Set if Initialize() was called
+static bool is_initialized = false;
 
 // Does initialization right after fork to daemon mode
 void Initialize(time_t *wakeuptime){
 
-  // install goobye message and remove pidfile handler
-  atexit(Goodbye);
+  // Call Goodbye() on exit
+  is_initialized = true;
   
-  // write PID file only after installing exit handler
+  // write PID file
   if (!debugmode)
     WritePidFile();
   
@@ -2767,11 +2954,10 @@ static void ToggleDebugMode()
 }
 #endif
 
-time_t dosleep(time_t wakeuptime){
-  time_t timenow=0;
-  
+static time_t dosleep(time_t wakeuptime, bool & sigwakeup)
+{
   // If past wake-up-time, compute next wake-up-time
-  timenow=time(NULL);
+  time_t timenow=time(NULL);
   while (wakeuptime<=timenow){
     int intervals=1+(timenow-wakeuptime)/checktime;
     wakeuptime+=intervals*checktime;
@@ -2805,6 +2991,7 @@ time_t dosleep(time_t wakeuptime){
     PrintOut(LOG_INFO,"Signal USR1 - checking devices now rather than in %d seconds.\n",
              wakeuptime-timenow>0?(int)(wakeuptime-timenow):0);
     caughtsigUSR1=0;
+    sigwakeup = true;
   }
   
   // return adjusted wakeuptime
@@ -2813,17 +3000,16 @@ time_t dosleep(time_t 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]");
+    PrintOut(priority, "never[,N][,q], sleep[,N][,q], standby[,N][,q], idle[,N][,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");
+    PrintOut(priority, "%s", smi()->get_valid_dev_types_str());
     break;
   case 'T':
     PrintOut(priority, "normal, permissive");
@@ -2839,12 +3025,7 @@ void printoutvaliddirectiveargs(int priority, char d) {
     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);
+    PrintOut(priority, "\n%s\n", create_vendor_attribute_arg_list().c_str());
     break;
   case 'P':
     PrintOut(priority, "use, ignore, show, showall");
@@ -2856,16 +3037,9 @@ void printoutvaliddirectiveargs(int priority, char d) {
 }
 
 // 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;
-  }
-
+int GetInteger(const char *arg, const char *name, const char *token, int lineno, const char *configfile,
+               int min, int max, char * suffix = 0)
+{
   // 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",
@@ -2874,8 +3048,18 @@ int GetInteger(char *arg, char *name, char *token, int lineno, char *configfile,
   }
   
   // 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 )  {
+  char *endptr;
+  int val = strtol(arg,&endptr,10);
+
+  // optional suffix present?
+  if (suffix) {
+    if (!strcmp(endptr, suffix))
+      endptr += strlen(suffix);
+    else
+      *suffix = 0;
+  }
+
+  if (!(!*endptr && min <= val && 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;
@@ -2912,16 +3096,15 @@ int Get3Integers(const char *arg, const char *name, const char *token, int linen
 // 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){
+static int ParseToken(char * token, dev_config & cfg)
+{
   char sym;
-  char *name=cfg->name;
-  int lineno=cfg->lineno;
-  char *delim = " \n\t";
+  const char * name = cfg.name.c_str();
+  int lineno=cfg.lineno;
+  const char *delim = " \n\t";
   int badarg = 0;
   int missingarg = 0;
-  char *arg = NULL;
-  int makemail=0;
-  maildata *mdat=NULL, tempmail;
+  const char *arg = 0;
 
   // is the rest of the line a comment
   if (*token=='#')
@@ -2938,46 +3121,26 @@ int ParseToken(char *token,cfgfile *cfg){
   // 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;
+  int val;
+  char plus[] = "+", excl[] = "!";
 
+  switch (sym) {
   case 'C':
     // monitor current pending sector count (default 197)
-    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0)
+    if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255, plus)) < 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;
+    cfg.curr_pending_id = (unsigned char)val;
+    cfg.curr_pending_incr = (*plus == '+');
+    cfg.curr_pending_set = true;
     break;
   case 'U':
     // monitor offline uncorrectable sectors (default 198)
-    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0)
+    if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255, plus)) < 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);
+    cfg.offl_pending_id = (unsigned char)val;
+    cfg.offl_pending_incr = (*plus == '+');
+    cfg.offl_pending_set = true;
     break;
   case 'T':
     // Set tolerance level for SMART command failures
@@ -2987,150 +3150,22 @@ int ParseToken(char *token,cfgfile *cfg){
       // 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;
+      cfg.permissive = false;
     } else if (!strcmp(arg, "permissive")) {
       // Permissive mode; ignore errors from Mandatory SMART commands
-      cfg->permissive=1;
+      cfg.permissive = true;
     } 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;
+      cfg.removable = true;
     } 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)) {
-          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>31) {
-              PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N (N=%d) must have 0 <= N <= 31\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;
-          }
-      } else if (!strncmp(s,"cciss,",6)) {
-          if (split_report_arg2(s, &i)){
-              PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d cciss,N requires N integer\n",
-                              configfile, lineno, name);
-              badarg=1;
-          } else if ( i<0 || i>127) {
-              PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d cciss,N (N=%d) must have 0 <= N <= 127\n",
-                       configfile, lineno, name, i);
-              badarg=1;
-          } else {
-              // NOTE: controller_port == disk number + 1
-              cfg->controller_type = CONTROLLER_CCISS;
-              cfg->controller_port = i+1;
-          }
-      } else {
-          badarg=1;
-      }
-      s=CheckFree(s, __LINE__,filenameandversion);
+      cfg.dev_type = arg;
     }
     break;
   case 'F':
@@ -3138,37 +3173,37 @@ int ParseToken(char *token,cfgfile *cfg){
     if ((arg = strtok(NULL, delim)) == NULL) {
       missingarg = 1;
     } else if (!strcmp(arg, "none")) {
-      cfg->fixfirmwarebug = FIX_NONE;
+      cfg.fix_firmwarebug = FIX_NONE;
     } else if (!strcmp(arg, "samsung")) {
-      cfg->fixfirmwarebug = FIX_SAMSUNG;
+      cfg.fix_firmwarebug = FIX_SAMSUNG;
     } else if (!strcmp(arg, "samsung2")) {
-      cfg->fixfirmwarebug = FIX_SAMSUNG2;
+      cfg.fix_firmwarebug = FIX_SAMSUNG2;
     } else if (!strcmp(arg, "samsung3")) {
-      cfg->fixfirmwarebug = FIX_SAMSUNG3;
+      cfg.fix_firmwarebug = FIX_SAMSUNG3;
     } else {
       badarg = 1;
     }
     break;
   case 'H':
     // check SMART status
-    cfg->smartcheck=1;
+    cfg.smartcheck = true;
     break;
   case 'f':
     // check for failure of usage attributes
-    cfg->usagefailed=1;
+    cfg.usagefailed = true;
     break;
   case 't':
     // track changes in all vendor attributes
-    cfg->prefail=1;
-    cfg->usage=1;
+    cfg.prefail = true;
+    cfg.usage = true;
     break;
   case 'p':
     // track changes in prefail vendor attributes
-    cfg->prefail=1;
+    cfg.prefail = true;
     break;
   case 'u':
     //  track changes in usage vendor attributes
-    cfg->usage=1;
+    cfg.usage = true;
     break;
   case 'l':
     // track changes in SMART logs
@@ -3176,31 +3211,31 @@ int ParseToken(char *token,cfgfile *cfg){
       missingarg = 1;
     } else if (!strcmp(arg, "selftest")) {
       // track changes in self-test log
-      cfg->selftest=1;
+      cfg.selftest = true;
     } else if (!strcmp(arg, "error")) {
       // track changes in ATA error log
-      cfg->errorlog=1;
+      cfg.errorlog = true;
     } 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;
+    cfg.smartcheck = true;
+    cfg.prefail = true;
+    cfg.usagefailed = true;
+    cfg.usage = true;
+    cfg.selftest = true;
+    cfg.errorlog = true;
     break;
   case 'o':
     // automatic offline testing enable/disable
     if ((arg = strtok(NULL, delim)) == NULL) {
       missingarg = 1;
     } else if (!strcmp(arg, "on")) {
-      cfg->autoofflinetest = 2;
+      cfg.autoofflinetest = 2;
     } else if (!strcmp(arg, "off")) {
-      cfg->autoofflinetest = 1;
+      cfg.autoofflinetest = 1;
     } else {
       badarg = 1;
     }
@@ -3209,60 +3244,82 @@ int ParseToken(char *token,cfgfile *cfg){
     // 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, ',');
+    else {
+      char *endptr = NULL;
+      char *next = strchr(const_cast<char*>(arg), ',');
+
+      cfg.powerquiet = false;
+      cfg.powerskipmax = 0;
+
+      if (next!=NULL) *next='\0';
+      if (!strcmp(arg, "never"))
+        cfg.powermode = 0;
+      else if (!strcmp(arg, "sleep"))
+        cfg.powermode = 1;
+      else if (!strcmp(arg, "standby"))
+        cfg.powermode = 2;
+      else if (!strcmp(arg, "idle"))
+        cfg.powermode = 3;
+      else
+        badarg = 1;
+
+      // if optional arguments are present
+      if (!badarg && next!=NULL) {
+        next++;
+        cfg.powerskipmax = strtol(next, &endptr, 10);
+        if (endptr == next)
+          cfg.powerskipmax = 0;
+        else {
+          next = endptr + (*endptr != '\0');
+          if (cfg.powerskipmax <= 0)
+            badarg = 1;
+        }
+        if (*next != '\0') {
+          if (!strcmp("q", next))
+            cfg.powerquiet = true;
+          else {
+            badarg = 1;
+          }
+        }
+      }
+    }
     break;
   case 'S':
     // automatic attribute autosave enable/disable
     if ((arg = strtok(NULL, delim)) == NULL) {
       missingarg = 1;
     } else if (!strcmp(arg, "on")) {
-      cfg->autosave = 2;
+      cfg.autosave = 2;
     } else if (!strcmp(arg, "off")) {
-      cfg->autosave = 1;
+      cfg.autosave = 1;
     } else {
       badarg = 1;
     }
     break;
   case 's':
     // warn user, and delete any previously given -s REGEXP Directives
-    if (cfg->testdata){
+    if (!cfg.test_regex.empty()){
       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);
+               configfile, lineno, name, cfg.test_regex.get_pattern());
+      cfg.test_regex = regular_expression();
     }
     // 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;
+    // Compile regex
+    else {
+      if (!cfg.test_regex.compile(arg, REG_EXTENDED)) {
+        // not a valid regular expression!
+        PrintOut(LOG_CRIT, "File %s line %d (drive %s): -s argument \"%s\" is INVALID extended regular expression. %s.\n",
+                 configfile, lineno, name, arg, cfg.test_regex.get_errmsg());
+        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).
-    else if ((int)strlen(arg) != (val=strspn(arg,"0123456789/.-+*|()?^$[]SLCO")))
+    if (arg[(val = strspn(arg, "0123456789/.-+*|()?^$[]SLCOcnr"))])
       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;
@@ -3271,12 +3328,10 @@ int ParseToken(char *token,cfgfile *cfg){
     if (!(arg = strtok(NULL,delim)))
       missingarg = 1;
     else {
-      if (mdat->address) {
+      if (!cfg.emailaddress.empty())
         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);
+                 configfile, lineno, name, cfg.emailaddress.c_str());
+      cfg.emailaddress = arg;
     }
     break;
   case 'M':
@@ -3284,13 +3339,13 @@ int ParseToken(char *token,cfgfile *cfg){
     if (!(arg = strtok(NULL, delim)))
       missingarg = 1;
     else if (!strcmp(arg, "once"))
-      mdat->emailfreq = 1;
+      cfg.emailfreq = 1;
     else if (!strcmp(arg, "daily"))
-      mdat->emailfreq = 2;
+      cfg.emailfreq = 2;
     else if (!strcmp(arg, "diminishing"))
-      mdat->emailfreq = 3;
+      cfg.emailfreq = 3;
     else if (!strcmp(arg, "test"))
-      mdat->emailtest = 1;
+      cfg.emailtest = 1;
     else if (!strcmp(arg, "exec")) {
       // Get the next argument (the command line)
       if (!(arg = strtok(NULL, delim))) {
@@ -3299,12 +3354,10 @@ int ParseToken(char *token,cfgfile *cfg){
         return -1;
       }
       // Free the last cmd line given if any, and copy new one
-      if (mdat->emailcmdline) {
+      if (!cfg.emailcmdline.empty())
         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);
+                 configfile, lineno, name, cfg.emailcmdline.c_str());
+      cfg.emailcmdline = arg;
     } 
     else
       badarg = 1;
@@ -3313,41 +3366,41 @@ int ParseToken(char *token,cfgfile *cfg){
     // 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__);
+    cfg.monitor_attr_flags.set(val, MONITOR_IGN_FAILUSE);
     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__);
+    cfg.monitor_attr_flags.set(val, MONITOR_IGNORE);
     break;
   case 'r':
     // print raw value when tracking
-    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
+    if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255, excl)) < 0)
       return -1;
-    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__);
+    cfg.monitor_attr_flags.set(val, MONITOR_RAW_PRINT);
+    if (*excl == '!') // attribute change is critical
+      cfg.monitor_attr_flags.set(val, MONITOR_AS_CRIT);
     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)
+    if ((val = GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255, excl)) < 0)
       return -1;
-    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__);
-    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAW, __LINE__);
+    cfg.monitor_attr_flags.set(val, MONITOR_RAW_PRINT|MONITOR_RAW);
+    if (*excl == '!') // raw value change is critical
+      cfg.monitor_attr_flags.set(val, MONITOR_RAW_AS_CRIT);
     break;
   case 'W':
     // track Temperature
     if ((val=Get3Integers(arg=strtok(NULL,delim), name, token, lineno, configfile,
-                          &cfg->tempdiff, &cfg->tempinfo, &cfg->tempcrit))<0)
+                          &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)){   
+    } else if (parse_attribute_def(arg, cfg.attributedefs)) {
       badarg = 1;
     }
     break;
@@ -3356,11 +3409,11 @@ int ParseToken(char *token,cfgfile *cfg){
     if (!(arg = strtok(NULL, delim))) {
       missingarg = 1;
     } else if (!strcmp(arg, "use")) {
-      cfg->ignorepresets = FALSE;
+      cfg.ignorepresets = false;
     } else if (!strcmp(arg, "ignore")) {
-      cfg->ignorepresets = TRUE;
+      cfg.ignorepresets = true;
     } else if (!strcmp(arg, "show")) {
-      cfg->showpresets = TRUE;
+      cfg.showpresets = true;
     } else if (!strcmp(arg, "showall")) {
       showallpresets();
     } else {
@@ -3389,99 +3442,13 @@ int ParseToken(char *token,cfgfile *cfg){
     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);
-}
-
-
+// Scan directive for configuration file
+#define SCANDIRECTIVE "DEVICESCAN"
 
-// 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.
+// This is the routine that adds things to the conf_entries list.
 //
 // Return values are:
 //  1: parsed a normal line
@@ -3490,11 +3457,11 @@ cfgfile *CreateConfigEntry(cfgfile *original){
 // -2: found an error
 //
 // Note: this routine modifies *line from the caller!
-int ParseConfigLine(int entry, int lineno,char *line){
+static int ParseConfigLine(dev_config_vector & conf_entries, int entry, int lineno, /*const*/ char * line)
+{
   char *token=NULL;
   char *name=NULL;
-  char *delim = " \n\t";
-  cfgfile *cfg=NULL;
+  const char *delim = " \n\t";
   int devscan=0;
 
   // get first token: device name. If a comment, skip line
@@ -3511,22 +3478,15 @@ int ParseConfigLine(int entry, int lineno,char *line){
     }
   }
   
-  // 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);
+  conf_entries.push_back( dev_config() );
+  dev_config & cfg = conf_entries.back();
+
+  cfg.name = name;
 
   // 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);
-  
+  cfg.lineno=lineno;
+
   // parse tokens one at a time from the file.
   while ((token=strtok(NULL,delim))){
     int retval=ParseToken(token,cfg);
@@ -3549,94 +3509,44 @@ int ParseConfigLine(int entry, int lineno,char *line){
     }
   }
   
-  // If we found 3ware/cciss 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/cciss 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 [%s_disk_%02d]", cfg->name, (cfg->controller_type == CONTROLLER_CCISS) ? "cciss" : "3ware", 
-                                               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   )) {
+  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.name.c_str(), cfg.lineno, configfile);
     
-    cfg->smartcheck=1;
-    cfg->usagefailed=1;
-    cfg->prefail=1;
-    cfg->usage=1;
-    cfg->selftest=1;
-    cfg->errorlog=1;
+    cfg.smartcheck = true;
+    cfg.usagefailed = true;
+    cfg.prefail = true;
+    cfg.usage = true;
+    cfg.selftest = true;
+    cfg.errorlog = true;
   }
   
   // 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)){
+  if (cfg.emailaddress.empty() && (!cfg.emailcmdline.empty() || cfg.emailfreq || cfg.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);
+             cfg.name.c_str(), cfg.lineno, configfile);
     return -2;
   }
   
   // has the user has set <nomailer>?
-  if (cfg->mailwarn && cfg->mailwarn->address && !strcmp(cfg->mailwarn->address,"<nomailer>")){
+  if (cfg.emailaddress == "<nomailer>") {
     // check that -M exec is also set
-    if (!cfg->mailwarn->emailcmdline){
+    if (cfg.emailcmdline.empty()){
       PrintOut(LOG_CRIT,"Drive: %s, -m <nomailer> Directive on line %d of file %s needs -M exec Directive\n",
-               cfg->name, cfg->lineno, configfile);
+               cfg.name.c_str(), 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);
+    // From here on the sign of <nomailer> is address.empty() and !cfg.emailcmdline.empty()
+    cfg.emailaddress.clear();
   }
 
-  // set cfg->emailfreq to 1 (once) if user hasn't set it
-  if (cfg->mailwarn && !cfg->mailwarn->emailfreq)
-    cfg->mailwarn->emailfreq = 1;
-
-  entry++;
+  // set cfg.emailfreq to 1 (once) if user hasn't set it
+  if ((!cfg.emailaddress.empty() || !cfg.emailcmdline.empty()) && !cfg.emailfreq)
+    cfg.emailfreq = 1;
 
   if (devscan)
     return -1;
@@ -3644,19 +3554,6 @@ int ParseConfigLine(int entry, int lineno,char *line){
     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
@@ -3665,21 +3562,20 @@ void cleanup(FILE **fpp, int is_stdin){
 //
 // 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
+// Empty configuration file ==> conf_entries.empty()
+// No configuration file    ==> conf_entries[0].lineno == 0
+// SCANDIRECTIVE found      ==> conf_entries[0].lineno != 0
+static int ParseConfigFile(dev_config_vector & conf_entries)
+{
+  // maximum line length in configuration file
+  const int MAXLINELEN = 256;
+  // maximum length of a continued line in configuration file
+  const int MAXCONTLINE = 1023;
 
+  stdio_file f;
   // Open config file, if it exists and is not <stdin>
-  if (!is_stdin) {
-    fp=fopen(configfile,"r");
-    if (fp==NULL && (errno!=ENOENT || configfile_alt)) {
+  if (!(configfile == configfile_stdin)) { // pointer comparison ok here
+    if (!f.open(configfile,"r") && (errno!=ENOENT || !configfile_alt.empty())) {
       // 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",
@@ -3688,34 +3584,31 @@ int ParseConfigFile(){
     }
   }
   else // read from stdin ('-c -' option)
-    fp = stdin;
-  
+    f.open(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);
+  int entry = 0;
+  if (!f) {
+    char fakeconfig[] = SCANDIRECTIVE" -a"; // TODO: Remove this hack, build cfg_entry.
+
+    if (ParseConfigLine(conf_entries, entry, 0, fakeconfig) != -1)
+      throw std::logic_error("Internal error parsing "SCANDIRECTIVE);
     return 0;
   }
 
 #ifdef __CYGWIN__
-  setmode(fileno(fp), O_TEXT); // Allow files with \r\n
+  setmode(fileno(f), 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 lineno = 1, cont = 0, contlineno = 0;
+  char line[MAXLINELEN+2];
+  char fullline[MAXCONTLINE+1];
+
+  for (;;) {
     int len=0,scandevice;
     char *lastslash;
     char *comment;
@@ -3725,22 +3618,18 @@ int ParseConfigFile(){
     memset(line,0,sizeof(line));
 
     // get a line
-    code=fgets(line,MAXLINELEN+2,fp);
+    code=fgets(line, MAXLINELEN+2, f);
     
     // are we at the end of the file?
     if (!code){
       if (cont) {
-        scandevice=ParseConfigLine(entry,contlineno,fullline);
+        scandevice = ParseConfigLine(conf_entries, entry, contlineno, fullline);
         // See if we found a SCANDIRECTIVE directive
-        if (scandevice==-1) {
-          cleanup(&fp, is_stdin);
+        if (scandevice==-1)
           return 0;
-        }
         // did we find a syntax error
-        if (scandevice==-2) {
-          cleanup(&fp, is_stdin);
+        if (scandevice==-2)
           return -1;
-        }
         // the final line is part of a continuation line
         cont=0;
         entry+=scandevice;
@@ -3754,14 +3643,13 @@ int ParseConfigFile(){
     // See if line is too long
     len=strlen(line);
     if (len>MAXLINELEN){
-      char *warn;
+      const 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;
     }
 
@@ -3775,7 +3663,6 @@ int ParseConfigFile(){
     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;
     }
     
@@ -3790,38 +3677,24 @@ int ParseConfigFile(){
     }
 
     // Not a continuation line. Parse it
-    scandevice=ParseConfigLine(entry,contlineno,fullline);
+    scandevice = ParseConfigLine(conf_entries, entry, contlineno, fullline);
 
     // did we find a scandevice directive?
-    if (scandevice==-1) {
-      cleanup(&fp, is_stdin);
+    if (scandevice==-1)
       return 0;
-    }
     // did we find a syntax error
-    if (scandevice==-2) {
-      cleanup(&fp, is_stdin);
+    if (scandevice==-2)
       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) {
@@ -3831,22 +3704,34 @@ void PrintValidArgs(char opt) {
   if (!(s = GetValidArgList(opt)))
     PrintOut(LOG_CRIT, "Error constructing argument list for option %c", opt);
   else
-    PrintOut(LOG_CRIT, (char *)s);
+    PrintOut(LOG_CRIT, "%s", (char *)s);
   PrintOut(LOG_CRIT, " <=======\n");
 }
 
+// Return true if absolute path name
+static bool is_abs_path(const char * path)
+{
+  if (*path == '/')
+    return true;
+#if defined(_WIN32) || defined(__CYGWIN__)
+  if (*path == '\\')
+    return true;
+  int n = -1;
+  sscanf(path, "%*1[A-Za-z]:%*1[/\\]%n", &n);
+  if (n > 0)
+    return true;
+#endif
+  return false;
+}
+
 // 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:dDni:p:r:Vh?";
-#ifdef HAVE_GETOPT_LONG
+  const char *shortopts = "c:l:q:dDni:p:r:s:A:B:Vh?";
   char *arg;
   // Please update GetValidArgList() if you edit longopts
   struct option longopts[] = {
@@ -3861,6 +3746,9 @@ void ParseOpts(int argc, char **argv){
 #endif
     { "pidfile",        required_argument, 0, 'p' },
     { "report",         required_argument, 0, 'r' },
+    { "savestates",     required_argument, 0, 's' },
+    { "attributelog",   required_argument, 0, 'A' },
+    { "drivedb",        required_argument, 0, 'B' },
 #if defined(_WIN32) || defined(__CYGWIN__)
     { "service",        no_argument,       0, 'n' },
 #endif
@@ -3871,19 +3759,15 @@ void ParseOpts(int argc, char **argv){
     { "usage",          no_argument,       0, 'h' },
     { 0,                0,                 0, 0   }
   };
-#endif
-  
+
   opterr=optopt=0;
-  badarg=FALSE;
-  
+  bool badarg = false;
+  bool no_defaultdb = false; // set true on '-B FILE'
+
   // 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) {
@@ -3904,7 +3788,7 @@ void ParseOpts(int argc, char **argv){
       } else if (!(strcmp(optarg,"errors"))) {
         quit=5;
       } else {
-        badarg = TRUE;
+        badarg = true;
       }
       break;
     case 'l':
@@ -3928,11 +3812,11 @@ void ParseOpts(int argc, char **argv){
       else if (!strcmp(optarg, "local7"))
         facility=LOG_LOCAL7;
       else
-        badarg = TRUE;
+        badarg = true;
       break;
     case 'd':
       // enable debug mode
-      debugmode = TRUE;
+      debugmode = 1;
       break;
     case 'n':
       // don't fork()
@@ -3942,7 +3826,7 @@ void ParseOpts(int argc, char **argv){
       break;
     case 'D':
       // print summary of all valid directives
-      debugmode = TRUE;
+      debugmode = 1;
       Directives();
       EXIT(0);
       break;
@@ -3974,7 +3858,7 @@ void ParseOpts(int argc, char **argv){
           EXIT(EXIT_NOMEM);
         }
         if (split_report_arg(s, &i)) {
-          badarg = TRUE;
+          badarg = true;
         } else if (i<1 || i>3) {
           debugmode=1;
           PrintHead();
@@ -3988,25 +3872,47 @@ void ParseOpts(int argc, char **argv){
         } else if (!strcmp(s,"scsiioctl")) {
           con->reportscsiioctl = i;
         } else {
-          badarg = TRUE;
+          badarg = true;
         }
-        s=CheckFree(s, __LINE__,filenameandversion);
+        free(s);  // TODO: use std::string
       }
       break;
     case 'c':
       // alternate configuration file
       if (strcmp(optarg,"-"))
-        configfile=configfile_alt=CustomStrDup(optarg, 1, __LINE__,filenameandversion);
+        configfile = (configfile_alt = optarg).c_str();
       else // read from stdin
         configfile=configfile_stdin;
       break;
     case 'p':
       // output file with PID number
-      pid_file=CustomStrDup(optarg, 1, __LINE__,filenameandversion);
+      pid_file = optarg;
+      break;
+    case 's':
+      // path prefix of persistent state file
+      state_path_prefix = optarg;
+      break;
+    case 'A':
+      // path prefix of attribute log file
+      attrlog_path_prefix = optarg;
+      break;
+    case 'B':
+      {
+        const char * path = optarg;
+        if (*path == '+' && path[1])
+          path++;
+        else
+          no_defaultdb = true;
+        unsigned char savedebug = debugmode; debugmode = 1;
+        if (!read_drive_database(path))
+          EXIT(EXIT_BADCMD);
+        debugmode = savedebug;
+      }
       break;
     case 'V':
       // print version and CVS info
-      PrintCopyleft();
+      debugmode = 1;
+      PrintOut(LOG_INFO, "%s", format_version_info("smartd", true /*full*/).c_str());
       EXIT(0);
       break;
     case 'h':
@@ -4021,7 +3927,6 @@ void ParseOpts(int argc, char **argv){
       // 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.
@@ -4036,7 +3941,6 @@ void ParseOpts(int argc, char **argv){
         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){
@@ -4076,87 +3980,81 @@ void ParseOpts(int argc, char **argv){
   }
 
   // no pidfile in debug mode
-  if (debugmode && pid_file) {
+  if (debugmode && !pid_file.empty()) {
     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);
+    PrintOut(LOG_CRIT, "Error: pid file %s not written in debug (-d) mode\n\n", pid_file.c_str());
     EXIT(EXIT_BADCMD);
   }
-  
+
+  // absolute path is required due to chdir('/') after fork().
+  if (!state_path_prefix.empty() && !debugmode && !is_abs_path(state_path_prefix.c_str())) {
+    debugmode=1;
+    PrintHead();
+    PrintOut(LOG_CRIT, "=======> INVALID CHOICE OF OPTIONS: -s <======= \n\n");
+    PrintOut(LOG_CRIT, "Error: relative path %s is only allowed in debug (-d) mode\n\n",
+      state_path_prefix.c_str());
+    EXIT(EXIT_BADCMD);
+  }
+
+  // absolute path is required due to chdir('/') after fork().
+  if (!attrlog_path_prefix.empty() && !debugmode && !is_abs_path(attrlog_path_prefix.c_str())) {
+    debugmode=1;
+    PrintHead();
+    PrintOut(LOG_CRIT, "=======> INVALID CHOICE OF OPTIONS: -s <======= \n\n");
+    PrintOut(LOG_CRIT, "Error: relative path %s is only allowed in debug (-d) mode\n\n",
+      attrlog_path_prefix.c_str());
+    EXIT(EXIT_BADCMD);
+  }
+
+  // Read or init drive database
+  if (!no_defaultdb) {
+    unsigned char savedebug = debugmode; debugmode = 1;
+    if (!read_default_drive_databases())
+        EXIT(EXIT_BADCMD);
+    debugmode = savedebug;
+  }
+
   // 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;
-
-  // Hack! This is to make DEVICESCAN work on ATA devices behind
-  // a SCSI to ATA Translation (SAT) Layer.
-  // This will work on a general OS if the way that SAT devices are
-  // named is the same as SCSI devices.
-  // The BETTER solution is to modify make_device_names to recognize
-  // the additional type "SAT".  This requires changing os_*.cpp.
-
-  const char *basetype = type;
-  if (!strcmp(type,"SAT") )
-    basetype = "SCSI";
-
+// names returned by scan_smart_devices() in os_OSNAME.cpp
+static int MakeConfigEntries(const dev_config & base_cfg,
+  dev_config_vector & conf_entries, smart_device_list & scanned_devs, const char * type)
+{
   // make list of devices
-  if ((num=make_device_names(&devlist,basetype))<0)
+  smart_device_list devlist;
+  if (!smi()->scan_smart_devices(devlist, (*type ? type : 0)))
     PrintOut(LOG_CRIT,"Problem creating device name scan list\n");
   
   // if no devices, or error constructing list, return
-  if (num<=0)
+  if (devlist.size() <= 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);
-    }
+  for (unsigned i = 0; i < devlist.size(); i++) {
+    // Move device pointer
+    smart_device * dev = devlist.release(i);
+    scanned_devs.push_back(dev);
 
-    // ATA or SCSI?
-    if (!strcmp(type,"ATA") )
-      cfg->controller_type = CONTROLLER_ATA;
-    if (!strcmp(type,"SCSI") ) 
-      cfg->controller_type = CONTROLLER_SCSI;
-    if (!strcmp(type,"SAT") )
-      cfg->controller_type = CONTROLLER_SAT;
-    
-    // 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];
+    // Copy configuration, update device and type name
+    conf_entries.push_back(base_cfg);
+    dev_config & cfg = conf_entries.back();
+    cfg.name = dev->get_info().info_name;
+    cfg.dev_type = type;
   }
   
-  // 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;
+  return devlist.size();
 }
  
-void CanNotRegister(char *name, char *type, int line, int scandirective){
-  if( !debugmode && scandirective == 1 ) { return; }
+static void CanNotRegister(const char *name, const char *type, int line, bool scandirective)
+{
+  if (!debugmode && scandirective)
+    return;
   if (line)
     PrintOut(scandirective?LOG_INFO:LOG_CRIT,
              "Unable to register %s device %s at line %d of file %s\n",
@@ -4171,205 +4069,172 @@ void CanNotRegister(char *name, char *type, int line, int scandirective){
 // 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();
-  
+static int ReadOrMakeConfigEntries(dev_config_vector & conf_entries, smart_device_list & scanned_devs)
+{
   // parse configuration file configfile (normally /etc/smartd.conf)  
-  if ((entries=ParseConfigFile())<0) {
+  int entries = ParseConfigFile(conf_entries);
+
+  if (entries < 0) {
     // There was an error reading the configuration file.
-    RmAllConfigEntries();
+    conf_entries.clear();
     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]) {
+  else if (conf_entries.size() == 1) {
     // 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];
-
-    // By default scan for ATA, SCSI and SAT devices
-    int doata=1, doscsi=1, dosat=1;
-
-    if (first->controller_type==CONTROLLER_SCSI) {
-      doata = 0;
-      dosat = 0;
-    } else if (first->controller_type==CONTROLLER_ATA) {
-      doscsi = 0;
-      dosat = 0;
-    } else if (first->controller_type==CONTROLLER_SAT) {
-      doata = 0;
-      doscsi = 0;
-    } else
-      dosat = 0;
-// The code in this block has been neutered by D. Gilbert
-// on 20070226. smartd can't cope ATA disk behind a SAT
-// transport seamlessly _without_ a bigger restructuring
-// of smartd than this code tried. It made ATA disks
-// behind a SAT interface automatically detected only by
-// killing support for real SCSI disks. Sorry, no.
-
-    *scanning=1;
-    
-    if (first->lineno)
+    dev_config first = conf_entries.front();
+    conf_entries.clear();
+
+    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);
-    if (dosat)
-      entries+=MakeConfigEntries("SAT", entries);
+    // make config list of devices to search for
+    MakeConfigEntries(first, conf_entries, scanned_devs, first.dev_type.c_str());
 
     // warn user if scan table found no devices
-    if (!entries) {
+    if (conf_entries.empty())
       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;
+  return conf_entries.size();
 }
 
 
-// This function tries devices from cfgentries.  Each one that can be
+// This function tries devices from conf_entries.  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;
-  
+// from the conf_entries list.
+static void RegisterDevices(const dev_config_vector & conf_entries, smart_device_list & scanned_devs,
+                            dev_config_vector & configs, dev_state_vector & states, smart_device_list & devices)
+{
   // start by clearing lists/memory of ALL existing devices
-  RmAllDevEntries();
-  numdevata=numdevscsi=0;
-  
+  configs.clear();
+  devices.clear();
+  states.clear();
+
   // Register entries
-  for (i=0; i<cfgentries_max ; i++){
-    
-    cfgfile *ent=cfgentries[i];
-    
-    // skip any NULL entries (holes)
-    if (!ent)
+  for (unsigned i = 0; i < conf_entries.size(); i++){
+
+    dev_config cfg = conf_entries[i];
+
+    // get device of appropriate type
+    // TODO: exception handling
+    smart_device * dev = 0;
+    bool scanning = false;
+
+    // Device may already be detected during devicescan
+    if (i < scanned_devs.size()) {
+      dev = scanned_devs.release(i);
+      if (dev)
+        scanning = true;
+    }
+
+    if (!dev) {
+      dev = smi()->get_smart_device(cfg.name.c_str(), cfg.dev_type.c_str());
+      if (!dev) {
+        if (cfg.dev_type.empty())
+          PrintOut(LOG_INFO,"Device: %s, unable to autodetect device type\n", cfg.name.c_str());
+        else
+          PrintOut(LOG_INFO,"Device: %s, unsupported device type '%s'\n", cfg.name.c_str(), cfg.dev_type.c_str());
+        continue;
+      }
+    }
+
+    // Save old info
+    smart_device::device_info oldinfo = dev->get_info();
+
+    // Open with autodetect support, may return 'better' device
+    dev = dev->autodetect_open();
+
+    // Report if type has changed
+    if (/* ent->dev_type && */ oldinfo.dev_type != dev->get_dev_type())
+      PrintOut(LOG_INFO,"Device: %s, type changed from '%s' to '%s'\n",
+        cfg.name.c_str(), oldinfo.dev_type.c_str(), dev->get_dev_type());
+
+    if (!dev->is_open()) {
+      // 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)
+        PrintOut(LOG_INFO, "Device: %s, open() failed: %s\n", dev->get_info_name(), dev->get_errmsg());
+      delete dev;
       continue;
-    
+    }
+
+    // Update informal name
+    cfg.name = dev->get_info().info_name;
+    PrintOut(LOG_INFO, "Device: %s, opened\n", cfg.name.c_str());
+
+    // Prepare initial state
+    dev_state state;
+
     // register ATA devices
-    if (ent->controller_type!=CONTROLLER_SCSI && ent->controller_type!=CONTROLLER_CCISS){
-      if (ATADeviceScan(ent, scanning))
-        CanNotRegister(ent->name, "ATA", ent->lineno, scanning);
+    if (dev->is_ata()){
+      if (ATADeviceScan(cfg, state, dev->to_ata())) {
+        CanNotRegister(cfg.name.c_str(), "ATA", cfg.lineno, scanning);
+        delete dev; dev = 0;
+      }
       else {
         // move onto the list of ata devices
-        cfgentries[i]=NULL;
-        while (numdevata+numdevscsi>=ATAandSCSIdevlist_max)
-           ATAandSCSIdevlist=AllocateMoreSpace(ATAandSCSIdevlist, &ATAandSCSIdevlist_max, "ATA and SCSI devices");
-       ent->WhichCheckDevice=0;
-        ATAandSCSIdevlist[numdevscsi+numdevata]=ent;
-        numdevata++;
+        configs.push_back(cfg);
+        states.push_back(state);
+        devices.push_back(dev);
       }
     }
     
-    // then register SCSI devices
-    if (ent->controller_type==CONTROLLER_SCSI || ent->controller_type==CONTROLLER_CCISS || 
-        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=SCSIandSATDeviceScan(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=SCSIandSATDeviceScan(ent, scanning);
-          alarm(0);
-        }
-        if (sigaction(SIGALRM, &defaultaction, NULL)){
-          PrintOut(LOG_CRIT, "Unable to clear SCSI timeout mechanism.\n");
-        }
-      }
-#else
-      retscsi=SCSIandSATDeviceScan(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);
+    // or register SCSI devices
+    else if (dev->is_scsi()){
+      if (SCSIDeviceScan(cfg, state, dev->to_scsi())) {
+        CanNotRegister(cfg.name.c_str(), "SCSI", cfg.lineno, scanning);
+        delete dev; dev = 0;
       }
       else {
         // move onto the list of scsi devices
-        cfgentries[i]=NULL;
-        while (numdevscsi+numdevata>=ATAandSCSIdevlist_max)
-         ATAandSCSIdevlist=AllocateMoreSpace(ATAandSCSIdevlist, &ATAandSCSIdevlist_max, "ATA and SCSI devices");
-        ATAandSCSIdevlist[numdevata+numdevscsi]=ent;
-        numdevscsi++;
+        configs.push_back(cfg);
+        states.push_back(state);
+        devices.push_back(dev);
       }
     }
     
     // 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);
+    if (!dev && !scanning) {
+      if (cfg.removable || quit==2)
+        PrintOut(LOG_INFO, "Device %s not available\n", cfg.name.c_str());
       else {
-        PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", ent->name);
+        PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", cfg.name.c_str());
         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
+// Main program without exception handling
+int main_worker(int argc, char **argv)
 {
+  // Initialize interface
+  smart_interface::init();
+  if (!smi())
+    return 1;
+
   // external control variables for ATA disks
   smartmonctrl control;
 
   // is it our first pass through?
-  int firstpass=1;
+  bool firstpass = true;
 
   // next time to wake up
   time_t wakeuptime;
@@ -4382,14 +4247,20 @@ static int smartd_main(int argc, char **argv)
   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;
+  con->printing_switchable = false;
+  con->dont_print = !debugmode;
   
+  // Configuration for each device
+  dev_config_vector configs;
+  // Device states
+  dev_state_vector states;
+  // Devices to monitor
+  smart_device_list devices;
+
+  bool write_states_always = true;
+
   // the main loop of the code
-  while (1){
+  for (;;) {
 
     // are we exiting from a signal?
     if (caughtsigEXIT) {
@@ -4400,14 +4271,19 @@ static int smartd_main(int argc, char **argv)
       
       PrintOut(isok?LOG_INFO:LOG_CRIT, "smartd received signal %d: %s\n",
                caughtsigEXIT, strsignal(caughtsigEXIT));
-      
-      EXIT(isok?0:EXIT_SIGNAL);
+
+      if (!isok)
+        return EXIT_SIGNAL;
+
+      // Write state files
+      if (!state_path_prefix.empty())
+        write_all_dev_states(configs, states);
+
+      return 0;
     }
 
     // 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
@@ -4422,6 +4298,10 @@ static int smartd_main(int argc, char **argv)
           caughtsigHUP=2;
         }
 #endif
+        // Write state files
+        if (!state_path_prefix.empty())
+          write_all_dev_states(configs, states);
+
         PrintOut(LOG_INFO,
                  caughtsigHUP==1?
                  "Signal HUP - rereading configuration file %s\n":
@@ -4429,28 +4309,40 @@ static int smartd_main(int argc, char **argv)
                  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);
+      {
+        dev_config_vector conf_entries; // Entries read from smartd.conf
+        smart_device_list scanned_devs; // Devices found during scan
+        // (re)reads config file, makes >=0 entries
+        int entries = ReadOrMakeConfigEntries(conf_entries, scanned_devs);
+
+        if (entries>=0) {
+          // checks devices, then moves onto ata/scsi list or deallocates.
+          RegisterDevices(conf_entries, scanned_devs, configs, states, devices);
+          if (!(configs.size() == devices.size() && configs.size() == states.size()))
+            throw std::logic_error("Invalid result from RegisterDevices");
+        }
+        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))
+      if (devices.size() > 0 || quit==2 || (quit==1 && !firstpass)) {
+        int numata = 0;
+        for (unsigned i = 0; i < devices.size(); i++) {
+          if (devices.at(i)->is_ata())
+            numata++;
+        }
         PrintOut(LOG_INFO,"Monitoring %d ATA and %d SCSI devices\n",
-                 numdevata, numdevscsi);
+                 numata, devices.size() - numata);
+      }
       else {
         PrintOut(LOG_INFO,"Unable to monitor any SMART enabled devices. Try debug (-d) option. Exiting...\n");
         EXIT(EXIT_NODEV);
@@ -4458,18 +4350,30 @@ static int smartd_main(int argc, char **argv)
 
       if (quit==4) {
         // user has asked to print test schedule
-        PrintTestSchedule(ATAandSCSIdevlist);
+        PrintTestSchedule(configs, states, devices);
         EXIT(0);
       }
       
       // reset signal
       caughtsigHUP=0;
+
+      // Always write state files after (re)configuration
+      write_states_always = true;
     }
 
     // check all devices once,
     // self tests are not started in first pass unless '-q onecheck' is specified
-    CheckDevicesOnce(ATAandSCSIdevlist, (!firstpass || quit==3)); 
-    
+    CheckDevicesOnce(configs, states, devices, (!firstpass || quit==3));
+
+     // Write state files
+    if (!state_path_prefix.empty())
+      write_all_dev_states(configs, states, write_states_always);
+    write_states_always = false;
+
+    // Write attribute logs
+    if (!attrlog_path_prefix.empty())
+      write_all_dev_attrlogs(configs, states);
+
     // 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"
@@ -4485,15 +4389,53 @@ static int smartd_main(int argc, char **argv)
     // set exit and signal handlers, write PID file, set wake-up time
     if (firstpass){
       Initialize(&wakeuptime);
-      firstpass=0;
+      firstpass = false;
     }
     
     // sleep until next check time, or a signal arrives
-    wakeuptime=dosleep(wakeuptime);
+    wakeuptime = dosleep(wakeuptime, write_states_always);
   }
 }
 
 
+#ifndef _WIN32
+// Main program
+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
+{
+  int status;
+  try {
+    // Do the real work ...
+    status = main_worker(argc, argv);
+  }
+  catch (int ex) {
+    // EXIT(status) arrives here
+    status = ex;
+  }
+  catch (const std::bad_alloc & /*ex*/) {
+    // Memory allocation failed (also thrown by std::operator new)
+    PrintOut(LOG_CRIT, "Smartd: Out of memory\n");
+    status = EXIT_NOMEM;
+  }
+  catch (const std::exception & ex) {
+    // Other fatal errors
+    PrintOut(LOG_CRIT, "Smartd: Exception: %s\n", ex.what());
+    status = EXIT_BADCODE;
+  }
+
+  if (is_initialized)
+    status = Goodbye(status);
+
+#ifdef _WIN32
+  daemon_winsvc_exitcode = status;
+#endif
+  return status;
+}
+
+
 #ifdef _WIN32
 // Main function for Windows
 int main(int argc, char **argv){
diff --git a/smartd.h b/smartd.h
deleted file mode 100644 (file)
index 9d0cfbb..0000000
--- a/smartd.h
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * smartd.h
- *
- * Home page of code is: http://smartmontools.sourceforge.net
- *
- * Copyright (C) 2002-8 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/
- *
- */
-
-#ifndef SMARTD_H_
-#define SMARTD_H_
-
-// Needed since some structure definitions below require POSIX
-// extended regular expressions.
-#include <sys/types.h>
-#include <regex.h>
-
-
-#ifndef SMARTD_H_CVSID
-#define SMARTD_H_CVSID "$Id: smartd.h,v 1.86 2008/03/04 22:09:47 ballen4705 Exp $\n"
-#endif
-
-// Configuration file
-#define CONFIGFILENAME "smartd.conf"
-
-// Scan directive for configuration file
-#define SCANDIRECTIVE "DEVICESCAN"
-
-// maximum line length in configuration file
-#define MAXLINELEN 256
-
-// maximum length of a continued line in configuration file
-#define MAXCONTLINE 1023
-
-// default for how often SMART status is checked, in seconds
-#define CHECKTIME 1800
-
-/* Boolean Values */
-#define TRUE 0x01
-#define FALSE 0x00
-
-// Number of monitoring flags per Attribute and offsets.  See
-// monitorattflags below.
-#define NMONITOR 4
-#define MONITOR_FAILUSE   0
-#define MONITOR_IGNORE    1
-#define MONITOR_RAWPRINT  2
-#define MONITOR_RAW       3
-
-
-// Number of allowed mail message types
-#define SMARTD_NMAIL 13
-
-typedef struct mailinfo_s {
-  int logged;// number of times an email has been sent
-  time_t firstsent;// time first email was sent, as defined by time(2)
-  time_t lastsent; // time last email was sent, as defined by time(2)
-} mailinfo;
-
-// If user has requested email warning messages, then this structure
-// stores the information about them, and track type/date of email
-// messages.
-typedef struct maildata_s {
-  mailinfo maillog[SMARTD_NMAIL];         // log info on when mail sent
-  char *emailcmdline;                     // script to execute
-  char *address;                          // email address, or null
-  unsigned char emailfreq;                // Emails once (1) daily (2) diminishing (3)
-  unsigned char emailtest;                // Send test email?
-} maildata;
-
-// If user has requested automatic testing, then this structure stores
-// their regular expression pattern, the compiled form of that regex,
-// and information about the disk capabilities and when the last text
-// took place
-
-typedef struct testinfo_s {
-  char *regex;                    // text form of regex
-  regex_t cregex;                 // compiled form of regex
-  unsigned short hour;            // 1+hour of year when last scheduled self-test done
-  char testtype;                  // type of test done at hour indicated just above
-  signed char not_cap_offline;    // 0==unknown OR capable of offline, 1==not capable 
-  signed char not_cap_conveyance;
-  signed char not_cap_short;
-  signed char not_cap_long;
-} testinfo;
-
-
-// cfgfile is the main data structure of smartd. It is used in two
-// ways.  First, to store a list of devices/options given in the
-// configuration smartd.conf or constructed with DEVICESCAN.  And
-// second, to point to or provide all persistent storage needed to
-// track a device, if registered either as SCSI or ATA.
-// 
-// After parsing the config file, each valid entry has a cfgfile data
-// structure allocated in memory for it.  In parsing the configuration
-// file, some storage space may be needed, of indeterminate length,
-// for example for the device name.  When this happens, memory should
-// be allocated and then pointed to from within the corresponding
-// cfgfile structure.
-
-// After parsing the configuration file, each device is then checked
-// to see if it can be monitored (this process is called "registering
-// the device".  This is done in [scsi|ata]devicescan, which is called
-// exactly once, after the configuration file has been parsed and
-// cfgfile data structures have been created for each of its entries.
-//
-// If a device can not be monitored, the memory for its cfgfile data
-// structure should be freed by calling rmconfigentry(cfgfile *). In
-// this case, we say that this device "was not registered".  All
-// memory associated with that particular cfgfile structure is thus
-// freed.
-// 
-// The remaining devices are polled in a timing look, where
-// [ata|scsi]CheckDevice looks at each entry in turn.
-// 
-// If you want to add small amounts of "private" data on a per-device
-// basis, just make a new field in cfgfile.  This is guaranteed zero
-// on startup (when ata|scsi]scsidevicescan(cfgfile *cfg) is first
-// called with a pointer to cfgfile.
-// 
-// If you need *substantial* persistent data space for a device
-// (dozens or hundreds of bytes) please add a pointer field to
-// cfgfile.  As before, this is guaranteed NULL when
-// ata|scsi]scsidevicescan(cfgfile *cfg) is called. Allocate space for
-// it in scsidevicescan or atadevicescan, if needed, and deallocate
-// the space in rmconfigentry(cfgfile *cfg). Be sure to make the
-// pointer NULL unless it points to an area of the heap that can be
-// deallocated with free().  In other words, a non-NULL pointer in
-// cfgfile means "this points to data space that should be freed if I
-// stop monitoring this device." If you don't need the space anymore,
-// please call free() and then SET THE POINTER IN cfgfile TO NULL.
-// 
-// Note that we allocate one cfgfile structure per device.  This is
-// why substantial persisent data storage should only be pointed to
-// from within cfgfile, not kept within cfgfile itself - it saves
-// memory for those devices that don't need that type of persistent
-// data.
-// 
-// In general, the capabilities of devices should be checked at
-// registration time within atadevicescan() and scsidevicescan(), and
-// then noted within *cfg.  So if device lacks some capability, this
-// should be visible within *cfg after returning from
-// [ata|scsi]devicescan.
-// 
-// Devices are then checked, once per polling interval, within
-// ataCheckDevice() and scsiCheckDevice().  These should only check
-// the capabilities that devices already are known to have (as noted
-// within *cfg).
-
-typedef struct configfile_s {
-  // FIRST SET OF ENTRIES CORRESPOND TO WHAT THE USER PUT IN THE
-  // CONFIG FILE.  SOME ENTRIES MAY BE MODIFIED WHEN A DEVICE IS
-  // 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_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
-  char usage;                             // Track changes in Usage Attributes
-  char selftest;                          // Monitor number of selftest errors
-  char errorlog;                          // Monitor number of ATA errors
-  char permissive;                        // Ignore failed SMART commands
-  char autosave;                          // 1=disable, 2=enable Autosave Attributes
-  char autoofflinetest;                   // 1=disable, 2=enable Auto Offline Test
-  unsigned char fixfirmwarebug;           // Fix firmware bug
-  char ignorepresets;                     // Ignore database of -v options
-  char showpresets;                       // Show database entry for this device
-  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
-  unsigned short pending;                 // lower 8 bits: ID of current pending sector count
-                                          // upper 8 bits: ID of offline pending sector count
-  
-  // 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 SuppressReport;           // minimize nuisance reports
-  unsigned char modese_len;               // mode sense/select cmd len: 0 (don't
-                                          // know yet) 6 or 10
-  unsigned char notused2[3];              // for packing alignment
-
-  // ATA ONLY FROM HERE ON TO THE END
-  int ataerrorcount;                      // Total number of ATA errors
-  
-  // following NMONITOR items each point to 32 bytes, in the form of
-  // 32x8=256 single bit flags 
-  // valid attribute numbers are from 1 <= x <= 255
-  // monitorattflags+0  set: ignore failure for a usage attribute
-  // monitorattflats+32 set: don't track attribute
-  // monitorattflags+64 set: print raw value when tracking
-  // monitorattflags+96 set: track changes in raw value
-  unsigned char *monitorattflags;
-
-  // NULL UNLESS (1) STORAGE IS ALLOCATED WHEN CONFIG FILE SCANNED
-  // (SET BY USER) or (2) IT IS SET WHEN DRIVE IS AUTOMATICALLY
-  // RECOGNIZED IN DATABASE (WHEN DRIVE IS REGISTERED)
-  unsigned char *attributedefs;            // -v options, see end of extern.h for def
-
-  // ATA ONLY - SAVE SMART DATA. NULL POINTERS UNLESS NEEDED.  IF
-  // NEEDED, ALLOCATED WHEN DEVICE REGISTERED.
-  struct ata_smart_values *smartval;       // Pointer to SMART data
-  struct ata_smart_thresholds_pvt *smartthres; // Pointer to SMART thresholds
-
-  // Added to distinguish ATA and SCSI entries on list
-  int WhichCheckDevice;
-
-} cfgfile;
-
-
-typedef struct changedattribute_s {
-  unsigned char newval;
-  unsigned char oldval;
-  unsigned char id;
-  unsigned char prefail;
-  unsigned char sameraw;
-} changedattribute_t;
-
-// Declare our own printing functions. Doing this provides error
-// messages if the argument number/types don't match the format.
-#ifndef __GNUC__
-#define __attribute__(x)      /* nothing */
-#endif
-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)));   
-
-/* Debugging notes: to check for memory allocation/deallocation problems, use:
-
-export LD_PRELOAD=libnjamd.so;
-export NJAMD_PROT=strict;           
-export NJAMD_CHK_FREE=error;
-export NJAMD_DUMP_LEAKS_ON_EXIT=num;
-export NJAMD_DUMP_LEAKS_ON_EXIT=3;
-export NJAMD_TRACE_LIBS=1
-
-*/
-
-// Number of seconds to allow for registering a SCSI device. If this
-// time expires without sucess or failure, then treat it as failure.
-// Set to 0 to eliminate this timeout feature from the code
-// (equivalent to an infinite timeout interval).
-#define SCSITIMEOUT 0
-
-// This is for solaris, where signal() resets the handler to SIG_DFL
-// after the first signal is caught.
-#ifdef HAVE_SIGSET
-#define SIGNALFN sigset
-#else
-#define SIGNALFN signal
-#endif
-
-
-#define SELFTEST_ERRORCOUNT(x) (x & 0xff)
-#define SELFTEST_ERRORHOURS(x) ((x >> 8) & 0xffff)
-
-// cfg->pending is a 16 bit unsigned quantity.  If the least
-// significant 8 bits are zero, this means monitor Attribute
-// CUR_UNC_DEFAULT's raw value.  If they are CUR_UNC_DEFAULT, this
-// means DON'T MONITOR.  If the most significant 8 bits are zero, this
-// means monitor Attribute OFF_UNC_DEFAULT's raw value.  If they are
-// OFF_UNC_DEFAULT, this means DON'T MONITOR.
-#define OFF_UNC_DEFAULT 198
-#define CUR_UNC_DEFAULT 197
-
-#define CURR_PEND(x) (x & 0xff)
-#define OFF_PEND(x) ((x >> 8) & 0xff)
-
-// if cfg->pending has this value, dont' monitor
-#define DONT_MONITOR_UNC (256*OFF_UNC_DEFAULT+CUR_UNC_DEFAULT)
-
-// Some return values from SCSIFilterKnown(), used to detect known
-// device types hidden behind SCSI devices.
-
-#define SCSIFK_FAILED   -1
-#define SCSIFK_NORMAL    0
-#define SCSIFK_3WARE    11
-#define SCSIFK_SAT      12
-#define SCSIFK_MARVELL  13
-
-// Make additions BEFORE this line.  The line is the end of
-// double-inclusion protection and should remain the final line.
-#endif  // #ifndef SMARTD_H_
diff --git a/smartmontools.spec b/smartmontools.spec
deleted file mode 100644 (file)
index 5a0f565..0000000
+++ /dev/null
@@ -1,1976 +0,0 @@
-Release:  1
-Summary:       smartmontools - for monitoring S.M.A.R.T. disks and devices
-Summary(cs):   smartmontools - pro monitorování S.M.A.R.T. diskù a zaøízení
-Summary(de):   smartmontools - zur Ãœberwachung von S.M.A.R.T.-Platten und-Geräten
-Summary(es):   smartmontools - para el seguimiento de discos y dispositivos S.M.A.R.T.
-Summary(fr):   smartmontools - pour le suivi des disques et instruments S.M.A.R.T.
-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.38
-License:       GPL
-Group:         Applications/System
-Group(de):     Applikationen/System
-Group(es):     Aplicaciones/Sistema
-Group(fr):     Applications/Système
-Group(pt):     Aplicativos/Sistema
-Group(it):      Applicazioni/Sistemi
-Source0:       %{name}-%{version}.tar.gz
-URL:            http://smartmontools.sourceforge.net/
-Prereq:                /sbin/chkconfig
-BuildRoot:     %{_tmppath}/%{name}-%{version}-root
-Obsoletes:     smartctl
-Obsoletes:      smartd
-Obsoletes:     ucsc-smartsuite
-Obsoletes:      smartsuite
-Packager:       Bruce Allen <smartmontools-support@lists.sourceforge.net>
-
-%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.172 2008/03/04 22:09:47 ballen4705 Exp $
-
-# Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-# Home page: http://smartmontools.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.
-#
-# 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/
-
-
-%description
-smartmontools 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.  This is used to check the
-reliability of the hard drive and to predict drive failures.  The suite
-is derived from the smartsuite package, and contains two utilities.  The
-first, smartctl, is a command line utility designed to perform simple
-S.M.A.R.T. tasks.  The second, smartd, is a daemon that periodically
-monitors smart status and reports errors to syslog.  The package is
-compatible with the ATA/ATAPI-5 specification.  Future releases will be
-compatible with the ATA/ATAPI-6 andATA/ATAPI-7 specifications.  The
-package is intended to incorporate as much "vendor specific" and
-"reserved" information as possible about disk drives.  man smartctl and
-man smartd will provide more information.  This RPM file is compatible
-with all RedHat releases back to at least 6.2 and should work OK on any
-modern linux distribution.  The most recent versions of this package and
-additional information can be found at the URL:
-http://smartmontools.sourceforge.net/
-
-%description -l cs
-smartmontools Ã¸Ã­dí a monitorují zaøízení pro ukládání dat za pou¾ití
-technologie automatického monitorování, analýzy a hlá¹ení
-(Self-Monitoring, Analysis and Reporting Technology System -
-S.M.A.R.T.) vestavìného do pevných diskù ATA a SCSI. Pou¾ívá se ke
-kontrole pou¾itelnosti pevného disku a pøedvídání havárií diskù.
-Nástroje jsou odvozeny od balíèku smartsuite a obsahují dva programy.
-První, smartctl, je nástroj pro provádìní jednoduchých S.M.A.R.T. Ãºloh
-na pøíkazové Ã¸Ã¡dce. Druhý, smartd, je démon, který periodicky
-monitoruje stav a hlásí chyby do systémového protokolu. Balíèek je
-kompatibilní se specifikací ATA/ATAPI-5. Dal¹í verze budou
-kompatibilní se specifikacemi ATA/ATAPI-6 a ATA/ATAPI-7. Balíèek je
-navr¾en tak, aby pokryl co nejvíce polo¾ek s informacemi "závislé na
-výrobci" a "rezervováno". Více informací získáte pomocí man smartctl a
-man smartd. Tento RPM balíèek je kompatibilní se v¹emi verzemi RedHatu
-a mìl by fungovat na v¹ech moderních distribucích Linuxu. Aktuální
-verzi najdete na URL http://smartmontools.sourceforge.net/
-
-%description -l de
-Die smartmontools steuern und Ã¼berwachen Speichergeräte mittels des
-S.M.A.R.T.-Systems (Self-Monitoring, Analysis and Reporting Technology,
-Technologie zur Selbst-Ãœberwachung, Analyse und Berichterstellung), das
-in ATA- und SCSI-Festplatten eingesetzt wird.  Sie werden benutzt, um
-die Zuverlässigkeit der Festplatte zu prüfen und Plattenfehler
-vorherzusagen.  Die Suite wurde vom smartsuite-Paket abgeleitet und
-enthält zwei Dienstprogramme.  Das erste, smartctl, ist ein
-Kommandozeilentool, das einfache S.M.A.R.T. Aufgaben ausführt.  Das
-zweite, smartd, ist ein Daemon, der periodisch den S.M.A.R.T.-Status
-überwacht und Fehler ins Syslog protokolliert.  Das Paket ist zur
-ATA/ATAPI-5 Spezifikation kompatibel. Zukünftige Versionen werden auch
-die ATA/ATAPI-6 und ATA/ATAPI-7 Spezifikationen umsetzen.  Das Paket
-versucht, so viele "herstellerspezifische" und "reservierte" Information
-über Plattenlaufwerke wie möglich bereitzustellen.  man smartctl und man
-smartd liefern mehr Informationen Ã¼ber den Einsatz.  Dieses RPM ist zu
-allen RedHat-Versionen ab spätestens 6.2 kompatibel und sollte unter
-jedem modernen Linux arbeiten.  Die aktuellsten Versionen dieses Pakets
-und zusätzliche Informationen sind zu finden unter der URL:
-http://smartmontools.sourceforge.net/
-
-%description -l es
-smartmontools controla y hace el seguimiento de dispositivos de
-almacenamiento usando el Self-Monitoring, Analysis and Reporting
-Technology System (S.M.A.R.T.) incorporado en discos duros ATA y SCSI. 
-Es usado para asegurar la fiabilidad de discos duros y predecir averias. 
-El conjunto de programas proviene del conjunto smartsuite y contiene dos
-utilidades.  La primera, smartctl, es una utilidad command-line hecha
-para hacer operaciones S.M.A.R.T. sencillas.  La segunda, smartd, es un
-programa que periodicamente chequea el estatus smart e informa de
-errores a syslog.  Estos programas son compatibles con el sistema
-ATA/ATAPI-5.  Futuras versiones seran compatibles con los sistemas
-ATA/ATAPI-6 y ATA/ATAPI-7.  Este conjunto de programas tiene el
-proposito de incorporar la mayor cantidad posible de informacion
-reservada y especifica de discos duros.  Los comandos 'man smartctl' y
-'man smartd' contienen mas informacion.  Este fichero RPM es compatible
-con todas las versiones de RedHat a partir de la 6.2 y posiblemente
-funcionaran sin problemas en cualquier distribucion moderna de linux. 
-La version mas reciente de estos programas ademas de informacion
-adicional pueden encontrarse en: http://smartmontools.sourceforge.net/
-
-%description -l fr
-smartmontools contrôle et fait le suivi de périphériques de stockage
-utilisant le système Self-Monitoring, Analysis and Reporting
-Technology (S.M.A.R.T) intégré dans les disques durs ATA et SCSI.  Ce
-système est utilisé pour vérifier la fiabilité du disque dur et prédire
-les défaillances du lecteur.  La suite logicielle dérive du paquet
-smartsuite et contient deux utilitaires.  Le premier, smartctl,
-fonctionne en ligne de commande et permet de réaliser des tâches
-S.M.A.R.T. simples.  Le second, smartd, est un démon qui fait
-périodiquement le suivi du statut smart et transmet les erreurs au
-syslog.  Ce paquet est compatible avec la spécification ATA/ATAPI-5. 
-Les prochaines versions seront compatibles avec les spécifications
-ATA/ATAPI-6 et ATA/ATAPI-7.  Ce paquet tente d'incorporer le plus
-d'informations possible sur les disques durs qu'elles soient spécifiques
-au constructeur ("vendor specific") ou réservées ("reserved").  man
-smartctl et man smartd donnent plus de renseignements.  Ce fichier RPM
-est compatible avec toutes les versions de RedHat v6.2 et ultérieures,
-et devrait fonctionner sur toutes les distributions récentes de Linux. 
-Les dernières versions de ce paquet et des informations supplémentaires
-peuvent Ãªtre trouvées Ã  l'adresse URL:
-http://smartmontools.sourceforge.net/
-
-%description -l pt
-smartmontools controla e monitora dispositivos de armazenamento
-utilizando o recurso Self-Monitoring, Analysis and Reporting Technology
-System (S.M.A.R.T.) integrado nos discos rígidos ATA e SCSI, cuja
-finalidade Ã© verificar a confiabilidade do disco rígido e prever falhas
-da unidade.  A suite Ã© derivada do pacote smartsuite, e contém dois
-utilitários.  O primeiro, smartctl, Ã© um utilitário de linha de comando
-projetado para executar tarefas simples de S.M.A.R.T.  O segundo,
-smartd, Ã© um daemon que monitora periodicamente estados do smart e
-reporta erros para o syslog.  O pacote Ã© compatível com a especificação
-ATA/ATAPI-5.  Futuras versões serão compatíveis com as especificações
-ATA/ATAPI-6 e ATA/ATAPI-7.  O pacote pretende incorporar o maior número
-possível de informações "específicas do fabricante" e "reservadas" sobre
-unidades de disco.  man smartctl e man smartd contém mais informações. 
-Este arquivo RPM Ã© compatível com todas as versões do RedHat a partir da
-6.2 e deverá funcionar perfeitamente em qualquer distribuição moderna do
-Linux.  As mais recentes versões deste pacote e informações adicionais
-podem ser encontradas em http://smartmontools.sourceforge.net/
-
-%description -l it
-smartmontools controlla e monitora dischi che usano il "Self-Monitoring,
-Analysis and Reporting Technology System" (S.M.A.R.T.), in hard drive
-ATA e SCSI. Esso Ã¨ usato per controllare l'affidabilità dei drive e
-predire i guasti. La suite Ã¨ derivata dal package smartsuite e contiene
-due utility. La prima, smartctl, Ã¨ una utility a linea di comando
-progettata per eseguire semplici task S.M.A.R.T.. La seconda, smartd, Ã¨
-un daemon che periodicamente monitora lo stato di smart e riporta errori
-al syslog. Il package Ã¨ compatibile con le specifiche ATA/ATAPI-6 e
-ATA/ATAPI-7. Il package vuole incorporare tutte le possibili
-informazioni riservate e "vendor specific" sui dischi. man smartctl e
-man smartd danno più informazioni. Questo file RPM Ã¨ compatibile con
-tutte le release di RedHat, almeno dalla 6.2 e dovrebbe funzionare bene
-su ogni moderna distribuzione di linux. Le versioni più recenti di
-questo package e informazioni addizionali possono essere trovate al sito
-http://smartmontools.sourceforge.net/
-
-%description -l pl
-Pakiet zawiera dwa programy (smartctl oraz smartd) do kontroli i
-monitorowania systemów przechowywania danych za pomoc± S.M.A.R.T -
-systemu wbudowanego w wiêkszo¶æ nowych dysków ATA oraz SCSI. Pakiet
-pochodzi od oprogramowania smartsuite i wspiera dyski ATA/ATAPI-5.
-
-# The following sections are executed by the SRPM file
-%prep
-
-%setup -q
-
-%build
-  %configure
-  make
-
-%install
-  rm -rf $RPM_BUILD_ROOT
-  rm -rf %{_buildroot}
-  %makeinstall
-  rm -f examplescripts/Makefile*
-  %if %{suse}
-    mkdir -p $RPM_BUILD_ROOT%{_defaultdocdir}
-    mv $RPM_BUILD_ROOT/usr/share/doc/%{name}-%{version} $RPM_BUILD_ROOT%{_defaultdocdir}/%{name}
-    ln -s ../../etc/rc.d/init.d/smartd $RPM_BUILD_ROOT%{_sbindir}/rcsmartd
-  %endif
-
-%files
-  %defattr(-,root,root)
-  %attr(755,root,root) %{_sbindir}/smartd
-  %attr(755,root,root) %{_sbindir}/smartctl
-  %if %{suse}
-    %attr(755,root,root) %{_sbindir}/rcsmartd
-  %endif
-  %attr(755,root,root) /etc/rc.d/init.d/smartd
-  %attr(644,root,root) %{_mandir}/man8/smartctl.8*
-  %attr(644,root,root) %{_mandir}/man8/smartd.8*
-  %attr(644,root,root) %{_mandir}/man5/smartd.conf.5*
-  %doc AUTHORS CHANGELOG COPYING INSTALL NEWS README TODO WARNINGS smartd.conf examplescripts
-  %config(noreplace) %{_sysconfdir}/smartd.conf
-
-%clean
-  rm -rf $RPM_BUILD_ROOT
-  rm -rf %{_buildroot}
-  rm -rf %{_builddir}/%{name}-%{version}
-
-# The following are executed only by the binary RPM at install/uninstall
-
-# since this installs the gzipped documentation files, remove
-# non-gzipped ones of the same name.
-
-# run before installation.  Passed "1" the first time package installed, else a larger number
-%pre
-if [ -f /usr/share/man/man8/smartctl.8 ] ; then
-       echo "You MUST delete (by hand) the outdated file /usr/share/man/man8/smartctl.8 to read the new manual page for smartctl."     
-fi
-if [ -f /usr/share/man/man8/smartd.8 ] ; then
-       echo "You MUST delete (by hand) the outdated file /usr/share/man/man8/smartd.8 to read the new manual page for smartd." 
-fi
-if [ -f /usr/share/man/man5/smartd.conf.5 ] ; then
-        echo "You MUST delete (by hand) the outdated file /usr/share/man/man5/smartd.conf.5 to read the new manual page for smartd.conf"
-fi
-
-if [ ! -f /etc/smartd.conf ]; then
-       echo "Note that you can use a configuration file /etc/smartd.conf to control the"
-       echo "startup behavior of the smartd daemon.  See man 8 smartd for details."
-fi
-
-# run after installation.  Passed "1" the first time package installed, else a larger number
-%post
-# if smartd is already running, restart it with the new daemon
-if [ -f /var/lock/subsys/smartd ]; then
-        /etc/rc.d/init.d/smartd restart 1>&2
-       echo "Restarted smartd services"
-else
-# else tell the user how to start it
-        echo "Run \"/etc/rc.d/init.d/smartd start\" to start smartd service now."
-fi
-
-# Now see if we should tell user to set service to start on boot       
-/sbin/chkconfig --list smartd > /dev/null 2> /dev/null
-printmessage=$?
-
-if [ $printmessage -ne 0 ] ; then
-       echo "Run \"/sbin/chkconfig --add smartd\", to start smartd service on system boot"
-else
-       echo "smartd will continue to start up on system boot"
-fi
-
-
-# run before uninstallation.  Passed zero when the last version uninstalled, else larger
-%preun
-
-# if uninstalling the final copy, stop and remove any links    
-if [ "$1" = "0" ]; then
-  if [ -f /var/lock/subsys/smartd ]; then
-    /etc/rc.d/init.d/smartd stop 1>&2
-    echo "Stopping smartd services"
-  fi
-
-# see if any links remain, and kill them if they do
-  /sbin/chkconfig --list smartd > /dev/null 2> /dev/null
-  notlinked=$?
-       
-  if [ $notlinked -eq 0 ]; then
-    /sbin/chkconfig --del smartd
-    echo "Removing chkconfig links to smartd boot-time startup scripts"
-  fi
-fi
-
-# run after uninstallation. Passed zero when the last version uninstalled, else larger
-# %postun
-
-%define date   %(echo `LC_ALL="C" date +"%a %b %d %Y"`)
-
-# Maintainers / Developers Key:
-# [BA] Bruce Allen
-# [EB] Erik Inge Bolsø
-# [SB] Stanislav Brabec
-# [PC] Peter Cassidy
-# [YD] Yuri Dario
-# [CD] Capser Dik
-# [CF] Christian Franke
-# [GF] Guilhem Frézou
-# [DG] Douglas Gilbert
-# [GG] Guido Guenther
-# [GK] Geoff Keating
-# [DK] David Kirkby
-# [KM] Kai Mäkisarai
-# [EM] Eduard Martinescu
-# [FM] Frédéric L. W. Meunier
-# [KS] Keiji Sawada
-# [DS] David Snyder
-# [SS] Sergey Svishchev
-# [PW] Phil Williams
-# [LW] Leon Woestenberg
-# [RZ] Richard Zybert
-# [SZ] Sf Zhou
-
-
-%changelog
-* Wed Dec 20 2006 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-  [CF] Windows: Added alternate method for (limited) monitoring of
-       3ware controllers by parsing the output of CLI or 3DM.
-       Either "tw_cli" can be run internally ("/dev/tw_cli/cx/py"),
-       or data can be read from standard input ("/dev/tw_cli/stdin")
-       or clipboard ("/dev/tw_cli/clip").
-  [DG] Remove linux specific libata detect code; rely on general SAT
-       code. smartd should now generate a sensible log message for
-       ATA devices behind a SAT layer on all architectures.
-  [BA] Increased max line length MAXLINELEN for /etc/smartd.conf from
-       128 to 256 characters to handle long strings in
-       /dev/disk/by-id.  Thanks to Martin Krafft.
-  [PW] Drive database: added missing drives from Seagate Momentus 5400.2
-       family
-  [BA] Finished Christian's fix (next item below) by removing
-       LINUX_86_64 hack from configure.in.
-  [CF] Fixed inclusion of PRI?64 macros from inttypes.h.
-  [CF] Windows: Added WRITE LOG to support selective self tests.
-  [CF] Fix selective self test log revision number if '-T permissive'
-       is specified (Tested with Samsung HD401LJ).
-  [CF] Windows: Fixed int64 printf format for MinGW runtime.
-  [PW] Drive database: added Seagate Barracuda 7200.10 family, Seagate
-       Momentus 42 family, Maxtor DiamondMax 60 ATA 66 family, Maxtor
-       DiamondMax 60 ATA 100 family, and Western Digital Caviar Serial
-       ATA family
-  [PW] Drive database: added missing drives from Seagate Barracuda
-       7200.9 family, Seagate Barracuda 7200.7 family, Seagate Momentus
-       7200.1 family, Toshiba 2.5" HDD family (80 GB and above), Western
-       Digital Caviar RE Serial ATA family, Hitachi Deskstar 7K80 family,
-       and Maxtor DiamondMax 4320 Ultra ATA family
-  [BA] Linux: compile fix for SuSE.  Check for existence
-       of linux/compiler.h and include in os_linux.h if
-       present.  Thanks to SB.
-  [BA] smartd: DEVICESCAN will now pick up SATA/SAT devices
-       attached to a SCSI device tree via SAT translation.
-       Note: this is a bit of a hack.  I will document it once
-       I know if this needs to be Linux only or can have more
-       general application.
-  [BA] Added a couple SATA commands to the tables -- thanks DG!
-       Phil -- how about going through and systematically adding
-       these new commands to atacmdnames.cpp?
-  [BA] Linux s86_64: get rid of some compiler warnings on
-       x86_64 Linux systems.
-  [CF] Windows: Added missing support for READ_LOG, ABORT_SELFTEST
-       and CHECK_POWER_STATE for 3ware 9000 controllers. Thanks to
-       Greg de Valois for implementing this new ioctl in the driver.
-  [PW] Drive database: added Seagate NL35 SATA family.  Thanks to Kai
-       Harrekilde-Petersen for providing a patch.
-  [DG] [SCSI, Windows] add SPT interface for NT and later. New device
-       names are "pd<n>", "sd<l>" and "tape<n>".
-  [PW] Drive database: added Western Digital Scorpio family, Fujitsu MHV
-       family, Maxtor MaXLine Pro 500 family, and Maxtor DiamondMax 11
-       family
-  [PW] Drive database: added missing drives from Toshiba 2.5" HDD
-       (30-60 GB) family, Maxtor DiamondMax 10 family, Seagate Barracuda
-       7200.8 family, Fujitsu MHT family, and Maxtor DiamondMax Plus 8
-       family
-  [SB] Added examplescripts/Example4 using powersave-notify.
-  [SB] More temperature monitoring examples in smartd.conf with DEVICESCAN.
-  [SB] Minor improvements of SuSE part of init script.
-  [CF] Drive database: added Samsung P80 series, P120 series, SP8004H
-       and T series.
-  [GG] Add CCISS (Compaq Smart Array Controller) support with contributions
-       from Praveen Chidambaram, Douglas Gilbert, Guido Guenther and Frédéric
-       BOITEUX
-  [PW] Drive database: added Hitachi Deskstar T7K250 and Hitachi
-       Deskstar 7K500 series.  Thanks to L. J. Wu for providing a
-       patch
-  [PW] Drive database: added Maxtor MaXLine III family, Seagate U7
-       family, Seagate ST34321A, FUJITSU MHM2060AT, FUJITSU MHT2040AS,
-       Western Digital Caviar SE16 family, IBM Travelstar 4GT family,
-       QUANTUM FIREBALLP KA9.1, QUANTUM FIREBALL SE4.3A, TOSHIBA
-       MK1032GAX, TOSHIBA MK4026GAX
-  [PW] Drive database: added missing drives from Western Digital Caviar
-       SE (Serial ATA) and WD Raptor families
-  [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.
-  [GK] Darwin: Add support for AHCI drivers found in Intel-based Macs.
-
-* Wed Apr 12 2006 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-  [BA] Linux: smartd/smartctl issue syntax hints to user if 3ware
-       disk controller present with EITHER 3ware OR AMCC vendor
-       name, and user syntax incorrect.
-  [BA] Update copyright dates to 2006.
-  [DG] [SCSI] Loosen sanity check on Seagate/Hitachi factory information
-       log page so it is not skipped on recent Seagate SCSI disks.
-  [CF] Added command 'smartd -q showtests' to list test schedules.
-  [CF] Added command 'smartctl -P showall MODEL [FIRMWARE]' to list
-       database entries for specific drives and firmware.
-  [PW] Automatically set -v 9,minutes and -v 194,unknown for Maxtor
-       DiamondMax D540X-4G drives.
-  [DG] [SCSI] suppress various outputs when data fails sanity checks.
-       Correct 'last n error events' log page indexing.
-  [DG] [SCSI] changed smartctl exit status to reflect any problems in
-       the most recent 20 self test logs [Leandro Santi]
-  [DG] [SCSI] Fix process return value when scsiGetSmartData() fails
-       in smartctl [Leandro Santi]
-  [BA] Updated docs and error message to reflect Linux libata
-       support for smartmontools starting with the 2.6.15 kernel
-       series. Also init script support for the 'tinysofa' release.
-  [DG] [SCSI] Mask dpofua bit when changing mode pages. Fix failure
-       of 'smartctl -l error'.
-  [EM] Fixed a problem with FreeBSD and 3Ware 'twe' devices
-  [CF] Fixed a regexp in knowndrives table, added regexp syntax check
-       via 'smartctl -P showall'.
-  [CF] Cygwin & Windows: Fixed memory leak in function calling
-       IOCTL_IDE_PASS_THROUGH. Thanks to Fred Schmidt for the problem
-       report.
-  [CF] Cygwin: added cygrunsrv support and commands "install", "remove"
-       and "status" to smartd.initd.
-  [SS] Fix runtime problems on big-engian NetBSD platforms (patch provided
-       by Martin Husemann)
-  [CF] Cygwin smartd: Open smartd.conf in textmode to allow use of
-       Windows editors.
-  [CF] Cygwin smartd: Added option '--service' to allow smartd running
-       as windows service via cygrunsrv. Useful in conjunction with new
-       syslogd support added in Cygwin 1.5.15.
-  [CF] Windows: Added patch to avoid output of non-ascii timezone names.
-  [EM] Incorporate various patches to provide TWE support and support for 
-       multiple 3Ware cards, Power Check Support, and FreeBSD 6.x support.
-       Thanks to Rudolf Cejka, Frank Behrens, and Jung-uk Kim.
-  [DG] Silence gcc 4.0.1 compile warning concerning the difference in
-       "signedness" in pointer assignments. Changes to SCSI code
-       and os_linux.c .
-  [PW] Additions to knowndrives table: added missing drive from Quantum
-       Fireball Plus LM series, added QUANTUM BIGFOOT TS10.0A, added
-       ExcelStor J680 and J880, added Western Digital Caviar RE Serial ATA
-       series, added missing drives from Western Digital Caviar SE series,
-       added Seagate Momentus 4200.2 series, added missing drives from
-       Maxtor DiamondMax 10 series, added Fujitsu MHG and MHH series, and
-       added Hitachi Travelstar 5K100 series.
-  [PW] Additions to knowndrives table: added Fujitsu MHU2100AT, added
-       Fujitsu M1623TAU, added missing drives from Seagate Barracuda
-       7200.8 series, added Seagate Momentus 5400.2 series, and added
-       QUANTUM FIREBALL CR8.4A.
-  [PW] Additions to knowndrives table: added missing drive from Maxtor
-       MaxLine II series, added Maxtor DiamondMax 2880 Ultra ATA series,
-       added Maxtor DiamondMax 17 VL series, added Hitachi Deskstar 7K80
-       series, and added Hitachi Deskstar 7K400 series.
-  [CF] Windows: Fixed unsupported 'smartctl -X' on Win2000/XP by using
-       IOCTL_IDE_PASS_THROUGH instead.
-
-* Tue Apr 20 2005 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-  [CF] Cygwin & Windows smartd: Increased SCSI DEVICESCAN range
-       from ASPI adapter 0-3 to 0-9. Added diagnostic messages.
-  [CF] Windows smartd: Added ability to run .bat files via '-M exec'
-       directive.
-  [CF] Cygwin smartd: Added FreeConsole() after fork() to avoid hang
-       of terminated shell console window.
-  [DG] [SCSI] Add code so 'smartctl -A' outputs the number of elements
-       in the grown defect list. When this number is increasing a
-       disk has problems. N.B. Similar logic should be added to smartd.
-  [CF] Windows smartd: Fixed event handling to allow start of another
-       smartd process when service is already running. Useful for testing
-       service configuration changes in debug mode.
-  [PW] Added following drives to knowndrives table: Western Digital Raptor
-       family, Seagate Barracuda 7200.8 family, Maxtor DiamondMax 2160
-       Ultra ATA and DiamondMax 10 families, Hitachi Travelstar E7K60
-       family, Seagate Medalist 17240, 13030, 10231, 8420, and 4310,
-       TOSHIBA MK4018GAP and MK6022GAX, ExcelStor Technology J360, and
-       Western Digital Caviar AC14300.
-  [PW] Added missing Fujitsu MHTxxxxAT and Seagate Barracuda 7200.7 drives
-       to knowndrives table.
-  [PW] Added QUANTUM FIREBALLP LM10.2 to knowndrives table.  Thanks to
-       Mike Fleetwood for submitting the patch.
-  [KS] Solaris/SPARC: fixed not to disable automatic offline test and
-       automatic save attributes incorrectly.  Thanks to Roy Badami.
-  [BA] Linux: smartd init script now recognizes 'trustix' distro.
-  [DG] [SCSI] Medium and hardware errors were slipping through
-       unreported. Fix linux SCSI sense reporting via SG_IO ioctl.
-  [DG] [SCSI] Change lba of first failure in selftest output to
-       decimal (was hex) to conform with ATA output.
-  [GK] smartd: Detect most self-test failures even if the hour counter
-       has wrapped.
-  [BA] smartctl: list 'marvell' as option if user give invalid
-       -d argument
-  [CF] Windows: fixed SCSI timeout handling to allow long timeouts
-       for selftests.
-  [CF] Fixed buffer overflow issues in printone() and safe_vsnprintf()
-       which results in crash on -V option (at least on Windows).
-  [DG] [SCSI] Add explicit timeouts to INQUIRY and REQUEST SENSE (that
-       were missed in an earlier patch). Could have impacted freebsd.
-  [DG] When linux detects a sata_via_libata disk suggest that user try
-       '-d ata' (rather then '-d libata). Anticipate kernel change.
-  [YD] Added OS/2 and eComStation platform support.
-  [PW] Added Seagate U4 family, Fujitsu MHJ and MHK families, Seagate
-       Barracuda 5400.1, QUANTUM FIREBALLP KX27.3, QUANTUM FIREBALLP KA10.1,
-       and ExcelStor J340 to knowndrives table.
-  [DG] [SCSI] After report of Hitachi IC35L073UCDY10 disks locking up
-       on log page 0x7 (last n error events), check log page (and some
-       others) is supported (via log page 0x0) before probing.
-  [CF] Added safe_v?snprintf() for platforms using v?snprintf()
-       with non standard behaviour on overflow (Windows, old Linux)
-  [CF] smartd: Added message if check power mode spins up disk.
-  [CF] Windows: Added support for READ_LOG on WinNT4 using undocumented
-       pseudo SCSI command via IOCTL_SCSI_PASS_THROUGH.
-  [CF] smartd: Added ',q' option for '-n' directive to suppress 'skipping
-       checks' log message. This prevents a laptop disk from spinning up
-       due to this message. Thanks to Rob MacLachlan and Manfred Schwarb
-       for pointing out problem & solution.
-  [CF] Windows: Added function get_os_version_str() to show OS flavor in
-       copyright message.
-  [CF] Windows: Added function ata_identify_is_cached() to check for outdated
-       SMART enabled bit in identify data.
-  [CF] Windows: Added fix to prevent linkage of smartd specific win32 modules
-       to smartctl.
-  [PW] Added Fujitsu MPG3153AH, Hitachi Endurastar J4K20/N4K20 (formerly
-       DK23FA-20J), Seagate Momentus family, and Maxtor Fireball 3 family
-       to knowndrives table.
-  [PW] Added missing Maxtor DiamondMax 16, Seagate Barracuda ATA IV, and
-       Western Digital Caviar WDxxxAA/WDxxxBA drives to knowndrives table.
-  [CF] Windows: Added ATA check power mode for smartd -n directive.
-  [CF] Windows: Fixed use of new service status flag which causes hang
-       of smartd service on WinNT4.
-  [CF] Windows: Fixed error checking of IOCTL_IDE_PASS_THROUGH (used
-       for READ_LOG on 2000/XP). Added some diagnostic messages on
-       -r ataioctl[,2]. Thanks to Manfred Schwarb for bug report and testing.
-  [BA] Fixed code bug that made it impossible to enable SMART on
-       disks with failing health status.  This would happen if the
-       os_*.c author made STATUS and STATUS_CHECK work the same way.
-       I have corrected this at a higher level; we now handle the
-       case where STATUS and STATUS_CHECK are identical without
-       issues. 
-  [LW] Make os_linux.c/marvell_command_interface() always return 0 on STATUS.
-       Needed for a disk having bad SMART status.
-  [CF] smartctl: Added drive family printing.
-  [CF] autogen.sh: Allow automake 1.9, added message if automake
-       version is unknown.
-  [BA] smartctl: use locale-specific separators for printing disk
-       capacity.  Also use AC_CHECK_HEADERS not AC_CHECK_HEADER in
-       configure.in.
-  [BA] clean-up of #include structure so that -V options to smartd
-       and smartctl work correctly.  Please, don't #include header
-       files into other header files.
-
-* Fri Sep 10 2004 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-  [BA] smartctl: ATA disks, if SMART ATTRIBUTE THRESHOLDS page has ID
-       errors with some Attributes having NULL IDs, print Attribute
-       info anyway (but issuing a warning to the user).
-  [DG] [SCSI] Decode Last n error events log page; decode track following
-       and positioning errors [Hitachi]
-  [EM] FreeBSD: another tweak, __packed__ introduced in Version 5.0040
-  [EM] Cleaner tweak of fixes for FreeBSD 4.x.
-  [EM] Fix compilation errors under FreeBSD 4.x, as it is still using
-       and old GCC
-  [EM] Remove 3ware/FreeBSD specific files and just include pieces we need
-  [DG] Add logic in smartd to detect 3ware, Marvell controllers and SATA
-       disks behind an ATA-SCSI simulator (in Linux). If specific device
-       types are not given and they are picked in a general SCSI device
-       scan then warn and skip.
-  [GG] insert correct path to smartd into smartd's init script
-  [BA] Changed all default paths in documentation to reflect /usr/local as
-       default path prefix.  This affects on-line man pages, primarily.
-  [DS] Added support for OpenBSD.
-  [BA] Added another environment variable SMART_FULLMESSAGE set by
-       the smartd mailing feature, and modified examplescripts/Example1
-       to illustrate it.
-  [BA] Fixed potentially misleading messages of the form:
-       XXX failed: success
-  [DG] emit warning if SATA disk detected using libata in Linux; then exit
-  [PW] Added Seagate U10 family, Hitachi Travelstar 7K60, Fujitsu MHR2020AT,
-       and QUANTUM FIREBALLP AS20.5 to knowndrives table.
-  [DG] Detect 3ware and Marvell controllers from SCSI INQUIRY vendor string
-       and suggest usage of appropriate '-d' argument in smartctl.
-  [LW] Tested the RELEASE_5_33_WITH_MARVELL_SUPPORT branch on
-       actual Marvell 88SX5041 hardware, with success.
-       Merged into HEAD.
-  [BA] Fixed nasty DEVICESCAN bug
-  [BA] Checked in RELEASE_5_33_WITH_MARVELL_SUPPORT branch with
-       some Marvell support.
-  [BA] Additional modifications of Ed's controller scheme.  Fixed
-       broken 3ware support under linux, problems with scanning
-       devices in smartd, and other small problems.
-  [EM] Minor change to FreeBSD inclusion of 'twe' include files.  Add 
-       code to check if they exising in /usr/include/sys to use those
-       in preference to ones added here
-  [EM] Very preliminary support attempt for 3Ware controllers under 
-       FreeBSD. Also, switched 'escalade_type/escalade_port' to
-       'controler_type/controller_port' and moved away from 
-       'tryata/tryscsi' to using new 'controller*' variables to 
-       determine which controller type (ATA/SCSI/3Ware) to use.
-  [GK] Added initscript support for Darwin.
-  [CF] Windows smartd: Added ability to run smartd as a windows service,
-       including new commands "smartd install ..." and "smartd remove"
-       to install and remove the service registry entry.
-  [BA] smartd: warn user if -s regexp regular expression contains
-       characters other than 0123456789.*()|+?[-]{}:=SLCO since such
-       characters are 'suspicous' and may indicate a poorly formed
-       regexp.  Extended regular expression gurus: can this list be
-       reduced somewhat?
-  [CF] Fixed bug in Windows smartd: Missing close of config file when
-       configuration is reloaded by smartd daemon.
-  [CF] Windows smartd: Added mail warning feature using the "Blat"
-       (http://blat.sourceforge.net/) mailer as a default.
-  [PW] Added Maxtor DiamondMax Plus 5120 Ultra ATA 33 series and TOSHIBA
-       MK3017GAP to knowndrives table.
-  [CF] Added fixes to build smartmontools on old Linux systems
-       (libc < 6, Kernel 2.0.x).
-  [BA] Added ATA minor version identity strings for latest ATA specification
-       updates: ATA/ATAPI-7 T13 1532D revision 4a and ATA/ATAPI-6 published,
-       ANSI INCITS 361-2002
-  [PW] Added Hitachi Travelstar 5K80 family and Fujitsu MHTxxxxAH family to
-       knowndrives table.
-  [EM] Fix up compilation under FreeBSD < 5.x
-  [PW] Added QUANTUM FIREBALL EX3.2A and missing Western Digital Caviar SE
-       drives to knowndrives table.
-  [BA] Modified Hitachi Travelstar 80GN family regexp in drive database.
-       Thanks to [GK/CF] for problem & solution.
-  [GK] Added os_darwin.[ch]
-  [PW] Added the following drives to the knowndrives table: IBM Travelstar
-       48GH, 30GN, and 15GN family; IBM Deskstar 37GP and 34GXP family;
-       Western Digital WDC WD272AA; Maxtor DiamondMax D540X-4D family;
-       TOSHIBA MK2016GAP, MK2018GAP, MK2018GAS, MK2023GAS; and
-       QUANTUM FIREBALL ST3.2A
-  [BA] smartd/smarctl now print build HOST/OS information as part
-       of startup slogan.  This should make it slightly easier to
-       read bug reports from users.
-  [RZ] Fixed the DEVICESCAN to do what it was supposed to do - give
-       error message unless scanning is in progress.  
-  [BA] Update documentation to describe 3ware character devices. Better
-       error detection for missing/malfunctioning devices behind 3ware
-       controllers. Now pack 3ware ioctl structures explicitly.
-  [BA] For ATA devices that support LBA mode, print capacity as part
-       of smartctl --info
-  [RZ] Made DEVICESCAN quiet about non-existing devices unless debug
-       is on.
-  [DG] treat "unit attention" SCSI warning as try again in some contexts
-       (test unit ready and mode sense)
-  [BA] on drives that store max/min rather than min/max, get order
-       correct in printing temp.
-  [BA] fixed typo in 'smartctl -h' output.  Thanks to Gabor Z. Papp.
-  [BA] linux: clean-up to 3ware/AMCC support; dynamically create
-       or fix /dev/tw[ae][0-15] device node entries if they don't
-       exist or are incorrect. One can now use the character devices
-       /dev/twe[0-15] OR /dev/sd? for 3ware 6000/7000/8000 series
-       cards.  One must use /dev/twa[0-15] for 3ware 9000 series cards.
-       Note that selective self-tests now work via /dev/tw[ae] devices.
-       Next step: documentation.
-  [BA] linux: experimental "support" for 3ware/AMCC 9000 series
-       controllers that use the 3w-9xxx driver.  This will be in a
-       state of flux for a few days.  Note that this requires the
-       character interface /dev/twa[0-15].
-  [DG] linux: extend general SCSI OS interface to use the SG_IO ioctl. If
-       not available, use the older SCSI_IOCTL_SEND_COMMAND ioctl.
-  [KS] Solaris/x86: fixed system identification problem in configure
-       script.  Thanks to Stuart Swales.
-
-* Mon Jul 5 2004 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-  [BA] Update link to revised/updated IBM Deskstar Firmware
-  [CF] Cygwin & Windows: Added missing ASPI manager initialization
-       with GetASPI32SupportInfo(). Thanks to Nikolai SAOUKH for pointing
-       this out and providing a patch.
-  [BA] modified smartd init script to work on whitebox (thanks to
-       Michael Falzon)
-  [BA] removed (reverted) additional Attribute definitions from
-       http://smart.friko.pl/attributes.php.  All (or most?) of these
-       appear to be return code values for the WD Digital Life Guard Utility.
-  [PW] Added Seagate Medalist 17242, 13032, 10232, 8422, and 4312 to
-       knowndrives table.  Added missing Seagate U Series 5 drives.
-  [PW] Added the following QUANTUM models to knowndrives table:
-       FIREBALL EX6.4A, FIREBALLP AS10.2, FIREBALLP AS40.0, FIREBALL CR4.3A,
-       FIREBALLP LM15, FIREBALLP LM30, and FIREBALLlct20 30
-  [PW] Added missing Western Digital Protege drives to knowndrives table.
-  [PW] Added Maxtor DiamondMax 40 ATA 66 series and DiamondMax 40 VL Ultra
-       ATA 100 series to knowndrives table.
-  [PW] Added the following Hitachi/IBM drives to knowndrives table:
-       HITACHI_DK14FA-20B, Travelstar 40GNX series, Travelstar 4LP series,
-       and Travelstar DK23XXB series.  Added the missing Travelstar 80GN
-       drives.
-  [PW] Added Fujitsu MPB series and MPG series to knowndrives table.  Added
-       the missing Fujitsu MHSxxxxAT drives.
-  [KS] Solaris: added workaround for dynamic change of time-zone.
-  [KS] Solaris: fixed problem that autogen.sh cannot detect absence of
-       auto* tools.
-  [BA] smartd: added time-zone bug information to man page. 
-       Reverted CF code for _WIN32 case. 
-  [CF] Cygwin & Windows: Added better error messages on IDE/ATA device
-       open error.
-  [BA] added additional Attribute definitions from
-       http://smart.friko.pl/attributes.php
-  [BA] smartd: reworked TimeZone bug workaround so it is only invoked
-       for glibc.  Note: this might not be right -- a similar bug may
-       exist in other platform's libcs.
-  [DG] SCSI smartmontools documentation updated [2004/5/6]. See:
-       http://smartmontools.sourceforge.net/smartmontools_scsi.html
-  [CF] Windows: Fixed reset of TZ=GMT in glibc timezone bug workaround.
-
-* Tue May 4 2004 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-  [DG] move SCSI device temperature and start-stop log page output
-       (smartctl) into --attributes section (was in --info section).
-  [GG] change default installation location to /usr/local
-  [CF] Cygwin smartd: Fixed crash on access of SCSI devices after fork().
-  [PW] Added TOSHIBA MK4018GAS and the following Maxtor drive families
-       to knowndrives table: DiamondMax D540X-4G, Fireball 541DX,
-       DiamondMax 3400 Ultra ATA, DiamondMax Plus 6800 Ultra ATA 66.
-  [PW] Added missing Maxtor DiamondMax 16, DiamondMax D540X-4K, and
-       DiamondMax Plus 45 Ulta ATA 100 drives to knowndrives table.
-  [PW] Added ExcelStor J240, Hitachi Travelstar 80GN family, Fujitsu
-       MHTxxxxAT family, and IBM Deskstar 25GP and 22GXP families to
-       knowndrives table.
-  [CF] Cygwin smartd: Added workaround for missing SIGQUIT via keyboard:
-       To exit smartd in debug mode, type CONTROL-C twice.
-  [BA] smartctl: printing of the selective self-test log is now
-       controlled by a new option: -l selective
-  [BA] Added entries for Samsung firmware versions -25 to -39 based
-       on latest info about firmware bug fixes.
-  [PW] Added Seagate U Series X family, Seagate U8 family, and Seagate
-       Medalist 8641 family to knowndrives table.
-  [CF] smartd: Added exit values 5/6 for missing/unreadable config file.
-  [BA] smartd: now monitor the Current Pending Sector count (Attribute 197)
-       and the Offline Pending Sector Count (Attribute 198).  Log a
-       warning (and send an email, if so configured) if the raw count
-       is nonzero.  These are controlled by new Directives: -C and -U.
-       Currently they are enabled by default.
-  [CF] Added option -c FILE, --configfile=FILE to smartd to specify
-       an alternate configuration FILE or '-' for standard input.
-  [KS] configure.in now searches for -lnsl and -lsocket for Solaris.
-  [CF] Win32/native smartd: Added thread to combine several syslog output
-       lines into one single event log entry.
-  [CF] Win32 smartd: Added DEVICESCAN for SCSI/ASPI devices.
-  [GG] Use gethostbyname() the get the DNS domain since getdomainname() 
-       returns the NIS domain when sending mails from smartd.
-  [GG] smartd.init.in: pass smartd_opts to smartd on startup, read distribution
-       specific configuration files if found
-  [SS] smartctl: added NetBSD support for Selective Self-tests.
-  [BA] smartd.conf example configuration file now has all examples
-       commented out except for 'DEVICESCAN'.
-  [CF] Win32/native smartd: Added ability to display warning "emails"
-       as message box by "-m msgbox" directive. With "-m sysmsgbox",
-       a system modal (always on top) message box is shown.
-  [BA] smartctl: printing of self-test log for disks that support
-       Selective self-testing now shows the status of the (optional)
-       read-scan after the selective self test.  Also, changed format
-       in printing self-test log to print failing LBA in base 10 not
-       base 16 (more compatible with kernel error messages).  Also,
-       in printing SMART error log, print timestamps in format
-       days+hours+minutes+seconds.
-  [CF] Win32 smartd: Added ability to log to stdout/stderr
-       (-l local1/2). Toggling debug console still works
-       if stdout is redirected.
-  [BA] smartctl: selective self-test log, print current status
-       in a  more detailed way.  Allow writing of selective self-test
-       log provided that no other self-test is underway.
-  [BA] Linux: eliminated dependency on kernel tree hdreg.h.
-  [BA] smartctl: -l selftest option now prints Selective self-test
-       log in addition to the normal self-test log.
-       Added additional options (-t pending, -t afterselect) to
-       control remaining Selective Self-test capabilities.  Tested
-       with several Maxtor disks. Modified error message printing
-       so that munged option messages print at the end not the
-       start of output.
-  [CF] Added daemon support to Win32 native version of smartd.
-       The daemon can be controlled by commands similar to initd
-       scripts: "smartd status|stop|reload|restart|sigusr1|sigusr2".
-  [CF] Added minor support for option "-l local[0-7]" to Win32 native
-       (not Cygwin) version of smartd. If specified, the log output
-       is written to file "./smartd[1-7]?.log" instead of event log.
-  [BA] Added Selective Self-test to smartctl (-t selective,M-N).
-       Currently only supported under Linux; Solaris, NetBSD, FreeBSD
-       and Windows developers must add WRITE LOG functionality to
-       os_*.c
-  [BA] Added workaround for an annoying glibc bug: if you change
-       timezones, (eg, flying with a laptop from USA to Europe)
-       localtime() does not notice this in a running
-       executable, so time that appears in the system log (syslog!)
-       will be incorrect.  See
-       http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=48184
-       for additional examples of this bug.
-  [DG] Set explicit timeouts for SCSI commands (most default to 6 seconds).
-       Previously a 0 second timeout was meant to be interpreted as a 
-       default timeout but the FreeBSD port had a problem in this area.
-  [CF] Fixed un-thread-safe exit signal handler for Win32
-  [BA] Fixed un-thread-safe exit signal handler pointed out
-       by CF.
-  [BA] Changed configure script to eliminate warnings under
-       Solaris from sys/int_type.h conflicts with int64.h
-       Added header files for umask to smartd.c.
-  [BA] Man page format change from Werner LEMBERG.  " " changed to \&
-  [CF] Added os_win32/syslogevt.* event message file tool for Win32
-       smartd (native+cygwin). May also be useful for other cygwin
-       programs writing to syslog().
-  [CF] Added Win32 version of smartd
-  [CF] Merged RELEASE_5_26_WIN32_BRANCH
-  [BA] Made some changes to man page markup suggested by
-       Richard Verhoeven to work around bugs in man2html.
-       Tested not to break anything under Linux and Solaris.
-  [CF] Moved PrintOut() from utility.c to smart{ctl,d}.c to avoid
-       syslog() output of smartctl.
-  [BA] Grew worried that some time-zone names could be very long (eg,
-       Mitteleuropaische Zeit) and put date string lengths into a
-       single macro in utility.c
-  [EM] Updated os_freebsd.c to handle older versions of FreeBSD in a 
-       more appropriate/obvious fashion.
-  [EM] Modified autogen.sh as FreeBSD installs automake 1.7 as 
-       'automake17' and NOT 'automake-1.7'
-
-* Sat Mar 6 2004 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-  [PW] Added QUANTUM FIREBALLlct15 30, QUANTUM FIREBALLlct20 40, and
-       Maxtor 6Y060P0 (DiamondMax Plus 9 60GB) to knowndrives table.
-  [PW] Added Maxtor MaXLine II family to knowndrives table (thanks to
-       Brett Russ for submitting the patch).
-  [BA] Added remaining read/write commands to detailed list of
-       error log commands that have text descriptions of problem
-       printed.  For commands that support it, print number of failed
-       sectors at problem LBA.
-  [BA] Made SuSE section of smartd init script more SuSE 9 compatible.
-       Thanks to Hans-Peter Jansen.
-  [CF] Windows smartd: Added IDE/ATA device scan
-       Added windows device names to smartctl.8.in, smartd.8.in
-  [BA] smartctl/smartd: user-provided '-F samsung' and '-F samsung2'
-       command line options/Directives did NOT over-ride preset values
-       unless user specified '-P ignore'.  Now they will always over-ride
-       preset values from the database.
-  [BA] Added error decoding for a few more READ and WRITE commands.
-  [PW] Added Maxtor MaXLine Plus II, Western Digital Caviar SE (Serial ATA)
-       series, Hitachi Deskstar 7K250 series, and Ultra ATA 66 models of
-       the Maxtor DiamondMax Plus 40 series to knowndrives table.
-  [BA] Added Maxtor Diamondmax 250 GB drives to database.  Note that
-       these model numbers are not listed in Maxtor documentation, but
-       they exist.
-  [BA] Removed the 'contact developers' phrase from the Samsung disk
-       warning messages.
-  [PW] Added TOSHIBA MK2017GAP, IBM Deskstar 14GXP and 16GP series,
-       Fujitsu MPC series, Seagate Barracuda ATA III family, and missing
-       Seagate Barracuda U Series drives to knowndrives table
-  [BA] smartd: wrong loglevel for message: Configuration file
-       /etc/smartd.conf parsed.  Changed to LOG_INFO from LOG_CRIT.
-       Thanks to  Emmanuel CHANTREAU for the report.
-  [CF] Checked in development version of windows code base.
-
-* Tue Feb 24 2004 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-  [BA] smartd: configure script did not set correct directory to search for
-       smartd.conf based on --prefix argument to ./configure.  Thanks to
-       GG for identifying the problem and fix.
-  [BA] make clean now removes man pages (generated from *.in) files as well
-       as object files.
-  [EM] Correct copying of sense data in FreeBSD SCSI implementation. Thanks
-       to Sergey Svishchev for noticing the bug.
-  [BA] On solaris, wrong warning message if no ATA support.  Warning message
-       concerns 3ware controller, not ATA.
-  [SS] Added SCSI support for NetBSD.
-  [BA] on big-endian linux machines, fixed interpretation of HDIO_GET_IDENTITY
-       to correctly identify ATAPI bit (was byte swapped).  This should
-       eliminate some SYSLOG noise if user queries a packet device (eg, CD
-       ROM or DVD reader).
-  [PW] Removed warning for IBM Deskstar 40GV & 75GXP series drives with
-       A5AA/A6AA firmware.  Thanks to Gerald Schnabel.
-  [PW] Added Toshiba TOS MK3019GAXB SUN30G to knowndrives table
-  [PW] Added Western Digital Caviar AC12500, AC24300, AC25100, AC36400,
-       and AC38400 to knowndrives table
-  [BA] When printing ATA error log, print the LBA at which READ
-       or WRITE commands failed.
-  [BA] Changed syntax of error message in smartctl
-  [BA] Added versioning info (-V options to smartd/smartctl) for
-       Solaris ATA module.
-
-* Thu Feb 12 2004 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-  [KS] Added ATA/IDE support for Solaris/SPARC (ATA/IDE not yet for
-       Solaris/x86).
-  [BA] 3ware controllers: documented that one can monitor any of the
-       physical disks from any of the 3ware /dev/sd? logical devices.
-       Better warnings if querying a disk that does not exist.
-  [PW] Added Hitachi Travelstar DK23DA series, Maxtor DiamondMax Plus 40
-       series, Western Digital Caviar WDxxxAA, WDxxxBA, and WDxxxAB series
-       to knowndrives table
-  [BA] missing 'pragma pack' on ATA IDENIFY DEVICE structure may have
-       caused odd or incorrect results on 64-bit machines.
-  [BA] smartctl/smartd allow inspection of self-test and error logs even
-       if disk firmware claims that these don't exist.  This is needed
-       for some Maxtor disks whose firmware does not indicate log support
-       even though the disk DOES support it.
-  [BA] Improved porting instructions and documentation in os_generic.c
-  [PW] Add Western Digital Caviar WD136AA and SAMSUNG SP40A2H (RR100-07
-       firmware) to knowndrives table.
-  [EM] FreeBSD:        remove extra definition of FreeNonZero
-  [BA] smartctl: the -q silent option was printing output for some
-       error conditions.  Fixed.  Will rename relevant variables to help
-       avoid these errors in the future.
-  [SS] NetBSD port added.
-  [BA] more sensible error messages for devfs and devfs-like systems.
-       Instead of saying that the DIRECTORY does not exist, say that
-       the DEVICE does not exist.
-  [BA] smartd: added -n Directive, to prevent disk spin-up depending
-       upon the power mode (SLEEP, STANDBY, or IDLE).
-  [PW] Added Maxtor DiamondMax 20 VL series, Fujitsu MPF series,
-       Maxtor DiamondMax 36 series, Maxtor DiamondMax 4320 series, and
-       Maxtor DiamondMax 536DX series to knowndrives table.
-  [BA] many warning messages now give the file name AND VERSION
-  [BA] smartd: when the user provides multiple address recipients
-       to the '-m' Directive in a comma-delineated list, the commas
-       are stripped out before passing the list of addresses to the
-       mailer program. (Thanks to Calin A. Culianu for pointing this out
-       and providing a patch.)
-  [BA] smartd: when the '-M exec path' Directive is used, any stdout OR
-       stderr output from the executable "path" is assumed to indicate a
-       problem, and is echoed to SYSLOG.
-  [BA] Added all missing IBM/Hitachi Deskstar 180GXP models to knowndrives
-       table.
-  [PW] Added some missing IBM/Hitachi Deskstar 120GXP models to knowndrives
-       table.
-  [PW] Added IBM Travelstar 14GS to knowndrives table.
-  [PW] Modified knowndrives table to match entire Hitachi Travelstar
-       DK23BA and DK23EA series of drives (thanks to Norikatsu Shigemura
-       for submitting the patch).
-  [PW] Added some missing Fujitsu MPE series drives to knowndrives table.
-  [PW] Added TOSHIBA MK4019GAX, TOSHIBA MK6409MAV, and QUANTUM
-       FIREBALLlct15 20 to knowndrives table.
-  [EM] Fixup example command output for FreeBSD
-  [PW] Added Maxtor DiamondMax 80 family to knowndrives table.
-  [EM] Catch up FreeBSD code to switch PROJECTHOME to PACKAGE_HOMEPAGE
-       macros.
-  [BA] smartd: now watches stdout/stderr when trying to run mail, mailx
-       or mail warning script, and reports any output to SYSLOG.  This
-       gives a clearer error message if something is wrong.
-  [BA] smartd: Solaris init script modified to accomodate grep that
-       lacks '-q' quiet option.  Also check for running process to kill
-       on stop.
-  [PW] Added some missing Seagate Barracuda 7200.7 and 7200.7 Plus drives
-       to knowndrives table.
-  [PW] Added Maxtor DiamondMax Plus 60 family and Seagate U Series 5 20413
-       to knowndrives table.
-  [BA] smartd: under Solaris, made default mailer be 'mailx' not
-       'mail', since Solaris 'mail' does not accept a '-s' argument.
-       A workaround for Solaris users of earlier versions is to
-       have '-M exec /bin/mailx' in their smartd.conf config file.
-  [DG] some SCSI controllers don't like odd length transfers so make
-       sure LOG SENSE transfers are rounded up to an even number when
-       and odd length is reported (i.e. there is a double fetch, the
-       first to find the length, the second gets the data)
-  [BA] smartd man pages: under Solaris, correct section numbers in the
-       'See also' section.
-  [KS/BA] smartd man page: describe how to set Solaris syslog.conf
-       file to catch all messages.  Give correct Solaris SYSLOG default
-       path /var/adm/messages in man pages.
-  [BA] smartd: incorporated Debian startup script submitted by user.
-  [BA] smartctl: modified printing of self-test log entry number.  Seagate
-       firmware can leave 'holes' in the self-test log while a test is
-       actually running.  We now print entry numbers consistently in this
-       case, not assuming that entries are contiguous.
-  [PW] Added QUANTUM FIREBALL CX10.2A and Western Digital Caviar AC23200L
-       to knowndrives table.
-  [PW] Added QUANTUM FIREBALLlct20 20 to knowndrives table.
-  [PW] Added Maxtor DiamondMax Plus D740X family to knowndrives table.
-  [PW] Added IBM Travelstar 32GH, 30GT, and 20GN family to knowndrives
-       table.
-  [BA] Slackware init script modified to search for /etc/slackware-version
-       rather than /etc/slackware-release.
-  [PW] Added Seagate Barracuda ATA II family and TOSHIBA MK4019GAXB to
-       knowndrives table.
-  [GG] explain howto use autoreconf in autogen.sh
-  [KS] Makefile.am/configure.in: changed manual page sections for
-       Solaris.
-  [BA] smartd: reduced number of scheduled self-test messages if
-       test already run in current hour.
-  [PW] Added Maxtor DiamondMax Plus 8 family to knowndrives table.
-  [BA] linux: check for linux/hdreg.h.  If it's there, use it. If
-       not, provide the necessary definitions ourselves.
-  [PW] Removed warning for IBM Deskstar 40GV & 75GXP series drives
-       with TXAOA5AA firmware
-  [PW] Added IBM Travelstar 25GS, 18GT, and 12GN family to knowndrives
-       table.
-  [PW] Added IBM/Hitachi Travelstar 60GH & 40GN family to knowndrives
-       table.
-  [BA] smartd: made '-s' Directive more efficient.  Now store
-       compiled regex, and re-use.  If device lacks certain self-test
-       capabilities, track it and don't try again.
-  [BA] smartd: made memory allocation for device lists completely
-       dynamic (eliminating compile-time maximum length constants).
-  [PW] Removed warning for SAMSUNG SP0802N with TK100-23 firmware
-  [PW] Added Seagate Barracuda ATA IV family to knowndrives table.
-  [BA] smartd: reduce per-device memory footprint by making
-       mail-warning info dynamically allocated.  Also remove
-       potential memory leak if use has -m Directive twice and
-       keeps reloading the config file (highly unlikely this would
-       ever be noticed!)  
-  [DG] smartd: added SCSI scheduled self-tests (Background
-       short or extended).
-  [BA] smartd: can now run scheduled offline immediate and
-       self-tests.  See man page and -s Directive for details.
-  [GG] don't include manpages in make-dist-tarball.
-  [BA] smartctl: on-line examples given with -h are now correct
-       for solaris and linux, but wrong for freebsd.  Ed?
-  [BA] smartd: man page now explains device scanning for solaris as
-       well as linux and freebsd.
-  [BA] smartd/smartctl: man pages now report correct CVS tag release
-       date, and executables '-V' options reports more build info.
-
-* Sat Nov 29 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-  [BA] Improved user messages that appear from 'make install'
-  [PW] Removed warning for SAMSUNG SP1213N with firmware TL100-23
-  [BA] incorporated SuSE init script from user.
-  [DG] if SCSI device is read only, then open it read only.
-  [BA] when compiled on non-supported system (NOT linux, freebsd or solaris) then
-       the run-time error messages now clearly say 'your system is not supported'
-       and give clear directions.
-  [BA] ./configure script now works correctly on SuSE linux boxes
-  [BA] minor improvements to man pages
-  [BA] simplified detection of packet (ATAPI, CD) devices.
-  [BA] init script (redhat, mandrake, yellowdog) now uses correct
-       strings for translation and is slightly more standard.
-  [DG] smartctl: output scsi Seagate vendor pages for disks (not tapes)
-* Wed Nov 19 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-  [DG] smartd/smartctl: changed scsiClearControlGLTSD() to
-       scsiSetControlGLTSD() with an 'enabled' argument so '-S on'
-       and '-S off' work for SCSI devices (if changing GLTSD supported).
-  [BA] smartd/smartctl: wired in scsiClearControlGLTSD(). Could still
-       use a corresponding Set function.  Left stubs for this purpose.
-  [DG] scsicmds: added scsiClearControlGLTSD() [still to be wired in]
-  [BA] smartctl: make SCSI -T options behave the same way as the
-       ATA ones.
-  [DG] smartctl: output scsi transport protocol if available
-  [DG] scsi: stop device scan in smartd and smartctl if badly formed
-       mode response [heuristic to filter out USB devices before we
-       (potentially) lock them up].
-  [BA] smartd: deviceclose()->CloseDevice(). Got rid of SCSIDEVELOPMENT
-       macro-enabled code.  Added -W to list of gcc specific options to
-       always enable. Made code clean for -W warnings.
-  [PW] Added Maxtor DiamondMax VL 30 family to knowndrives table.
-  [DG] scsi: add warning (when '-l error' active) if Control mode page
-       GLTSD bit is set (global disable of saving log counters)
-  [DG] scsi: remember mode sense cmd length. Output trip temperature
-       from IE lpage (IBM extension) when unavailable from temp lpage.
-  [BA] smartd: for both SCSI and ATA now warns user if either
-       the number of self-test errors OR timestamp of most
-       recent self-test error have increased.
-  [DG] smartctl: output Seagate scsi Cache and Factory log pages (if
-       available) when vendor attributes chosen
-  [DG] smartd: add scsiCountFailedSelfTests() function.
-  [DG] Do more sanity checking of scsi log page responses.
-  [BA] smartd: now warns user if number of self-test errors has
-       increased for SCSI devices.
-  [BA] smartd: warn user if number of ATA self-test errors increases
-       (as before) OR if hour time stamp of most recent self-test
-       error changes.
-  [DG] More checks for well formed mode page responses. This has the side
-       effect of stopping scans on bad SCSI implementations (e.g. some
-       USB disks) prior to sending commands (typically log sense) that
-       locks them up.
-  [PW] Added Western Digital Caviar family and Caviar SE family to
-       knowndrives table.
-  [BA] smartd: added -l daemon (which is the default value if -l
-       is not used).
-  [PW] Added Seagate Barracuda ATA V family to knowndrives table.
-  [BA] smartd: added additional command line argument -l FACILITY
-       or --logfacility FACILITY.  This can be used to redirect
-       messages from smartd to a different file than the one used
-       by other system daemons.
-  [PW] Added Seagate Barracuda 7200.7, Western Digital Protege WD400EB,
-       and Western Digital Caviar AC38400 to knowndrives table.
-  [BA] smartd: scanning should now also work correctly for
-       devfs WITHOUT traditional links /dev/hd[a-t] or /dev/sd[a-z].
-  [PW] Added Maxtor 4W040H3, Seagate Barracuda 7200.7 Plus,
-       IBM Deskstar 120GXP (40GB), Seagate U Series 20410,
-       Fujitsu MHM2100AT, MHL2300AT, MHM2150AT, and IBM-DARA-212000
-       to knowndrives table.
-  [PW] Added remaining Maxtor DiamondMax Plus 9 models to knowndrives
-       table.
-  [EM] smartd: If no matches found, then return 0, rather than an error
-       indication, as it just means no devices of the given type exist.
-       Adjust FreeBSD scan code to mirror Linux version.
-  [BA] smartd: made device scan code simpler and more robust. If
-       too many devices detected, warn user but scan as many
-       as possible.  If error in scanning, warn user but don't
-       die right away.
-  [EM] smartd: To keep as consistent as possible, migrate FreeBSD
-       devicescan code to also use glob(3). Also verified clean 
-       compile on a 4.7 FreeBSD system.
-  [BA] smartd: Modified device scan code to use glob(3). Previously
-       it appeared to have trouble when scanning devices on an XFS
-       file system, and used non-public interface to directory
-       entries. Problems were also reported when /dev/ was on an
-       ext2/3 file system, but there was a JFS partition on the same
-       disk.
-  [BA] Clearer error messages when device scanning finds no suitable
-       devices.
-  [EM] FreeBSD:        Fixup code to allow for proper compilation under 
-       -STABLE branch.
-
-* Fri Oct 31 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] smartd: didn't close file descriptors of ATA packet devices
-       that are scanned. Fixed.
-- [BA] Added reload/report targets to the smartmontools init script.
-       reload: reloads config file
-       report: send SIGUSR1 to check devices now
-
-* Mon Oct 27 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [EM] Fix compile issues for FreeBSD < 5-CURRENT.
-- [PW] Added Fujitsu MHM2200AT to knowndrives table.
-- [BA] To help catch bugs, clear ATA error structures before all
-       ioctl calls.  Disable code that attempted to time-out on SCSI
-       devices when they hung (doesn't work).
-- [BA] Documented STATUS/ERROR flags added by [PW] below.
-- [BA] Improved algorithm to recognize ATA packet devices. Should
-       no longer generate SYSLOG kernel noise when user tries either
-       smartd or smartctl on packet device (CD-ROM or DVD).  Clearer
-       warning messages from smartd when scanning ATA packet device.
-- [PW] Added TOSHIBA MK4025GAS to knowndrives table.
-- [PW] Added a textual interpretation of the status and error registers
-       in the SMART error log (ATA).  The interpretation is
-       command-dependent and currently only eight commands are supported
-       (those which produced errors in the error logs that I happen to
-       have seen).
-- [BA] added memory allocation tracking to solaris code.
-       Fixed solaris signal handling (reset handler to default
-       after first call to handler) by using sigset. Added
-       HAVE_SIGSET to configure.in
-- [CD] solaris port: added SCSI functionality to solaris
-       stubs.
-- [BA] smartd: attempt to address bug report about smartd
-       hanging on USB devices when scanning:
-       https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=107615
-       Set a timeout of SCSITIMEOUT (nominally 7 seconds) before
-       giving up.
-- [EM] smartd: DEVICESCAN will follow links in a devfs filesystem and
-       make sure the end point is a disc.  Update documentation, added
-       note about FreeBSD scanning
-- [BA] smartd: DEVICESCAN also looks for block devices in
-       /dev.  Updated documentation.  Now scans for up to
-       20 ATA devices /dev/hda-t rather than previous 12
-       /dev/hda-l.
-- [EM] smartd: mirror the FreeBSD DEVICESCAN logic for Linux,
-       so that smartd now scans only devices found in /dev/. Also,
-       make utility memory functions take a line number and file so
-       that we report errors with the correct location.
-- [GG] add a note about Debian bug #208964 to WARNINGS.
-- [BA] smartctl: -T verypermissive option broken.  Use
-       -T verpermissive until the next release, please.
-- [BA] Syntax mods so that code also compiles on Solaris using
-       Sun Workshop compiler.  Need -xmemalign 1i -xCC flags
-       for cc.
-
-* Wed Oct 15 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-  [DK] Changed configure.in so -Wall is only included if gcc
-       is used (this is a gcc specific flag) and -fsignedchar
-       is not used at all (this is a gcc specific compiler 
-       flag).
-  [BA] Modifications so that code now compiles under solaris. Now
-       all that's needed (:-) is to fill in os_solaris.[hc].  Added
-       os_generic.[hc] as guide to future ports.  Fixed -D option
-       of smartd (no file name).  Modified -h opt of smartd/smartctl
-       to work properly with solaris getopt().
-  [EM] Update MAN pages with notes that 3ware drives are NOT supported
-       under FreeBSD. Cleanup FreeBSD warning message handling.
-  [EM] FreeBSD only: Fix first user found bug....I guess I was making
-       the wrong assumption on how to convert ATA devnames to
-       channel/unit numbers.
-  [EM] Allow for option --enable-sample to append '.sample' to installed
-       smartd.conf and rc script files. Also, let rc script shell setting
-       be determined by configure
-  [EM] Minor autoconf update to include -lcam for FreeBSD
-  [EM] Add conditional logic to allow FreeBSD to compile pre-ATAng.
-       -- note, not tested
-       Add some documentation to INSTALL for FreeBSD.
-  [EM] Implement SCSI CAM support for FreeBSD.  NOTE: I am not an expert
-       in the use of CAM.  It seems to work for me, but I may be doing
-       something horribly wrong, so please exercise caution.
-  [EM] Switch over to using 'atexit' rather than 'on_exit' routine. This also
-       meant we needed to save the exit status elsewhere so our 'Goodbye'
-       routine could examine it.
-  [EM] Move the DEVICESCAN code to os specific files. Also moved some of the
-       smartd Memory functions to utility.c to make available to smartctl.
-  [EM] Code janitor work on os_freebsd.c.
-  [EM] Added os_freebsd.[hc] code.  Additional code janitor
-       work.
-  [BA] Code janitor working, moving OS dependent code into
-       os_linux.[hc].
-  [GG] conditionally compile os_{freebsd,linux}.o depending on
-       host architecture
-  [PW] Print estimated completion time for tests
-  [BA] Added -F samsung2 flag to correct firmware byte swap.
-       All samsung drives with *-23 firmware revision string.
-
-* Sun Oct 05 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [GG] Fixed broken Makefile.am (zero length smartd.conf.5
-       was being created)
-- [FM] Improved Slackware init script added to /etc/smartd.initd
-
-* Fri Oct 03 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] smartctl: added '-T verypermissive' option which is
-       equivalent to giving '-T permissive' many times.
-- [BA] Try harder to identify from IDENTIFY DEVICE structure
-       if SMART supported/enabled.  smartd now does a more
-       thorough job of trying to assess this before sending
-       a SMART status command to find out for sure.
-- [BA] smartctl: it's now possible to override the program's
-       guess of the device type (ATA or SCSI) with -d option.
-- [BA] try hard to avoid sending IDENTIFY DEVICE to packet
-       devices (CDROMS).  They can't do SMART, and this generates
-       annoying syslog messages. At the same time, identify type
-       of Packet device.
-- [BA] smartctl: Can now use permissive option more
-       than once, to control how far to go before giving up.
-- [BA] smartd: if user asked to monitor either error or self-test
-       logs (-l error or -l selftest) WITHOUT monitoring any of the
-       Attribute values, code will SEGV.  For 5.1-18 and earlier,
-       a good workaround is to enable Auto offline (-o on).
-- [BA] smartctl: If enable auto offline command given, update auto
-       offline status before printing capabilities.
-- [GG] Make autotools build the default, remove autotools.diff
-- [GG] Add auto{conf,make} support, not enabled by default. 
-- [BA] Eliminated #include <linux/hdreg.h> from code. This
-       should simplify porting to solaris, FreeBSD, etc. The
-       only linux-specific code is now isolated to three routines,
-       one for SCSI, one for Escalade, one for ATA.
-
-* Fri Aug 22 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] smartd: fixed serious bug - Attributes not monitored unless
-       user told smartd to ignore at least one of them!
-
-* Tue Aug 19 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] Default runlevels for smartd changed from 3 and 5 to
-       2, 3, 4, and 5.
-- [BA] Removed as much dynamic memory allocation as possible from
-       configuration file parsing. Reloading config file, even in
-       presence of syntax errors etc. should not cause memory leaks.
-- [PW] It is no longer permissible for the integer part (if any) of
-       arguments to --report and --device to be followed by non-digits.
-       For example, the "foo" in --report=ioctl,2foo was previously
-       ignored, but now causes an error.
-- [BA] smartd: added -q/--quit command line option to specify
-       under what circumstances smartd should exit.  The old
-       -c/--checkonce option is now obsoleted by this more
-       general-purpose option.
-- [BA] smartd now responds to a HUP signal by re-reading its
-       configuration file /etc/smartd.conf.  If there are
-       errors in this file, then the configuration file is
-       ignored and smartd continues to monitor the devices that
-       it was monitoring prior to receiving the HUP signal.
-- [BA] Now correctly get SMART status from disks behind 3ware
-       controllers, thanks to Adam Radford. Need 3w-xxxx driver
-       version 1.02.00.037 or later. Previously the smartmontools
-       SMART status always returned "OK" for 3ware controllers.
-- [BA] Additional work on dynamic memory allocation/deallocation.
-       This should have no effect on smartctl, but clears that way
-       for smartd to dynamically add and remove entries.  It should
-       also now be easier to modify smartd to re-read its config
-       file on HUP (which is easy) without leaking memory (which is
-       harder). The philosophy is that memory for data structures in
-       smartd is now allocated only on demand, the first time it
-       is needed.
-- [BA] smartd: finished cleanup.  Now use create/rm functions for
-       cfgentries and dynamic memory allocation almost everywhere.
-       Philosophy: aggresively try and provoke SEGV to help find
-       bad code.
-- [BA] Added SAMSUNG SV0412H to knowndrives table.
-- [BA] smartd: if DEVICESCAN used then knowndrives table might not set
-       the -v attributes correctly -- may have been the same for all
-       the drives.  Cleaned up some data structures and memory
-       allocation to try and ensure segvs if such problems are
-       introduced again.
-- [BA] Now allow -S on and -o on for the 3ware device type.  For these
-       commands to be passed through, the stock 3ware 3w-xxxx driver
-       must be patched (8 lines).  I'll post a patch on the smartmontools
-       home page after it's been tested by a few other people and 3ware
-       have had a chance to look it over.
-
-* Wed Aug 06 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] smartd - can now monitor ATA drives behind 3ware controllers.
-- [BA] smartd - changed some FATAL out of memory error messages from
-       syslog level LOG_INFO to LOG_CRIT.
-- [BA] smartctl - added code to look at ATA drives behind 3ware RAID
-       controllers using the 3w-xxxx driver.  Note that for technical
-       reasons related to the 3w-xxxx driver, the "Enable Autosave",
-       "Enable Automatic Offline" commands are not implemented.
-       I will add this to smartd shortly.
-- [BA] smartd - modified sleep loop, so that smartd no longer comes
-       on the run queue every second.  Instead, unless interrupted,
-       it sleeps until the next polling time, when it wakes up. Now
-       smartd also tries to wake up at exactly the right
-       intervals (nominally 30 min) even if the user has been sending
-       signals to it.
-- [GG] add Fujitsu MHN2300AT to vendoropts_9_seconds.
-- [EB] Fujitsu change in knowndrives ... match the whole MPD and
-       MPE series for vendoropts_9_seconds.
-- [BA] smartd bug, might cause segv if a device can not be opened. Was
-       due to missing comma in char* list.  Consequence is that email
-       failure messages might have had the wrong Subject: heading for
-       errorcount, FAILEDhealthcheck, FAILEDreadsmartdata, FAILEDreadsmarterrorlog,
-       FAILEDreadsmartsefltestlog, FAILEDopendevice were all displaced by
-       one.  And FAILEDopendevice might have caused a segv if -m was being
-       used as a smartd Directive.
-
-* Wed Jul 23 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] Cleaned up smartmontools.spec so that upgrading, removing
-       and other such operations correctly preserve running behavior
-       and booting behavior of smartd.
-- [BA] Improved formatting of ATA Error Log printout, and added
-       listing of names of commands that caused the error. Added
-       obsolete ATA-4 SMART feature commands to table, along with
-       obsolete SFF-8035i SMART feature command.
-- [PW] Added atacmdnames.[hc], which turn command register &
-       feature register pairs into ATA command names.
-- [BA] Added conveyance self-test.  Some code added for selective
-       self-tests, but #ifdefed out.
-- [BA] Modified smartd exit status and log levels.  If smartd is
-       "cleanly" terminated, for example with SIGTERM, then its
-       exit messages are now logged at LOG_INFO not LOG_CRIT
-- [BA] Added Attribute IDs  (Fujitsu) 0xCA - 0xCE.  This is decimal
-       202-206. Added -v switches for interpretation of Attributes
-       192, 198 and 201. 
-- [BA] Made smartmontools work with any endian order machine for:
-       - SMART selftest log
-       - SMART ATA error log
-       - SMART Attributes values
-       - SMART Attributes thesholds
-       - IDENTIFY DEVICE information
-       - LOG DIRECTORY
-       Smartmontools is now free of endian bias and works correctly
-       on both little- and big-endian hardware.  This has been tested by
-       three independent PPC users on a variety of ATA and SCSI hardware.
-- [DG] Check that certain SCSI command responses are well formed. If
-       IEC mode page response is not well formed exit smartctl. This
-       is to protect aacraid. smartd should ignore a aacraid device.
-
-* Mon Jun 16 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] smartctl: added column to -A output to show if Attributes are
-       updated only during off-line testing or also during normal
-       operation.
-
-* Thu Jun 10 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] smartd: attempt to enable/disable automatic offline testing even
-       if the disk appears not to support it.  Now the same logic
-       as smartctl.
-- [BA] Added definition of Attribute 201, soft read error rate.
-- [BA] Added IBM/Hitachi IC35L120AVV207-1 (GXP-180) and corresponding
-       8MB Cache GXP-120 to drive database.
-- [BA] smartd: if DEVICESCAN Directive used in smartd.conf, and
-       -I, -R or -r Directives used in conjunction with this, got
-       segv errors.  Fixed by correcting memory allocation calls.
-- [BA] smartd: enable automatic offline testing was broken due
-       to cut-and-paste error that disabled it instead of
-       enabling it.  Thanks to Maciej W. Rozycki for pointing
-       out the problem and solution.
-- [BA] Fixed "spelling" of some Attribute names to replace spaces
-       in names by underscores. (Fixed field width easier for awk
-       style parsing.)
-- [BA] Added mods submitted by Guilhem Frezou to support Attribute 193
-       being load/unload cycles. Add -v 193,loadunload option, useful
-       for Hitachi drive DK23EA-30, and add this drive to knowndrive.c
-       Add meaning of attribute 250 : Read error retry rate
-- [BA] Added another entry for Samsung drives to knowndrive table.
-- [DG] Refine SCSI log sense command to do a double fetch in most cases
-       (but not for the TapeAlert log page). Fix TapeAlert and Self Test
-       log pgae response truncation.
-- [PW] Added 'removable' argument to -d Directive for smartd.  This indicates
-       that smartd should continue (rather than exit) if the device does not 
-       appear to be present.
-- [BA] Modified smartmontools.spec [Man pages location] and
-       smartd.initd [Extra space kills chkconfig!] for Redhat 6.x
-       compatibility (thanks to Gerald Schnabel).
-
-* Wed May 7 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [EB] Add another Fujitsu disk to knowndrives.c
-- [GG] match for scsi/ and ide/ in case of devfs to exclude false postives
-- [BA] If SCSI device listed in /etc/smartd.conf fails to open or do
-       SMART stuff correctly, or not enough space
-       to list all SCSI devices, fail with error unless
-       -DSCSIDEVELOPMENT set during compile-time.
-- [BA] Added automatic recognition of /dev/i* (example: /dev/ide/...)
-       as an ATA device.
-- [DG] Add "Device type: [disk | tape | medium changer | ...]" line to
-       smartctl -i output for SCSI devices.
-- [PW] Fixed bug in smartd where test email would be sent regularly (for
-       example, daily if the user had specified -M daily) instead of just
-       once on startup.
-- [KM] More TapeAlert work. Added translations for media changer
-       alerts. TapeAlert support reported according to the log page
-       presence. ModeSense not attempted for non-ready tapes (all
-       drives do not support this after all). Get peripheral type from
-       Inquiry even if drive info is not printed. Add QUIETON()
-       QUIETOFF() to TapeAlert log check.
-- [BA] Stupid bug in atacmds.c minor_str[] affected ataVersionInfo().
-       Two missing commas meant that minor_str[] had two few elements,
-       leading to output like this:
-       Device Model:     Maxtor 6Y120L0
-       Serial Number:    Y40BF74E
-       Firmware Version: YAR41VW0
-       Device is:        Not in smartctl database [for details use: -P showall]
-       ATA Version is:   7
-       ATA Standard is:  9,minutes
-                         ^^^^^^^^^
-       Missing commas inserted.
-- [BA] Fixed smartd bug.  On device registration, if ATA device did
-       not support SMART error or self-test logs but user had asked to
-       monitor them, an attempt would be made to read them anyway,
-       possibly generating "Drive Seek" errors.  We now check that
-       the self-test and error logs are supported before trying to
-       access them the first time.
-- [GG/BA] Fixed bug where if SMART ATA error log not supported,
-       command was tried anyway. Changed some error printing to use
-       print handlers.
-- [GG] Makefile modifications to ease packaging
-- [DG] Did work for TapeAlerts (SCSI). Now can detect /dev/nst0 as a
-       SCSI device. Also open SCSI devices O_NONBLOCK so they don't
-       hang on open awaiting media. The ATA side should worry about
-       this also: during a DEVICESCAN a cd/dvd device without media
-       will hang. Added some TapeAlert code suggested by Kai Makisara.
-
-* Mon Apr 21 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [PW] Extended the -F option/Directive to potentially fix other firmware
-       bugs in addition to the Samsung byte-order bug.  Long option name is
-       now --firmwarebug and the option/Directive accepts an argument
-       indicating the type of firmware bug to fix.
-- [BA] Fixed a bug that prevented the enable automatic off-line
-       test feature from enabling.  It also prevented the enable Attribute
-       autosave from working.  See CVS entry for additional details.
-- [PW] Modified the -r/--report option (smartctl and smartd) to allow the
-       user to specify the debug level as a positive integer.
-- [BA] Added --log directory option to smartctl.  If the disk
-       supports the general-purpose logging feature set (ATA-6/7)
-       then this option enables the Log Directory to be printed.
-       This Log Directory shows which device logs are available, and
-       their lengths in sectors.
-- [PW] Added -P/--presets option to smartctl and -P Directive to smartd.
-- [GG] Introduce different exit codes indicating the type of problem
-       encountered for smartd.
-- [DG] Add non-medium error count to '-l error' and extended self test
-       duration to '-l selftest'. Get scsi IEs and temperature changes
-       working in smartd. Step over various scsi disk problems rather
-       than abort smartd startup.
-- [DG] Support -l error for SCSI disks (and tapes). Output error counter
-       log pages.
-- [BA] Added -F/--fixbyteorder option to smartctl.  This allows us to read
-       SMART data from some disks that have byte-reversed two- and four-
-       byte quantities in their SMART data structures.
-- [BA] Fixed serious bug: the -v options in smartd.conf were all put
-       together and used together, not drive-by-drive.
-- [PW] Added knowndrives.h and knowndrives.c.  The knowndrives array
-       supersedes the drivewarnings array.
-- [GG] add {-p,--pidfile} option to smartd to write a PID file on
-       startup. Update the manpage accordingly.
-- [DG] Fix scsi smartd problem detecting SMART support. More cleaning
-       and fix (and rename) scsiTestUnitReady(). More scsi renaming.
-- [BA] Fixed smartd so that if a disk that is explictily listed is not
-       found, then smartd will exit with nonzero status BEFORE forking.
-       If a disk can't be registered, this will also be detected before
-       forking, so that init scripts can react correctly.
-- [BA] Replaced all linux-specific ioctl() calls in atacmds.c with
-       a generic handler smartcommandhandler().  Now the only routine
-       that needs to be implemented for a given OS is os_specific_handler().
-       Also implemented the --report ataioctl. This provides 
-       two levels of reporting.  Using the option once gives a summary
-       report of device IOCTL transactions.  Using the option twice give
-       additional info (a printout of ALL device raw 512 byte SMART
-       data structures).  This is useful for debugging.
-- [DG] more scsi cleanup. Output scsi device serial number (VPD page
-       0x80) if available as part of '-i'. Implement '-t offline' as
-       default self test (only self test older disks support).
-- [BA] Changed crit to info in loglevel of smartd complaint to syslog
-       if DEVICESCAN enabled and device not found.
-- [BA] Added -v 194,10xCelsius option/Directive. Raw Attribute number
-       194 is ten times the disk temperature in Celsius.
-- [DG] scsicmds.[hc] + scsiprint.c: clean up indentation, remove tabs.
-       Introduce new intermediate interface based on "struct scsi_cmnd_io"
-       to isolate SCSI generic commands + responses from Linux details;
-       should help port to FreeBSD of SCSI part of smartmontools.
-       Make SCSI command builders more parametric.
-
-* Thu Mar 13 2003  Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] smartctl: if HDIO_DRIVE_TASK ioctl() is not implemented (no
-       kernel support) then try to assess drive health by examining
-       Attribute values/thresholds directly.
-- [BA] smartd/smartctl: added -v 200,writeerrorcount option/Directive
-       for Fujitsu disks.
-- [BA] smartd: Now send email if any of the SMART commands fails,
-       or if open()ing the device fails.  This is often noted
-       as a common disk failure mode.
-- [BA] smartd/smartctl: Added -v N,raw8 -v N,raw16 and -v N,raw48
-       Directives/Options for printing Raw Attributes in different
-       Formats.
-- [BA] smartd: Added -r ID and -R ID for reporting/tracking Raw
-       values of Attributes.
-- [BA] smartd/smartctl: Changed printing of spin-up-time attribute
-       raw value to reflect current/average as per IBM standard.
-- [BA] smartd/smartctl: Added -v 9,seconds option for disks which
-       use Attribute 9 for power-on lifetime in seconds.
-- [BA] smartctl: Added a warning message so that users of some IBM
-       disks are warned to update their firmware.  Note: we may want
-       to add a command-line flag to disable the warning messages.
-       I have done this in a general way, using regexp, so that we
-       can add warnings about any type of disk that we wish..
-
-* Wed Feb 12 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] smartd: Created a subdirectory examplescripts/ of source
-       directory that contains executable scripts for the -M exec PATH
-       Directive of smartd.
-- [BA] smartd: DEVICESCAN in /etc/smartd.conf
-       can now be followed by all the same Directives as a regular
-       device name like /dev/hda takes.  This allows one to use
-       (for example):
-       DEVICESCAN -m root@example.com
-       in the /etc/smartd.conf file.
-- [BA] smartd: Added -c (--checkonce) command-line option. This checks
-       all devices once, then exits.  The exit status can be
-       used to learn if devices were detected, and if smartd is
-       functioning correctly. This is primarily for Distribution
-       scripters.
-- [BA] smartd: Implemented -M exec Directive for
-       smartd.conf.  This makes it possible to run an
-       arbitrary script or mailing program with the
-       -m option.
-- [PW] smartd: Modified -M Directive so that it can be given
-       multiple times.  Added -M exec Directive.
-
-* Tue Jan 21 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] Fixed bug in smartctl pointed out by Pierre Gentile.
-       -d scsi didn't work because tryata and tryscsi were 
-       reversed -- now works on /devfs SCSI devices.
-- [BA] Fixed bug in smartctl pointed out by Gregory Goddard
-       <ggoddard@ufl.edu>.  Manual says that bit 6 of return
-       value turned on if errors found in smart error log.  But
-       this wasn't implemented.
-- [BA] Modified printing format for 9,minutes to read
-       Xh+Ym not X h + Y m, so that fields are fixed width.
-- [BA] Added Attribute 240 "head flying hours"
-
-* Sun Jan 12 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [BA] As requested, local time/date now printed by smartctl -i
-
-* Thu Jan 9 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [PW] Added 'help' argument to -v for smartctl
-- [PW] Added -D, --showdirectives option to smartd
-
-* Sat Jan 4 2003 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [DG] add '-l selftest' capability for SCSI devices (update smartctl.8)
-- [BA] smartd,smartctl: added additional Attribute modification option
-  -v 220,temp and -v 9,temp.
-- [PW] Renamed smartd option -X to -d
-- [PW] Changed smartd.conf Directives -- see man page
-- [BA/DG] Fixed uncommented comment in smartd.conf
-- [DG] Correct 'Recommended start stop count' for SCSI devices
-- [PW] Replaced smartd.conf directive -C with smartd option -i
-- [PW] Changed options for smartctl -- see man page.
-- [BA] Use strerror() to generate system call error messages.
-- [BA] smartd: fflush() all open streams before fork().
-- [BA] smartctl, smartd simplified internal handling of checksums
-  for simpler porting and less code.
-
-* Sun Dec 8 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- [PW] smartd --debugmode changed to --debug
-- [BA] smartd/smartctl added attribute 230 Head Amplitude from
-  IBM DPTA-353750.
-- [PW] Added list of proposed new options for smartctl to README.
-- [PW] smartd: ParseOpts() now uses getopt_long() if HAVE_GETOPT_LONG is
-  defined and uses getopt() otherwise.  This is controlled by CPPFLAGS in
-  the Makefile.
-- [BA] smartd: Fixed a couple of error messages done with perror()
-  to redirect them as needed.
-- [BA] smartctl: The -O option to enable an Immediate off-line test
-  did not print out the correct time that the test would take to
-  complete.  This is because the test timer is volatile and not
-  fixed.  This has been fixed, and the smartctl.8 man page has been
-  updated to explain how to track the Immediate offline test as it
-  progresses, and to further emphasize the differences between the
-  off-line immediate test and the self-tests.
-- [BA] smartd/smartctl: Added new attribute (200) Multi_Zone_Error_Rate
-- [BA] smartctl: modified so that arguments could have either a single -
-  as in -ea or multiple ones as in -e -a.  Improved warning message for
-  device not opened, and fixed error in redirection of error output of
-  HD identity command.
-- [PW] smartd: added support for long options.  All short options are still
-  supported; see manpage for available long options.
-- [BA] smartctl.  When raw Attribute value was 2^31 or larger, did
-  not print correctly.
-
-* Fri Nov 22 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- Allen: smartd: added smartd.conf Directives -T and -s.  The -T Directive
-  enables/disables Automatic Offline Testing.  The -s Directive
-  enables/disables Attribute Autosave. Documentation and
-  example configuration file updated to agree.
-- Allen: smartd: user can make smartd check the disks at any time
-  (ie, interrupt sleep) by sending signal SIGUSR1 to smartd.  This
-  can be done for example with:
-  kill -USR1 <pid>
-  where <pid> is the process ID number of smartd.
-- Bolso: scsi: don't trust the data we receive from the drive too
-  much. It very well might have errors (like zero response length).
-  Seen on Megaraid logical drive, and verified in the driver source.
-- Allen: smartd: added Directive -m for sending test email and
-  for modifying email reminder behavior.  Updated manual, and sample
-  configuration file to illustrate & explain this.
-- Allen: smartd: increased size of a continued smartd.conf line to
-  1023 characters.
-- Allen: Simplified Directive parsers and improved warning/error
-  messages.
-
-* Sun Nov 17 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- Fixed bug in smartd where testunitready logic inverted
-  prevented functioning on scsi devices.
-- Added testunitnotready to smartctl for symmetry with smartd.
-- Brabec: added Czech descriptions to .spec file
-- Brabec: corrected comment in smartd.conf example
-- Changed way that entries in the ATA error log are printed,
-  to make it clearer which is the most recent error and
-  which is the oldest one.
-- Changed Temperature_Centigrade to Temperature_Celsius.
-  The term "Centigrade" ceased to exist in 1948.  (c.f
-  http://www.bartleby.com/64/C004/016.html).
-
-* Wed Nov 13 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- smartd SCSI devices: can now send warning email message on failure
-- Added a new smartd configuration file Directive: -M ADDRESS.
-  This sends a single warning email to ADDRESS for failures or
-  errors detected with the -c, -L, -l, or -f Directives.
-
-* Mon Nov 11 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- Modified perror() statements in atacmds.c so that printout for SMART
-  commands errors is properly suppressed or queued depending upon users
-  choices for error reporting modes.
-- Added Italian descriptions to smartmontools.spec file.
-- Started impementing send-mail-on-error for smartd; not yet enabled.
-* Sun Nov 10 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- Added -P (Permissive) Directive to smartd.conf file to allow SMART monitoring of
-  pre-ATA-3 Rev 4 disks that have SMART but do not have a SMART capability bit.
-
-* Thu Nov 7 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- Added a Man section 5 page for smartd.conf
-- Changed Makefile so that the -V option does not reflect file state
-  before commit!
-- modified .spec file so that locale information now contains
-  character set definition.   Changed pt_BR to pt since we do not use any
-  aspect other than language.  See man setlocale.
-- smartctl: added new options -W, -U, and -P to control if and how the
-  smartctl exits if an error is detected in either a SMART data
-  structure checksum, or a SMART command returns an error.
-- modified manual page to break options into slightly more logical
-  categories.
-- reformatted 'usage' message order to agree with man page ordering
-
-* Mon Nov 4 2002 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-- smartctl: added new options -n and -N to force device to be ATA or SCSI
-- smartctl: no longer dies silently if device path does not start/dev/X
-- smartctl: now handles arbitrary device paths
-- Added additional macros for manual and sbin paths in this SPEC file.
-- Modified Makefile to install /etc/smartd.conf, but without overwriting existing config file
-- Modified this specfile to do the same, and to not remove any files that it did not install
-
-* Thu Oct 30 2002 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-- Fixed typesetting error in man page smartd.8
-- Removed redundant variable (harmless) from smartd.c
-
-* Wed Oct 29 2002 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-- Added a new directive for the configuration file.  If the word
-  DEVICESCAN appears before any non-commented material in the
-  configuration file, then the confi file will be ignored and the
-  devices wil be scanned.
-- Note: it has now been confirmed that the code modifications between
-  5.0.23 and 5.0.24 have eliminated the GCC 3.2 problems.  Note that
-  there is a GCC bug howerver, see #8404 at
-  http://gcc.gnu.org/bugzilla/show_bug.cgi?id=8404
-- Added new Directive for Configuration file:
-  -C <N> This sets the time in between disk checks to be <N>
-  seconds apart.  Note that  although  you  can  give
-  this Directive multiple times on different lines of
-  the configuration file, only the final  value  that
-  is  given  has  an  effect,  and applies to all the
-  disks.  The default value of <N> is 1800  sec,  and
-  the minimum allowed value is ten seconds.
-- Problem wasn't the print format. F.L.W. Meunier <0@pervalidus.net>
-  sent me a gcc 3.2 build and I ran it under a debugger.  The
-  problem seems to be with passing the very large (2x512+4) byte
-  data structures as arguments.  I never liked this anyway; it was
-  inherited from smartsuite.  So I've changed all the heavyweight
-  functions (ATA ones, anyone) to just passing pointers, not hideous
-  kB size structures on the stack.  Hopefully this will now build OK
-  under gcc 3.2 with any sensible compilation options.
-- Because of reported problems with GCC 3.2 compile, I have gone
-  thorough the code and explicitly changed all print format
-  parameters to correspond EXACTLY to int unless they have to be
-  promoted to long longs.  To quote from the glibc bible: [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
-  required.
-- smartd, smartctl now warn if they find an attribute whose ID
-  number does not match between Data and Threshold structures.
-- Fixed nasty bug which led to wrong number of arguments for a
-  varargs statement, with attendent stack corruption.  Sheesh!
-  Have added script to CVS attic to help find such nasties in the
-  future.
-
-* Tue Oct 29 2002 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-- Eliminated some global variables out of header files and other
-  minor cleanup of smartd.
-- Did some revision of the man page for smartd and made the usage
-  messages for Directives consistent.
-- smartd: prints warning message when it gets SIGHUP, saying that it is
-  NOT re-reading the config file.
-- smartctl: updated man page to say self-test commands -O,x,X,s,S,A
-  appear to be supported in the code.  [I can't test these,  can anyone
-  report?]
-- smartctl: smartctl would previously print the LBA of a self-test
-  if it completed, and the LBA was not 0 or 0xff...f However
-  according to the specs this is not correct.  According to the
-  specs, if the self-test completed without error then LBA is
-  undefined.  This version fixes that.  LBA value only printed if
-  self-test encountered an error.
-- smartd has changed significantly. This is the first CVS checkin of
-  code that extends the options available for smartd.  The following
-  options can be placed into the /etc/smartd.conf file, and control the
-  behavior of smartd.
-- Configuration file Directives (following device name):
-  -A     Device is an ATA device
-  -S     Device is a SCSI device
-  -c     Monitor SMART Health Status
-  -l     Monitor SMART Error Log for changes
-  -L     Monitor SMART Self-Test Log for new errors
-  -f     Monitor for failure of any 'Usage' Attributes
-  -p     Report changes in 'Prefailure' Attributes
-  -u     Report changes in 'Usage' Attributes
-  -t     Equivalent to -p and -u Directives
-  -a     Equivalent to -c -l -L -f -t Directives
-  -i ID  Ignore Attribute ID for -f Directive
-  -I ID  Ignore Attribute ID for -p, -u or -t Directive
-  #      Comment: text after a hash sign is ignored
-  \      Line continuation character
-- cleaned up functions used for printing CVS IDs.  Now use string
-  library, as it should be.
-- modified length of device name string in smartd internal structure
-  to accomodate max length device name strings
-- removed un-implemented (-e = Email notification) option from
-  command line arg list.  We'll put it back on when implemeneted.
-- smartd now logs serious (fatal) conditions in its operation at
-  loglevel LOG_CRIT rather than LOG_INFO before exiting with error.
-- smartd used to open a file descriptor for each SMART enabled
-- device, and then keep it open the entire time smartd was running.
-  This meant that some commands, like IOREADBLKPART did not work,
-  since the fd to the device was open.  smartd now opens the device
-  when it needs to read values, then closes it.  Also, if one time
-  around it can't open the device, it simply prints a warning
-  message but does not give up.  Have eliminated the .fd field from
-  data structures -- no longer gets used.
-- smartd now opens SCSI devices as well using O_RDONLY rather than
-  O_RDWR.  If someone can no longer monitor a SCSI device that used
-  to be readable, this may well be the reason why.
-- smartd never checked if the number of ata or scsi devices detected
-  was greater than the max number it could monitor.  Now it does.
-
-* Fri Oct 25 2002 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
-- changes to the Makefile and spec file so that if there are ungzipped manual
-  pages in place these will be removed so that the new gzipped man pages are
-  visible.
-- smartd on startup now looks in the configuration file /etc/smartd.conf for
-  a list of devices which to include in its monitoring list.  See man page
-  (man smartd) for syntax. If not found, try all ata and ide devices.
-- smartd: close file descriptors of SCSI device if not SMART capable
-  Closes ALL file descriptors after forking to daemon.
-- added new temperature attribute (231, temperature)
-- smartd: now open ATA disks using O_RDONLY
-
-* Thu Oct 24 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-- smartd now prints the name of a failed or changed attribute into logfile,
-  not just ID number
-- Changed name of -p (print version) option to -V
-- Minor change in philosophy: if a SMART command fails or the device
-    appears incapable of a SMART command that the user has asked for,
-    complain by printing an error message, but go ahead and try
-    anyway.  Since unimplemented SMART commands should just return an
-    error but not cause disk problems, this should't cause any
-    difficulty.
-- Added two new flags: q and Q.  q is quiet mode - only print: For
-    the -l option, errors recorded in the SMART error log; For the -L
-    option, errors recorded in the device self-test log; For the -c
-    SMART "disk failing" status or device attributes (pre-failure or
-    usage) which failed either now or in the past; For the -v option
-    device attributes (pre-failure or usage) which failed either now
-    or in the past.  Q is Very Quiet mode: Print no ouput.  The only
-    way to learn about what was found is to use the exit status of
-    smartctl.
-- smartctl now returns sensible values (bitmask).  See smartctl.h
-    for the values, and the man page for documentation.
-- The SMART status check now uses the correct ATA call.  If failure
-    is detected we search through attributes to list the failed ones.
-    If the SMART status check shows GOOD, we then look to see if their
-    are any usage attributes or prefail attributes have failed at any
-    time.  If so we print them.
-- Modified function that prints vendor attributes to say if the
-    attribute has currently failed or has ever failed.
-- -p option now prints out license info and CVS strings for all
-    modules in the code, nicely formatted.
-- Previous versions of this code (and Smartsuite) only generate
-    SMART failure errors if the value of an attribute is below the
-    threshold and the prefailure bit is set.  However the ATA Spec
-    (ATA4 <=Rev 4) says that it is a SMART failure if the value of an
-    attribute is LESS THAN OR EQUAL to the threshold and the
-    prefailure bit is set.  This is now fixed in both smartctl and
-    smartd.  Note that this is a troubled subject -- the original
-    SFF 8035i specification defining SMART was inconsistent about
-    this.  One section says that Attribute==Threshold is pass,
-    and another section says it is fail.  However the ATA specs are
-    consistent and say Attribute==Threshold is a fail.
-- smartd did not print the correct value of any failing SMART attribute.  It
-    printed the index in the attribute table, not the attribute
-    ID. This is fixed.
-- when starting self-tests in captive mode ioctl returns EIO because
-    the drive has been busied out.  Detect this and don't return an eror
-    in this case.  Check this this is correct (or how to fix it?)
- - fixed possible error in how to determine ATA standard support
-    for devices with no ATA minor revision number.
-- device opened only in read-only not read-write mode.  Don't need R/W 
-    access to get smart data. Check this with Andre.
-- smartctl now handles all possible choices of "multiple options"
-    gracefully.  It goes through the following phases of operation,
-    in order: INFORMATION, ENABLE/DISABLE, DISPLAY DATA, RUN/ABORT TESTS.
-    Documentation has bee updated to explain the different phases of
-    operation.  Control flow through ataPrintMain()
-    simplified.
-- If reading device identity information fails, try seeing if the info
-    can be accessed using a "DEVICE PACKET" command.  This way we can
-    at least get device info.
-- Modified Makefile to automatically tag CVS archive on issuance of
-    a release
-- Modified drive detection so minor device ID code showing ATA-3 rev
-    0 (no SMART) is known to not be SMART capable.
-- Now verify the checksum of the device ID data structure, and of the
-    attributes threshold structure.  Before neither of these
-    structures had their checksums verified.
-- New behavior vis-a-vis checksums.  If they are wrong, we log
-    warning messages to stdout, stderr, and syslog, but carry on
-    anyway.  All functions now call a checksumwarning routine if the
-    checksum doesn't vanish as it should.
-- Changed Read Hard Disk Identity function to get fresh info from
-    the disk on each call rather than to use the values that were read
-    upon boot-up into the BIOS.  This is the biggest change in this
-    release.  The ioctl(device, HDIO_GET_IDENTITY, buf ) call should
-    be avoided in such code.  Note that if people get garbled strings
-    for the model, serial no and firmware versions of their drives,
-    then blame goes here (the BIOS does the byte swapping for you,
-    apparently!)
-- Function ataSmartSupport now looks at correct bits in drive
-    identity structure to verify first that these bits are valid,
-    before using them.
-- Function ataIsSmartEnabled() written which uses the Drive ID state
-    information to tell if SMART is enabled or not.  We'll carry this
-    along for the moment without using it.
-- Function ataDoesSmartWork() guaranteed to work if the device
-    supports SMART.
-- Replace some numbers by #define MACROS
-- Wrote Function TestTime to return test time associated with each
-    different type of test.
-- Thinking of the future, have added a new function called
-    ataSmartStatus2().  Eventually when I understand how to use the
-    TASKFILE API and am sure that this works correctly, it will
-    replace ataSmartStatus().  This queries the drive directly to
-    see if the SMART status is OK, rather than comparing thresholds to
-    attribute values ourselves. But I need to get some drives that fail
-    their SMART status to check it.
-
-* Thu Oct 17 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
--   Removed extraneous space before some error message printing.
--   Fixed some character buffers that were too short for contents.
-    Only used for unrecognized drives, so probably damage was minimal.
-
-* Wed Oct 16 2002 Bruce Allen <smartmontools-support@lists.sourceforge.net>
--   Initial release.  Code is derived from smartsuite, and is
-    intended to be compatible with the ATA/ATAPI-5 specifications.
--   For IBM disks whose raw temp data includes three temps. print all
-    three
--   print timestamps for error log to msec precision
--   added -m option for Hitachi disks that store power on life in
-    minutes
--   added -L option for printing self-test error logs
--   in -l option, now print power on lifetime, so that one can see
-    when the error took place
--   updated SMART structure definitions to ATA-5 spec
--   added -p option
--   added -f and -F options to enable/disable autosave threshold
-    parameters
-
index 3eaf27f64d373ed23b4be67897033709352ad7f1..670df9e7ad9b5c67f9451ea73561fb95d9101ecf 100644 (file)
@@ -3,7 +3,8 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-9 Christian Franke <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
 #include <mbstring.h> // _mbsinc()
 #endif
 
+#include <stdexcept>
+
 #include "config.h"
+#include "svnversion.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.65 2008/03/04 22:09:47 ballen4705 Exp $"
-CONFIG_H_CVSID INT64_H_CVSID UTILITY_H_CVSID;
+#include "atacmds.h"
+#include "dev_interface.h"
+
+const char * utility_cpp_cvsid = "$Id: utility.cpp 2848 2009-07-18 20:14:38Z chrfranke $"
+                                 UTILITY_H_CVSID INT64_H_CVSID;
 
 const char * packet_types[] = {
         "Direct-access (disk)",
@@ -70,13 +76,50 @@ const char * packet_types[] = {
 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;
 
+// BUILD_INFO can be provided by package maintainers
+#ifndef BUILD_INFO
+#define BUILD_INFO "(local build)"
+#endif
+
+// Make version information string
+std::string format_version_info(const char * prog_name, bool full /*= false*/)
+{
+  std::string info = strprintf(
+    "%s "PACKAGE_VERSION" "SMARTMONTOOLS_SVN_DATE" r"SMARTMONTOOLS_SVN_REV
+      " [%s] "BUILD_INFO"\n"
+    "Copyright (C) 2002-9 by Bruce Allen, http://smartmontools.sourceforge.net\n",
+    prog_name, smi()->get_os_version_str()
+  );
+  if (!full)
+    return info;
+
+  info += strprintf(
+    "\n"
+    "%s comes with ABSOLUTELY NO WARRANTY. This is free\n"
+    "software, and you are welcome to redistribute it under\n"
+    "the terms of the GNU General Public License Version 2.\n"
+    "See http://www.gnu.org for further details.\n"
+    "\n"
+    "smartmontools release "PACKAGE_VERSION
+      " dated "SMARTMONTOOLS_RELEASE_DATE" at "SMARTMONTOOLS_RELEASE_TIME"\n"
+    "smartmontools SVN rev "SMARTMONTOOLS_SVN_REV
+      " dated "SMARTMONTOOLS_SVN_DATE" at "SMARTMONTOOLS_SVN_TIME"\n"
+    "smartmontools build host: "SMARTMONTOOLS_BUILD_HOST"\n"
+    "smartmontools build configured: "SMARTMONTOOLS_CONFIGURE_DATE "\n"
+    "%s compile dated "__DATE__" at "__TIME__"\n",
+    prog_name, prog_name
+  );
+  info += strprintf(
+    "smartmontools configure arguments: %s\n",
+      (sizeof(SMARTMONTOOLS_CONFIGURE_ARGS) > 1 ?
+       SMARTMONTOOLS_CONFIGURE_ARGS : "[no arguments given]")
+  );
+
+  return info;
+}
 
 // Solaris only: Get site-default timezone. This is called from
 // UpdateTimezone() when TZ environment variable is unset at startup.
@@ -117,9 +160,9 @@ static char *ReadSiteDefaultTimezone(){
 void FixGlibcTimeZoneBug(){
 #if __GLIBC__  
   if (!getenv("TZ")) {
-    putenv("TZ=GMT");
+    putenv((char *)"TZ=GMT"); // POSIX prototype is 'int putenv(char *)'
     tzset();
-    putenv("TZ");
+    putenv((char *)"TZ");
     tzset();
   }
 #elif _WIN32
@@ -230,7 +273,7 @@ int isbigendian(){
 // timezone info (sigh).
 void dateandtimezoneepoch(char *buffer, time_t tval){
   struct tm *tmval;
-  char *timezonename;
+  const char *timezonename;
   char datebuffer[DATEANDEPOCHLEN];
   int lenm1;
 #ifdef _WIN32
@@ -285,61 +328,6 @@ void dateandtimezone(char *buffer){
   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){
@@ -361,21 +349,6 @@ void syserror(const char *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."
@@ -385,7 +358,8 @@ void printregexwarning(int errcode, regex_t *compiled){
 // 
 // 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.
+// But it should work for the regex subset used in drive database
+// and smartd '-s' directives.
 static int check_regex_nesting(const char * pattern)
 {
   int level = 0, i;
@@ -398,22 +372,88 @@ static int check_regex_nesting(const char * pattern)
   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;
+// Wrapper class for regex(3)
 
-  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;
+regular_expression::regular_expression()
+: m_flags(0)
+{
+  memset(&m_regex_buf, 0, sizeof(m_regex_buf));
+}
+
+regular_expression::regular_expression(const char * pattern, int flags)
+{
+  memset(&m_regex_buf, 0, sizeof(m_regex_buf));
+  compile(pattern, flags);
+}
+
+regular_expression::~regular_expression()
+{
+  free_buf();
+}
+
+regular_expression::regular_expression(const regular_expression & x)
+{
+  memset(&m_regex_buf, 0, sizeof(m_regex_buf));
+  copy(x);
+}
+
+regular_expression & regular_expression::operator=(const regular_expression & x)
+{
+  free_buf();
+  copy(x);
+  return *this;
+}
+
+void regular_expression::free_buf()
+{
+  if (nonempty(&m_regex_buf, sizeof(m_regex_buf))) {
+    regfree(&m_regex_buf);
+    memset(&m_regex_buf, 0, sizeof(m_regex_buf));
   }
-  return 0;
+}
+
+void regular_expression::copy(const regular_expression & x)
+{
+  m_pattern = x.m_pattern;
+  m_flags = x.m_flags;
+  m_errmsg = x.m_errmsg;
+
+  if (!m_pattern.empty() && m_errmsg.empty()) {
+    // There is no POSIX compiled-regex-copy command.
+    if (!compile())
+      throw std::runtime_error(strprintf(
+        "Unable to recompile regular expression \"%s\": %s",
+        m_pattern.c_str(), m_errmsg.c_str()));
+  }
+}
+
+bool regular_expression::compile(const char * pattern, int flags)
+{
+  free_buf();
+  m_pattern = pattern;
+  m_flags = flags;
+  return compile();
+}
+
+bool regular_expression::compile()
+{
+  int errcode = regcomp(&m_regex_buf, m_pattern.c_str(), m_flags);
+  if (errcode) {
+    char errmsg[512];
+    regerror(errcode, &m_regex_buf, errmsg, sizeof(errmsg));
+    m_errmsg = errmsg;
+    free_buf();
+    return false;
+  }
+
+  if (check_regex_nesting(m_pattern.c_str()) < 0) {
+    m_errmsg = "Unmatched ')'";
+    free_buf();
+    return false;
+  }
+
+  m_errmsg.clear();
+  return true;
 }
 
 // Splits an argument to the -r option into a name part and an (optional) 
@@ -465,25 +505,27 @@ int split_report_arg2(char *s, int *i){
 }
 
 #ifndef HAVE_STRTOULL
-// Replacement for missing strtoull() (Linux with libc < 6, MSVC 6.0)
-// Functionality reduced to split_selective_arg()'s requirements.
+// Replacement for missing strtoull() (Linux with libc < 6, MSVC)
+// Functionality reduced to requirements of smartd and split_selective_arg().
 
-static uint64_t strtoull(const char * p, char * * endp, int base)
+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++;
+
+  if (!base) {
+    if (c == '0') {
+      if (p[i] == 'x' || p[i] == 'X') {
+        base = 16; i++;
+      }
+      else
+        base = 8;
+      c = p[i++];
     }
     else
-      base = 8;
-    c = p[i++];
+      base = 10;
   }
-  else
-    base = 10;
 
   result = 0;
   maxres = ~(uint64_t)0 / (unsigned)base;
@@ -507,7 +549,8 @@ static uint64_t strtoull(const char * p, char * * endp, int base)
     result = result * (unsigned)base + digit;
     c = p[i++];
   }
-  *endp = (char *)p + i - 1;
+  if (endp)
+    *endp = (char *)p + i - 1;
   return result;
 }
 #endif // HAVE_STRTOLL
@@ -565,7 +608,14 @@ int split_selective_arg(char *s, uint64_t *start,
   return 0;
 }
 
+#ifdef OLD_INTERFACE
+
+// smartd exit codes
+#define EXIT_NOMEM     8   // out of memory
+#define EXIT_BADCODE   10  // internal error - should NEVER happen
+
 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.
@@ -634,13 +684,16 @@ char *CustomStrDup(const char *ptr, int mustexist, int whatline, const char* fil
   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;
+#endif // OLD_INTERFACE
+
+
+// Returns true if region of memory contains non-zero entries
+bool nonempty(const void * data, int size)
+{
+  for (int i = 0; i < size; i++)
+    if (((const unsigned char *)data)[i])
+      return true;
+  return false;
 }
 
 
@@ -672,6 +725,24 @@ void MsecToText(unsigned int msec, char *txt){
   return;
 }
 
+// return (v)sprintf() formatted std::string
+
+std::string vstrprintf(const char * fmt, va_list ap)
+{
+  char buf[512];
+  vsnprintf(buf, sizeof(buf), fmt, ap);
+  buf[sizeof(buf)-1] = 0;
+  return buf;
+}
+
+std::string strprintf(const char * fmt, ...)
+{
+  va_list ap; va_start(ap, fmt);
+  std::string str = vstrprintf(fmt, ap);
+  va_end(ap);
+  return str;
+}
+
 
 #ifndef HAVE_WORKING_SNPRINTF
 // Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
index 579aae0089dba1f1b669b81880c51b4345b94650..3533948a818736952348ff52b9f5fa0a96c586fc 100644 (file)
--- a/utility.h
+++ b/utility.h
@@ -3,7 +3,8 @@
  *
  * Home page of code is: http://smartmontools.sourceforge.net
  *
- * Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2002-9 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2008-9 Christian Franke <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
 #ifndef UTILITY_H_
 #define UTILITY_H_
 
-#define UTILITY_H_CVSID "$Id: utility.h,v 1.51 2008/03/04 22:09:47 ballen4705 Exp $\n"
+#define UTILITY_H_CVSID "$Id: utility.h 2848 2009-07-18 20:14:38Z chrfranke $"
 
 #include <time.h>
 #include <sys/types.h> // for regex.h (according to POSIX)
 #include <regex.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <string>
+
+#if !defined(__GNUC__) && !defined(__attribute__)
+#define __attribute__(x)  /**/
+#endif
+
+// Make version information string
+std::string format_version_info(const char * prog_name, bool full = false);
+
+// return (v)sprintf() formated std::string
+std::string strprintf(const char * fmt, ...)
+    __attribute__ ((format (printf, 1, 2)));
+std::string vstrprintf(const char * fmt, va_list ap);
 
 #ifndef HAVE_WORKING_SNPRINTF
 // Substitute by safe replacement functions
-#include <stdarg.h>
-int safe_snprintf(char *buf, int size, const char *fmt, ...);
+int safe_snprintf(char *buf, int size, const char *fmt, ...)
+    __attribute__ ((format (printf, 3, 4)));
 int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap);
 #define snprintf  safe_snprintf
 #define vsnprintf safe_vsnprintf
 #endif
 
+#ifndef HAVE_STRTOULL
+// Replacement for missing strtoull() (Linux with libc < 6, MSVC)
+uint64_t strtoull(const char * p, char * * endp, int base);
+#endif
+
 // Utility function prints current date and time and timezone into a
 // character buffer of length>=64.  All the fuss is needed to get the
 // right timezone info (sigh).
@@ -48,40 +70,21 @@ void dateandtimezone(char *buffer);
 // Same, but for time defined by epoch tval
 void dateandtimezoneepoch(char *buffer, time_t tval);
 
-// utility function for printing out CVS strings
-#define CVSMAXLEN 1024
-void printone(char *block, const char *cvsid);
-
 // like printf() except that we can control it better. Note --
 // although the prototype is given here in utility.h, the function
 // itself is defined differently in smartctl and smartd.  So the
 // function definition(s) are in smartd.c and in smartctl.c.
-#ifndef __GNUC__
-#define __attribute__(x)      /* nothing */
-#endif
 void pout(const char *fmt, ...)  
      __attribute__ ((format (printf, 1, 2)));
 
 // replacement for perror() with redirected output.
 void syserror(const char *message);
 
-// Prints a warning message for a failed regular expression compilation from
-// regcomp().
-void printregexwarning(int errcode, regex_t *compiled);
-
-// A wrapper for regcomp().  Returns zero for success, non-zero otherwise.
-int compileregex(regex_t *compiled, const char *pattern, int cflags);
-
 // Function for processing -r option in smartctl and smartd
 int split_report_arg(char *s, int *i);
 // Function for processing -c option in smartctl and smartd
 int split_report_arg2(char *s, int *i);
 
-// Possible values for smartselectivemode
-#define SEL_RANGE            0 // MIN-MAX
-#define SEL_REDO             1 // redo this
-#define SEL_NEXT             2 // do next range
-#define SEL_CONT             3 // redo or next depending of last test status
 // Function for processing -t selective... option in smartctl
 int split_selective_arg(char *s, uint64_t *start, uint64_t *stop, int *mode);
 
@@ -89,14 +92,20 @@ int split_selective_arg(char *s, uint64_t *start, uint64_t *stop, int *mode);
 // Guess device type (ata or scsi) based on device name 
 // Guessing will now use Controller Type defines below
 
-int guess_device_type(const char * dev_name);
+// Moved to C++ interface
+//int guess_device_type(const char * dev_name);
 
 // Create and return the list of devices to probe automatically
 // if the DEVICESCAN option is in the smartd config file
-int make_device_names (char ***devlist, const char* name);
+// Moved to C++ interface
+//int make_device_names (char ***devlist, const char* name);
+
+// Replacement for exit(status)
+// (exit is not compatible with C++ destructors)
+#define EXIT(status) { throw (int)(status); }
 
 
-#define EXIT(x)  { exitstatus = (x); exit((x)); }
+#ifdef OLD_INTERFACE
 
 // replacement for calloc() that tracks memory usage
 void *Calloc(size_t nmemb, size_t size);
@@ -123,6 +132,8 @@ template <class T>
 inline T * CheckFree(T * address, int whatline, const char* file)
   { return (T *)CheckFree1((void *)address, whatline, file); }
 
+#endif // OLD_INTERFACE
+
 // This function prints either to stdout or to the syslog as needed
 
 // [From GLIBC Manual: Since the prototype doesn't specify types for
@@ -141,18 +152,19 @@ int isbigendian();
 // the ATA standard for packet devices to define the device type.
 const char *packetdevicetype(int type);
 
-int deviceopen(const char *pathname, char *type);
+// Moved to C++ interface
+//int deviceopen(const char *pathname, char *type);
 
-int deviceclose(int fd);
+//int deviceclose(int fd);
 
 // Optional functions of os_*.c
 #ifdef HAVE_GET_OS_VERSION_STR
 // Return build host and OS version as static string
-const char * get_os_version_str(void);
+//const char * get_os_version_str(void);
 #endif
 
-// returns 1 if any of the n bytes are nonzero, else zero.
-int nonempty(unsigned char *testarea,int n);
+// returns true if any of the n bytes are nonzero, else zero.
+bool nonempty(const void * data, int size);
 
 // needed to fix glibc bug
 void FixGlibcTimeZoneBug();
@@ -160,27 +172,151 @@ void FixGlibcTimeZoneBug();
 // convert time in msec to a text string
 void MsecToText(unsigned int msec, char *txt);
 
-// Exit codes
-#define EXIT_BADCMD    1   // command line did not parse
-#define EXIT_BADCONF   2   // syntax error in config file
-#define EXIT_STARTUP   3   // problem forking daemon
-#define EXIT_PID       4   // problem creating pid file
-#define EXIT_NOCONF    5   // config file does not exist
-#define EXIT_READCONF  6   // config file exists but cannot be read
-
-#define EXIT_NOMEM     8   // out of memory
-#define EXIT_BADCODE   10  // internal error - should NEVER happen
-
-#define EXIT_BADDEV    16  // we can't monitor this device
-#define EXIT_NODEV     17  // no devices to monitor
-
-#define EXIT_SIGNAL    254 // abort on signal
-
+// Wrapper class for a raw data buffer
+class raw_buffer
+{
+public:
+  explicit raw_buffer(unsigned sz, unsigned char val = 0)
+    : m_data(new unsigned char[sz]),
+      m_size(sz)
+    { memset(m_data, val, m_size); }
+
+  ~raw_buffer()
+    { delete [] m_data; }
+
+  unsigned size() const
+    { return m_size; }
+
+  unsigned char * data()
+    { return m_data; }
+  const unsigned char * data() const
+    { return m_data; }
+
+private:
+  unsigned char * m_data;
+  unsigned m_size;
+
+  raw_buffer(const raw_buffer &);
+  void operator=(const raw_buffer &);
+};
+
+/// Wrapper class for FILE *.
+class stdio_file
+{
+public:
+  explicit stdio_file(FILE * f = 0, bool owner = false)
+    : m_file(f), m_owner(owner) { }
+
+  stdio_file(const char * name, const char * mode)
+    : m_file(fopen(name, mode)), m_owner(true) { }
+
+  ~stdio_file()
+    {
+      if (m_file && m_owner)
+        fclose(m_file);
+    }
+
+  bool open(const char * name, const char * mode)
+    {
+      m_file = fopen(name, mode);
+      m_owner = true;
+      return !!m_file;
+    }
+
+  void open(FILE * f, bool owner = false)
+    {
+      m_file = f;
+      m_owner = owner;
+    }
+
+  bool close()
+    {
+      if (!m_file)
+        return true;
+      bool ok = !ferror(m_file);
+      if (fclose(m_file))
+        ok = false;
+      m_file = 0;
+      return ok;
+    }
+
+  operator FILE * ()
+    { return m_file; }
+
+  bool operator!() const
+    { return !m_file; }
+
+private:
+  FILE * m_file;
+  bool m_owner;
+
+  stdio_file(const stdio_file &);
+  void operator=(const stdio_file &);
+};
+
+/// Wrapper class for regex(3).
+/// Supports copy & assignment and is compatible with STL containers.
+class regular_expression
+{
+public:
+  // Construction & assignment
+  regular_expression();
+
+  regular_expression(const char * pattern, int flags);
+
+  ~regular_expression();
+
+  regular_expression(const regular_expression & x);
+
+  regular_expression & operator=(const regular_expression & x);
+
+  /// Set and compile new pattern, return false on error.
+  bool compile(const char * pattern, int flags);
+
+  // Get pattern from last compile().
+  const char * get_pattern() const
+    { return m_pattern.c_str(); }
+
+  /// Get error message from last compile().
+  const char * get_errmsg() const
+    { return m_errmsg.c_str(); }
+
+  // Return true if pattern is not set or bad.
+  bool empty() const
+    { return (m_pattern.empty() || !m_errmsg.empty()); }
+
+  /// Return true if substring matches pattern
+  bool match(const char * str, int flags = 0) const
+    { return !regexec(&m_regex_buf, str, 0, (regmatch_t*)0, flags); }
+
+  /// Return true if full string matches pattern
+  bool full_match(const char * str, int flags = 0) const
+    {
+      regmatch_t range;
+      return (   !regexec(&m_regex_buf, str, 1, &range, flags)
+              && range.rm_so == 0 && range.rm_eo == (int)strlen(str));
+    }
+
+  /// Return true if substring matches pattern, fill regmatch_t array.
+  bool execute(const char * str, unsigned nmatch, regmatch_t * pmatch, int flags = 0) const
+    { return !regexec(&m_regex_buf, str, nmatch, pmatch, flags); }
+
+private:
+  std::string m_pattern;
+  int m_flags;
+  regex_t m_regex_buf;
+  std::string m_errmsg;
+
+  void free_buf();
+  void copy(const regular_expression & x);
+  bool compile();
+};
 
 // macros to control printing
-#define PRINT_ON(control)  {if (control->printing_switchable) control->dont_print=0;}
-#define PRINT_OFF(control) {if (control->printing_switchable) control->dont_print=1;}
+#define PRINT_ON(control)  {if (control->printing_switchable) control->dont_print=false;}
+#define PRINT_OFF(control) {if (control->printing_switchable) control->dont_print=true;}
 
+#ifdef OLD_INTERFACE
 // possible values for controller_type in extern.h
 #define CONTROLLER_UNKNOWN              0x00
 #define CONTROLLER_ATA                  0x01
@@ -194,5 +330,8 @@ void MsecToText(unsigned int msec, char *txt);
 #define CONTROLLER_HPT                  0x09  // SATA drives behind HighPoint Raid controllers
 #define CONTROLLER_CCISS               0x10  // CCISS controller 
 #define CONTROLLER_PARSEDEV             0x11  // "smartctl -r ataioctl,2 ..." output parser pseudo-device
+#define CONTROLLER_USBCYPRESS          0x12  // ATA device behind Cypress USB bridge
+#define CONTROLLER_ARECA                0x13  // Areca controller
+#endif
 
 #endif