]> git.proxmox.com Git - mirror_smartmontools-debian.git/commitdiff
import upstream version 5.36
authorGuido Guenther <agx@bogon.sigxcpu.org>
Sun, 23 Apr 2006 15:49:33 +0000 (17:49 +0200)
committerGuido Guenther <agx@bogon.sigxcpu.org>
Sun, 23 Apr 2006 15:49:33 +0000 (17:49 +0200)
83 files changed:
AUTHORS [new file with mode: 0644]
CHANGELOG [new file with mode: 0644]
COPYING [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
Makefile.in [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
WARNINGS [new file with mode: 0644]
aclocal.m4 [new file with mode: 0644]
atacmdnames.c [new file with mode: 0644]
atacmdnames.h [new file with mode: 0644]
atacmds.c [new file with mode: 0644]
atacmds.h [new file with mode: 0644]
ataprint.c [new file with mode: 0644]
ataprint.h [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
config.guess [new file with mode: 0755]
config.h.in [new file with mode: 0644]
config.sub [new file with mode: 0755]
configure [new file with mode: 0755]
configure.in [new file with mode: 0644]
depcomp [new file with mode: 0755]
examplescripts/Example1 [new file with mode: 0755]
examplescripts/Example2 [new file with mode: 0755]
examplescripts/Example3 [new file with mode: 0755]
examplescripts/Makefile.am [new file with mode: 0644]
examplescripts/Makefile.in [new file with mode: 0644]
examplescripts/README [new file with mode: 0644]
extern.h [new file with mode: 0644]
install-sh [new file with mode: 0755]
int64.h [new file with mode: 0644]
knowndrives.c [new file with mode: 0644]
knowndrives.h [new file with mode: 0644]
missing [new file with mode: 0755]
os_darwin.c [new file with mode: 0644]
os_darwin.h [new file with mode: 0644]
os_darwin/English_Localizable.strings [new file with mode: 0644]
os_darwin/SMART.in [new file with mode: 0644]
os_darwin/StartupParameters.plist [new file with mode: 0644]
os_freebsd.c [new file with mode: 0644]
os_freebsd.h [new file with mode: 0644]
os_generic.c [new file with mode: 0644]
os_generic.h [new file with mode: 0644]
os_linux.c [new file with mode: 0644]
os_linux.h [new file with mode: 0644]
os_netbsd.c [new file with mode: 0644]
os_netbsd.h [new file with mode: 0644]
os_openbsd.c [new file with mode: 0644]
os_openbsd.h [new file with mode: 0644]
os_solaris.c [new file with mode: 0644]
os_solaris.h [new file with mode: 0644]
os_solaris_ata.s [new file with mode: 0644]
os_win32.c [new file with mode: 0644]
os_win32/daemon_win32.c [new file with mode: 0644]
os_win32/daemon_win32.h [new file with mode: 0644]
os_win32/hostname_win32.c [new file with mode: 0644]
os_win32/hostname_win32.h [new file with mode: 0644]
os_win32/syslog.h [new file with mode: 0644]
os_win32/syslog_win32.c [new file with mode: 0644]
posix/regcomp.c [new file with mode: 0644]
posix/regex.c [new file with mode: 0644]
posix/regex.h [new file with mode: 0644]
posix/regex_internal.c [new file with mode: 0644]
posix/regex_internal.h [new file with mode: 0644]
posix/regexec.c [new file with mode: 0644]
scsicmds.c [new file with mode: 0644]
scsicmds.h [new file with mode: 0644]
scsiprint.c [new file with mode: 0644]
scsiprint.h [new file with mode: 0644]
smartctl.8.in [new file with mode: 0644]
smartctl.c [new file with mode: 0644]
smartctl.h [new file with mode: 0644]
smartd.8.in [new file with mode: 0644]
smartd.c [new file with mode: 0644]
smartd.conf [new file with mode: 0644]
smartd.conf.5.in [new file with mode: 0644]
smartd.h [new file with mode: 0644]
smartd.initd.in [new file with mode: 0755]
smartmontools.spec [new file with mode: 0644]
utility.c [new file with mode: 0644]
utility.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..1acac05
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,32 @@
+$Id: AUTHORS,v 1.15 2004/11/11 17:56:07 ydario Exp $
+
+This code was originally developed as a Senior Thesis by Michael
+Cornwell at the Concurrent Systems Laboratory (now part of the Storage
+Systems Research Center), Jack Baskin School of Engineering, University
+of California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+
+This package is meant to be an up-to-date replacement for the
+ucsc-smartsuite and smartsuite packages, and is derived from that code.
+
+Maintainers / Developers:
+
+Bruce Allen            <smartmontools-support@lists.sourceforge.net>
+Erik Inge Bolsø                <knan@mo.himolde.no>
+Stanislav Brabec       <sbrabec@suse.cz>
+Peter Cassidy          <pcassidy@mac.com>
+Casper Dik             <casper@holland.sun.com>
+Christian Franke       <franke@computer.org>
+Guilhem Frézou         <guilhem.frezou@catii.fr>
+Douglas Gilbert                <dougg@torque.net>
+Guido Guenther         <agx@sigxcpu.org>
+Geoff Keating          <geoffk@geoffk.org>
+Dr. David Kirkby       <drkirkby@ntlworld.com>
+Kai Mäkisara           <kai.makisara@kolumbus.fi>
+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>
+David Snyder            <dasnyderx@yahoo.com>
+Sergey Svishchev       <svs@ropnet.ru>
+Phil Williams          <phil@subbacultcha.demon.co.uk>
+Richard Zybert         <richard.zybert@zybert.co.uk>
+Yuri Dario             <mc6530@mclink.it>
diff --git a/CHANGELOG b/CHANGELOG
new file mode 100644 (file)
index 0000000..7857883
--- /dev/null
+++ b/CHANGELOG
@@ -0,0 +1,2138 @@
+CHANGELOG for smartmontools
+
+$Id: CHANGELOG,v 1.537 2006/04/12 16:11:44 ballen4705 Exp $
+
+The most recent version of this file is:
+http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/CHANGELOG?sortby=date&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
+[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
+
+NOTES FOR FUTURE RELEASES: see TODO file.
+
+<DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
+
+smartmontools 5.36 Stable Release
+
+  [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.
+
+smartmontools 5.34 Stable Release [NOTE: never officially released]
+
+  [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.
+
+smartmontools 5.33 Experimental Release
+
+  [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.
+
+  [CF] Added make targets to build formatted man pages (htmlman, txtman),
+       Windows distribution (dist-win32) and MSVC6 config.h (config-vc6).
+
+  [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.
+
+smartmontools 5.32
+
+  [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.
+
+smartmontools 5.31
+
+  [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'
+
+smartmontools 5.30
+
+  [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.
+
+smartmontools 5.29 (note: there was NO 5.28 release)
+
+  [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.
+
+smartmontools 5.27
+
+  [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 IDENTIFY 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.
+
+smartmontools 5.26
+
+  [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)
+
+smartmontools 5.25
+
+Note: there was no '5.24' release. From this point on, even numbered
+releases will be 'stable' ones and odd numbered releases will be
+unstable/testing/development ones.
+
+  [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.
+
+smartmontools 5.23
+
+  [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
+
+smartmontools 5.22
+
+  [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.
+
+smartmontools 5.21
+
+  [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.
+
+smartmontools 5.20
+
+  [GG] Fixed broken Makefile.am (zero length smartd.conf.5
+       was being created), fix broken uninstall/distcheck targets
+
+  [FM] Improved Slackware init script added to /etc/smartd.initd
+
+smartmontools 5.19 [NOTE CHANGE OF RELEASE NUMBERING]
+
+  [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.
+
+smartmontools 5.1-18
+
+  [BA] smartd: fixed serious bug - Attributes not monitored unless
+       user told smartd to ignore at least one of them!
+
+smartmontools 5.1-17
+
+  [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.
+
+smartmontools-5.1-16
+
+  [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.
+
+smartmontools-5.1-15
+
+  [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.
+
+smartmontools-5.1-14
+
+  [BA] smartctl: added column to -A output to show if Attributes are
+       updated only during off-line testing or also during normal
+       operation.
+
+smartmontools-5.1-13
+
+  [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,GF] Added mods submitted by [GF] 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
+
+smartmontools-5.1-12
+
+  [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 page 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).
+
+smartmontools-5.1-11
+
+  [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.
+
+smartmontools-5.1-10
+
+  [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.
+
+smartmontools-5.1-9
+  
+  [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...
+
+smartmontools-5.1-7
+
+  [BA] smartd: Created a subdirectory examplescripts/ of source
+       directory that contains executable scripts for the -M exec PATH
+       Directive of smartd.
+
+smartmontools-5.1-5
+
+  [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.
+
+smartmontools-5.1-4
+
+  [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.
+
+smartmontools-5.1-3
+
+  [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"
+
+smartmontools-5.1.1
+
+  [BA] As requested, local time/date now printed by smartctl -i
+
+  [PW] Added "help" argument to -v for smartctl
+
+  [PW] Added -D, --showdirectives option to smartd
+
+  [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
+
+START OF SMARTMONTOOLS 5.1 series
+
+smartmontools-5.0.50
+
+  [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.
+
+smartmontools-5.0.49
+
+  [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.
+
+smartmontools-5.0.48
+
+  [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.
+
+smartmontools-5.0.46
+
+  [BA] 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.
+
+  [BA] 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.
+
+  [EB] 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.
+
+  [BA] smartd: added Directive -m for sending test email and
+  for modifying email reminder behavior.  Updated manual, and sample
+  configuration file to illustrate & explain this.
+
+  [BA] smartd: increased size of a continued smartd.conf line to
+  1023 characters.
+
+  [BA] Simplified Directive parsers and improved warning/error
+  messages.
+
+smartmontools-5.0.45
+
+  [EB] Fixed bug in smartd where testunitready logic inverted
+  prevented functioning on scsi devices.
+  The bug in question only affects smartd users with scsi devices.
+  To see if your version of smartd has the testunitready() bug, do
+     smartd -V
+  If the version of the module smartd.c in a line like:
+     Module: smartd.c      revision: 1.66   date: 2002/11/17
+  has a revision greater than or equal to 1.30, and less than or equal to
+  1.64, then your version of the code has this problem.
+  This problem affected releases starting with RELEASE_5_0_16 up to and
+  including RELEASE_5_0_43.
+
+  [BA] Added testunitnotready to smartctl for symmetry with smartd.
+
+  [SB] added Czech descriptions to .spec file
+  [SB]  corrected comment in smartd.conf example
+
+  [BA] 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.
+
+  NOTE: All changes made prior to this point were done by Bruce Allen
+  [BA] although several of them had been suggested by earlier postings
+  by Stanislav Brabec [SB].
+
+smartmontools-5.0.43
+
+  Changed Temperature_Centigrade to Temperature_Celsius.
+  The term "Centigrade" ceased to exist in 1948.  (c.f
+  http://www.bartleby.com/64/C004/016.html).
+
+smartmontools-5.0.42
+
+  Modified SCSI device check to also send warning emails if
+  requested in directives file.
+
+  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.
+
+smartmontools-5.0.38
+
+  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.
+
+  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.
+  Removed charset encodings from smartmontools.spec file for non-English
+  fields.
+
+smartmontools-5.0.32
+
+  Added manual page smartd.conf.5 for configuration file.
+
+  smartctl: Missing ANSI prototype in failuretest(); fixed.
+
+  smartctl: Checksum warnings now printed on stdout, or are silent,
+  depending upon -q and -Q settings.
+
+smartmontools-5.0.31
+
+  Changed Makefile so that the -V option does not reflect file state
+  before commit!
+
+  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
+
+  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.
+
+smartmontools-5.0.30
+  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
+
+smartmontools-5.0.29
+  Modified .spec file and Makefile to make them more compliant with
+  the "right" way of doing things.
+
+smartmontools-5.0.26
+  Fixed typesetting error in man page smartd.8
+
+  Removed redundant variable (harmless) from smartd.c
+
+smartmontools-5.0.25
+
+  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.
+
+smartmontools-5.0.24
+
+    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/cgi-bin/gnatsweb.pl?database=gcc&cmd=query
+    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.
+
+smartmontools-5.0.23
+
+    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
+    appropriate.]
+
+smartmontools-5.0.22
+
+    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.
+
+smartmontools-5.0.21
+
+    Eliminated some global variables out of header files and other
+    minor cleanup of smartd.
+
+smartmontools-5.0.20
+
+    Did some revision of the man page for smartd and made the usage
+    messages for Directives 100% consistent.
+
+smartmontools-5.0-19
+
+    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?]
+
+smartmontools-5.0-18
+
+    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.
+
+smartmontools-5.0-17
+
+    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.
+
+smartmontools-5.0-16
+
+    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.
+
+    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
+
+smartmontools-5.0-11
+
+    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.
+
+
+smartmontools-5.0-10
+    Removed extraneous space before printing in some error messages
+    Fixed additional typos in documentation
+    Fixed some character buffers that were too short for their contents.
+
+smartmontools-5.0-9
+
+    Put project home path into all source files near the top
+    Corrected typos in the documentation
+    Modified Makefile so that Mandrake Cooker won't increment version number
+    (unless they happen to be working on my machine, which I doubt!)
+
+smartmontools-5.0-8:
+
+    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
+
+    changed argv parsing to use getops -- elminate buffer overflow
+    vulnerability
+
+    expanded and corrected documentation
+
+    fixed problem with smartd.  It did not actually call
+    ataSmartEnable()!  Since the argument was left out, the test
+    always suceeded because it evaluated to a pointer to the function.
+
+    smartd: closed open file descriptors if device does not support
+    smart. Note: this still needs to be fixed for SCSI devices
+
+
+smartmontools-5.0-0  STARTED with smartsuite-2.1-2
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..d60c31a
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..c1ca8ca
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,637 @@
+Smartmontools installation instructions
+=======================================
+
+$Id: INSTALL,v 1.62 2005/10/22 17:11:39 chrfranke Exp $
+
+Please also see the smartmontools home page:
+http://smartmontools.sourceforge.net/
+
+Table of contents:
+
+[1] System requirements
+[2] Installing from CVS
+[3] Installing from source tarball
+[4] Guidelines for different Linux distributions
+[5] Guidelines for FreeBSD
+[6] Guidelines for Darwin
+[7] Guidelines for NetBSD
+[8] Guidelines for Solaris
+[9] Guidelines for Cygwin
+[10] Guidelines for Windows
+[11] Guidelines for OS/2, eComStation
+[12] Guidelines for OpenBSD
+[13] Comments
+[14] Detailed description of ./configure options
+
+[1] System requirements
+=======================
+
+    A) Linux
+
+    Any Linux distribution will support smartmontools if it has a
+    kernel version greater than or equal to 2.2.14. So any recent
+    Linux distribution should support smartmontools.
+
+    There are two parts of smartmontools that may require a patched or
+    nonstandard kernel:
+
+    (1) To get the ATA RETURN SMART STATUS command, the kernel needs
+    to support the HDIO_DRIVE_TASK ioctl().
+
+    (2) To run Selective Self-tests, the kernel needs to support the
+    HDIO_DRIVE_TASKFILE ioctl().
+
+    If your kernel does not support one or both of these ioctls, then
+    smartmontools will "mostly" work.  The things that don't work will
+    give you harmless warning messages.
+
+    Although "not officially supported" by the developers, smartmontools
+    has also been successfully build and run on a legacy Linux system
+    with kernel 2.0.33 and libc.so.5. On such systems, the restrictions
+    above apply.
+
+    For item (1) above, any 2.4 or 2.6 series kernel will provide
+    HDIO_DRIVE_TASK support.  Some 2.2.20 and later kernels also
+    provide this support IF they're properly patched and
+    configured. [Andre Hedrick's IDE patches may be found at
+    http://www.funet.fi/pub/linux/kernel/people/hedrick/ide-2.2.20/ or
+    are available from your local kernel.org mirror.  They are not
+    updated for 2.2.21 or later, and may contain a few bugs.].
+    If the configuration option CONFIG_IDE_TASK_IOCTL
+    exists in your 2.2.X kernel source code tree, then your 2.2.X
+    kernel will probably support this ioctl. [Note that this kernel
+    configuration option does NOT need to be enabled. Its presence
+    merely indicates that the required HDIO_DRIVE_TASK ioctl() is
+    supported.]
+
+    For item (2) above, your kernel must be configured with the kernel
+    configuration option CONFIG_IDE_TASKFILE_IO enabled.  This
+    configuration option is present in all 2.4 and 2.6 series
+    kernels. Some 2.2.20 and later kernels also provide this support
+    IF they're properly patched and configured as described above.
+
+    Please see FAQ section of the URL above for additional details.
+
+    If you are using 3ware controllers, for full functionality you
+    must either use version 1.02.00.037 or greater of the 3w-xxxx
+    driver, or patch earlier 3ware 3w-xxxx drivers.  See
+    http://smartmontools.sourceforge.net/3w-xxxx.txt
+    for the patch.  The version 1.02.00.037 3w-xxxx.c driver was
+    incorporated into kernel 2.4.23-bk2 on 3 December 2003 and into
+    kernel 2.6.0-test5-bk11 on 23 September 2003.
+
+    B) FreeBSD
+
+    For FreeBSD support, a 5-current kernel that includes ATAng is
+    required in order to support ATA drives.  Even current versions of
+    ATAng will not support 100% operation, as the SMART status can not
+    be reliably retrieved.  There is patch pending approval of the
+    ATAng driver maintainer that will address this issue.
+
+    C) Solaris
+
+    The SCSI code has been tested on a variety of Solaris 8 and 9
+    systems.  ATA/IDE code only works on SPARC platform.  All tested
+    kernels worked correctly.
+
+    D) NetBSD/OpenBSD
+
+    The code was tested on a 1.6ZG (i.e., 1.6-current) system. It should
+    also function under 1.6.1 and later releases (unverified).  Currently
+    it doesn't support ATA devices on 3ware RAID controllers.
+
+    E) Cygwin
+
+    The code was tested on Cygwin 1.5.7, 1.5.11 and 1.5.18. It should also
+    work on other recent releases.
+
+    Release 1.5.15 or later is recommended for Cygwin smartd. Older versions
+    do not provide syslogd support.
+
+    Both Cygwin and Windows versions of smartmontools share the same code
+    to access the IDE/ATA or SCSI devices. The information in the "Windows"
+    section below also applies to the Cygwin version.
+
+    F) Windows
+
+    The code was tested on Windows 98SE, NT4(SP5,SP6), 2000(SP4) and
+    XP(no SP,SP1a,SP2). It should also work on Windows 95(OSR2), 98,
+    ME and 2003.
+
+    On 9x/ME, only standard (legacy) IDE/ATA devices 0-3 are supported.
+    The driver SMARTVSD.VXD must be present in WINDOWS\SYSTEM\IOSUBSYS
+    to get loaded at Windows startup. The default location in a new
+    installation of some versions of Windows is the WINDOWS\SYSTEM folder.
+    In this case, move SMARTVSD.VXD to WINDOWS\SYSTEM\IOSUBSYS and reboot
+    (http://support.microsoft.com/default.aspx?scid=kb;en-us;265854).
+
+    SMARTVSD.VXD relies on the standard IDE port driver ESDI_506.PDR.
+    If the system uses a vendor specific driver, access of SMART data
+    is not possible on 9x/ME. This is the case if e.g. the optional
+    "IDE miniport driver" is installed on a system with VIA chipset.
+
+    On NT4/2000/XP, also other ATA or SATA devices are supported if
+    the device driver implements the SMART IOCTL.
+
+    The IDE/ATA read log command (smartctl -l, --log, -a, --all) is
+    not supported by the SMART IOCTL of NT4/2000/XP. Undocumented
+    and possibly buggy system calls are used for this purpose,
+    see WARNINGS file for details.
+
+    SCSI devices are supported on all versions of Windows. An installed
+    ASPI interface (WNASPI32.DLL) is required to access SCSI devices.
+    The code was tested with Adaptec Windows ASPI drivers 4.71.2.
+    (http://www.adaptec.com/worldwide/support/drivers_by_product.jsp?cat=/Product/ASPI-4.70)
+
+    G) MacOS/Darwin
+
+    The code was tested on MacOS 10.3.4.  It should work from 10.3
+    forwards.  It doesn't support 10.2.
+
+    There's an important limitation (see WARNINGS); due to bugs in
+    the libraries used, you cannot run a short test or switch SMART
+    support off on a drive; if you try, you will just run an extended
+    test or switch SMART support on.  So don't panic when your "short"
+    test seems to be taking hours.
+
+    It's also not possible at present to control when the offline
+    routine runs. If your drive doesn't have it running automatically by
+    default, you can't run it at all.
+
+    SCSI devices are not currently supported.  Detecting the power
+    status of a drive is also not currently supported.
+
+    To summarize this, from another point of view, the things that
+    are not supported fall into two categories:
+
+   * Can't be implemented easily without more kernel-level support,
+     so far as I know:
+     - running immediate offline, conveyance, or selective tests
+     - running any test in captive mode
+     - aborting tests
+     - switching automatic offline testing on or off
+     - support for SCSI
+     - checking the power mode [-n Directive of smartd] (this is not
+       completely impossible, but not by using a documented API)
+
+   * Are implemented, but don't work due to OS bugs:
+     - switching off SMART (switching *on* works fine)
+     - switching off auto-save (but why would you want to?)
+     - running the short test (that leaves you with only the extended test)
+
+    The last set have been filed in Apple's bug tracking system and
+    hopefully will be fixed in the next major version of Mac OS.
+
+    However, some things do work well.  For ATA devices, all the
+    informational output is available, unless you want something that only
+    an offline test updates.
+
+    H) OS/2, eComStation
+
+    The code was tested on eComStation 1.1, but it should work on all versions
+    of OS/2.
+    Innotek LibC 0.5 runtime is required.
+    Currently only ATA disks are supported, SCSI support will be added.
+
+
+[2] Installing from CVS
+=======================
+    Get the sources from the CVS repository:
+    cvs -d :pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools login
+    cvs -d :pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools co sm5
+    (when prompted for a password, just press Enter)
+
+    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
+    to run it.  You can get these here:
+    http://www.gnu.org/directory/GNU/autoconf.html
+    http://www.gnu.org/directory/GNU/automake.html
+
+[3] Installing from the source tarball
+======================================
+
+    If you are NOT installing from CVS, then unpack the tarball: 
+    tar zxvf smartmontools-5.VERSION.tar.gz
+
+    Then:
+    ./configure
+    make
+    make install (you may need to be root to do this)
+
+    As shown (with no options to ./configure) this defaults to the
+    following set of installation directories:   
+    --prefix=/usr/local
+    --sbindir=/usr/local/sbin
+    --sysconfdir=/usr/local/etc
+    --mandir=/usr/local/share/man
+    --with-docdir=/usr/local/share/doc/smartmontools-VERSION
+    --with-initscriptdir=/usr/local/etc/rc.d/init.d
+    --disable-sample
+
+    These will usually not overwrite existing "distribution" installations on
+    Linux Systems since the FHS reserves this area for use by the system
+    administrator.
+
+    For different installation locations or distributions, simply add
+    arguments to ./configure as shown in [4] below.
+
+    If you wish to alter the default C compiler flags, set an
+    environment variable CFLAGS='your options' before doing
+    ./configure, or else do:
+    make CFLAGS='your options'
+
+[4] Guidelines for different Linux distributions
+================================================
+
+Note: Please send corrections/additions to:
+smartmontools-support@lists.sourceforge.net
+
+Debian:
+  If you don't want to overwrite any distribution package, use:
+  ./configure
+
+Filesystem Hierarchy Standard (FHS, http://www.pathname.com/fhs/):
+  ./configure --sbindir=/usr/local/sbin                               \
+              --sysconfdir=/usr/local/etc                             \
+              --mandir=/usr/local/man                                 \
+              --with-initscriptdir=/usr/local/etc/rc.d/init.d         \
+              --with-docdir=/usr/local/share/doc/smartmontools-VERSION
+
+Red Hat:
+  ./configure --sbindir=/usr/sbin                               \
+             --sysconfdir=/etc                                 \
+             --mandir=/usr/share/man                           \
+             --with-initscriptdir=/etc/rc.d/init.d             \
+             --with-docdir=/usr/share/doc/smartmontools-VERSION
+
+Slackware:
+  If you don't want to overwrite any "distribution" package, use:
+  ./configure
+
+  Otherwise use:
+  ./configure --sbindir=/usr/sbin                               \
+              --sysconfdir=/etc                                 \
+              --mandir=/usr/share/man                           \
+              --with-initscriptdir=/etc/rc.d                    \
+              --with-docdir=/usr/share/doc/smartmontools-VERSION
+
+  And
+  removepkg smartmontools smartsuite (only root can do this)
+  before make install
+
+  The init script works on Slackware. You just have to add an entry like
+  the following in /etc/rc.d/rc.M or /etc/rc.d/rc.local:
+
+  if [ -x /etc/rc.d/smartd ]; then
+    . /etc/rc.d/smartd start
+  fi
+
+  To disable it:
+  chmod 644 /etc/rc.d/smartd
+
+  For a list of options:
+  /etc/rc.d/smartd
+
+SuSE:
+  ./configure --sbindir=/usr/sbin                                        \
+              --sysconfdir=/etc                                          \
+              --mandir=/usr/share/man                                    \
+              --with-initscriptdir=/etc/init.d                           \
+              --with-docdir=/usr/share/doc/packages/smartmontools-VERSION
+
+[5] Guidelines for FreeBSD
+==========================
+  To match the way it will installed when it becomes available as a PORT, use
+  the following:
+
+  ./configure --prefix=/usr/local                                      \
+              --with-initscriptdir=/usr/local/etc/rc.d/                \
+              --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:
+       /usr/local/etc/smartd.conf.sample
+       /usr/local/etc/rc.d/smartd.sample
+
+
+[6] Guidelines for Darwin
+=========================
+  ./configure --with-initscriptdir=/Library/StartupItems
+
+
+[7] Guidelines for NetBSD/OpenBSD
+=================================
+  ./configure --prefix=/usr/pkg                                       \
+             --with-docdir=/usr/pkg/share/doc/smartmontools
+
+  On OpenBSD, it is important that you use GNU make (gmake from 
+  /usr/ports/devel/gmake) to build smartmontools, as the BSD make doesn't
+  know how to make the manpages.
+
+[8] Guidelines for Solaris
+==========================
+
+    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:
+
+    ./configure [args]
+    make
+
+    To compile with Sun cc:
+
+    setenv CC cc  [csh syntax], or
+    CC=cc         [sh syntax]
+    ./configure [args]
+    make
+
+    The correct arguments [args] to configure are:
+     --sbindir=/usr/sbin                                \
+     --sysconfdir=/etc                                  \
+     --mandir=/usr/share/man                            \
+     --with-docdir=/usr/share/doc/smartmontools-VERSION \
+     --with-initscriptdir=/etc/init.d
+
+    To start the script automatically on bootup, create hardlinks that
+    indicate when to start/stop in:
+                   /etc/rc[S0123].d/
+    pointing to /etc/init.d/smartd. Create:
+           K<knum>smartd in rcS.d, rc0.d, rc1.d, rc2.d
+           S<snum>smartd in rc3.d
+    where <knum> is related to <snum> such that the higher snum is the
+    lower knum must be.
+
+    On usual configuration, '95' would be suitable for <snum> and '05'
+    for <knum> respectively.  If you choose these value, you can
+    create hardlinks by:
+
+    cd /etc
+    sh -c 'for n in S 0 1 2; do ln init.d/smartd rc$n.d/K05smartd; done'
+    sh -c 'for n in 3      ; do ln init.d/smartd rc$n.d/S95smartd; done'
+
+[9] Guidelines for Cygwin
+=========================
+
+Same as Red Hat:
+  ./configure --prefix=/usr                 \
+              --sysconfdir=/etc             \
+              --mandir='${prefix}/share/man'
+
+  OR EQUIVALENTLY
+  ./configure --sbindir=/usr/sbin                               \
+              --sysconfdir=/etc                                 \
+              --mandir=/usr/share/man                           \
+              --with-initscriptdir=/etc/rc.d/init.d             \
+              --with-docdir=/usr/share/doc/smartmontools-VERSION
+
+  Using DOS text file type as default for the working directories ("textmode"
+  mount option) is not recommended. Building the binaries and man pages using
+  "make" is possible, but "make dist" and related targets work only with UNIX
+  file type ("binmode" mount option) set. The "autogen.sh" script prints a
+  warning if DOS type is selected.
+
+[10] Guidelines for Windows
+==========================
+
+To compile the Windows release with MinGW, use the following on Cygwin:
+
+  ./configure --build=mingw32
+  make
+
+  Instead of using "make install", copy the .exe files into
+  some directory in the PATH.
+
+To build the Windows binary distribution, use:
+
+  make dist-win32
+  
+  This builds the distribution in directory
+
+  ./smartmontools-VERSION.win32/
+
+  and packs it into
+
+  ./smartmontools-VERSION.win32.zip
+
+  Additional make targets are distdir-win32 to build the directory
+  only and cleandist-win32 for cleanup.
+
+  The binary distribution includes all documentation files converted
+  to DOS text file format and *.html and *.txt preformatted man pages.
+  The tools unix2dos.exe (package cygutils) and zip.exe (package zip
+  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:
+
+  make config-vc6
+
+  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.
+
+
+[11] Guidelines for OS/2, eComStation
+=====================================
+
+To compile the OS/2 code, please run
+
+  ./os_os2/configure.os2
+  make
+  make install
+
+[12] Guidelines for OpenBSD
+==========================
+  To match the way it will installed when it becomes available as a PORT, use
+  the following:
+
+  ./configure --prefix=/usr/local                                      \
+             --sysconfdir=/etc
+              --with-initscriptdir=/usr/local/share/doc/smartmontools-VERSION \
+              --with-docdir=/usr/local/share/doc/smartmontools-VERSION \
+             --enable-sample
+
+  It is important that you use GNU make (gmake from /usr/ports/devel/gmake)
+  to build smartmontools, as the default OpenBSD make doesn't know how to build
+  the man pages.
+
+  NOTE1: --with-initscriptdir installs a SystemV startup script.  It really
+  should be --without-initscriptdir, but the Makefile code is incorrect and
+  trys to install the initscript (smartd) to /no.  So, an interim fix it to
+  set the initscript dir to the doc dir.
+
+  NOTE2: --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:
+       /usr/local/etc/smartd.conf.sample
+       /usr/local/etc/rc.d/smartd.sample
+
+[13] Comments
+============
+
+To compile from another directory, you can replace the step
+  ./configure [options]
+by the following:
+  mkdir objdir
+  cd objdir
+  ../configure [options]
+
+To install to another destination (used mainly by package maintainers,
+or to examine the package contents without risk of modifying any
+system files) you can replace the step:
+  make install
+with:
+  make DESTDIR=/home/myself/smartmontools-package install
+
+Use a full path. Paths like ~/smartmontools-package may not work.
+
+After installing smartmontools, you can read the man pages, and try
+out the commands:
+   
+man smartd.conf
+man smartctl
+man smartd
+
+/usr/sbin/smartctl -s on -o on -S on /dev/hda (only root can do this)
+/usr/sbin/smartctl -a /dev/hda (only root can do this)
+
+Note that the default location for the manual pages are
+/usr/share/man/man5 and /usr/share/man/man8.  If "man" doesn't find
+them, you may need to add /usr/share/man to your MANPATH environment
+variable.
+
+Source and binary RPM packages are available at
+http://sourceforge.net/project/showfiles.php?group_id=64297
+
+Refer to http://smartmontools.sourceforge.net/index.html#howtodownload
+for any additional download and installation instructions.
+
+The following files are installed if ./configure has no options:
+
+/usr/local/sbin/smartd                                  [Executable daemon]
+/usr/local/sbin/smartctl                                [Executable command-line utility]
+/usr/local/etc/smartd.conf                              [Configuration file for smartd daemon]
+/usr/local/etc/rc.d/init.d/smartd                       [Init/Startup script for smartd]
+/usr/local/share/man/man5/smartd.conf.5                 [Manual page]
+/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/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]
+/usr/local/share/doc/smartmontools-5.X/README           [Overview]
+/usr/local/share/doc/smartmontools-5.X/TODO             [Things that need to be done/fixed]
+/usr/local/share/doc/smartmontools-5.X/WARNINGS         [Systems where lockups or other serious problems were reported]
+/usr/local/share/doc/smartmontools-5.X/smartd.conf      [Example configuration file for smartd]
+/usr/local/share/doc/smartmontools-5.X/examplescripts   [Executable scripts for -M exec of smartd.conf (4 files)]
+
+The commands:
+
+make htmlman
+make txtman
+
+may be used to build .html and .txt preformatted man pages.
+These are used by the dist-win32 make target to build the Windows
+distribution.
+The commands also work on other operating system configurations
+if suitable versions of man2html, groff and grotty are installed.
+On systems without man2html, the following command should work
+if groff is available:
+
+make MAN2HTML='groff -man -Thtml' htmlman
+
+
+[14] Detailed description of arguments to configure command
+===========================================================
+
+When you type:
+./configure [options]
+there are six particularly important variables that affect where the
+smartmontools software is installed.  The variables are listed here,
+with their default values in square brackets, and the quantities that
+they affect described following that.  This is a very wide table: please read
+it in a wide window.
+
+OPTIONS              DEFAULT                                      AFFECTS
+-------              -------                                      -------
+--prefix             /usr/local                                   Please see below
+--sbindir            ${prefix}/sbin                               Directory for smartd/smartctl executables;
+                                                                  Contents of smartd/smartctl man pages
+--mandir             ${prefix}/share/man                          Directory for smartctl/smartd/smartd.conf man pages
+--sysconfdir         ${prefix}/etc                                Directory for smartd.conf;
+                                                                  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
+
+Here's an example:
+If you set --prefix=/home/joe and none of the other four
+variables then the different directories that are used would be:
+--sbindir             /home/joe/sbin
+--mandir              /home/joe/share/man
+--sysconfdir          /home/joe/etc
+--with-initscriptdir  /home/joe/etc/init.d/rc.d
+--with-docdir        /home/joe/doc/smartmontools-5.X
+
+This is useful for test installs in a harmless subdirectory somewhere.
+
+Here are the four possible cases for the four variables above:
+
+Case 1:
+--prefix not set
+--variable not set
+===> VARIABLE gets default value above
+
+Case 2:
+--prefix set
+--variable not set
+===> VARIABLE gets PREFIX/ prepended to default value above
+
+Case 3:
+--prefix not set
+--variable set
+===> VARIABLE gets value that is set
+
+Case 4:
+--prefix is set
+--variable is set
+===> PREFIX is IGNORED, VARIABLE gets value that is set
+
+
+Here are the differences with and without --enable-sample, assuming
+no other options specified (see above for details)
+
+Case 1:
+--enable-sample provided
+==> Files installed are:
+       /usr/local/etc/smartd.conf.sample
+       /usr/local/etc/rc.d/init.d/smartd.sample
+
+Case 2:
+--disable-sample provided or parameter left out
+==> Files installed are:
+       /usr/local/etc/smartd.conf
+       /usr/local/etc/rc.d/init.d/smartd
+
+Additional information about using configure can be found here:
+http://www.gnu.org/software/autoconf/manual/autoconf-2.57/html_mono/autoconf.html#SEC139
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..9bc740d
--- /dev/null
@@ -0,0 +1,436 @@
+## Process this file with automake to produce Makefile.in
+#
+# $Id: Makefile.am,v 1.74 2005/11/29 18:22:51 chrfranke Exp $
+#
+
+@SET_MAKE@
+
+AM_CPPFLAGS = -DSMARTMONTOOLS_SYSCONFDIR=\"$(sysconfdir)\"
+
+sbin_PROGRAMS = smartd         \
+               smartctl
+
+smartd_SOURCES =  smartd.c      \
+                  smartd.h      \
+                  atacmdnames.c        \
+                  atacmdnames.h        \
+                  atacmds.c    \
+                  atacmds.h    \
+                  ataprint.c   \
+                  ataprint.h   \
+                  extern.h      \
+                  int64.h       \
+                  knowndrives.c        \
+                  knowndrives.h        \
+                  scsicmds.c   \
+                  scsicmds.h   \
+                  scsiprint.c  \
+                  scsiprint.h  \
+                  utility.c    \
+                  utility.h
+
+smartd_LDADD = @os_deps@ @os_libs@
+smartd_DEPENDENCIES = @os_deps@
+EXTRA_smartd_SOURCES = os_darwin.c      \
+                      os_darwin.h      \
+                      os_linux.c       \
+                       os_linux.h       \
+                      os_freebsd.c     \
+                      os_freebsd.h     \
+                      os_netbsd.c      \
+                      os_netbsd.h      \
+                      os_openbsd.c     \
+                      os_openbsd.h     \
+                      os_solaris.c     \
+                      os_solaris.h     \
+                      os_solaris_ata.s \
+                      os_win32.c       \
+                      os_generic.c     \
+                      os_generic.h
+
+
+if OS_WIN32_MINGW
+
+smartd_SOURCES +=                         \
+                posix/regex.h             \
+                posix/regex.c             \
+                os_win32/daemon_win32.h   \
+                os_win32/daemon_win32.c   \
+                os_win32/hostname_win32.h \
+                os_win32/hostname_win32.c \
+                os_win32/syslog.h         \
+                os_win32/syslog_win32.c
+
+# Included by regex.c:
+EXTRA_smartd_SOURCES +=                   \
+                posix/regcomp.c           \
+                posix/regexec.c           \
+                posix/regex_internal.c    \
+                posix/regex_internal.h
+
+endif
+
+smartctl_SOURCES= smartctl.c    \
+                  smartctl.h    \
+                  atacmdnames.c        \
+                  atacmdnames.h        \
+                  atacmds.c    \
+                  atacmds.h    \
+                  ataprint.c   \
+                  ataprint.h   \
+                  extern.h      \
+                  int64.h       \
+                  knowndrives.c        \
+                  knowndrives.h        \
+                  scsicmds.c   \
+                  scsicmds.h   \
+                  scsiprint.c  \
+                  scsiprint.h  \
+                  utility.c    \
+                  utility.h
+
+smartctl_LDADD = @os_deps@ @os_libs@
+smartctl_DEPENDENCIES = @os_deps@
+EXTRA_smartctl_SOURCES = os_linux.c \
+                       os_linux.h   \
+                      os_freebsd.c \
+                      os_freebsd.h \
+                      os_netbsd.c  \
+                      os_netbsd.h  \
+                      os_openbsd.c \
+                      os_openbsd.h \
+                      os_solaris.c \
+                      os_solaris.h \
+                      os_win32.c   \
+                      os_generic.c \
+                      os_generic.h
+
+if OS_WIN32_MINGW
+
+smartctl_SOURCES +=                    \
+                posix/regex.h          \
+                posix/regex.c          \
+                os_win32/syslog.h
+
+# Included by regex.c:
+EXTRA_smartctl_SOURCES +=              \
+                posix/regcomp.c        \
+                posix/regexec.c        \
+                posix/regex_internal.c \
+                posix/regex_internal.h
+
+endif
+
+if OS_SOLARIS
+# 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.
+extra_MANS =      smartd.conf.4 \
+                  smartctl.1m   \
+                  smartd.1m
+install-man: $(extra_MANS)
+       @$(NORMAL_INSTALL)
+       $(mkinstalldirs) $(DESTDIR)$(mandir)/man4
+       $(mkinstalldirs) $(DESTDIR)$(mandir)/man1m
+       for i in $(extra_MANS); do \
+         if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+         else file=$$i; fi; \
+         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+         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)$(mandir)/man$$ext/$$inst"; \
+         $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$$ext/$$inst; \
+       done
+uninstall-man:
+       @$(NORMAL_UNINSTALL)
+       for i in $(extra_MANS); do \
+         if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+         else file=$$i; fi; \
+         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+         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)$(mandir)/man$$ext/$$inst"; \
+         rm -f $(DESTDIR)$(mandir)/man$$ext/$$inst; \
+       done
+%.1m: %.8
+       awk '/^.TH/ {$$3="1m"} {print}' < $< | \
+       sed -e 's/smartd\.conf\(.*\)(5)/smartd.conf\1(4)/g' \
+            -e 's/syslog\.conf\(.*\)(5)/syslog.conf\1(4)/g' \
+           -e 's/smartctl\(.*\)(8)/smartctl\1(1m)/g' \
+           -e 's/syslogd\(.*\)(8)/syslogd\1(1m)/g' \
+            -e 's|/var/log/messages|/var/adm/messages|g' \
+           -e 's/smartd\(.*\)(8)/smartd\1(1m)/g' > $@
+%.4: %.5
+       awk '/^.TH/ {$$3="4"}  {print}' < $< | \
+       sed -e 's/smartd\.conf\(.*\)(5)/smartd.conf\1(4)/g' \
+            -e 's/syslog\.conf\(.*\)(5)/syslog.conf\1(4)/g' \
+           -e 's/smartctl\(.*\)(8)/smartdctl\1(1m)/g' \
+           -e 's/syslogd\(.*\)(8)/syslogd\1(1m)/g' \
+            -e 's|/var/log/messages|/var/adm/messages|g' \
+           -e 's/smartd\(.*\)(8)/smartd\1(1m)/g' > $@
+else
+# For systems that adopts traditional manner
+man_MANS =        smartd.conf.5 \
+                  smartctl.8    \
+                  smartd.8
+endif
+
+docsdir=$(docdir)
+docs_DATA = AUTHORS     \
+            CHANGELOG   \
+            COPYING     \
+            INSTALL     \
+            NEWS        \
+            README      \
+            TODO        \
+            WARNINGS    \
+            smartd.conf
+
+sysconf_DATA = smartd.conf$(smartd_suffix)
+
+if SMARTD_SUFFIX
+smartd.conf$(smartd_suffix): smartd.conf
+       cp ${srcdir}/smartd.conf smartd.conf$(smartd_suffix)
+endif
+
+EXTRA_DIST = smartmontools.spec                    \
+             smartd.initd.in                       \
+             smartd.8.in                           \
+             smartctl.8.in                         \
+             smartd.conf.5.in                      \
+             smartd.conf                           \
+             autogen.sh                            \
+             os_darwin/SMART.in                    \
+             os_darwin/StartupParameters.plist     \
+             os_darwin/English_Localizable.strings \
+             $(docs_DATA)
+
+CLEANFILES = smartd.conf.5      \
+             smartd.conf.4      \
+             smartd.8           \
+             smartd.1m          \
+             smartd.8.html      \
+             smartd.8.txt       \
+             smartctl.8         \
+             smartctl.1m        \
+             smartctl.8.html    \
+             smartctl.8.txt     \
+             smartd.conf.5.html \
+             smartd.conf.5.txt  \
+             smartd.initd      \
+             SMART
+
+if SMARTD_SUFFIX
+CLEANFILES += smartd.conf$(smartd_suffix)
+endif
+
+
+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
+
+if OS_DARWIN
+initd_DATA = SMART                            \
+       os_darwin/StartupParameters.plist     \
+       os_darwin/English_Localizable.strings
+
+initd_install_name = SMART
+
+initd_DATA_install = install-initdDATA-darwin
+
+SMART : os_darwin/SMART.in
+       sed "s|/usr/sbin/|$(sbindir)/|" $< > $@
+
+install-initdDATA-darwin: $(initd_DATA)
+       $(mkinstalldirs) $(DESTDIR)$(initddir)
+       $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART
+       $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART/Resources
+       $(INSTALL_SCRIPT) $(top_builddir)/SMART $(DESTDIR)$(initddir)/SMART
+       $(INSTALL_DATA) $(srcdir)/os_darwin/StartupParameters.plist \
+           $(DESTDIR)$(initddir)/SMART/StartupParameters.plist
+       for i in English ; do \
+         RDIR=$(DESTDIR)$(initddir)/SMART/Resources/$${i}.lproj ; \
+         $(mkinstalldirs) $$RDIR ;\
+         $(INSTALL_DATA) $(srcdir)/os_darwin/$${i}_Localizable.strings \
+           $$RDIR/Localizable.strings ; \
+       done
+       @echo -e "\n\n####################################################################\n#"
+       @echo -e "#                       PLEASE READ THIS BOX!\n#"
+       @echo -e "#   To manually start the smartd daemon, run:\n#   ${initddir}/SMART/SMART start\n#"
+       @echo -e "#   To automatically start smartd on bootup, add the line:\n#   SMARTd=-YES-\n#   to /etc/hostconfig\n#"
+       @echo -e "#   smartd can now use a configuration file ${sysconfdir}/smartd.conf. Do:\n#   man smartd"
+       @echo -e "#   to learn about it. A sample configuration file can be found in:\n#   ${docdir}\n#"
+       @echo -e "####################################################################\n\n"
+
+else
+
+initd_DATA = smartd.initd
+
+smartd.initd: $(srcdir)/smartd.initd.in Makefile
+       sed "s|/usr/local/sbin/|$(sbindir)/|g" $(srcdir)/smartd.initd.in > $@
+
+initd_install_name = smartd$(smartd_suffix)
+
+initd_DATA_install = install-initdDATA-generic
+
+install-initdDATA-generic: $(initd_DATA)
+       $(mkinstalldirs) $(DESTDIR)$(initddir)
+       $(INSTALL_SCRIPT) $(top_builddir)/smartd.initd $(DESTDIR)$(initddir)/smartd$(smartd_suffix)
+       @echo -e "\n\n####################################################################\n#"
+       @echo -e "#                       PLEASE READ THIS BOX!\n#"
+       @echo -e "#   To manually start the smartd daemon, run:\n#   ${initddir}/smartd start\n#"
+       @echo -e "#   To automatically start smartd on bootup, run:\n#   /sbin/chkconfig --add smartd\n#"
+       @echo -e "#   smartd can now use a configuration file ${sysconfdir}/smartd.conf. Do:\n#   man smartd"
+       @echo -e "#   to learn about it. A sample configuration file can be found in:\n#   ${docdir}\n#"
+       @echo -e "####################################################################\n\n"
+
+endif
+
+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" > $@
+
+
+# Commands to convert man pages into .html and .txt
+# TODO: configure
+MAN2HTML = man2html
+#MAN2HTML = groff -man -Thtml
+MAN2TXT = groff -man -Tascii -P'-bcou'
+
+# Fix links in man2html output
+FIXHTML = sed 's,<A HREF="http://[-a-z/]*/man2html?\([1-8]\)+\(smart[cd][.a-z]*\)">,<A HREF="\2.\1.html">,g' \
+        | sed 's,<A HREF="http://[-a-z/]*/man2html">,<A HREF=".">,g' \
+        | sed 's,<A HREF="http://[-a-z/]*/man2html?[^"]*">\([^<]*\)</A>,\1,g' \
+        | sed 's,<A HREF="mailto:[^s][^m][^a][^"]*">\([^<]*\)</A>,\1,g'
+
+# Convert man pages into .html and .txt
+
+htmlman: smartctl.8.html smartd.8.html smartd.conf.5.html
+
+txtman:  smartctl.8.txt smartd.8.txt smartd.conf.5.txt
+
+%.5.html: %.5
+       $(MAN2HTML) $< | $(FIXHTML) > $@
+
+%.8.html: %.8
+       $(MAN2HTML) $< | $(FIXHTML) > $@
+
+%.5.txt: %.5
+       $(MAN2TXT) $< > $@
+
+%.8.txt: %.8
+       $(MAN2TXT) $< > $@
+
+
+
+if OS_WIN32_MINGW
+# Definitions for Windows distribution
+
+distdir_win32 = $(PACKAGE)-$(VERSION).win32
+distzip_win32 = $(PACKAGE)-$(VERSION).win32.zip
+
+exedir_win32 = $(distdir_win32)/bin
+docdir_win32 = $(distdir_win32)/doc
+
+FILES_WIN32 = $(exedir_win32)/smartctl.exe \
+              $(exedir_win32)/smartd.exe \
+              $(docdir_win32)/AUTHORS.txt \
+              $(docdir_win32)/CHANGELOG.txt \
+              $(docdir_win32)/COPYING.txt \
+              $(docdir_win32)/INSTALL.txt \
+              $(docdir_win32)/NEWS.txt \
+              $(docdir_win32)/README.txt \
+              $(docdir_win32)/TODO.txt \
+              $(docdir_win32)/WARNINGS.txt \
+              $(docdir_win32)/smartd.conf \
+              $(docdir_win32)/smartctl.8.html \
+              $(docdir_win32)/smartctl.8.txt \
+              $(docdir_win32)/smartd.8.html \
+              $(docdir_win32)/smartd.8.txt \
+              $(docdir_win32)/smartd.conf.5.html \
+              $(docdir_win32)/smartd.conf.5.txt
+
+CLEANFILES += $(FILES_WIN32) $(exedir_win32)/syslogevt.exe distdir.mkdir syslogevt.check
+
+# Textfile converter from cygutils
+UNIX2DOS = unix2dos -D
+
+# Build Windows distribution
+
+dist-win32: $(distzip_win32)
+
+distdir-win32: distdir.mkdir $(FILES_WIN32) syslogevt.check
+
+$(distzip_win32): distdir.mkdir $(FILES_WIN32) syslogevt.check
+       @rm -fv $(distzip_win32)
+       cd $(distdir_win32) && zip -9Dr ../$(distzip_win32) .
+
+cleandist-win32:
+       rm -rf $(distdir_win32) distdir.mkdir syslogevt.check
+
+distdir.mkdir:
+       @test -d $(exedir_win32) || mkdir -pv $(exedir_win32)
+       @test -d $(docdir_win32) || mkdir -pv $(docdir_win32)
+       touch $@
+
+syslogevt.check:
+       @if [ -f $(srcdir)/os_win32/syslogevt.exe ]; then \
+         cp -pv $(srcdir)/os_win32/syslogevt.exe $(exedir_win32)/syslogevt.exe; \
+        else echo "Warning: $(srcdir)/os_win32/syslogevt.exe missing."; fi
+       touch $@
+
+$(exedir_win32)/%.exe: %.exe
+       cp -p $< $@
+       strip -s $@
+       touch -r $< $@
+
+$(docdir_win32)/%: %
+       $(UNIX2DOS) < $< > $@
+       touch -r $< $@
+
+$(docdir_win32)/%.txt: $(srcdir)/%
+       $(UNIX2DOS) < $< > $@
+       touch -r $< $@
+
+$(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\|STRTOULL\|U*INT64_T\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */,' | \
+       sed 's,i.86-pc-mingw32,i686-pc-win32vc6,' > $@
+
+endif
+
+SUBDIRS= . examplescripts
diff --git a/Makefile.in b/Makefile.in
new file mode 100644 (file)
index 0000000..5d65d9f
--- /dev/null
@@ -0,0 +1,1311 @@
+# Makefile.in generated by automake 1.9.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+#
+# $Id: Makefile.am,v 1.74 2005/11/29 18:22:51 chrfranke Exp $
+#
+
+
+SOURCES = $(smartctl_SOURCES) $(EXTRA_smartctl_SOURCES) $(smartd_SOURCES) $(EXTRA_smartd_SOURCES)
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = .
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+sbin_PROGRAMS = smartd$(EXEEXT) smartctl$(EXEEXT)
+@OS_WIN32_MINGW_TRUE@am__append_1 = \
+@OS_WIN32_MINGW_TRUE@                posix/regex.h             \
+@OS_WIN32_MINGW_TRUE@                posix/regex.c             \
+@OS_WIN32_MINGW_TRUE@                os_win32/daemon_win32.h   \
+@OS_WIN32_MINGW_TRUE@                os_win32/daemon_win32.c   \
+@OS_WIN32_MINGW_TRUE@                os_win32/hostname_win32.h \
+@OS_WIN32_MINGW_TRUE@                os_win32/hostname_win32.c \
+@OS_WIN32_MINGW_TRUE@                os_win32/syslog.h         \
+@OS_WIN32_MINGW_TRUE@                os_win32/syslog_win32.c
+
+
+# Included by regex.c:
+@OS_WIN32_MINGW_TRUE@am__append_2 = \
+@OS_WIN32_MINGW_TRUE@                posix/regcomp.c           \
+@OS_WIN32_MINGW_TRUE@                posix/regexec.c           \
+@OS_WIN32_MINGW_TRUE@                posix/regex_internal.c    \
+@OS_WIN32_MINGW_TRUE@                posix/regex_internal.h
+
+@OS_WIN32_MINGW_TRUE@am__append_3 = \
+@OS_WIN32_MINGW_TRUE@                posix/regex.h          \
+@OS_WIN32_MINGW_TRUE@                posix/regex.c          \
+@OS_WIN32_MINGW_TRUE@                os_win32/syslog.h
+
+
+# Included by regex.c:
+@OS_WIN32_MINGW_TRUE@am__append_4 = \
+@OS_WIN32_MINGW_TRUE@                posix/regcomp.c        \
+@OS_WIN32_MINGW_TRUE@                posix/regexec.c        \
+@OS_WIN32_MINGW_TRUE@                posix/regex_internal.c \
+@OS_WIN32_MINGW_TRUE@                posix/regex_internal.h
+
+@SMARTD_SUFFIX_TRUE@am__append_5 = smartd.conf$(smartd_suffix)
+@OS_WIN32_MINGW_TRUE@am__append_6 = $(FILES_WIN32) $(exedir_win32)/syslogevt.exe distdir.mkdir syslogevt.check
+DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
+       $(srcdir)/Makefile.in $(srcdir)/config.h.in \
+       $(top_srcdir)/configure AUTHORS COPYING INSTALL NEWS TODO \
+       config.guess config.sub depcomp install-sh missing
+subdir = .
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
+ configure.lineno configure.status.lineno
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" \
+       "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docsdir)" \
+       "$(DESTDIR)$(initddir)" "$(DESTDIR)$(sysconfdir)"
+sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(sbin_PROGRAMS)
+am__smartctl_SOURCES_DIST = smartctl.c smartctl.h atacmdnames.c \
+       atacmdnames.h atacmds.c atacmds.h ataprint.c ataprint.h \
+       extern.h int64.h knowndrives.c knowndrives.h scsicmds.c \
+       scsicmds.h scsiprint.c scsiprint.h utility.c utility.h \
+       posix/regex.h posix/regex.c os_win32/syslog.h
+@OS_WIN32_MINGW_TRUE@am__objects_1 = regex.$(OBJEXT)
+am_smartctl_OBJECTS = smartctl.$(OBJEXT) atacmdnames.$(OBJEXT) \
+       atacmds.$(OBJEXT) ataprint.$(OBJEXT) knowndrives.$(OBJEXT) \
+       scsicmds.$(OBJEXT) scsiprint.$(OBJEXT) utility.$(OBJEXT) \
+       $(am__objects_1)
+am__EXTRA_smartctl_SOURCES_DIST = os_linux.c os_linux.h os_freebsd.c \
+       os_freebsd.h os_netbsd.c os_netbsd.h os_openbsd.c os_openbsd.h \
+       os_solaris.c os_solaris.h os_win32.c os_generic.c os_generic.h \
+       posix/regcomp.c posix/regexec.c posix/regex_internal.c \
+       posix/regex_internal.h
+smartctl_OBJECTS = $(am_smartctl_OBJECTS)
+am__smartd_SOURCES_DIST = smartd.c smartd.h atacmdnames.c \
+       atacmdnames.h atacmds.c atacmds.h ataprint.c ataprint.h \
+       extern.h int64.h knowndrives.c knowndrives.h scsicmds.c \
+       scsicmds.h scsiprint.c scsiprint.h utility.c utility.h \
+       posix/regex.h posix/regex.c os_win32/daemon_win32.h \
+       os_win32/daemon_win32.c os_win32/hostname_win32.h \
+       os_win32/hostname_win32.c os_win32/syslog.h \
+       os_win32/syslog_win32.c
+@OS_WIN32_MINGW_TRUE@am__objects_3 = regex.$(OBJEXT) \
+@OS_WIN32_MINGW_TRUE@  daemon_win32.$(OBJEXT) \
+@OS_WIN32_MINGW_TRUE@  hostname_win32.$(OBJEXT) \
+@OS_WIN32_MINGW_TRUE@  syslog_win32.$(OBJEXT)
+am_smartd_OBJECTS = smartd.$(OBJEXT) atacmdnames.$(OBJEXT) \
+       atacmds.$(OBJEXT) ataprint.$(OBJEXT) knowndrives.$(OBJEXT) \
+       scsicmds.$(OBJEXT) scsiprint.$(OBJEXT) utility.$(OBJEXT) \
+       $(am__objects_3)
+am__EXTRA_smartd_SOURCES_DIST = os_darwin.c os_darwin.h os_linux.c \
+       os_linux.h os_freebsd.c os_freebsd.h os_netbsd.c os_netbsd.h \
+       os_openbsd.c os_openbsd.h os_solaris.c os_solaris.h \
+       os_solaris_ata.s os_win32.c os_generic.c os_generic.h \
+       posix/regcomp.c posix/regexec.c posix/regex_internal.c \
+       posix/regex_internal.h
+smartd_OBJECTS = $(am_smartd_OBJECTS)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+       $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+CCASCOMPILE = $(CCAS) $(AM_CCASFLAGS) $(CCASFLAGS)
+SOURCES = $(smartctl_SOURCES) $(EXTRA_smartctl_SOURCES) \
+       $(smartd_SOURCES) $(EXTRA_smartd_SOURCES)
+DIST_SOURCES = $(am__smartctl_SOURCES_DIST) \
+       $(am__EXTRA_smartctl_SOURCES_DIST) $(am__smartd_SOURCES_DIST) \
+       $(am__EXTRA_smartd_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
+       html-recursive info-recursive install-data-recursive \
+       install-exec-recursive install-info-recursive \
+       install-recursive installcheck-recursive installdirs-recursive \
+       pdf-recursive ps-recursive uninstall-info-recursive \
+       uninstall-recursive
+man5dir = $(mandir)/man5
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man_MANS)
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+docsDATA_INSTALL = $(INSTALL_DATA)
+initdDATA_INSTALL = $(INSTALL_DATA)
+sysconfDATA_INSTALL = $(INSTALL_DATA)
+DATA = $(docs_DATA) $(initd_DATA) $(sysconf_DATA)
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+distdir = $(PACKAGE)-$(VERSION)
+top_distdir = $(distdir)
+am__remove_distdir = \
+  { test ! -d $(distdir) \
+    || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \
+         && rm -fr $(distdir); }; }
+DIST_ARCHIVES = $(distdir).tar.gz
+GZIP_ENV = --best
+distuninstallcheck_listfiles = find . -type f -print
+distcleancheck_listfiles = find . -type f -print
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+ASFLAGS = @ASFLAGS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCAS = @CCAS@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+OBJEXT = @OBJEXT@
+OS_DARWIN_FALSE = @OS_DARWIN_FALSE@
+OS_DARWIN_TRUE = @OS_DARWIN_TRUE@
+OS_SOLARIS_FALSE = @OS_SOLARIS_FALSE@
+OS_SOLARIS_TRUE = @OS_SOLARIS_TRUE@
+OS_WIN32_MINGW_FALSE = @OS_WIN32_MINGW_FALSE@
+OS_WIN32_MINGW_TRUE = @OS_WIN32_MINGW_TRUE@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SMARTD_SUFFIX_FALSE = @SMARTD_SUFFIX_FALSE@
+SMARTD_SUFFIX_TRUE = @SMARTD_SUFFIX_TRUE@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+docdir = @docdir@
+exampledir = @exampledir@
+exec_prefix = @exec_prefix@
+gcc_have_attr_packed = @gcc_have_attr_packed@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+initddir = @initddir@
+install_sh = @install_sh@
+libc_have_working_snprintf = @libc_have_working_snprintf@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+os_deps = @os_deps@
+os_libs = @os_libs@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+releaseversion = @releaseversion@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+smartd_suffix = @smartd_suffix@
+smartmontools_release_date = @smartmontools_release_date@
+smartmontools_release_time = @smartmontools_release_time@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+AM_CPPFLAGS = -DSMARTMONTOOLS_SYSCONFDIR=\"$(sysconfdir)\"
+smartd_SOURCES = smartd.c smartd.h atacmdnames.c atacmdnames.h \
+       atacmds.c atacmds.h ataprint.c ataprint.h extern.h int64.h \
+       knowndrives.c knowndrives.h scsicmds.c scsicmds.h scsiprint.c \
+       scsiprint.h utility.c utility.h $(am__append_1)
+smartd_LDADD = @os_deps@ @os_libs@
+smartd_DEPENDENCIES = @os_deps@
+EXTRA_smartd_SOURCES = os_darwin.c os_darwin.h os_linux.c os_linux.h \
+       os_freebsd.c os_freebsd.h os_netbsd.c os_netbsd.h os_openbsd.c \
+       os_openbsd.h os_solaris.c os_solaris.h os_solaris_ata.s \
+       os_win32.c os_generic.c os_generic.h $(am__append_2)
+smartctl_SOURCES = smartctl.c smartctl.h atacmdnames.c atacmdnames.h \
+       atacmds.c atacmds.h ataprint.c ataprint.h extern.h int64.h \
+       knowndrives.c knowndrives.h scsicmds.c scsicmds.h scsiprint.c \
+       scsiprint.h utility.c utility.h $(am__append_3)
+smartctl_LDADD = @os_deps@ @os_libs@
+smartctl_DEPENDENCIES = @os_deps@
+EXTRA_smartctl_SOURCES = os_linux.c os_linux.h os_freebsd.c \
+       os_freebsd.h os_netbsd.c os_netbsd.h os_openbsd.c os_openbsd.h \
+       os_solaris.c os_solaris.h os_win32.c os_generic.c os_generic.h \
+       $(am__append_4)
+
+# This block is required because Solaris uses manual page section 1m
+# for administrative command (linux/freebsd use section 8) and Solaris
+# uses manual page section 4 for file formats (linux/freebsd use
+# section 5).  Automake can deal cleanly with man page sections 1-8
+# and n, but NOT with sections of the form 1m.
+@OS_SOLARIS_TRUE@extra_MANS = smartd.conf.4 \
+@OS_SOLARIS_TRUE@                  smartctl.1m   \
+@OS_SOLARIS_TRUE@                  smartd.1m
+
+# For systems that adopts traditional manner
+@OS_SOLARIS_FALSE@man_MANS = smartd.conf.5 \
+@OS_SOLARIS_FALSE@                  smartctl.8    \
+@OS_SOLARIS_FALSE@                  smartd.8
+
+docsdir = $(docdir)
+docs_DATA = AUTHORS     \
+            CHANGELOG   \
+            COPYING     \
+            INSTALL     \
+            NEWS        \
+            README      \
+            TODO        \
+            WARNINGS    \
+            smartd.conf
+
+sysconf_DATA = smartd.conf$(smartd_suffix)
+EXTRA_DIST = smartmontools.spec                    \
+             smartd.initd.in                       \
+             smartd.8.in                           \
+             smartctl.8.in                         \
+             smartd.conf.5.in                      \
+             smartd.conf                           \
+             autogen.sh                            \
+             os_darwin/SMART.in                    \
+             os_darwin/StartupParameters.plist     \
+             os_darwin/English_Localizable.strings \
+             $(docs_DATA)
+
+CLEANFILES = smartd.conf.5 smartd.conf.4 smartd.8 smartd.1m \
+       smartd.8.html smartd.8.txt smartctl.8 smartctl.1m \
+       smartctl.8.html smartctl.8.txt smartd.conf.5.html \
+       smartd.conf.5.txt smartd.initd SMART $(am__append_5) \
+       $(am__append_6)
+@OS_DARWIN_FALSE@initd_DATA = smartd.initd
+@OS_DARWIN_TRUE@initd_DATA = SMART                            \
+@OS_DARWIN_TRUE@       os_darwin/StartupParameters.plist     \
+@OS_DARWIN_TRUE@       os_darwin/English_Localizable.strings
+
+@OS_DARWIN_FALSE@initd_install_name = smartd$(smartd_suffix)
+@OS_DARWIN_TRUE@initd_install_name = SMART
+@OS_DARWIN_FALSE@initd_DATA_install = install-initdDATA-generic
+@OS_DARWIN_TRUE@initd_DATA_install = install-initdDATA-darwin
+
+# Commands to convert man pages into .html and .txt
+# TODO: configure
+MAN2HTML = man2html
+#MAN2HTML = groff -man -Thtml
+MAN2TXT = groff -man -Tascii -P'-bcou'
+
+# Fix links in man2html output
+FIXHTML = sed 's,<A HREF="http://[-a-z/]*/man2html?\([1-8]\)+\(smart[cd][.a-z]*\)">,<A HREF="\2.\1.html">,g' \
+        | sed 's,<A HREF="http://[-a-z/]*/man2html">,<A HREF=".">,g' \
+        | sed 's,<A HREF="http://[-a-z/]*/man2html?[^"]*">\([^<]*\)</A>,\1,g' \
+        | sed 's,<A HREF="mailto:[^s][^m][^a][^"]*">\([^<]*\)</A>,\1,g'
+
+
+# Definitions for Windows distribution
+@OS_WIN32_MINGW_TRUE@distdir_win32 = $(PACKAGE)-$(VERSION).win32
+@OS_WIN32_MINGW_TRUE@distzip_win32 = $(PACKAGE)-$(VERSION).win32.zip
+@OS_WIN32_MINGW_TRUE@exedir_win32 = $(distdir_win32)/bin
+@OS_WIN32_MINGW_TRUE@docdir_win32 = $(distdir_win32)/doc
+@OS_WIN32_MINGW_TRUE@FILES_WIN32 = $(exedir_win32)/smartctl.exe \
+@OS_WIN32_MINGW_TRUE@              $(exedir_win32)/smartd.exe \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/AUTHORS.txt \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/CHANGELOG.txt \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/COPYING.txt \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/INSTALL.txt \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/NEWS.txt \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/README.txt \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/TODO.txt \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/WARNINGS.txt \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartd.conf \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartctl.8.html \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartctl.8.txt \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartd.8.html \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartd.8.txt \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartd.conf.5.html \
+@OS_WIN32_MINGW_TRUE@              $(docdir_win32)/smartd.conf.5.txt
+
+
+# Textfile converter from cygutils
+@OS_WIN32_MINGW_TRUE@UNIX2DOS = unix2dos -D
+SUBDIRS = . examplescripts
+all: config.h
+       $(MAKE) $(AM_MAKEFLAGS) all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj .s
+am--refresh:
+       @:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \
+             cd $(srcdir) && $(AUTOMAKE) --foreign  \
+               && exit 0; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign  Makefile'; \
+       cd $(top_srcdir) && \
+         $(AUTOMAKE) --foreign  Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           echo ' $(SHELL) ./config.status'; \
+           $(SHELL) ./config.status;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       $(SHELL) ./config.status --recheck
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+       cd $(srcdir) && $(AUTOCONF)
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+       cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS)
+
+config.h: stamp-h1
+       @if test ! -f $@; then \
+         rm -f stamp-h1; \
+         $(MAKE) stamp-h1; \
+       else :; fi
+
+stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
+       @rm -f stamp-h1
+       cd $(top_builddir) && $(SHELL) ./config.status config.h
+$(srcdir)/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) 
+       cd $(top_srcdir) && $(AUTOHEADER)
+       rm -f stamp-h1
+       touch $@
+
+distclean-hdr:
+       -rm -f config.h stamp-h1
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+       @$(NORMAL_INSTALL)
+       test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)"
+       @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+         p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+         if test -f $$p \
+         ; then \
+           f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+          echo " $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
+          $(INSTALL_PROGRAM_ENV) $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \
+         else :; fi; \
+       done
+
+uninstall-sbinPROGRAMS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+         f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+         echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
+         rm -f "$(DESTDIR)$(sbindir)/$$f"; \
+       done
+
+clean-sbinPROGRAMS:
+       -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+smartctl$(EXEEXT): $(smartctl_OBJECTS) $(smartctl_DEPENDENCIES) 
+       @rm -f smartctl$(EXEEXT)
+       $(LINK) $(smartctl_LDFLAGS) $(smartctl_OBJECTS) $(smartctl_LDADD) $(LIBS)
+smartd$(EXEEXT): $(smartd_OBJECTS) $(smartd_DEPENDENCIES) 
+       @rm -f smartd$(EXEEXT)
+       $(LINK) $(smartd_LDFLAGS) $(smartd_OBJECTS) $(smartd_LDADD) $(LIBS)
+
+mostlyclean-compile:
+       -rm -f *.$(OBJEXT)
+
+distclean-compile:
+       -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atacmdnames.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/atacmds.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ataprint.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/daemon_win32.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hostname_win32.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/knowndrives.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_darwin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_freebsd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_generic.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_linux.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_netbsd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_openbsd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_solaris.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_win32.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regcomp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex_internal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regexec.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scsicmds.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scsiprint.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smartctl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smartd.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/syslog_win32.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utility.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@   if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@   if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+regex.o: posix/regex.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex.o -MD -MP -MF "$(DEPDIR)/regex.Tpo" -c -o regex.o `test -f 'posix/regex.c' || echo '$(srcdir)/'`posix/regex.c; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regex.Tpo" "$(DEPDIR)/regex.Po"; else rm -f "$(DEPDIR)/regex.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regex.c' object='regex.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex.o `test -f 'posix/regex.c' || echo '$(srcdir)/'`posix/regex.c
+
+regex.obj: posix/regex.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex.obj -MD -MP -MF "$(DEPDIR)/regex.Tpo" -c -o regex.obj `if test -f 'posix/regex.c'; then $(CYGPATH_W) 'posix/regex.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex.c'; fi`; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regex.Tpo" "$(DEPDIR)/regex.Po"; else rm -f "$(DEPDIR)/regex.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regex.c' object='regex.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex.obj `if test -f 'posix/regex.c'; then $(CYGPATH_W) 'posix/regex.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex.c'; fi`
+
+regcomp.o: posix/regcomp.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regcomp.o -MD -MP -MF "$(DEPDIR)/regcomp.Tpo" -c -o regcomp.o `test -f 'posix/regcomp.c' || echo '$(srcdir)/'`posix/regcomp.c; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regcomp.Tpo" "$(DEPDIR)/regcomp.Po"; else rm -f "$(DEPDIR)/regcomp.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regcomp.c' object='regcomp.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regcomp.o `test -f 'posix/regcomp.c' || echo '$(srcdir)/'`posix/regcomp.c
+
+regcomp.obj: posix/regcomp.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regcomp.obj -MD -MP -MF "$(DEPDIR)/regcomp.Tpo" -c -o regcomp.obj `if test -f 'posix/regcomp.c'; then $(CYGPATH_W) 'posix/regcomp.c'; else $(CYGPATH_W) '$(srcdir)/posix/regcomp.c'; fi`; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regcomp.Tpo" "$(DEPDIR)/regcomp.Po"; else rm -f "$(DEPDIR)/regcomp.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regcomp.c' object='regcomp.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regcomp.obj `if test -f 'posix/regcomp.c'; then $(CYGPATH_W) 'posix/regcomp.c'; else $(CYGPATH_W) '$(srcdir)/posix/regcomp.c'; fi`
+
+regexec.o: posix/regexec.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regexec.o -MD -MP -MF "$(DEPDIR)/regexec.Tpo" -c -o regexec.o `test -f 'posix/regexec.c' || echo '$(srcdir)/'`posix/regexec.c; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regexec.Tpo" "$(DEPDIR)/regexec.Po"; else rm -f "$(DEPDIR)/regexec.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regexec.c' object='regexec.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regexec.o `test -f 'posix/regexec.c' || echo '$(srcdir)/'`posix/regexec.c
+
+regexec.obj: posix/regexec.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regexec.obj -MD -MP -MF "$(DEPDIR)/regexec.Tpo" -c -o regexec.obj `if test -f 'posix/regexec.c'; then $(CYGPATH_W) 'posix/regexec.c'; else $(CYGPATH_W) '$(srcdir)/posix/regexec.c'; fi`; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regexec.Tpo" "$(DEPDIR)/regexec.Po"; else rm -f "$(DEPDIR)/regexec.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regexec.c' object='regexec.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regexec.obj `if test -f 'posix/regexec.c'; then $(CYGPATH_W) 'posix/regexec.c'; else $(CYGPATH_W) '$(srcdir)/posix/regexec.c'; fi`
+
+regex_internal.o: posix/regex_internal.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex_internal.o -MD -MP -MF "$(DEPDIR)/regex_internal.Tpo" -c -o regex_internal.o `test -f 'posix/regex_internal.c' || echo '$(srcdir)/'`posix/regex_internal.c; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regex_internal.Tpo" "$(DEPDIR)/regex_internal.Po"; else rm -f "$(DEPDIR)/regex_internal.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regex_internal.c' object='regex_internal.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex_internal.o `test -f 'posix/regex_internal.c' || echo '$(srcdir)/'`posix/regex_internal.c
+
+regex_internal.obj: posix/regex_internal.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT regex_internal.obj -MD -MP -MF "$(DEPDIR)/regex_internal.Tpo" -c -o regex_internal.obj `if test -f 'posix/regex_internal.c'; then $(CYGPATH_W) 'posix/regex_internal.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex_internal.c'; fi`; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/regex_internal.Tpo" "$(DEPDIR)/regex_internal.Po"; else rm -f "$(DEPDIR)/regex_internal.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='posix/regex_internal.c' object='regex_internal.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o regex_internal.obj `if test -f 'posix/regex_internal.c'; then $(CYGPATH_W) 'posix/regex_internal.c'; else $(CYGPATH_W) '$(srcdir)/posix/regex_internal.c'; fi`
+
+daemon_win32.o: os_win32/daemon_win32.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT daemon_win32.o -MD -MP -MF "$(DEPDIR)/daemon_win32.Tpo" -c -o daemon_win32.o `test -f 'os_win32/daemon_win32.c' || echo '$(srcdir)/'`os_win32/daemon_win32.c; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/daemon_win32.Tpo" "$(DEPDIR)/daemon_win32.Po"; else rm -f "$(DEPDIR)/daemon_win32.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/daemon_win32.c' object='daemon_win32.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o daemon_win32.o `test -f 'os_win32/daemon_win32.c' || echo '$(srcdir)/'`os_win32/daemon_win32.c
+
+daemon_win32.obj: os_win32/daemon_win32.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT daemon_win32.obj -MD -MP -MF "$(DEPDIR)/daemon_win32.Tpo" -c -o daemon_win32.obj `if test -f 'os_win32/daemon_win32.c'; then $(CYGPATH_W) 'os_win32/daemon_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/daemon_win32.c'; fi`; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/daemon_win32.Tpo" "$(DEPDIR)/daemon_win32.Po"; else rm -f "$(DEPDIR)/daemon_win32.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/daemon_win32.c' object='daemon_win32.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o daemon_win32.obj `if test -f 'os_win32/daemon_win32.c'; then $(CYGPATH_W) 'os_win32/daemon_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/daemon_win32.c'; fi`
+
+hostname_win32.o: os_win32/hostname_win32.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hostname_win32.o -MD -MP -MF "$(DEPDIR)/hostname_win32.Tpo" -c -o hostname_win32.o `test -f 'os_win32/hostname_win32.c' || echo '$(srcdir)/'`os_win32/hostname_win32.c; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/hostname_win32.Tpo" "$(DEPDIR)/hostname_win32.Po"; else rm -f "$(DEPDIR)/hostname_win32.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/hostname_win32.c' object='hostname_win32.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hostname_win32.o `test -f 'os_win32/hostname_win32.c' || echo '$(srcdir)/'`os_win32/hostname_win32.c
+
+hostname_win32.obj: os_win32/hostname_win32.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hostname_win32.obj -MD -MP -MF "$(DEPDIR)/hostname_win32.Tpo" -c -o hostname_win32.obj `if test -f 'os_win32/hostname_win32.c'; then $(CYGPATH_W) 'os_win32/hostname_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/hostname_win32.c'; fi`; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/hostname_win32.Tpo" "$(DEPDIR)/hostname_win32.Po"; else rm -f "$(DEPDIR)/hostname_win32.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/hostname_win32.c' object='hostname_win32.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hostname_win32.obj `if test -f 'os_win32/hostname_win32.c'; then $(CYGPATH_W) 'os_win32/hostname_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/hostname_win32.c'; fi`
+
+syslog_win32.o: os_win32/syslog_win32.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT syslog_win32.o -MD -MP -MF "$(DEPDIR)/syslog_win32.Tpo" -c -o syslog_win32.o `test -f 'os_win32/syslog_win32.c' || echo '$(srcdir)/'`os_win32/syslog_win32.c; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/syslog_win32.Tpo" "$(DEPDIR)/syslog_win32.Po"; else rm -f "$(DEPDIR)/syslog_win32.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/syslog_win32.c' object='syslog_win32.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o syslog_win32.o `test -f 'os_win32/syslog_win32.c' || echo '$(srcdir)/'`os_win32/syslog_win32.c
+
+syslog_win32.obj: os_win32/syslog_win32.c
+@am__fastdepCC_TRUE@   if $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT syslog_win32.obj -MD -MP -MF "$(DEPDIR)/syslog_win32.Tpo" -c -o syslog_win32.obj `if test -f 'os_win32/syslog_win32.c'; then $(CYGPATH_W) 'os_win32/syslog_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/syslog_win32.c'; fi`; \
+@am__fastdepCC_TRUE@   then mv -f "$(DEPDIR)/syslog_win32.Tpo" "$(DEPDIR)/syslog_win32.Po"; else rm -f "$(DEPDIR)/syslog_win32.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='os_win32/syslog_win32.c' object='syslog_win32.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o syslog_win32.obj `if test -f 'os_win32/syslog_win32.c'; then $(CYGPATH_W) 'os_win32/syslog_win32.c'; else $(CYGPATH_W) '$(srcdir)/os_win32/syslog_win32.c'; fi`
+
+.s.o:
+       $(CCASCOMPILE) -c $<
+
+.s.obj:
+       $(CCASCOMPILE) -c `$(CYGPATH_W) '$<'`
+uninstall-info-am:
+install-man5: $(man5_MANS) $(man_MANS)
+       @$(NORMAL_INSTALL)
+       test -z "$(man5dir)" || $(mkdir_p) "$(DESTDIR)$(man5dir)"
+       @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
+       l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+       for i in $$l2; do \
+         case "$$i" in \
+           *.5*) list="$$list $$i" ;; \
+         esac; \
+       done; \
+       for i in $$list; do \
+         if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+         else file=$$i; fi; \
+         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+         case "$$ext" in \
+           5*) ;; \
+           *) ext='5' ;; \
+         esac; \
+         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+         inst=`echo $$inst | sed -e 's/^.*\///'`; \
+         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+         echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
+         $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst"; \
+       done
+uninstall-man5:
+       @$(NORMAL_UNINSTALL)
+       @list='$(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS)'; \
+       l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+       for i in $$l2; do \
+         case "$$i" in \
+           *.5*) list="$$list $$i" ;; \
+         esac; \
+       done; \
+       for i in $$list; do \
+         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+         case "$$ext" in \
+           5*) ;; \
+           *) ext='5' ;; \
+         esac; \
+         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+         inst=`echo $$inst | sed -e 's/^.*\///'`; \
+         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+         echo " rm -f '$(DESTDIR)$(man5dir)/$$inst'"; \
+         rm -f "$(DESTDIR)$(man5dir)/$$inst"; \
+       done
+install-man8: $(man8_MANS) $(man_MANS)
+       @$(NORMAL_INSTALL)
+       test -z "$(man8dir)" || $(mkdir_p) "$(DESTDIR)$(man8dir)"
+       @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+       l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+       for i in $$l2; do \
+         case "$$i" in \
+           *.8*) list="$$list $$i" ;; \
+         esac; \
+       done; \
+       for i in $$list; do \
+         if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+         else file=$$i; fi; \
+         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+         case "$$ext" in \
+           8*) ;; \
+           *) ext='8' ;; \
+         esac; \
+         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+         inst=`echo $$inst | sed -e 's/^.*\///'`; \
+         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+         echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+         $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \
+       done
+uninstall-man8:
+       @$(NORMAL_UNINSTALL)
+       @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+       l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+       for i in $$l2; do \
+         case "$$i" in \
+           *.8*) list="$$list $$i" ;; \
+         esac; \
+       done; \
+       for i in $$list; do \
+         ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+         case "$$ext" in \
+           8*) ;; \
+           *) ext='8' ;; \
+         esac; \
+         inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+         inst=`echo $$inst | sed -e 's/^.*\///'`; \
+         inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+         echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \
+         rm -f "$(DESTDIR)$(man8dir)/$$inst"; \
+       done
+install-docsDATA: $(docs_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(docsdir)" || $(mkdir_p) "$(DESTDIR)$(docsdir)"
+       @list='$(docs_DATA)'; for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         f=$(am__strip_dir) \
+         echo " $(docsDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(docsdir)/$$f'"; \
+         $(docsDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(docsdir)/$$f"; \
+       done
+install-sysconfDATA: $(sysconf_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(sysconfdir)" || $(mkdir_p) "$(DESTDIR)$(sysconfdir)"
+       @list='$(sysconf_DATA)'; for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         f=$(am__strip_dir) \
+         echo " $(sysconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(sysconfdir)/$$f'"; \
+         $(sysconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(sysconfdir)/$$f"; \
+       done
+
+uninstall-sysconfDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(sysconf_DATA)'; for p in $$list; do \
+         f=$(am__strip_dir) \
+         echo " rm -f '$(DESTDIR)$(sysconfdir)/$$f'"; \
+         rm -f "$(DESTDIR)$(sysconfdir)/$$f"; \
+       done
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run `make' without going through this Makefile.
+# To change the values of `make' variables: instead of editing Makefiles,
+# (1) if the variable is set in `config.status', edit `config.status'
+#     (which will cause the Makefiles to be regenerated when you run `make');
+# (2) otherwise, pass the desired values on the `make' command line.
+$(RECURSIVE_TARGETS):
+       @set fnord $$MAKEFLAGS; amf=$$2; \
+       dot_seen=no; \
+       target=`echo $@ | sed s/-recursive//`; \
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+         echo "Making $$target in $$subdir"; \
+         if test "$$subdir" = "."; then \
+           dot_seen=yes; \
+           local_target="$$target-am"; \
+         else \
+           local_target="$$target"; \
+         fi; \
+         (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+          || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+       done; \
+       if test "$$dot_seen" = "no"; then \
+         $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+       fi; test -z "$$fail"
+
+mostlyclean-recursive clean-recursive distclean-recursive \
+maintainer-clean-recursive:
+       @set fnord $$MAKEFLAGS; amf=$$2; \
+       dot_seen=no; \
+       case "$@" in \
+         distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+         *) list='$(SUBDIRS)' ;; \
+       esac; \
+       rev=''; for subdir in $$list; do \
+         if test "$$subdir" = "."; then :; else \
+           rev="$$subdir $$rev"; \
+         fi; \
+       done; \
+       rev="$$rev ."; \
+       target=`echo $@ | sed s/-recursive//`; \
+       for subdir in $$rev; do \
+         echo "Making $$target in $$subdir"; \
+         if test "$$subdir" = "."; then \
+           local_target="$$target-am"; \
+         else \
+           local_target="$$target"; \
+         fi; \
+         (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+          || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \
+       done && test -z "$$fail"
+tags-recursive:
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+         test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
+       done
+ctags-recursive:
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+         test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
+       done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+       list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       mkid -fID $$unique
+tags: TAGS
+
+TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       tags=; \
+       here=`pwd`; \
+       if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+         include_option=--etags-include; \
+         empty_fix=.; \
+       else \
+         include_option=--include; \
+         empty_fix=; \
+       fi; \
+       list='$(SUBDIRS)'; for subdir in $$list; do \
+         if test "$$subdir" = .; then :; else \
+           test ! -f $$subdir/TAGS || \
+             tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \
+         fi; \
+       done; \
+       list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+         test -n "$$unique" || unique=$$empty_fix; \
+         $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+           $$tags $$unique; \
+       fi
+ctags: CTAGS
+CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
+               $(TAGS_FILES) $(LISP)
+       tags=; \
+       here=`pwd`; \
+       list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
+       unique=`for i in $$list; do \
+           if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+         done | \
+         $(AWK) '    { files[$$0] = 1; } \
+              END { for (i in files) print i; }'`; \
+       test -z "$(CTAGS_ARGS)$$tags$$unique" \
+         || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+            $$tags $$unique
+
+GTAGS:
+       here=`$(am__cd) $(top_builddir) && pwd` \
+         && cd $(top_srcdir) \
+         && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+       -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+       $(am__remove_distdir)
+       mkdir $(distdir)
+       $(mkdir_p) $(distdir)/os_darwin
+       @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+       list='$(DISTFILES)'; for file in $$list; do \
+         case $$file in \
+           $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+           $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+         esac; \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+         if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+           dir="/$$dir"; \
+           $(mkdir_p) "$(distdir)$$dir"; \
+         else \
+           dir=''; \
+         fi; \
+         if test -d $$d/$$file; then \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+           fi; \
+           cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+         else \
+           test -f $(distdir)/$$file \
+           || cp -p $$d/$$file $(distdir)/$$file \
+           || exit 1; \
+         fi; \
+       done
+       list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+         if test "$$subdir" = .; then :; else \
+           test -d "$(distdir)/$$subdir" \
+           || $(mkdir_p) "$(distdir)/$$subdir" \
+           || exit 1; \
+           distdir=`$(am__cd) $(distdir) && pwd`; \
+           top_distdir=`$(am__cd) $(top_distdir) && pwd`; \
+           (cd $$subdir && \
+             $(MAKE) $(AM_MAKEFLAGS) \
+               top_distdir="$$top_distdir" \
+               distdir="$$distdir/$$subdir" \
+               distdir) \
+             || exit 1; \
+         fi; \
+       done
+       -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
+         ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
+         ! -type d ! -perm -400 -exec chmod a+r {} \; -o \
+         ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
+       || chmod -R a+r $(distdir)
+dist-gzip: distdir
+       tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+       $(am__remove_distdir)
+
+dist-bzip2: distdir
+       tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
+       $(am__remove_distdir)
+
+dist-tarZ: distdir
+       tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
+       $(am__remove_distdir)
+
+dist-shar: distdir
+       shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
+       $(am__remove_distdir)
+
+dist-zip: distdir
+       -rm -f $(distdir).zip
+       zip -rq $(distdir).zip $(distdir)
+       $(am__remove_distdir)
+
+dist dist-all: distdir
+       tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
+       $(am__remove_distdir)
+
+# This target untars the dist file and tries a VPATH configuration.  Then
+# it guarantees that the distribution is self-contained by making another
+# tarfile.
+distcheck: dist
+       case '$(DIST_ARCHIVES)' in \
+       *.tar.gz*) \
+         GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\
+       *.tar.bz2*) \
+         bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\
+       *.tar.Z*) \
+         uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
+       *.shar.gz*) \
+         GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\
+       *.zip*) \
+         unzip $(distdir).zip ;;\
+       esac
+       chmod -R a-w $(distdir); chmod a+w $(distdir)
+       mkdir $(distdir)/_build
+       mkdir $(distdir)/_inst
+       chmod a-w $(distdir)
+       dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
+         && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
+         && cd $(distdir)/_build \
+         && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+           $(DISTCHECK_CONFIGURE_FLAGS) \
+         && $(MAKE) $(AM_MAKEFLAGS) \
+         && $(MAKE) $(AM_MAKEFLAGS) dvi \
+         && $(MAKE) $(AM_MAKEFLAGS) check \
+         && $(MAKE) $(AM_MAKEFLAGS) install \
+         && $(MAKE) $(AM_MAKEFLAGS) installcheck \
+         && $(MAKE) $(AM_MAKEFLAGS) uninstall \
+         && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \
+               distuninstallcheck \
+         && chmod -R a-w "$$dc_install_base" \
+         && ({ \
+              (cd ../.. && umask 077 && mkdir "$$dc_destdir") \
+              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \
+              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \
+              && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \
+                   distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \
+             } || { rm -rf "$$dc_destdir"; exit 1; }) \
+         && rm -rf "$$dc_destdir" \
+         && $(MAKE) $(AM_MAKEFLAGS) dist \
+         && rm -rf $(DIST_ARCHIVES) \
+         && $(MAKE) $(AM_MAKEFLAGS) distcleancheck
+       $(am__remove_distdir)
+       @(echo "$(distdir) archives ready for distribution: "; \
+         list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
+         sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
+distuninstallcheck:
+       @cd $(distuninstallcheck_dir) \
+       && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
+          || { echo "ERROR: files left after uninstall:" ; \
+               if test -n "$(DESTDIR)"; then \
+                 echo "  (check DESTDIR support)"; \
+               fi ; \
+               $(distuninstallcheck_listfiles) ; \
+               exit 1; } >&2
+distcleancheck: distclean
+       @if test '$(srcdir)' = . ; then \
+         echo "ERROR: distcleancheck can only run from a VPATH build" ; \
+         exit 1 ; \
+       fi
+       @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \
+         || { echo "ERROR: files left in build directory after distclean:" ; \
+              $(distcleancheck_listfiles) ; \
+              exit 1; } >&2
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS) $(MANS) $(DATA) config.h
+installdirs: installdirs-recursive
+installdirs-am:
+       for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(docsdir)" "$(DESTDIR)$(initddir)" "$(DESTDIR)$(sysconfdir)"; do \
+         test -z "$$dir" || $(mkdir_p) "$$dir"; \
+       done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+       -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+       -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+       distclean-hdr distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-docsDATA install-initdDATA install-man
+
+install-exec-am: install-sbinPROGRAMS install-sysconfDATA
+
+install-info: install-info-recursive
+
+@OS_SOLARIS_FALSE@install-man: install-man5 install-man8
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+       -rm -f $(am__CONFIG_DISTCLEAN_FILES)
+       -rm -rf $(top_srcdir)/autom4te.cache
+       -rm -rf ./$(DEPDIR)
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-docsDATA uninstall-info-am uninstall-initdDATA \
+       uninstall-man uninstall-sbinPROGRAMS uninstall-sysconfDATA
+
+uninstall-info: uninstall-info-recursive
+
+@OS_SOLARIS_FALSE@uninstall-man: uninstall-man5 uninstall-man8
+
+.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \
+       check-am clean clean-generic clean-recursive \
+       clean-sbinPROGRAMS ctags ctags-recursive dist dist-all \
+       dist-bzip2 dist-gzip dist-shar dist-tarZ dist-zip distcheck \
+       distclean distclean-compile distclean-generic distclean-hdr \
+       distclean-recursive distclean-tags distcleancheck distdir \
+       distuninstallcheck dvi dvi-am html html-am info info-am \
+       install install-am install-data install-data-am \
+       install-docsDATA install-exec install-exec-am install-info \
+       install-info-am install-initdDATA install-man install-man5 \
+       install-man8 install-sbinPROGRAMS install-strip \
+       install-sysconfDATA installcheck installcheck-am installdirs \
+       installdirs-am maintainer-clean maintainer-clean-generic \
+       maintainer-clean-recursive mostlyclean mostlyclean-compile \
+       mostlyclean-generic mostlyclean-recursive pdf pdf-am ps ps-am \
+       tags tags-recursive uninstall uninstall-am uninstall-docsDATA \
+       uninstall-info-am uninstall-initdDATA uninstall-man \
+       uninstall-man5 uninstall-man8 uninstall-sbinPROGRAMS \
+       uninstall-sysconfDATA
+
+
+@SET_MAKE@
+@OS_SOLARIS_TRUE@install-man: $(extra_MANS)
+@OS_SOLARIS_TRUE@      @$(NORMAL_INSTALL)
+@OS_SOLARIS_TRUE@      $(mkinstalldirs) $(DESTDIR)$(mandir)/man4
+@OS_SOLARIS_TRUE@      $(mkinstalldirs) $(DESTDIR)$(mandir)/man1m
+@OS_SOLARIS_TRUE@      for i in $(extra_MANS); do \
+@OS_SOLARIS_TRUE@        if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+@OS_SOLARIS_TRUE@        else file=$$i; fi; \
+@OS_SOLARIS_TRUE@        ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+@OS_SOLARIS_TRUE@        inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+@OS_SOLARIS_TRUE@        inst=`echo $$inst | sed -e 's/^.*\///'`; \
+@OS_SOLARIS_TRUE@        inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+@OS_SOLARIS_TRUE@        echo " $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$$ext/$$inst"; \
+@OS_SOLARIS_TRUE@        $(INSTALL_DATA) $$file $(DESTDIR)$(mandir)/man$$ext/$$inst; \
+@OS_SOLARIS_TRUE@      done
+@OS_SOLARIS_TRUE@uninstall-man:
+@OS_SOLARIS_TRUE@      @$(NORMAL_UNINSTALL)
+@OS_SOLARIS_TRUE@      for i in $(extra_MANS); do \
+@OS_SOLARIS_TRUE@        if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+@OS_SOLARIS_TRUE@        else file=$$i; fi; \
+@OS_SOLARIS_TRUE@        ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+@OS_SOLARIS_TRUE@        inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+@OS_SOLARIS_TRUE@        inst=`echo $$inst | sed -e 's/^.*\///'`; \
+@OS_SOLARIS_TRUE@        inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+@OS_SOLARIS_TRUE@        echo " rm -f $(DESTDIR)$(mandir)/man$$ext/$$inst"; \
+@OS_SOLARIS_TRUE@        rm -f $(DESTDIR)$(mandir)/man$$ext/$$inst; \
+@OS_SOLARIS_TRUE@      done
+@OS_SOLARIS_TRUE@%.1m: %.8
+@OS_SOLARIS_TRUE@      awk '/^.TH/ {$$3="1m"} {print}' < $< | \
+@OS_SOLARIS_TRUE@      sed -e 's/smartd\.conf\(.*\)(5)/smartd.conf\1(4)/g' \
+@OS_SOLARIS_TRUE@            -e 's/syslog\.conf\(.*\)(5)/syslog.conf\1(4)/g' \
+@OS_SOLARIS_TRUE@          -e 's/smartctl\(.*\)(8)/smartctl\1(1m)/g' \
+@OS_SOLARIS_TRUE@          -e 's/syslogd\(.*\)(8)/syslogd\1(1m)/g' \
+@OS_SOLARIS_TRUE@            -e 's|/var/log/messages|/var/adm/messages|g' \
+@OS_SOLARIS_TRUE@          -e 's/smartd\(.*\)(8)/smartd\1(1m)/g' > $@
+@OS_SOLARIS_TRUE@%.4: %.5
+@OS_SOLARIS_TRUE@      awk '/^.TH/ {$$3="4"}  {print}' < $< | \
+@OS_SOLARIS_TRUE@      sed -e 's/smartd\.conf\(.*\)(5)/smartd.conf\1(4)/g' \
+@OS_SOLARIS_TRUE@            -e 's/syslog\.conf\(.*\)(5)/syslog.conf\1(4)/g' \
+@OS_SOLARIS_TRUE@          -e 's/smartctl\(.*\)(8)/smartdctl\1(1m)/g' \
+@OS_SOLARIS_TRUE@          -e 's/syslogd\(.*\)(8)/syslogd\1(1m)/g' \
+@OS_SOLARIS_TRUE@            -e 's|/var/log/messages|/var/adm/messages|g' \
+@OS_SOLARIS_TRUE@          -e 's/smartd\(.*\)(8)/smartd\1(1m)/g' > $@
+
+@SMARTD_SUFFIX_TRUE@smartd.conf$(smartd_suffix): smartd.conf
+@SMARTD_SUFFIX_TRUE@   cp ${srcdir}/smartd.conf smartd.conf$(smartd_suffix)
+
+smartd.conf.5.in: smartd.8.in
+       sed '1,/STARTINCLUDE/ D;/ENDINCLUDE/,$$D' < $(srcdir)/smartd.8.in > $(top_builddir)/tmp.directives
+       sed '/STARTINCLUDE/,$$D'  < $(srcdir)/smartd.conf.5.in > $(top_builddir)/tmp.head
+       sed '1,/ENDINCLUDE/D'   < $(srcdir)/smartd.conf.5.in > $(top_builddir)/tmp.tail
+       cat $(top_builddir)/tmp.head > $(srcdir)/smartd.conf.5.in
+       echo '.\" STARTINCLUDE' >> $(srcdir)/smartd.conf.5.in
+       cat $(top_builddir)/tmp.directives >> $(srcdir)/smartd.conf.5.in
+       echo '.\" ENDINCLUDE'   >> $(srcdir)/smartd.conf.5.in
+       cat $(top_builddir)/tmp.tail >> $(srcdir)/smartd.conf.5.in
+       rm -f $(top_builddir)/tmp.head $(top_builddir)/tmp.tail $(top_builddir)/tmp.directives
+
+@OS_DARWIN_TRUE@SMART : os_darwin/SMART.in
+@OS_DARWIN_TRUE@       sed "s|/usr/sbin/|$(sbindir)/|" $< > $@
+
+@OS_DARWIN_TRUE@install-initdDATA-darwin: $(initd_DATA)
+@OS_DARWIN_TRUE@       $(mkinstalldirs) $(DESTDIR)$(initddir)
+@OS_DARWIN_TRUE@       $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART
+@OS_DARWIN_TRUE@       $(mkinstalldirs) $(DESTDIR)$(initddir)/SMART/Resources
+@OS_DARWIN_TRUE@       $(INSTALL_SCRIPT) $(top_builddir)/SMART $(DESTDIR)$(initddir)/SMART
+@OS_DARWIN_TRUE@       $(INSTALL_DATA) $(srcdir)/os_darwin/StartupParameters.plist \
+@OS_DARWIN_TRUE@           $(DESTDIR)$(initddir)/SMART/StartupParameters.plist
+@OS_DARWIN_TRUE@       for i in English ; do \
+@OS_DARWIN_TRUE@         RDIR=$(DESTDIR)$(initddir)/SMART/Resources/$${i}.lproj ; \
+@OS_DARWIN_TRUE@         $(mkinstalldirs) $$RDIR ;\
+@OS_DARWIN_TRUE@         $(INSTALL_DATA) $(srcdir)/os_darwin/$${i}_Localizable.strings \
+@OS_DARWIN_TRUE@           $$RDIR/Localizable.strings ; \
+@OS_DARWIN_TRUE@       done
+@OS_DARWIN_TRUE@       @echo -e "\n\n####################################################################\n#"
+@OS_DARWIN_TRUE@       @echo -e "#                       PLEASE READ THIS BOX!\n#"
+@OS_DARWIN_TRUE@       @echo -e "#   To manually start the smartd daemon, run:\n#   ${initddir}/SMART/SMART start\n#"
+@OS_DARWIN_TRUE@       @echo -e "#   To automatically start smartd on bootup, add the line:\n#   SMARTd=-YES-\n#   to /etc/hostconfig\n#"
+@OS_DARWIN_TRUE@       @echo -e "#   smartd can now use a configuration file ${sysconfdir}/smartd.conf. Do:\n#   man smartd"
+@OS_DARWIN_TRUE@       @echo -e "#   to learn about it. A sample configuration file can be found in:\n#   ${docdir}\n#"
+@OS_DARWIN_TRUE@       @echo -e "####################################################################\n\n"
+
+@OS_DARWIN_FALSE@smartd.initd: $(srcdir)/smartd.initd.in Makefile
+@OS_DARWIN_FALSE@      sed "s|/usr/local/sbin/|$(sbindir)/|g" $(srcdir)/smartd.initd.in > $@
+
+@OS_DARWIN_FALSE@install-initdDATA-generic: $(initd_DATA)
+@OS_DARWIN_FALSE@      $(mkinstalldirs) $(DESTDIR)$(initddir)
+@OS_DARWIN_FALSE@      $(INSTALL_SCRIPT) $(top_builddir)/smartd.initd $(DESTDIR)$(initddir)/smartd$(smartd_suffix)
+@OS_DARWIN_FALSE@      @echo -e "\n\n####################################################################\n#"
+@OS_DARWIN_FALSE@      @echo -e "#                       PLEASE READ THIS BOX!\n#"
+@OS_DARWIN_FALSE@      @echo -e "#   To manually start the smartd daemon, run:\n#   ${initddir}/smartd start\n#"
+@OS_DARWIN_FALSE@      @echo -e "#   To automatically start smartd on bootup, run:\n#   /sbin/chkconfig --add smartd\n#"
+@OS_DARWIN_FALSE@      @echo -e "#   smartd can now use a configuration file ${sysconfdir}/smartd.conf. Do:\n#   man smartd"
+@OS_DARWIN_FALSE@      @echo -e "#   to learn about it. A sample configuration file can be found in:\n#   ${docdir}\n#"
+@OS_DARWIN_FALSE@      @echo -e "####################################################################\n\n"
+
+install-initdDATA : $(initd_DATA_install)
+
+uninstall-initdDATA:
+       rm -rf $(DESTDIR)$(initddir)/$(initd_install_name)
+
+uninstall-docsDATA:
+       rm -rf $(DESTDIR)$(docsdir)
+
+smart%: $(srcdir)/smart%.in Makefile
+       sed "s|CURRENT_CVS_VERSION|$(releaseversion)|g" $< | \
+       sed "s|CURRENT_CVS_DATE|$(smartmontools_release_date)|g" | \
+       sed "s|CURRENT_CVS_TIME|$(smartmontools_release_time)|g" | \
+       sed "s|/usr/local/share/man/|$(mandir)/|g" | \
+       sed "s|/usr/local/sbin/|$(sbindir)/|g" | \
+       sed "s|/usr/local/etc/rc\\.d/init.d/|$(initddir)/|g" | \
+       sed "s|/usr/local/share/doc/smartmontools-5.1/|$(docsdir)/|g" | \
+       sed "s|/usr/local/etc/smartd\\.conf|$(sysconfdir)/smartd.conf|g" > $@
+
+# Convert man pages into .html and .txt
+
+htmlman: smartctl.8.html smartd.8.html smartd.conf.5.html
+
+txtman:  smartctl.8.txt smartd.8.txt smartd.conf.5.txt
+
+%.5.html: %.5
+       $(MAN2HTML) $< | $(FIXHTML) > $@
+
+%.8.html: %.8
+       $(MAN2HTML) $< | $(FIXHTML) > $@
+
+%.5.txt: %.5
+       $(MAN2TXT) $< > $@
+
+%.8.txt: %.8
+       $(MAN2TXT) $< > $@
+
+# Build Windows distribution
+
+@OS_WIN32_MINGW_TRUE@dist-win32: $(distzip_win32)
+
+@OS_WIN32_MINGW_TRUE@distdir-win32: distdir.mkdir $(FILES_WIN32) syslogevt.check
+
+@OS_WIN32_MINGW_TRUE@$(distzip_win32): distdir.mkdir $(FILES_WIN32) syslogevt.check
+@OS_WIN32_MINGW_TRUE@  @rm -fv $(distzip_win32)
+@OS_WIN32_MINGW_TRUE@  cd $(distdir_win32) && zip -9Dr ../$(distzip_win32) .
+
+@OS_WIN32_MINGW_TRUE@cleandist-win32:
+@OS_WIN32_MINGW_TRUE@  rm -rf $(distdir_win32) distdir.mkdir syslogevt.check
+
+@OS_WIN32_MINGW_TRUE@distdir.mkdir:
+@OS_WIN32_MINGW_TRUE@  @test -d $(exedir_win32) || mkdir -pv $(exedir_win32)
+@OS_WIN32_MINGW_TRUE@  @test -d $(docdir_win32) || mkdir -pv $(docdir_win32)
+@OS_WIN32_MINGW_TRUE@  touch $@
+
+@OS_WIN32_MINGW_TRUE@syslogevt.check:
+@OS_WIN32_MINGW_TRUE@  @if [ -f $(srcdir)/os_win32/syslogevt.exe ]; then \
+@OS_WIN32_MINGW_TRUE@    cp -pv $(srcdir)/os_win32/syslogevt.exe $(exedir_win32)/syslogevt.exe; \
+@OS_WIN32_MINGW_TRUE@   else echo "Warning: $(srcdir)/os_win32/syslogevt.exe missing."; fi
+@OS_WIN32_MINGW_TRUE@  touch $@
+
+@OS_WIN32_MINGW_TRUE@$(exedir_win32)/%.exe: %.exe
+@OS_WIN32_MINGW_TRUE@  cp -p $< $@
+@OS_WIN32_MINGW_TRUE@  strip -s $@
+@OS_WIN32_MINGW_TRUE@  touch -r $< $@
+
+@OS_WIN32_MINGW_TRUE@$(docdir_win32)/%: %
+@OS_WIN32_MINGW_TRUE@  $(UNIX2DOS) < $< > $@
+@OS_WIN32_MINGW_TRUE@  touch -r $< $@
+
+@OS_WIN32_MINGW_TRUE@$(docdir_win32)/%.txt: $(srcdir)/%
+@OS_WIN32_MINGW_TRUE@  $(UNIX2DOS) < $< > $@
+@OS_WIN32_MINGW_TRUE@  touch -r $< $@
+
+@OS_WIN32_MINGW_TRUE@$(docdir_win32)/%.conf: $(srcdir)/%.conf
+@OS_WIN32_MINGW_TRUE@  $(UNIX2DOS) < $< > $@
+@OS_WIN32_MINGW_TRUE@  touch -r $< $@
+
+# Build config_vc6.h for MSVC 6 from MinGW config.h
+
+@OS_WIN32_MINGW_TRUE@config-vc6: $(srcdir)/os_win32/config_vc6.h
+
+@OS_WIN32_MINGW_TRUE@$(srcdir)/os_win32/config_vc6.h: config.h
+@OS_WIN32_MINGW_TRUE@  sed '1i/* config_vc6.h.  Generated by Makefile.  */' $< | \
+@OS_WIN32_MINGW_TRUE@  sed 's,^#define HAVE_\(ATTR_PACKED\|INTTYPES_H\|STDINT_H\|STRTOULL\|U*INT64_T\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */,' | \
+@OS_WIN32_MINGW_TRUE@  sed 's,i.86-pc-mingw32,i686-pc-win32vc6,' > $@
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..21aaf9e
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,230 @@
+smartmontools NEWS
+------------------
+CVS ID: $Id: NEWS,v 1.29 2006/04/12 15:46:30 ballen4705 Exp $
+
+The most up-to-date version of this file is:
+http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/NEWS?sortby=date&view=markup
+
+Date 2006-04-12
+Summary: smartmontools release 5.36 (STABLE)
+-----------------------------------------------------------
+This is a stable smartmontools release.  The 5.34 version
+described just below was never officially released because
+Bruce Allen decided to wait until Linux support for
+accessing SATA devices through libata was in the official
+kernel.org sources.  Changes include:
+
+- Win 2000/XP:ability to cancel drive self-tests
+- Additions to the table of known drives
+- FreeBSD support for 3ware char device interface and
+  multiple 3ware cards
+- Various cygwin improvements for running as service
+- Works 'out of the box' with Linux SATA libata
+- smartd option added to list test schedules
+- smartctl option added to list part of drive database
+- various improvements for SCSI disks and logs
+
+
+Date 2005-04-19
+Summary: smartmontools release 5.34 (STABLE)
+-----------------------------------------------------------
+This is a stable smartmontools release.  It includes:
+- OS/2 and eComStation support
+All Platforms:
+ - Printing of drive family info
+ - SCSI disks: output size of grown defect list
+ - Added info about drive family to 'smartctl -i' output.
+ - Added option ',q' for smartd '-n' directive to suppress
+   'skipping checks' message which may spin up laptop disk.
+ - Added message if smartd '-n' check power mode spins up disk.
+Cygwin and Windows:
+ - Added info about Windows version and service pack to banner line.
+ - Added support for smartd '-n' directive for Win2000/XP.
+ - Added support for READ_LOG for WinNT4 also.
+ - Fixed bug that prevents display of empty logs on Win2000/XP
+ - Fixed use of cached smart enabled state in 'smartctl -i' output. 
+Windows:
+ - Fixed bug that prevents running smartd as service on WinNT4.
+
+
+Date 2004-9-5
+Summary: smartmontools release 5.33 (UNSTABLE/EXPERIMENTAL)
+-----------------------------------------------------------
+This is an unstable/experimental release of smartmontools.  It includes
+ - support for Darwin/Mac OSX
+ - support for OpenBSD
+ - support for 3ware ATA RAID controllers under FreeBSD
+ - support for 3ware 9500 series SATA RAID controllers under
+   Linux.  Use /dev/twa[0-15] devices to access these.
+ - support for 3ware character device interfaces /dev/twe[0-15]
+   under Linux.  This allows (for example) Selective Self-tests.
+ - support for Marvell chip-set based SATA controllers under Linux.
+ - smartd mail now works also under Windows (using "Blat" mailer).
+ - smartd can now be run as a Windows service.
+Please report sucess/failure with these items to the
+smartmontools-support mailing list.
+
+
+Date 2004-7-5
+Summary: smartmontools release 5.32 (STABLE)
+-----------------------------------------------------------
+This is an stable release of smartmontools.
+Note added 2004/7/7: users building a Solaris/Intel version of the code should 
+modify the 'configure' file, changing "pc-*-solaris*" on line 106
+to read "*-pc-solaris*".  Reference:
+http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/configure.in?r1=1.83&r2=1.84
+
+
+Date: 2004-5-4
+Summary: smartmontools release 5.31 (UNSTABLE/EXPERIMENTAL)
+-----------------------------------------------------------
+This is an unstable/experimental release of smartmontools.  It includes
+several new features:
+- Windows smartd daemon
+- smartd now monitors current and pending sector counts
+- Support for ATA-7 selective self-test features (Linux/NetBSD only)
+  Please report sucess/failure with this option to the smartmontools-support
+  mailing list.
+
+Date: 2004-3-6
+Summary: smartmontools release 5.30 (STABLE)
+--------------------------------------------
+This is a stable release of smartmontools: the first stable release
+since 5.26.
+- KNOWN BUG (identified/fixed by CF): smartd will segv and crash if
+  the configuration file /etc/smartd.conf contains NO valid entries.
+  This bug was introduced in version 1.259 of smartd.c by BA and
+  is present in smartmontools releases 5.27-5.30 inclusive. This can
+  be fixed by editing line 3165 of smartd.c, and changing:
+  "else if (cfgentries[0]) {"
+  to read:
+  "else if (cfgentries && cfgentries[0]) {"
+
+
+Date: 2004-2-24
+Summary: smartmontools release 5.29 (Experimental, not STABLE)
+--------------------------------------------------------------
+This is another experimental release, to replace the 5.27 release that
+had a damaged configure script.  The next stable release will be 5.30
+- This release has SCSI support for NetBSD
+
+
+Date: 2004-2-12 
+Summary: smartmontools release 5.27 (Experimental, not STABLE)
+--------------------------------------------------------------
+- WARNING: this release has a broken --prefix=/a/path option to the
+  configure script.  The consequence is that smartd will not look for the
+  configuration file (smartd.conf) at the desired location.
+- NetBSD support added
+- A new Directive (-s) for smartd.conf now enables flexible automatic
+  scheduled self-testing for both ATA and SCSI devices.
+- Solaris now has ATA device support (SPARC only)
+- A new Directive (-n) for smartd.conf to avoid spinning up disks
+- Errors when smartd sends mail are now logged to SYSLOG
+- Solaris smartd mail now works correctly (uses mailx not mail)
+
+
+Date: 2003-11-29
+Summary: smartmontools release 5.26
+-----------------------------------
+This is a stable smartmontools release.  The only known problem is
+that under Solaris, the email features of smartd do not work 'out of
+the box'.  Three workarounds are:
+  [1] use '-M exec mailx' in /etc/smartd.conf
+  [2] in the start script for smartd, put /usr/ucb into PATH before
+      /bin
+  [3] upgrade to release 5.27 or later, or the latest CVS snapshot
+
+
+Date: 2003-11-19
+Summary: smartmontools release 5.25
+-----------------------------------
+This release should not hang when accessing USB devices. It provides
+smartd SCSI self-test log monitoring for self-test errors, and a
+larger table of known ATA drives.  DEVICESCAN should work correctly
+even on file systems containing XFS or JFS partitions, and on machines
+that use devfs, even without traditional links.
+
+From this time on, even numbered releases will be 'stable' ones and
+odd numbered releases (like 5.25) will be unstable/testing/development
+releases.
+
+
+Date: 2003-10-30
+Summary: smartmontools release 5.23
+-----------------------------------
+This release has one known problem: DEVICESCAN device scanning does
+not work correctly if the disk with the /dev directory also has XFS
+or JFS file systems on it.
+
+
+Date: 2003-10-28
+Summary: smartmontools release 5.22
+-----------------------------------
+Replaces flawed 5.21 release: the -T verypermissive option had to be
+entered as -T verpermissive. First experimental solaris support (SCSI
+only).  This release had a serious flaw: smartd left open file descriptors
+for devices that it couldn't monitor.
+
+
+Date: 2003-10-14
+Summary: smartmontools release 5.21
+-----------------------------------
+Preliminary support for FreeBSD added to smartmontools.  For FreeBSD,
+ATA support requires a 5.1-CURRENT kernel while SCSI support should
+work across multiple versions (any that support CAM).
+
+
+Date: 2003-10-04
+Summary: smartmontools release 5.20
+-----------------------------------
+Replaces flawed 5.19 release (which had a zero-length man page
+smartd.conf.5).
+
+
+Date: 2003-10-03
+Summary: smartmontools release 5.19
+-----------------------------------
+This is the first release of smartmontools based on autoconf/automake.
+For this reason, it is a very experimental release.  Please let us
+know in particular about documenation errors/omissions, missing or
+unneccesary files, and similar oversights.  The major changes are:
+ [1]  installation scripts based on autoconfig/automake
+ [2] ./configure [options] lets you set arbitrary paths
+ [3] supports FHS with ./configure --prefix=/usr/local
+ [4] correct paths are inserted into all man pages, binaries, etc.
+ [5] tarballs and RPMs are now GPG-signed
+
+
+Date: 2003-10-02 11:35
+Summary: smartd SEGV
+--------------------
+Some versions of smartd, including smartmontools release 5.1-18, will
+SEGV if the combination of Directives in /etc/smartd.conf contains
+-l error
+AND/OR
+-l selftest
+without any Attribute monitoring Directives.  This is fixed in 5.19
+and above.
+
+A good workaround is to add:
+-o on
+OR
+-o off
+to enable or disable automatic offline data collection.
+
+
+Date: 2002-11-17 07:41
+Summary: testunitready bug in smartd
+------------------------------------
+A bug in smartd prevented functioning on scsi devices.
+The bug in question only affects smartd users with scsi devices.
+To see if your version of smartd has the testunitready() bug, do
+smartd -V
+If the version of the module smartd.c in a line like:
+Module: smartd.c revision: 1.66 date: 2002/11/17
+has a revision greater than or equal to 1.30, and less than or
+equal to 1.64, then your version of the code has this problem.
+
+This problem affected releases starting with RELEASE_5_0_16 up to and
+including RELEASE_5_0_43.
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..6d6e1c8
--- /dev/null
+++ b/README
@@ -0,0 +1,125 @@
+==========================================================
+smartmontools - S.M.A.R.T. utility toolset for Darwin/Mac
+OSX, FreeBSD, Linux, NetBSD, OpenBSD, Solaris, and Windows.
+==========================================================
+
+$Id: README,v 1.55 2006/04/12 14:54:28 ballen4705 Exp $
+
+== HOME ==
+The home for smartmontools is located at:
+    
+    http://smartmontools.sourceforge.net/
+
+Please see this web site for updates, documentation, and for submitting
+patches and bug reports.
+
+You will find a mailing list for support and other questions at:
+
+    http://lists.sourceforge.net/lists/listinfo/smartmontools-support
+
+
+== COPYING ==
+Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+You should have received a copy of the GNU General Public License (for
+example COPYING); if not, write to the Free Software Foundation, Inc., 675
+Mass Ave, Cambridge, MA 02139, USA.
+
+
+== CREDITS ==
+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/
+
+
+== OVERVIEW ==
+smartmontools contains utilities that control and monitor storage
+devices using the Self-Monitoring, Analysis and Reporting Technology
+(S.M.A.R.T.) system build into ATA and SCSI Hard Drives. This is used
+to check the reliability of the hard drive and to predict drive
+failures.  smartmontools Version 5.x is designed to comply to the
+ATA/ATAPI-5 specification (Revision 1).  Future releases of
+smartmontools (Versions 6.x and 7.x) will comply with the ATA/ATAPI-6
+and ATA/ATAPI-7 specifications.
+
+This package is meant to be an up-to-date replacement for the
+ucsc-smartsuite and smartsuite packages, and is derived from that
+code.
+
+
+== CONTENTS ==
+The suite contains two utilities:
+
+smartctl is a command line utility designed to perform S.M.A.R.T. tasks
+        such as disk self-checks, and to report the S.M.A.R.T. status of
+        the disk.
+
+smartd   is a daemon that periodically monitors S.M.A.R.T. status and
+         reports errors and changes in S.M.A.R.T. attributes to syslog.
+
+
+== OBTAINING SMARTMONTOOLS ==
+
+Source tarballs
+---------------
+
+http://sourceforge.net/project/showfiles.php?group_id=64297
+
+CVS
+---
+
+cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools login (when prompted for a password, just press Enter)
+cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools co sm5
+
+This will create a subdirectory called sm5/ containing the code.
+
+To instead get the 5.1-16 release:
+
+cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/smartmontools co -r RELEASE_5_1_16 sm5
+
+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
+
+You can see what the different tags are by looking at
+http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/smartmontools/sm5/ .
+You'll see the tag names in the little scroll window where it says "Show
+only files with tag".
+
+== BUILDING/INSTALLING SMARTMONTOOLS ==
+
+Refer to the "INSTALL" file for detailed installation instructions.
+
+See the "WARNINGS" file for reports of hardware where these utilities
+might cause serious problems such as lockups.
+
+== GETTING STARTED ==
+
+To examine SMART data from a disk, try:
+  smartctl -a /dev/hda
+for ATA disks, or
+  smartctl -a /dev/sda
+for SCSI disks.  See the manual page 'man smartctl' for more
+information.
+
+To start automatic monitoring of your disks with the smartd daemon,
+try:
+  smartd -d
+to start the daemon in foreground (debug) mode, or
+  smartd
+to start the daemon in background mode.  This will log messages to
+SYSLOG.  If you would like to get email warning messages, please set
+up the configuration file smartd.conf with the '-m' mail warning
+Directive.  See the manual page 'man smartd' for more information.
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..1a5945d
--- /dev/null
+++ b/TODO
@@ -0,0 +1,142 @@
+TODO list for smartmontools:
+
+$Id: TODO,v 1.59 2005/12/11 18:40:35 ballen4705 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.
+
+SATA devices
+------------
+The ATA PASS THROUGH SCSI command (12 and 16 byte cdb) as defined in
+SAT: http://www.t10.org/ftp/t10/drafts/sat/sat-r07.pdf (section 12.2)
+provides a general way to pass ATA SMART commands through to SATA
+devices.
+
+Doug Gilbert will in the future add a '-d sat' type (note, this is not
+a typo, we do not mean '-d sata') which will instruct the generic
+smartmontools code to assume an ATA device but use those ATA PASS
+THROUGH commands.
+
+This should provide additional support for SATA devices under most or
+all operating systems.
+
+USB devices under Linux
+-----------------------
+Some USB devices can hang smartctl or smartd.  This is because these
+devices fail to comply with SCSI specifications for their packet
+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
+
+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.
+
+Modify the SMART self-test log table printing so that we ALSO print
+the value of the self-test failure checkpoint byte, if it's one of the
+recognized values.  See routine SelfTestFailureCodeName and
+documentation in atacmds.h.
+
+smartd:
+-------
+Perhaps change <nomailer> special argument to -m to have also
+<nomailer_fork> which would actually work with -M exec to run the
+executable/script in the background rather than in the foreground.
+But let's wait for someone to request this. At that point we should
+probably use fork/exec rather than system().
+
+Perhaps change smartd to look in /proc/ide and /proc/scsi to see what
+exists? If something doesn't exit then don't try to open it?  This
+should probably be the default option if there is no configuration
+file.
+
+Add ability to monitor "worst" value from attributes (sometimes it
+gets larger!) and to monitor the threshold value (sometimes it
+changes!).
+
+Add command line option that scans devices then WRITES
+/etc/smartd.conf, perhaps as /etc/smartd.conf.output, just for devices
+that can be monitored.
+
+FreeBSD
+-------
+
+Add support for 3ware 9000 series SATA controllers.
+
+Cygwin and Windows
+------------------
+Add IDE/ATA selective self test.
+
+Access SCSI devices via IOCTL_SCSI_PASS_THROUGH on 2000/XP
+to support systems with missing ASPI driver.
+
+Windows
+-------
+Provide some installer.
+
+Packaging
+---------
+Under freebsd and solaris, the following are wrong:
+smartd.conf: has linux device paths
+smart*.in  : man pages have (mostly) linux device paths
diff --git a/WARNINGS b/WARNINGS
new file mode 100644 (file)
index 0000000..21e23d8
--- /dev/null
+++ b/WARNINGS
@@ -0,0 +1,132 @@
+$Id: WARNINGS,v 1.32 2005/04/20 19:17:33 geoffk1 Exp $
+
+The most recent version of this file can be found here:
+http://cvs.sourceforge.net/viewcvs.py/smartmontools/sm5/WARNINGS?view=markup
+
+The following are reports of serious problems (eg system lockup) which
+were due to smartmontools.  There are DARWIN, LINUX, FREEBSD, SOLARIS
+and WINDOWS sections below.
+
+
+LINUX
+-----
+
+You may also wish to search the linux-kernel mailing list for problem
+reports concerning smartmontools.  Here is the URL:
+http://groups.google.com/groups?as_q=smartmontools&safe=images&ie=UTF-8&oe=UTF-8&as_ugroup=linux.kernel&lr=&num=100&hl=en
+
+SYSTEM:   Any system with USB ports and USB storage devices
+PROBLEM:  Using smartd/smartctl on USB "SCSI" storage devices can cause kernel hang
+REPORTER: see link below
+LINK:     https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=107615
+NOTE:     USB storage devices are handled as SCSI devices by the kernel. But many of these
+          devices do not comply with SCSI specs, and can cause the kernel to hang.
+          Avoid using smartd/smartctl on these devices (they don't do SMART anyway).
+          In particular, the use of smartd DEVICESCAN in /etc/smartd.conf can cause
+          these devices (typically represented by /dev/sda or /dev/sdb) to hang, and
+          the kernel to lock up.
+FIXED:    This problem should be fixed in smartmontools-5.25 and greater.
+
+
+SYSTEM:   Intel 875WP1-E motherboard with SATA drives on motherboard's SATA ports
+PROBLEM:  smartd makes NTP time drift
+REPORTER: nohez@cmie.com
+LINK:     http://groups.google.de/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&safe=off&selm=Pine.LNX.4.33.0310111545530.1047-100000%40venus.cmie.ernet.in.lucky.linux.kernel
+NOTE:     When using SATA disks, linux kernel k_smp-2.4.21-108 (SMP because
+          of hyper-threading) and xntp-4.1.1-177, the server time went
+          out of sync with system time.  Problem goes away when SATA
+          disks removed.
+
+
+SYSTEM:   Dell servers using AACRAID (SCSI)
+PROBLEM:  Locked up, needed to be rebooted
+REPORTER: drew@eastvan.bc.ca
+LINK:     http://sourceforge.net/mailarchive/forum.php?thread_id=1311313&forum_id=12495
+
+
+SYSTEM:   Box with Promise 20265 IDE-controller (pdc202xx-driver) and > 2.4.18 kernel with ide-taskfile support
+PROBLEM:  Smartctl locks system solid when used on /dev/hd[ef].
+REPORTER: Georg Acher <acher@in.tum.de>
+LINK:     http://sourceforge.net/mailarchive/forum.php?thread_id=1457979&forum_id=12495
+NOTE:     Lockup doesn't happen with 2.4.18 kernel, and doesn't affect /dev/hd[a-d]
+          This appears to be a problem with the pdc202xx-driver and has been reported
+          to the pdcx maintainers.  If you enable the Promise-BIOS (ATA100-BIOS) then
+          everything will work fine.  But if you disable it, then the machine will hang.
+
+
+SYSTEM:   Box with Promise 20262 IDE-controller
+PROBLEM:  Smartctl locks system solid
+REPORTER: Ben Low <ben@bdlow.net>
+LINK:     http://sourceforge.net/mailarchive/message.php?msg_id=5074201
+NOTE:     Similar to previous report: Promise Ultra66 2-port card (20262) which, with
+          linux 2.4.20, suffers from the lockups reported above.  But it was
+          impossible to enable the Promiste BIOS.  A kernel patch is referenced
+          to fix the problem.
+
+
+SYSTEM:   Promise 20265 IDE-controller
+PROBLEM:  Smartctl locks system solid when used on CDROM/DVD device
+REPORTER: see link below
+LINK:     http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=208964
+NOTE:     Problem seems to affect kernel 2.4.21 only.
+
+
+SYSTEM:   Promise IDE-controllers and perhaps others also
+PROBLEM:  System freezes under heavy load, perhaps when running SMART commands
+REPORTER: Mario 'BitKoenig' Holbe Mario.Holbe@RZ.TU-Ilmenau.DE
+LINK:     http://groups.google.de/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=1wUXW-2FA-9%40gated-at.bofh.it
+NOTE:     Before freezing, SYSLOG shows the following message(s)
+          kernel: hdf: dma timer expiry: dma status == 0xXX
+                 where XX is two hexidecimal digits. This may be a kernel bug
+          or an underlying hardware problem.  It's not clear if
+          smartmontools plays a role in provoking this problem.  FINAL
+          NOTE: Problem was COMPLETELY resolved by replacing the power
+          supply.  See URL above, entry on May 29, 2004 by Holbe.  Other
+          things to try are exchanging cables, and cleaning PCI slots.
+
+FREEBSD
+-------
+
+[No problem reports yet.]
+
+
+SOLARIS
+-------
+
+[No problem reports yet.]
+
+
+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.
+
+
+DARWIN
+------
+
+SYSTEM:   Any system before Tiger
+PROBLEM:  Can't switch off SMART, can't switch off auto-save, can't run
+          short tests.
+REPORTER: Geoff Keating <geoffk@geoffk.org>
+NOTE:     There's a bug in the system library: when you ask it to
+          do any of these things, it does the inverse (switches on,
+          runs extended tests).  Radar 3727283.
+
+SYSTEM:          All known systems
+PROBLEM:  When drive is asleep, SMART commands fail
+REPORTER: Geoff Keating <geoffk@geoffk.org>
+NOTE:    You can prevent the drive from sleeping by saying
+         pmset -a disksleep 0
+         or by unchecking the 'Put the hard disk(s) to sleep when possible'
+         checkbox in the Energy Saver preferences.  Radar 4094403.
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644 (file)
index 0000000..cf94d1b
--- /dev/null
@@ -0,0 +1,1116 @@
+# generated automatically by aclocal 1.9.3 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+# Free Software Foundation, Inc.
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+#                                                        -*- Autoconf -*-
+# Copyright (C) 2002, 2003  Free Software Foundation, Inc.
+# Generated from amversion.in; do not edit by hand.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+
+# AM_AUTOMAKE_VERSION(VERSION)
+# ----------------------------
+# Automake X.Y traces this macro to ensure aclocal.m4 has been
+# generated from the m4 files accompanying Automake X.Y.
+AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
+
+# AM_SET_CURRENT_AUTOMAKE_VERSION
+# -------------------------------
+# Call AM_AUTOMAKE_VERSION so it can be traced.
+# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
+AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
+        [AM_AUTOMAKE_VERSION([1.9.3])])
+
+# Figure out how to run the assembler.             -*- Autoconf -*-
+
+# serial 3
+
+# Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# AM_PROG_AS
+# ----------
+AC_DEFUN([AM_PROG_AS],
+[# By default we simply use the C compiler to build assembly code.
+AC_REQUIRE([AC_PROG_CC])
+test "${CCAS+set}" = set || CCAS=$CC
+test "${CCASFLAGS+set}" = set || CCASFLAGS=$CFLAGS
+AC_ARG_VAR([CCAS],      [assembler compiler command (defaults to CC)])
+AC_ARG_VAR([CCASFLAGS], [assembler compiler flags (defaults to CFLAGS)])
+])
+
+# AM_AUX_DIR_EXPAND
+
+# Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
+# $ac_aux_dir to `$srcdir/foo'.  In other projects, it is set to
+# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+#
+# Of course, Automake must honor this variable whenever it calls a
+# tool from the auxiliary directory.  The problem is that $srcdir (and
+# therefore $ac_aux_dir as well) can be either absolute or relative,
+# depending on how configure is run.  This is pretty annoying, since
+# it makes $ac_aux_dir quite unusable in subdirectories: in the top
+# source directory, any form will work fine, but in subdirectories a
+# relative path needs to be adjusted first.
+#
+# $ac_aux_dir/missing
+#    fails when called from a subdirectory if $ac_aux_dir is relative
+# $top_srcdir/$ac_aux_dir/missing
+#    fails if $ac_aux_dir is absolute,
+#    fails when called from a subdirectory in a VPATH build with
+#          a relative $ac_aux_dir
+#
+# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
+# are both prefixed by $srcdir.  In an in-source build this is usually
+# harmless because $srcdir is `.', but things will broke when you
+# start a VPATH build or use an absolute $srcdir.
+#
+# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
+# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
+#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
+# and then we would define $MISSING as
+#   MISSING="\${SHELL} $am_aux_dir/missing"
+# This will work as long as MISSING is not called from configure, because
+# unfortunately $(top_srcdir) has no meaning in configure.
+# However there are other variables, like CC, which are often used in
+# configure, and could therefore not use this "fixed" $ac_aux_dir.
+#
+# Another solution, used here, is to always expand $ac_aux_dir to an
+# absolute PATH.  The drawback is that using absolute paths prevent a
+# configured tree to be moved without reconfiguration.
+
+AC_DEFUN([AM_AUX_DIR_EXPAND],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])dnl
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+])
+
+# AM_CONDITIONAL                                              -*- Autoconf -*-
+
+# Copyright (C) 1997, 2000, 2001, 2003, 2004 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 6
+
+# AM_CONDITIONAL(NAME, SHELL-CONDITION)
+# -------------------------------------
+# Define a conditional.
+AC_DEFUN([AM_CONDITIONAL],
+[AC_PREREQ(2.52)dnl
+ ifelse([$1], [TRUE],  [AC_FATAL([$0: invalid condition: $1])],
+       [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+AC_SUBST([$1_TRUE])
+AC_SUBST([$1_FALSE])
+if $2; then
+  $1_TRUE=
+  $1_FALSE='#'
+else
+  $1_TRUE='#'
+  $1_FALSE=
+fi
+AC_CONFIG_COMMANDS_PRE(
+[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then
+  AC_MSG_ERROR([[conditional "$1" was never defined.
+Usually this means the macro was only invoked conditionally.]])
+fi])])
+
+# serial 7                                             -*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+
+# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# written in clear, in which case automake, when reading aclocal.m4,
+# will think it sees a *use*, and therefore will trigger all it's
+# C support machinery.  Also note that it means that autoscan, seeing
+# CC etc. in the Makefile, will ask for an AC_PROG_CC use...
+
+
+
+# _AM_DEPENDENCIES(NAME)
+# ----------------------
+# See how the compiler implements dependency checking.
+# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# We try a few techniques and use that to set a single cache variable.
+#
+# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
+# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular
+# dependency, and given that the user is not expected to run this macro,
+# just rely on AC_PROG_CC.
+AC_DEFUN([_AM_DEPENDENCIES],
+[AC_REQUIRE([AM_SET_DEPDIR])dnl
+AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
+AC_REQUIRE([AM_MAKE_INCLUDE])dnl
+AC_REQUIRE([AM_DEP_TRACK])dnl
+
+ifelse([$1], CC,   [depcc="$CC"   am_compiler_list=],
+       [$1], CXX,  [depcc="$CXX"  am_compiler_list=],
+       [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+       [$1], GCJ,  [depcc="$GCJ"  am_compiler_list='gcc3 gcc'],
+                   [depcc="$$1"   am_compiler_list=])
+
+AC_CACHE_CHECK([dependency style of $depcc],
+               [am_cv_$1_dependencies_compiler_type],
+[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_$1_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp`
+  fi
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+      # Solaris 8's {/usr,}/bin/sh.
+      touch sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    case $depmode in
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+       continue
+      else
+       break
+      fi
+      ;;
+    none) break ;;
+    esac
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.
+    if depmode=$depmode \
+       source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_$1_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_$1_dependencies_compiler_type=none
+fi
+])
+AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type])
+AM_CONDITIONAL([am__fastdep$1], [
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_$1_dependencies_compiler_type" = gcc3])
+])
+
+
+# AM_SET_DEPDIR
+# -------------
+# Choose a directory name for dependency files.
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+AC_DEFUN([AM_SET_DEPDIR],
+[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
+])
+
+
+# AM_DEP_TRACK
+# ------------
+AC_DEFUN([AM_DEP_TRACK],
+[AC_ARG_ENABLE(dependency-tracking,
+[  --disable-dependency-tracking  speeds up one-time build
+  --enable-dependency-tracking   do not reject slow dependency extractors])
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+fi
+AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
+AC_SUBST([AMDEPBACKSLASH])
+])
+
+# Generate code to set up dependency tracking.   -*- Autoconf -*-
+
+# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
+#   Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+#serial 2
+
+# _AM_OUTPUT_DEPENDENCY_COMMANDS
+# ------------------------------
+AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
+[for mf in $CONFIG_FILES; do
+  # Strip MF so we end up with the name of the file.
+  mf=`echo "$mf" | sed -e 's/:.*$//'`
+  # Check whether this is an Automake generated Makefile or not.
+  # We used to match only the files named `Makefile.in', but
+  # some people rename them; so instead we look at the file content.
+  # Grep'ing the first line is not enough: some people post-process
+  # each Makefile.in and add a new line on top of each file to say so.
+  # So let's grep whole file.
+  if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
+    dirpart=`AS_DIRNAME("$mf")`
+  else
+    continue
+  fi
+  # Extract the definition of DEPDIR, am__include, and am__quote
+  # from the Makefile without running `make'.
+  DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+  test -z "$DEPDIR" && continue
+  am__include=`sed -n 's/^am__include = //p' < "$mf"`
+  test -z "am__include" && continue
+  am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+  # When using ansi2knr, U may be empty or an underscore; expand it
+  U=`sed -n 's/^U = //p' < "$mf"`
+  # Find all dependency output files, they are included files with
+  # $(DEPDIR) in their names.  We invoke sed twice because it is the
+  # simplest approach to changing $(DEPDIR) to its actual value in the
+  # expansion.
+  for file in `sed -n "
+    s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+       sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+    # Make sure the directory exists.
+    test -f "$dirpart/$file" && continue
+    fdir=`AS_DIRNAME(["$file"])`
+    AS_MKDIR_P([$dirpart/$fdir])
+    # echo "creating $dirpart/$file"
+    echo '# dummy' > "$dirpart/$file"
+  done
+done
+])# _AM_OUTPUT_DEPENDENCY_COMMANDS
+
+
+# AM_OUTPUT_DEPENDENCY_COMMANDS
+# -----------------------------
+# This macro should only be invoked once -- use via AC_REQUIRE.
+#
+# This code is only required when automatic dependency tracking
+# is enabled.  FIXME.  This creates each `.P' file that we will
+# need in order to bootstrap the dependency handling code.
+AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
+[AC_CONFIG_COMMANDS([depfiles],
+     [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS],
+     [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"])
+])
+
+# Like AC_CONFIG_HEADER, but automatically create stamp file. -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 7
+
+# AM_CONFIG_HEADER is obsolete.  It has been replaced by AC_CONFIG_HEADERS.
+AU_DEFUN([AM_CONFIG_HEADER], [AC_CONFIG_HEADERS($@)])
+
+# Do all the work for Automake.                            -*- Autoconf -*-
+
+# This macro actually does too much some checks are only needed if
+# your package does certain things.  But this isn't really a big deal.
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 11
+
+# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
+# AM_INIT_AUTOMAKE([OPTIONS])
+# -----------------------------------------------
+# The call with PACKAGE and VERSION arguments is the old style
+# call (pre autoconf-2.50), which is being phased out.  PACKAGE
+# and VERSION should now be passed to AC_INIT and removed from
+# the call to AM_INIT_AUTOMAKE.
+# We support both call styles for the transition.  After
+# the next Automake release, Autoconf can make the AC_INIT
+# arguments mandatory, and then we can depend on a new Autoconf
+# release and drop the old call support.
+AC_DEFUN([AM_INIT_AUTOMAKE],
+[AC_PREREQ([2.58])dnl
+dnl Autoconf wants to disallow AM_ names.  We explicitly allow
+dnl the ones we care about.
+m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
+AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
+AC_REQUIRE([AC_PROG_INSTALL])dnl
+# test to see if srcdir already configured
+if test "`cd $srcdir && pwd`" != "`pwd`" &&
+   test -f $srcdir/config.status; then
+  AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+AC_SUBST([CYGPATH_W])
+
+# Define the identity of the package.
+dnl Distinguish between old-style and new-style calls.
+m4_ifval([$2],
+[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+ AC_SUBST([PACKAGE], [$1])dnl
+ AC_SUBST([VERSION], [$2])],
+[_AM_SET_OPTIONS([$1])dnl
+ AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
+ AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
+
+_AM_IF_OPTION([no-define],,
+[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
+ AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+
+# Some tools Automake needs.
+AC_REQUIRE([AM_SANITY_CHECK])dnl
+AC_REQUIRE([AC_ARG_PROGRAM])dnl
+AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
+AM_MISSING_PROG(AUTOCONF, autoconf)
+AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
+AM_MISSING_PROG(AUTOHEADER, autoheader)
+AM_MISSING_PROG(MAKEINFO, makeinfo)
+AM_PROG_INSTALL_SH
+AM_PROG_INSTALL_STRIP
+AC_REQUIRE([AM_PROG_MKDIR_P])dnl
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+AC_REQUIRE([AC_PROG_AWK])dnl
+AC_REQUIRE([AC_PROG_MAKE_SET])dnl
+AC_REQUIRE([AM_SET_LEADING_DOT])dnl
+_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
+              [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])],
+                            [_AM_PROG_TAR([v7])])])
+_AM_IF_OPTION([no-dependencies],,
+[AC_PROVIDE_IFELSE([AC_PROG_CC],
+                  [_AM_DEPENDENCIES(CC)],
+                  [define([AC_PROG_CC],
+                          defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_CXX],
+                  [_AM_DEPENDENCIES(CXX)],
+                  [define([AC_PROG_CXX],
+                          defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+])
+])
+
+
+# When config.status generates a header, we must update the stamp-h file.
+# This file resides in the same directory as the config header
+# that is generated.  The stamp files are numbered to have different names.
+
+# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the
+# loop where config.status creates the headers, so we can generate
+# our stamp files there.
+AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK],
+[# Compute $1's index in $config_headers.
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $1 | $1:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count])
+
+# AM_PROG_INSTALL_SH
+# ------------------
+# Define $install_sh.
+
+# Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+AC_DEFUN([AM_PROG_INSTALL_SH],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+install_sh=${install_sh-"$am_aux_dir/install-sh"}
+AC_SUBST(install_sh)])
+
+#                                                          -*- Autoconf -*-
+# Copyright (C) 2003  Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 1
+
+# Check whether the underlying file-system supports filenames
+# with a leading dot.  For instance MS-DOS doesn't.
+AC_DEFUN([AM_SET_LEADING_DOT],
+[rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+AC_SUBST([am__leading_dot])])
+
+# Add --enable-maintainer-mode option to configure.
+# From Jim Meyering
+
+# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004
+# Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 3
+
+AC_DEFUN([AM_MAINTAINER_MODE],
+[AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
+  dnl maintainer-mode is disabled by default
+  AC_ARG_ENABLE(maintainer-mode,
+[  --enable-maintainer-mode  enable make rules and dependencies not useful
+                         (and sometimes confusing) to the casual installer],
+      USE_MAINTAINER_MODE=$enableval,
+      USE_MAINTAINER_MODE=no)
+  AC_MSG_RESULT([$USE_MAINTAINER_MODE])
+  AM_CONDITIONAL(MAINTAINER_MODE, [test $USE_MAINTAINER_MODE = yes])
+  MAINT=$MAINTAINER_MODE_TRUE
+  AC_SUBST(MAINT)dnl
+]
+)
+
+AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE])
+
+# Check to see how 'make' treats includes.     -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 2
+
+# AM_MAKE_INCLUDE()
+# -----------------
+# Check to see how make treats includes.
+AC_DEFUN([AM_MAKE_INCLUDE],
+[am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+       @echo done
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+AC_MSG_CHECKING([for style of include used by $am_make])
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# We grep out `Entering directory' and `Leaving directory'
+# messages which can occur if `w' ends up in MAKEFLAGS.
+# In particular we don't look at `^make:' because GNU make might
+# be invoked under some other name (usually "gmake"), in which
+# case it prints its new name instead of `make'.
+if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
+   am__include=include
+   am__quote=
+   _am_result=GNU
+fi
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
+      am__include=.include
+      am__quote="\""
+      _am_result=BSD
+   fi
+fi
+AC_SUBST([am__include])
+AC_SUBST([am__quote])
+AC_MSG_RESULT([$_am_result])
+rm -f confinc confmf
+])
+
+#  -*- Autoconf -*-
+
+
+# Copyright (C) 1997, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 3
+
+# AM_MISSING_PROG(NAME, PROGRAM)
+# ------------------------------
+AC_DEFUN([AM_MISSING_PROG],
+[AC_REQUIRE([AM_MISSING_HAS_RUN])
+$1=${$1-"${am_missing_run}$2"}
+AC_SUBST($1)])
+
+
+# AM_MISSING_HAS_RUN
+# ------------------
+# Define MISSING if not defined so far and test if it supports --run.
+# If it does, set am_missing_run to use it, otherwise, to nothing.
+AC_DEFUN([AM_MISSING_HAS_RUN],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+  am_missing_run="$MISSING --run "
+else
+  am_missing_run=
+  AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+
+# AM_PROG_MKDIR_P
+# ---------------
+# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise.
+
+# Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories
+# created by `make install' are always world readable, even if the
+# installer happens to have an overly restrictive umask (e.g. 077).
+# This was a mistake.  There are at least two reasons why we must not
+# use `-m 0755':
+#   - it causes special bits like SGID to be ignored,
+#   - it may be too restrictive (some setups expect 775 directories).
+#
+# Do not use -m 0755 and let people choose whatever they expect by
+# setting umask.
+#
+# We cannot accept any implementation of `mkdir' that recognizes `-p'.
+# Some implementations (such as Solaris 8's) are not thread-safe: if a
+# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c'
+# concurrently, both version can detect that a/ is missing, but only
+# one can create it and the other will error out.  Consequently we
+# restrict ourselves to GNU make (using the --version option ensures
+# this.)
+AC_DEFUN([AM_PROG_MKDIR_P],
+[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
+  # We used to keeping the `.' as first argument, in order to
+  # allow $(mkdir_p) to be used without argument.  As in
+  #   $(mkdir_p) $(somedir)
+  # where $(somedir) is conditionally defined.  However this is wrong
+  # for two reasons:
+  #  1. if the package is installed by a user who cannot write `.'
+  #     make install will fail,
+  #  2. the above comment should most certainly read
+  #     $(mkdir_p) $(DESTDIR)$(somedir)
+  #     so it does not work when $(somedir) is undefined and
+  #     $(DESTDIR) is not.
+  #  To support the latter case, we have to write
+  #     test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
+  #  so the `.' trick is pointless.
+  mkdir_p='mkdir -p --'
+else
+  # On NextStep and OpenStep, the `mkdir' command does not
+  # recognize any option.  It will interpret all options as
+  # directories to create, and then abort because `.' already
+  # exists.
+  for d in ./-p ./--version;
+  do
+    test -d $d && rmdir $d
+  done
+  # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
+  if test -f "$ac_aux_dir/mkinstalldirs"; then
+    mkdir_p='$(mkinstalldirs)'
+  else
+    mkdir_p='$(install_sh) -d'
+  fi
+fi
+AC_SUBST([mkdir_p])])
+
+# Helper functions for option handling.                    -*- Autoconf -*-
+
+# Copyright (C) 2001, 2002, 2003  Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 2
+
+# _AM_MANGLE_OPTION(NAME)
+# -----------------------
+AC_DEFUN([_AM_MANGLE_OPTION],
+[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
+
+# _AM_SET_OPTION(NAME)
+# ------------------------------
+# Set option NAME.  Presently that only means defining a flag for this option.
+AC_DEFUN([_AM_SET_OPTION],
+[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+
+# _AM_SET_OPTIONS(OPTIONS)
+# ----------------------------------
+# OPTIONS is a space-separated list of Automake options.
+AC_DEFUN([_AM_SET_OPTIONS],
+[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
+
+# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET])
+# -------------------------------------------
+# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
+AC_DEFUN([_AM_IF_OPTION],
+[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
+
+#
+# Check to make sure that the build environment is sane.
+#
+
+# Copyright (C) 1996, 1997, 2000, 2001, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 3
+
+# AM_SANITY_CHECK
+# ---------------
+AC_DEFUN([AM_SANITY_CHECK],
+[AC_MSG_CHECKING([whether build environment is sane])
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
+   if test "$[*]" = "X"; then
+      # -L didn't work.
+      set X `ls -t $srcdir/configure conftest.file`
+   fi
+   rm -f conftest.file
+   if test "$[*]" != "X $srcdir/configure conftest.file" \
+      && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      AC_MSG_ERROR([ls -t appears to fail.  Make sure there is not a broken
+alias in your environment])
+   fi
+
+   test "$[2]" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   AC_MSG_ERROR([newly created file is older than distributed files!
+Check your system clock])
+fi
+AC_MSG_RESULT(yes)])
+
+# AM_PROG_INSTALL_STRIP
+
+# Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# One issue with vendor `install' (even GNU) is that you can't
+# specify the program used to strip binaries.  This is especially
+# annoying in cross-compiling environments, where the build's strip
+# is unlikely to handle the host's binaries.
+# Fortunately install-sh will honor a STRIPPROG variable, so we
+# always use install-sh in `make install-strip', and initialize
+# STRIPPROG with the value of the STRIP variable (set by the user).
+AC_DEFUN([AM_PROG_INSTALL_STRIP],
+[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'.  However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+if test "$cross_compiling" != no; then
+  AC_CHECK_TOOL([STRIP], [strip], :)
+fi
+INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
+AC_SUBST([INSTALL_STRIP_PROGRAM])])
+
+# Check how to create a tarball.                            -*- Autoconf -*-
+
+# Copyright (C) 2004  Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# serial 1
+
+
+# _AM_PROG_TAR(FORMAT)
+# --------------------
+# Check how to create a tarball in format FORMAT.
+# FORMAT should be one of `v7', `ustar', or `pax'.
+#
+# Substitute a variable $(am__tar) that is a command
+# writing to stdout a FORMAT-tarball containing the directory
+# $tardir.
+#     tardir=directory && $(am__tar) > result.tar
+#
+# Substitute a variable $(am__untar) that extract such
+# a tarball read from stdin.
+#     $(am__untar) < result.tar
+AC_DEFUN([_AM_PROG_TAR],
+[# Always define AMTAR for backward compatibility.
+AM_MISSING_PROG([AMTAR], [tar])
+m4_if([$1], [v7],
+     [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
+     [m4_case([$1], [ustar],, [pax],,
+              [m4_fatal([Unknown tar format])])
+AC_MSG_CHECKING([how to create a $1 tar archive])
+# Loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
+_am_tools=${am_cv_prog_tar_$1-$_am_tools}
+# Do not fold the above two line into one, because Tru64 sh and
+# Solaris sh will not grok spaces in the rhs of `-'.
+for _am_tool in $_am_tools
+do
+  case $_am_tool in
+  gnutar)
+    for _am_tar in tar gnutar gtar;
+    do
+      AM_RUN_LOG([$_am_tar --version]) && break
+    done
+    am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+    am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+    am__untar="$_am_tar -xf -"
+    ;;
+  plaintar)
+    # Must skip GNU tar: if it does not support --format= it doesn't create
+    # ustar tarball either.
+    (tar --version) >/dev/null 2>&1 && continue
+    am__tar='tar chf - "$$tardir"'
+    am__tar_='tar chf - "$tardir"'
+    am__untar='tar xf -'
+    ;;
+  pax)
+    am__tar='pax -L -x $1 -w "$$tardir"'
+    am__tar_='pax -L -x $1 -w "$tardir"'
+    am__untar='pax -r'
+    ;;
+  cpio)
+    am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+    am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+    am__untar='cpio -i -H $1 -d'
+    ;;
+  none)
+    am__tar=false
+    am__tar_=false
+    am__untar=false
+    ;;
+  esac
+
+  # If the value was cached, stop now.  We just wanted to have am__tar
+  # and am__untar set.
+  test -n "${am_cv_prog_tar_$1}" && break
+
+  # tar/untar a dummy directory, and stop if the command works
+  rm -rf conftest.dir
+  mkdir conftest.dir
+  echo GrepMe > conftest.dir/file
+  AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+  rm -rf conftest.dir
+  if test -s conftest.tar; then
+    AM_RUN_LOG([$am__untar <conftest.tar])
+    grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+  fi
+done
+rm -rf conftest.dir
+
+AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+AC_SUBST([am__tar])
+AC_SUBST([am__untar])
+]) # _AM_PROG_TAR
+
diff --git a/atacmdnames.c b/atacmdnames.c
new file mode 100644 (file)
index 0000000..52b68f1
--- /dev/null
@@ -0,0 +1,520 @@
+/*
+ * atacmdnames.c
+ *
+ * This module is based on the T13/1532D Volume 1 Revision 3 (ATA/ATAPI-7)
+ * specification, which is available from http://www.t13.org/#FTP_site
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ * Address of support mailing list: smartmontools-support@lists.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Philip Williams
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "atacmdnames.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+#define COMMAND_TABLE_SIZE 256
+
+const char *atacmdnames_c_cvsid="$Id: atacmdnames.c,v 1.13 2006/04/12 14:54:28 ballen4705 Exp $" ATACMDNAMES_H_CVSID;
+
+const char cmd_reserved[]        = "[RESERVED]";
+const char cmd_vendor_specific[] = "[VENDOR SPECIFIC]";
+const char cmd_reserved_sa[]     = "[RESERVED FOR SERIAL ATA]";
+const char cmd_reserved_cf[]     = "[RESERVED FOR COMPACTFLASH ASSOCIATION]";
+const char cmd_reserved_mcpt[]   = "[RESERVED FOR MEDIA CARD PASS THROUGH]";
+const char cmd_recalibrate_ret4[]= "RECALIBRATE [RET-4]";
+const char cmd_seek_ret4[]       = "SEEK [RET-4]";
+
+const char *command_table[COMMAND_TABLE_SIZE] = {
+/*-------------------------------------------------- 00h-0Fh -----*/
+  "NOP",
+  cmd_reserved,
+  cmd_reserved,
+  "CFA REQUEST EXTENDED ERROR CODE",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  "DEVICE RESET",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- 10h-1Fh -----*/
+  "RECALIBRATE [OBS-4]",
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+  cmd_recalibrate_ret4,
+/*-------------------------------------------------- 20h-2Fh -----*/
+  "READ SECTOR(S)",
+  "READ SECTOR(S) [OBS-5]",
+  "READ LONG (w/ retry) [OBS-4]",
+  "READ LONG (w/o retry) [OBS-4]",
+  "READ SECTOR(S) EXT",
+  "READ DMA EXT",
+  "READ DMA QUEUED EXT",
+  "READ NATIVE MAX ADDRESS EXT",
+  cmd_reserved,
+  "READ MULTIPLE EXT",
+  "READ STREAM DMA",
+  "READ STREAM PIO",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  "READ LOG EXT",
+/*-------------------------------------------------- 30h-3Fh -----*/
+  "WRITE SECTOR(S)",
+  "WRITE SECTOR(S) [OBS-5]",
+  "WRITE LONG(w/ retry) [OBS-4]",
+  "WRITE LONG(w/o retry) [OBS-4]",
+  "WRITE SECTORS(S) EXT",
+  "WRITE DMA EXT",
+  "WRITE DMA QUEUED EXT",
+  "SET MAX ADDRESS EXT",
+  "CFA WRITE SECTORS WITHOUT ERASE",
+  "WRITE MULTIPLE EXT",
+  "WRITE STREAM DMA",
+  "WRITE STREAM PIO",
+  "WRITE VERIFY [OBS-4]",
+  "WRITE DMA FUA EXT",
+  "WRITE DMA QUEUED FUA EXT",
+  "WRITE LOG EXT",
+/*-------------------------------------------------- 40h-4Fh -----*/
+  "READ VERIFY SECTOR(S)",
+  "READ VERIFY SECTOR(S) [OBS-5]",
+  "READ VERIFY SECTOR(S) EXT",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- 50h-5Fh -----*/
+  "FORMAT TRACK [OBS-4]",
+  "CONFIGURE STREAM",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- 60h-6Fh -----*/
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved_sa,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- 70h-7Fh -----*/
+  "SEEK [OBS-7]",
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+  cmd_seek_ret4,
+/*-------------------------------------------------- 80h-8Fh -----*/
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  "CFA TRANSLATE SECTOR [VS IF NO CFA]",
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+/*-------------------------------------------------- 90h-9Fh -----*/
+  "EXECUTE DEVICE DIAGNOSTIC",
+  "INITIALIZE DEVICE PARAMETERS [OBS-6]",
+  "DOWNLOAD MICROCODE",
+  cmd_reserved,
+  "STANDBY IMMEDIATE [RET-4]",
+  "IDLE IMMEDIATE [RET-4]",
+  "STANDBY [RET-4]",
+  "IDLE [RET-4]",
+  "CHECK POWER MODE [RET-4]",
+  "SLEEP [RET-4]",
+  cmd_vendor_specific,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- A0h-AFh -----*/
+  "PACKET",
+  "IDENTIFY PACKET DEVICE",
+  "SERVICE",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+/*-------------------------------------------------- B0h-BFh -----*/
+  "SMART",
+  "DEVICE CONFIGURATION",
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+  cmd_reserved_cf,
+/*-------------------------------------------------- C0h-CFh -----*/
+  "CFA ERASE SECTORS [VS IF NO CFA]",
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  "READ MULTIPLE",
+  "WRITE MULTIPLE",
+  "SET MULTIPLE MODE",
+  "READ DMA QUEUED",
+  "READ DMA",
+  "READ DMA [OBS-5]",
+  "WRITE DMA",
+  "WRITE DMA [OBS-5]",
+  "WRITE DMA QUEUED",
+  "CFA WRITE MULTIPLE WITHOUT ERASE",
+  "WRITE MULTIPLE FUA EXT",
+  cmd_reserved,
+/*-------------------------------------------------- D0h-DFh -----*/
+  cmd_reserved,
+  "CHECK MEDIA CARD TYPE",
+  cmd_reserved_mcpt,
+  cmd_reserved_mcpt,
+  cmd_reserved_mcpt,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  cmd_reserved,
+  "GET MEDIA STATUS",
+  "ACKNOWLEDGE MEDIA CHANGE [RET-4]",
+  "BOOT POST-BOOT [RET-4]",
+  "BOOT PRE-BOOT [RET-4]",
+  "MEDIA LOCK",
+  "MEDIA UNLOCK",
+/*-------------------------------------------------- E0h-EFh -----*/
+  "STANDBY IMMEDIATE",
+  "IDLE IMMEDIATE",
+  "STANDBY",
+  "IDLE",
+  "READ BUFFER",
+  "CHECK POWER MODE",
+  "SLEEP",
+  "FLUSH CACHE",
+  "WRITE BUFFER",
+  "WRITE SAME [RET-4]",  /* Warning!  This command is retired but the value of
+                            f_reg is used in look_up_ata_command().  If this
+                            command code is reclaimed in a future standard then
+                            be sure to update look_up_ata_command(). */
+  "FLUSH CACHE EXIT",
+  cmd_reserved,
+  "IDENTIFY DEVICE",
+  "MEDIA EJECT",
+  "IDENTIFY DEVICE DMA [OBS-4]",
+  "SET FEATURES",
+/*-------------------------------------------------- F0h-FFh -----*/
+  cmd_vendor_specific,
+  "SECURITY SET PASSWORD",
+  "SECURITY UNLOCK",
+  "SECURITY ERASE PREPARE",
+  "SECURITY ERASE UNIT",
+  "SECURITY FREEZE LOCK",
+  "SECURITY DISABLE PASSWORD",
+  cmd_vendor_specific,
+  "READ NATIVE MAX ADDRESS",
+  "SET MAX",
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific,
+  cmd_vendor_specific
+};
+
+/* Returns the name of the command (and possibly sub-command) with the given
+   command code and feature register values.   For most command codes this
+   simply returns the corresponding entry in the command_table array, but for
+   others the value of the feature register specifies a subcommand or
+   distinguishes commands. */
+const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg) {
+
+  // check that command table not messed up.  The compiler will issue
+  // warnings if there are too many array elements, but won't issue
+  // warnings if there are not enough of them.
+  if (sizeof(command_table) != sizeof(char *)*COMMAND_TABLE_SIZE){
+    fprintf(stderr, 
+            "Problem in atacmdnames.c.  Command Table command_table[] does\n"
+            "not have %d entries!  It has %d entries. Please fix it.\n",
+            COMMAND_TABLE_SIZE, (int)(sizeof(command_table)/sizeof(char *)));
+    abort();
+  }
+
+  switch (c_code) {
+  case 0x00:  /* NOP */
+    switch (f_reg) {
+    case 0x00:
+      return "NOP [Abort queued commands]";
+    case 0x01:
+      return "NOP [Don't abort queued commands]";
+    default:
+      return "NOP [Reserved subcommand]";
+    }
+  case 0x92:  /* DOWNLOAD MICROCODE */
+    switch (f_reg) {
+    case 0x01:
+      return "DOWNLOAD MICROCODE [Temporary]";
+    case 0x07:
+      return "DOWNLOAD MICROCODE [Save]";
+    default:
+      return "DOWNLOAD MICROCODE [Reserved subcommand]";
+    }
+  case 0xB0:  /* SMART */
+    switch (f_reg) {
+    case 0xD0:
+      return "SMART READ DATA";
+    case 0xD1:
+      return "SMART READ ATTRIBUTE THRESHOLDS [OBS-4]";
+    case 0xD2:
+      return "SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE";
+    case 0xD3:
+      return "SMART SAVE ATTRIBUTE VALUES [OBS-6]";
+    case 0xD4:
+      return "SMART EXECUTE OFF-LINE IMMEDIATE";
+    case 0xD5:
+      return "SMART READ LOG";
+    case 0xD6:
+      return "SMART WRITE LOG";
+    case 0xD7:
+      return "SMART WRITE ATTRIBUTE THRESHOLDS [NS, OBS-4]";
+    case 0xD8:
+      return "SMART ENABLE OPERATIONS";
+    case 0xD9:
+      return "SMART DISABLE OPERATIONS";
+    case 0xDA:
+      return "SMART RETURN STATUS";
+    case 0xDB:
+      return "SMART EN/DISABLE AUTO OFFLINE [NS (SFF-8035i)]";
+    default:
+        if (f_reg >= 0xE0)
+          return "[Vendor specific SMART command]";
+        else
+          return "[Reserved SMART command]";
+    }
+  case 0xB1:  /* DEVICE CONFIGURATION */
+    switch (f_reg) {
+    case 0xC0:
+      return "DEVICE CONFIGURATION RESTORE";
+    case 0xC1:
+      return "DEVICE CONFIGURATION FREEZE LOCK";
+    case 0xC2:
+      return "DEVICE CONFIGURATION IDENTIFY";
+    case 0xC3:
+      return "DEVICE CONFIGURATION SET";
+    default:
+      return "DEVICE CONFIGURATION [Reserved command]";
+    }
+  case 0xE9:  /* WRITE SAME */
+    switch (f_reg) {
+    case 0x22:
+      return "WRITE SAME [Start specified] [RET-4]";
+    case 0xDD:
+      return "WRITE SAME [Start unspecified] [RET-4]";
+    default:
+      return "WRITE SAME [Invalid subcommand] [RET-4]";
+    } 
+  case 0xEF:  /* SET FEATURES */
+    switch (f_reg) {
+    case 0x01:
+      return "SET FEATURES [Enable 8-bit PIO]";
+    case 0x02:
+      return "SET FEATURES [Enable write cache]";
+    case 0x03:
+      return "SET FEATURES [Set transfer mode]";
+    case 0x04:
+      return "SET FEATURES [Enable auto DR] [OBS-4]";
+    case 0x05:
+      return "SET FEATURES [Enable APM]";
+    case 0x06:
+      return "SET FEATURES [Enable Pwr-Up In Standby]";
+    case 0x07:
+      return "SET FEATURES [Set device spin-up]";
+    case 0x09:
+      return "SET FEATURES [Reserved (address offset)]";
+    case 0x0A:
+      return "SET FEATURES [Enable CFA power mode 1]";
+    case 0x10:
+      return "SET FEATURES [Reserved for Serial ATA]";
+    case 0x20:
+      return "SET FEATURES [Set Time-ltd R/W WCT]";
+    case 0x21:
+      return "SET FEATURES [Set Time-ltd R/W EH]";
+    case 0x31:
+      return "SET FEATURES [Disable Media Status Notf]";
+    case 0x33:
+      return "SET FEATURES [Disable retry] [OBS-4]";
+    case 0x42:
+      return "SET FEATURES [Enable AAM]";
+    case 0x43:
+      return "SET FEATURES [Set Max Host I/F S Times]";
+    case 0x44:
+      return "SET FEATURES [Length of VS data] [OBS-4]";
+    case 0x54:
+      return "SET FEATURES [Set cache segs] [OBS-4]";
+    case 0x55:
+      return "SET FEATURES [Disable read look-ahead]";
+    case 0x5D:
+      return "SET FEATURES [Enable release interrupt]";
+    case 0x5E:
+      return "SET FEATURES [Enable SERVICE interrupt]";
+    case 0x66:
+      return "SET FEATURES [Disable revert defaults]";
+    case 0x77:
+      return "SET FEATURES [Disable ECC] [OBS-4]";
+    case 0x81:
+      return "SET FEATURES [Disable 8-bit PIO]";
+    case 0x82:
+      return "SET FEATURES [Disable write cache]";
+    case 0x84:
+      return "SET FEATURES [Disable auto DR] [OBS-4]";
+    case 0x85:
+      return "SET FEATURES [Disable APM]";
+    case 0x86:
+      return "SET FEATURES [Disable Pwr-Up In Standby]";
+    case 0x88:
+      return "SET FEATURES [Disable ECC] [OBS-4]";
+    case 0x89:
+      return "SET FEATURES [Reserved (address offset)]";
+    case 0x8A:
+      return "SET FEATURES [Disable CFA power mode 1]";
+    case 0x90:
+      return "SET FEATURES [Reserved for Serial ATA]";
+    case 0x95:
+      return "SET FEATURES [Enable Media Status Notf]";
+    case 0x99:
+      return "SET FEATURES [Enable retries] [OBS-4]";
+    case 0x9A:
+      return "SET FEATURES [Set max avg curr] [OBS-4]";
+    case 0xAA:
+      return "SET FEATURES [Enable read look-ahead]";
+    case 0xAB:
+      return "SET FEATURES [Set max prefetch] [OBS-4]";
+    case 0xBB:
+      return "SET FEATURES [4 bytes VS data] [OBS-4]";
+    case 0xC2:
+      return "SET FEATURES [Disable AAM]";
+    case 0xCC:
+      return "SET FEATURES [Enable revert to defaults]";
+    case 0xDD:
+      return "SET FEATURES [Disable release interrupt]";
+    case 0xDE:
+      return "SET FEATURES [Disable SERVICE interrupt]";
+    case 0xE0:
+      return "SET FEATURES [Obsolete subcommand]";
+    default:
+      if (f_reg >= 0xF0)
+        return "SET FEATURES [Reserved for CFA]";
+      else
+        return "SET FEATURES [Reserved subcommand]";
+    }
+  case 0xF9:  /* SET MAX */
+    switch (f_reg) {
+    case 0x00:
+      return "SET MAX ADDRESS [OBS-6]";
+    case 0x01:
+      return "SET MAX SET PASSWORD";
+    case 0x02:
+      return "SET MAX LOCK";
+    case 0x03:
+      return "SET MAX UNLOCK";
+    case 0x04:
+      return "SET MAX FREEZE LOCK";
+    default:
+      return "[Reserved SET MAX command]";
+    }
+  default:
+    return command_table[c_code];
+  }
+}
diff --git a/atacmdnames.h b/atacmdnames.h
new file mode 100644 (file)
index 0000000..810cf94
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * atacmdnames.h
+ *
+ * This module is based on the T13/1532D Volume 1 Revision 3 (ATA/ATAPI-7)
+ * specification, which is available from http://www.t13.org/#FTP_site
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ * Address of support mailing list: smartmontools-support@lists.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Philip Williams
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef ATACMDNAMES_H_
+#define ATACMDNAMES_H_
+
+#define ATACMDNAMES_H_CVSID "$Id: atacmdnames.h,v 1.5 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+/* Returns the name of the command (and possibly sub-command) with the given
+   command code and feature register values. */
+const char *look_up_ata_command(unsigned char c_code, unsigned char f_reg);
+
+#endif
diff --git a/atacmds.c b/atacmds.c
new file mode 100644 (file)
index 0000000..4a74f40
--- /dev/null
+++ b/atacmds.c
@@ -0,0 +1,1887 @@
+/*
+ * atacmds.c
+ * 
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
+ * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ * 
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "extern.h"
+#include "utility.h"
+
+const char *atacmds_c_cvsid="$Id: atacmds.c,v 1.168 2006/04/12 17:01:46 ballen4705 Exp $"
+ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID UTILITY_H_CVSID;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// for passing global control variables
+extern smartmonctrl *con;
+
+// These Drive Identity tables are taken from hdparm 5.2, and are also
+// given in the ATA/ATAPI specs for the IDENTIFY DEVICE command.  Note
+// that SMART was first added into the ATA/ATAPI-3 Standard with
+// Revision 3 of the document, July 25, 1995.  Look at the "Document
+// Status" revision commands at the beginning of
+// http://www.t13.org/project/d2008r6.pdf to see this.
+#define NOVAL_0                 0x0000
+#define NOVAL_1                 0xffff
+/* word 81: minor version number */
+#define MINOR_MAX 0x22
+const char *minor_str[] = {                     /* word 81 value: */
+  "Device does not report version",             /* 0x0000       */
+  "ATA-1 X3T9.2 781D prior to revision 4",      /* 0x0001       */
+  "ATA-1 published, ANSI X3.221-1994",          /* 0x0002       */
+  "ATA-1 X3T9.2 781D revision 4",               /* 0x0003       */
+  "ATA-2 published, ANSI X3.279-1996",          /* 0x0004       */
+  "ATA-2 X3T10 948D prior to revision 2k",      /* 0x0005       */
+  "ATA-3 X3T10 2008D revision 1",               /* 0x0006       */ /* SMART NOT INCLUDED */
+  "ATA-2 X3T10 948D revision 2k",               /* 0x0007       */
+  "ATA-3 X3T10 2008D revision 0",               /* 0x0008       */ 
+  "ATA-2 X3T10 948D revision 3",                /* 0x0009       */
+  "ATA-3 published, ANSI X3.298-199x",          /* 0x000a       */
+  "ATA-3 X3T10 2008D revision 6",               /* 0x000b       */ /* 1st VERSION WITH SMART */
+  "ATA-3 X3T13 2008D revision 7 and 7a",        /* 0x000c       */
+  "ATA/ATAPI-4 X3T13 1153D revision 6",         /* 0x000d       */
+  "ATA/ATAPI-4 T13 1153D revision 13",          /* 0x000e       */
+  "ATA/ATAPI-4 X3T13 1153D revision 7",         /* 0x000f       */
+  "ATA/ATAPI-4 T13 1153D revision 18",          /* 0x0010       */
+  "ATA/ATAPI-4 T13 1153D revision 15",          /* 0x0011       */
+  "ATA/ATAPI-4 published, ANSI NCITS 317-1998", /* 0x0012       */
+  "ATA/ATAPI-5 T13 1321D revision 3",           /* 0x0013       */
+  "ATA/ATAPI-4 T13 1153D revision 14",          /* 0x0014       */
+  "ATA/ATAPI-5 T13 1321D revision 1",           /* 0x0015       */
+  "ATA/ATAPI-5 published, ANSI NCITS 340-2000", /* 0x0016       */
+  "ATA/ATAPI-4 T13 1153D revision 17",          /* 0x0017       */
+  "ATA/ATAPI-6 T13 1410D revision 0",           /* 0x0018       */
+  "ATA/ATAPI-6 T13 1410D revision 3a",          /* 0x0019       */
+  "ATA/ATAPI-7 T13 1532D revision 1",           /* 0x001a       */
+  "ATA/ATAPI-6 T13 1410D revision 2",           /* 0x001b       */
+  "ATA/ATAPI-6 T13 1410D revision 1",           /* 0x001c       */
+  "reserved",                                   /* 0x001d       */
+  "ATA/ATAPI-7 T13 1532D revision 0",           /* 0x001e       */
+  "reserved",                                   /* 0x001f       */
+  "reserved",                                   /* 0x0020       */
+  "ATA/ATAPI-7 T13 1532D revision 4a",          /* 0x0021       */
+  "ATA/ATAPI-6 published, ANSI INCITS 361-2002" /* 0x0022       */
+};
+
+// NOTE ATA/ATAPI-4 REV 4 was the LAST revision where the device
+// attribute structures were NOT completely vendor specific.  So any
+// disk that is ATA/ATAPI-4 or above can not be trusted to show the
+// vendor values in sensible format.
+
+// Negative values below are because it doesn't support SMART
+const int actual_ver[] = { 
+  /* word 81 value: */
+  0,            /* 0x0000       WARNING:        */
+  1,            /* 0x0001       WARNING:        */
+  1,            /* 0x0002       WARNING:        */
+  1,            /* 0x0003       WARNING:        */
+  2,            /* 0x0004       WARNING:   This array           */
+  2,            /* 0x0005       WARNING:   corresponds          */
+  -3, /*<== */  /* 0x0006       WARNING:   *exactly*            */
+  2,            /* 0x0007       WARNING:   to the ATA/          */
+  -3, /*<== */  /* 0x0008       WARNING:   ATAPI version        */
+  2,            /* 0x0009       WARNING:   listed in            */
+  3,            /* 0x000a       WARNING:   the                  */
+  3,            /* 0x000b       WARNING:   minor_str            */
+  3,            /* 0x000c       WARNING:   array                */
+  4,            /* 0x000d       WARNING:   above.               */
+  4,            /* 0x000e       WARNING:                        */
+  4,            /* 0x000f       WARNING:   If you change        */
+  4,            /* 0x0010       WARNING:   that one,            */
+  4,            /* 0x0011       WARNING:   change this one      */
+  4,            /* 0x0012       WARNING:   too!!!               */
+  5,            /* 0x0013       WARNING:        */
+  4,            /* 0x0014       WARNING:        */
+  5,            /* 0x0015       WARNING:        */
+  5,            /* 0x0016       WARNING:        */
+  4,            /* 0x0017       WARNING:        */
+  6,            /* 0x0018       WARNING:        */
+  6,            /* 0x0019       WARNING:        */
+  7,            /* 0x001a       WARNING:        */
+  6,            /* 0x001b       WARNING:        */
+  6,            /* 0x001c       WARNING:        */
+  0,            /* 0x001d       WARNING:        */
+  7,            /* 0x001e       WARNING:        */
+  0,            /* 0x001f       WARNING:        */
+  0,            /* 0x0020       WARNING:        */
+  7,            /* 0x0021       WARNING:        */
+  6             /* 0x0022       WARNING:        */
+};
+
+// When you add additional items to this list, you should then:
+// 0 -- update this list
+// 1 -- modify the following function parse_attribute_def()
+// 2 -- if needed, modify ataPrintSmartAttribRawValue()
+// 3 -  if needed, modify ataPrintSmartAttribName()
+// 4 -- add #define PRESET_N_DESCRIPTION at top of knowndrives.c
+// 5 -- add drive in question into knowndrives[] table in knowndrives.c
+// 6 -- update smartctl.8
+// 7 -- update smartd.8
+// 8 -- do "make smartd.conf.5" to update smartd.conf.5
+// 9 -- update CHANGELOG file
+const char *vendorattributeargs[] = {
+  // 0  defs[9]=1
+  "9,minutes",
+  // 1  defs[9]=3
+  "9,seconds",
+  // 2  defs[9]=2
+  "9,temp",
+  // 3  defs[220]=1
+  "220,temp",
+  // 4  defs[*]=253
+  "N,raw8",
+  // 5  defs[*]=254
+  "N,raw16",
+  // 6  defs[*]=255
+  "N,raw48",
+  // 7  defs[200]=1
+  "200,writeerrorcount",
+  // 8  defs[9]=4
+  "9,halfminutes",
+  // 9  defs[194]=1
+  "194,10xCelsius",
+  // 10 defs[194]=2
+  "194,unknown",
+  // 11 defs[193]=1
+  "193,loadunload",
+  // 12 defs[201]=1
+  "201,detectedtacount",
+  // 13 defs[192]=1
+  "192,emergencyretractcyclect",
+  // 14 defs[198]=1
+  "198,offlinescanuncsectorct",
+  // NULL should always terminate the array
+  NULL
+};
+
+// This are the meanings of the Self-test failure checkpoint byte.
+// This is in the self-test log at offset 4 bytes into the self-test
+// descriptor and in the SMART READ DATA structure at byte offset
+// 371. These codes are not well documented.  The meanings returned by
+// this routine are used (at least) by Maxtor and IBM. Returns NULL if
+// not recognized.  Currently the maximum length is 15 bytes.
+const char *SelfTestFailureCodeName(unsigned char which){
+  
+  switch (which) {
+  case 0:
+    return "Write_Test";
+  case 1:
+    return "Servo_Basic";
+  case 2:
+    return "Servo_Random";
+  case 3:
+    return "G-list_Scan";
+  case 4:
+    return "Handling_Damage";
+  case 5:
+    return "Read_Scan";
+  default:
+    return NULL;
+  }
+}
+
+// This is a utility function for parsing pairs like "9,minutes" or
+// "220,temp", and putting the correct flag into the attributedefs
+// array.  Returns 1 if problem, 0 if pair has been recongized.
+int parse_attribute_def(char *pair, unsigned char **defsptr){
+  int i,j;
+  char temp[32];
+  unsigned char *defs;
+
+  // If array does not exist, allocate it
+  if (!*defsptr && !(*defsptr=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM, 1))){
+    pout("Out of memory in parse_attribute_def\n");
+    EXIT(1);
+  }
+
+  defs=*defsptr;
+
+  // look along list and see if we find the pair
+  for (i=0; vendorattributeargs[i] && strcmp(pair, vendorattributeargs[i]); i++);
+
+  switch (i) {
+  case 0:
+    // attribute 9 is power on time in minutes
+    defs[9]=1;
+    return 0;
+  case 1:
+    // attribute 9 is power-on-time in seconds
+    defs[9]=3;
+    return 0;
+  case 2:
+    // attribute 9 is temperature in celsius
+    defs[9]=2;
+    return 0;
+  case 3:
+    // attribute 220 is temperature in celsius
+    defs[220]=1;
+    return 0;
+  case 4:
+    // print all attributes in raw 8-bit form
+    for (j=0; j<MAX_ATTRIBUTE_NUM; j++)
+      defs[j]=253;
+    return 0;
+  case 5:
+    // print all attributes in raw 16-bit form
+    for (j=0; j<MAX_ATTRIBUTE_NUM; j++)
+      defs[j]=254;
+    return 0;
+  case 6:
+    // print all attributes in raw 48-bit form
+    for (j=0; j<MAX_ATTRIBUTE_NUM; j++)
+      defs[j]=255;
+    return 0;
+  case 7:
+    // attribute 200 is write error count
+    defs[200]=1;
+    return 0;
+  case 8:
+    // attribute 9 increments once every 30 seconds (power on time
+    // measure)
+    defs[9]=4;
+    return 0;
+  case 9:
+    // attribute 194 is ten times disk temp in Celsius
+    defs[194]=1;
+    return 0;
+  case 10:
+    // attribute 194 is unknown
+    defs[194]=2;
+    return 0;
+  case 11:
+    // Hitachi : Attributes 193 has 2 values : 1 load, 1 normal unload
+    defs[193]=1;
+    return 0;
+  case 12:
+    // Fujitsu
+    defs[201]=1;
+    return 0;
+  case 13:
+    // Fujitsu
+    defs[192]=1;
+    return 0;
+  case 14:
+    // Fujitsu
+    defs[198]=1;
+    return 0;
+  default:
+    // pair not found
+    break;
+  }
+  // At this point, either the pair was not found, or it is of the
+  // form N,uninterpreted, in which case we need to parse N
+  j=sscanf(pair,"%d,%14s", &i, temp);
+  // if no match to pattern, unrecognized
+  if (j!=2 || i<0 || i >255)
+    return 1;
+
+  // check for recognized strings
+  if (!strcmp(temp, "raw8")) {
+    defs[i]=253;
+    return 0;
+  }
+  
+  // check for recognized strings
+  if (!strcmp(temp, "raw16")) {
+    defs[i]=254;
+    return 0;
+  }
+  
+  // check for recognized strings
+  if (!strcmp(temp, "raw48")) {
+    defs[i]=255;
+    return 0;
+  }
+  // didn't recognize the string
+  return 1;
+}
+
+// Structure used in sorting the array vendorattributeargs[].
+typedef struct vaa_pair_s {
+  const char *vaa;
+  const char *padded_vaa;
+} vaa_pair;
+
+// Returns a copy of s with all numbers of less than three digits padded with
+// leading zeros.  Returns NULL if there isn't enough memory available.  The
+// memory for the string is dynamically allocated and should be freed by the
+// caller.
+char *pad_numbers(const char *s)
+{
+  char c, *t, *u;
+  const char *r;
+  int i, len, ndigits = 0;
+
+  // Allocate the maximum possible amount of memory needed.
+  if (!(t = (char *)malloc(strlen(s)*2+2)))
+    return NULL;
+
+  // Copy the string s to t, padding any numbers of less than three digits
+  // with leading zeros.  The string is copied backwards to simplify the code.
+  r = s + strlen(s);
+  u = t;
+  while (( r-- >= s)) {
+    if (isdigit((int)*r))
+      ndigits++;
+    else if (ndigits > 0) {
+      while (ndigits++ < 3)
+        *u++ = '0';
+      ndigits = 0;
+    }
+    *u++ = *r;
+  }
+  *u = '\0';
+
+  // Reverse the string in t.
+  len = strlen(t);
+  for (i = 0; i < len/2; i++) {
+    c          = t[i];
+    t[i]       = t[len-1-i];
+    t[len-1-i] = c;
+  }
+
+  return t;
+}
+
+// Comparison function for qsort().  Used by sort_vendorattributeargs().
+int compare_vaa_pairs(const void *a, const void *b)
+{
+  vaa_pair *p = (vaa_pair *)a;
+  vaa_pair *q = (vaa_pair *)b;
+
+  return strcmp(p->padded_vaa, q->padded_vaa);
+}
+
+// Returns a sorted list of vendorattributeargs or NULL if there is not enough
+// memory available.  The memory for the list is allocated dynamically and
+// should be freed by the caller.
+// To perform the sort, any numbers in the strings are padded out to three
+// digits by adding leading zeros.  For example,
+//
+//    "9,minutes"  becomes  "009,minutes"
+//    "N,raw16"    becomes  "N,raw016"
+//
+// and the original strings are paired with the padded strings.  The list of
+// pairs is then sorted by comparing the padded strings (using strcmp) and the
+// result is then the list of unpadded strings.
+//
+const char **sort_vendorattributeargs(void) {
+  const char **ps, **sorted_list = NULL;
+  vaa_pair *pairs, *pp;
+  int count, i;
+
+  // Figure out how many strings are in vendorattributeargs[] (not including
+  // the terminating NULL).
+  count = (sizeof vendorattributeargs) / sizeof(char *) - 1;
+
+  // Construct a list of pairs of strings from vendorattributeargs[] and their
+  // padded equivalents.
+  if (!(pairs = (vaa_pair *)malloc(sizeof(vaa_pair) * count)))
+    goto END;
+  for (ps = vendorattributeargs, pp = pairs; *ps; ps++, pp++) {
+    pp->vaa = *ps;
+    if (!(pp->padded_vaa = pad_numbers(*ps)))
+      goto END;
+  }
+
+  // Sort the array of vaa_pair structures by comparing the padded strings
+  // using strcmp().
+  qsort(pairs, count, sizeof(vaa_pair), compare_vaa_pairs);
+
+  // Construct the sorted list of strings.
+  if (!(sorted_list = (const char **)malloc(sizeof vendorattributeargs)))
+    goto END;
+  for (ps = sorted_list, pp = pairs, i = 0; i < count; ps++, pp++, i++)
+    *ps = pp->vaa;
+  *ps = NULL;
+
+END:
+  if (pairs) {
+    for (i = 0; i < count; i++)
+      if (pairs[i].padded_vaa)
+        free((void *)pairs[i].padded_vaa);
+    free((void *)pairs);
+  }
+
+  // If there was a problem creating the list then sorted_list should now
+  // contain NULL.
+  return sorted_list;
+}
+
+// Function to return a multiline string containing a list of the arguments in 
+// vendorattributeargs[].  The strings are preceeded by tabs and followed
+// (except for the last) by newlines.
+// This function allocates the required memory for the string and the caller
+// must use free() to free it.  It returns NULL if the required memory can't
+// be allocated.
+char *create_vendor_attribute_arg_list(void){
+  const char **ps, **sorted;
+  char *s;
+  int len;
+
+  // Get a sorted list of vendor attribute arguments.  If the sort fails
+  // (which should only happen if the system is really low on memory) then just
+  // use the unordered list.
+  if (!(sorted = (const char **) sort_vendorattributeargs()))
+    sorted = vendorattributeargs;
+
+  // Calculate the required number of characters
+  len = 1;                // At least one char ('\0')
+  for (ps = sorted; *ps != NULL; ps++) {
+    len += 1;             // For the tab
+    len += strlen(*ps);   // For the actual argument string
+    if (*(ps+1))
+      len++;              // For the newline if required
+  }
+
+  // Attempt to allocate memory for the string
+  if (!(s = (char *)malloc(len)))
+    return NULL;
+
+  // Construct the string
+  *s = '\0';
+  for (ps = sorted; *ps != NULL; ps++) {
+    strcat(s, "\t");
+    strcat(s, *ps);
+    if (*(ps+1))
+      strcat(s, "\n");
+  }
+
+  free((char **)sorted);
+
+  // Return a pointer to the string
+  return s;
+}
+
+// swap two bytes.  Point to low address
+void swap2(char *location){
+  char tmp=*location;
+  *location=*(location+1);
+  *(location+1)=tmp;
+  return;
+}
+
+// swap four bytes.  Point to low address
+void swap4(char *location){
+  char tmp=*location;
+  *location=*(location+3);
+  *(location+3)=tmp;
+  swap2(location+1);
+  return;
+}
+
+// swap eight bytes.  Points to low address
+void swap8(char *location){
+  char tmp=*location;
+  *location=*(location+7);
+  *(location+7)=tmp;
+  tmp=*(location+1);
+  *(location+1)=*(location+6);
+  *(location+6)=tmp;
+  swap4(location+2);
+  return;
+}
+
+static char *commandstrings[]={
+  "SMART ENABLE",
+  "SMART DISABLE",
+  "SMART AUTOMATIC ATTRIBUTE SAVE",
+  "SMART IMMEDIATE OFFLINE",
+  "SMART AUTO OFFLINE",
+  "SMART STATUS",
+  "SMART STATUS CHECK",
+  "SMART READ ATTRIBUTE VALUES",
+  "SMART READ ATTRIBUTE THRESHOLDS",
+  "SMART READ LOG",
+  "IDENTIFY DEVICE",
+  "IDENTIFY PACKET DEVICE",
+  "CHECK POWER MODE",
+  "SMART WRITE LOG",
+  "WARNING (UNDEFINED COMMAND -- CONTACT DEVELOPERS AT " PACKAGE_BUGREPORT ")\n"
+};
+
+void prettyprint(unsigned char *stuff, char *name){
+  int i,j;
+  pout("\n===== [%s] DATA START (BASE-16) =====\n", name);
+  for (i=0; i<32; i++){
+    pout("%03d-%03d: ", 16*i, 16*(i+1)-1);
+    for (j=0; j<15; j++)
+      pout("%02x ",*stuff++);
+    pout("%02x\n",*stuff++);
+  }
+  pout("===== [%s] DATA END (512 Bytes) =====\n\n", name);
+}
+
+// This function provides the pretty-print reporting for SMART
+// commands: it implements the various -r "reporting" options for ATA
+// ioctls.
+int smartcommandhandler(int device, smart_command_set command, int select, char *data){
+  int retval;
+
+  // This conditional is true for commands that return data
+  int getsdata=(command==PIDENTIFY || 
+                command==IDENTIFY || 
+                command==READ_LOG || 
+                command==READ_THRESHOLDS || 
+                command==READ_VALUES ||
+               command==CHECK_POWER_MODE);
+
+  int sendsdata=(command==WRITE_LOG);
+  
+  // If reporting is enabled, say what the command will be before it's executed
+  if (con->reportataioctl){
+          // conditional is true for commands that use parameters
+          int usesparam=(command==READ_LOG || 
+                         command==AUTO_OFFLINE || 
+                         command==AUTOSAVE || 
+                         command==IMMEDIATE_OFFLINE ||
+                         command==WRITE_LOG);
+                  
+    pout("\nREPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]);
+    if (usesparam)
+      pout(" InputParameter=%d\n", select);
+    else
+      pout("\n");
+  }
+  
+  if ((getsdata || sendsdata) && !data){
+    pout("REPORT-IOCTL: Unable to execute command %s : data destination address is NULL\n", commandstrings[command]);
+    return -1;
+  }
+  
+  // The reporting is cleaner, and we will find coding bugs faster, if
+  // the commands that failed clearly return empty (zeroed) data
+  // structures
+  if (getsdata) {
+    if (command==CHECK_POWER_MODE)
+      data[0]=0;
+    else
+      memset(data, '\0', 512);
+  }
+
+
+  // If reporting is enabled, say what input was sent to the command
+  if (con->reportataioctl && sendsdata){
+    pout("REPORT-IOCTL: DeviceFD=%d Command=%s", device, commandstrings[command]);
+    // if requested, pretty-print the output data structure
+    if (con->reportataioctl>1)
+      prettyprint((unsigned char *)data, commandstrings[command]);
+  }
+
+  // In case the command produces an error, we'll want to know what it is:
+  errno=0;
+  
+  // now execute the command
+  switch (con->controller_type) {
+  case CONTROLLER_3WARE_678K:
+  case CONTROLLER_3WARE_678K_CHAR:
+  case CONTROLLER_3WARE_9000_CHAR:
+    retval=escalade_command_interface(device, con->controller_port-1, con->controller_type, command, select, data);
+    if (retval &&  con->controller_port<=0)
+      pout("WARNING: apparently missing '-d 3ware,N' disk specification\n");
+    break;
+  case CONTROLLER_MARVELL_SATA:
+    retval=marvell_command_interface(device, command, select, data);
+    break;
+  default:
+    retval=ata_command_interface(device, command, select, data);
+  }
+
+  // If reporting is enabled, say what output was produced by the command
+  if (con->reportataioctl){
+    if (errno)
+      pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d errno=%d [%s]\n", 
+           device, commandstrings[command], retval, errno, strerror(errno));
+    else
+      pout("REPORT-IOCTL: DeviceFD=%d Command=%s returned %d\n",
+           device, commandstrings[command], retval);
+    
+    // if requested, pretty-print the output data structure
+    if (con->reportataioctl>1 && getsdata) {
+      if (command==CHECK_POWER_MODE)
+       pout("Sector Count Register (BASE-16): %02x\n", (unsigned char)(*data));
+      else
+       prettyprint((unsigned char *)data, commandstrings[command]);
+    }
+  }
+  return retval;
+}
+
+
+// This function computes the checksum of a single disk sector (512
+// bytes).  Returns zero if checksum is OK, nonzero if the checksum is
+// incorrect.  The size (512) is correct for all SMART structures.
+unsigned char checksum(unsigned char *buffer){
+  unsigned char sum=0;
+  int i;
+  
+  for (i=0; i<512; i++)
+    sum+=buffer[i];
+
+  return sum;
+}
+
+// returns -1 if command fails or the device is in Sleep mode, else
+// value of Sector Count register.  Sector Count result values:
+//   00h device is in Standby mode. 
+//   80h device is in Idle mode.
+//   FFh device is in Active mode or Idle mode.
+
+int ataCheckPowerMode(int device) {
+  unsigned char result;
+
+  if ((smartcommandhandler(device, CHECK_POWER_MODE, 0, (char *)&result)))
+    return -1;
+
+  if (result!=0 && result!=0x80 && result!=0xff)
+    pout("ataCheckPowerMode(): ATA CHECK POWER MODE returned unknown Sector Count Register value %02x\n", result);
+
+  return (int)result;
+}
+
+
+
+
+// Reads current Device Identity info (512 bytes) into buf.  Returns 0
+// if all OK.  Returns -1 if no ATA Device identity can be
+// established.  Returns >0 if Device is ATA Packet Device (not SMART
+// capable).  The value of the integer helps identify the type of
+// Packet device, which is useful so that the user can connect the
+// formal device number with whatever object is inside their computer.
+int ataReadHDIdentity (int device, struct ata_identify_device *buf){
+  unsigned short *rawshort=(unsigned short *)buf;
+  unsigned char  *rawbyte =(unsigned char  *)buf;
+
+  // See if device responds either to IDENTIFY DEVICE or IDENTIFY
+  // PACKET DEVICE
+  if ((smartcommandhandler(device, IDENTIFY, 0, (char *)buf))){
+    if (smartcommandhandler(device, PIDENTIFY, 0, (char *)buf)){
+      return -1; 
+    }
+  }
+
+#ifndef __NetBSD__
+  // if machine is big-endian, swap byte order as needed
+  // (the NetBSD kernel does deliver the results in host byte order)
+  if (isbigendian()){
+    int i;
+    
+    // swap various capability words that are needed
+    for (i=0; i<33; i++)
+      swap2((char *)(buf->words047_079+i));
+    
+    for (i=80; i<=87; i++)
+      swap2((char *)(rawshort+i));
+    
+    for (i=0; i<168; i++)
+      swap2((char *)(buf->words088_255+i));
+  }
+#endif
+  
+  // If there is a checksum there, validate it
+  if ((rawshort[255] & 0x00ff) == 0x00a5 && checksum(rawbyte))
+    checksumwarning("Drive Identity Structure");
+  
+  // If this is a PACKET DEVICE, return device type
+  if (rawbyte[1] & 0x80)
+    return 1+(rawbyte[1] & 0x1f);
+  
+  // Not a PACKET DEVICE
+  return 0;
+}
+
+// Returns ATA version as an integer, and a pointer to a string
+// describing which revision.  Note that Revision 0 of ATA-3 does NOT
+// support SMART.  For this one case we return -3 rather than +3 as
+// the version number.  See notes above.
+int ataVersionInfo (const char** description, struct ata_identify_device *drive, unsigned short *minor){
+  unsigned short major;
+  int i;
+
+  // check that arrays at the top of this file are defined
+  // consistently
+  if (sizeof(minor_str) != sizeof(char *)*(1+MINOR_MAX)){
+    pout("Internal error in ataVersionInfo().  minor_str[] size %d\n"
+         "is not consistent with value of MINOR_MAX+1 = %d\n", 
+         (int)(sizeof(minor_str)/sizeof(char *)), MINOR_MAX+1);
+    fflush(NULL);
+    abort();
+  }
+  if (sizeof(actual_ver) != sizeof(int)*(1+MINOR_MAX)){
+    pout("Internal error in ataVersionInfo().  actual_ver[] size %d\n"
+         "is not consistent with value of MINOR_MAX = %d\n",
+         (int)(sizeof(actual_ver)/sizeof(int)), MINOR_MAX+1);
+    fflush(NULL);
+    abort();
+  }
+
+  // get major and minor ATA revision numbers
+  major=drive->major_rev_num;
+  *minor=drive->minor_rev_num;
+  
+  // First check if device has ANY ATA version information in it
+  if (major==NOVAL_0 || major==NOVAL_1) {
+    *description=NULL;
+    return -1;
+  }
+  
+  // The minor revision number has more information - try there first
+  if (*minor && (*minor<=MINOR_MAX)){
+    int std = actual_ver[*minor];
+    if (std) {
+      *description=minor_str[*minor];
+      return std;
+    }
+  }
+  
+  // HDPARM has a very complicated algorithm from here on. Since SMART only
+  // exists on ATA-3 and later standards, let's punt on this.  If you don't
+  // like it, please fix it.  The code's in CVS.
+  for (i=15; i>0; i--)
+    if (major & (0x1<<i))
+      break;
+  
+  *description=NULL; 
+  if (i==0)
+    return 1;
+  else
+    return i;
+}
+
+// returns 1 if SMART supported, 0 if SMART unsupported, -1 if can't tell
+int ataSmartSupport(struct ata_identify_device *drive){
+  unsigned short word82=drive->command_set_1;
+  unsigned short word83=drive->command_set_2;
+  
+  // check if words 82/83 contain valid info
+  if ((word83>>14) == 0x01)
+    // return value of SMART support bit 
+    return word82 & 0x0001;
+  
+  // since we can're rely on word 82, we don't know if SMART supported
+  return -1;
+}
+
+// returns 1 if SMART enabled, 0 if SMART disabled, -1 if can't tell
+int ataIsSmartEnabled(struct ata_identify_device *drive){
+  unsigned short word85=drive->cfs_enable_1;
+  unsigned short word87=drive->csf_default;
+  
+  // check if words 85/86/87 contain valid info
+  if ((word87>>14) == 0x01)
+    // return value of SMART enabled bit
+    return word85 & 0x0001;
+  
+  // Since we can't rely word85, we don't know if SMART is enabled.
+  return -1;
+}
+
+
+// Reads SMART attributes into *data
+int ataReadSmartValues(int device, struct ata_smart_values *data){      
+  
+  if (smartcommandhandler(device, READ_VALUES, 0, (char *)data)){
+    syserror("Error SMART Values Read failed");
+    return -1;
+  }
+
+  // compute checksum
+  if (checksum((unsigned char *)data))
+    checksumwarning("SMART Attribute Data Structure");
+  
+  // byte swap if needed
+  if (isbigendian()){
+    int i;
+    swap2((char *)&(data->revnumber));
+    swap2((char *)&(data->total_time_to_complete_off_line));
+    swap2((char *)&(data->smart_capability));
+    for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
+      struct ata_smart_attribute *x=data->vendor_attributes+i;
+      swap2((char *)&(x->flags));
+    }
+  }
+
+  return 0;
+}
+
+
+// This corrects some quantities that are byte reversed in the SMART
+// SELF TEST LOG
+void fixsamsungselftestlog(struct ata_smart_selftestlog *data){
+  int i;
+  
+  // bytes 508/509 (numbered from 0) swapped (swap of self-test index
+  // with one byte of reserved.
+  swap2((char *)&(data->mostrecenttest));
+
+  // LBA low register (here called 'selftestnumber", containing
+  // information about the TYPE of the self-test) is byte swapped with
+  // Self-test execution status byte.  These are bytes N, N+1 in the
+  // entries.
+  for (i=0; i<21; i++)
+    swap2((char *)&(data->selftest_struct[i].selftestnumber));
+
+  return;
+}
+
+// Reads the Self Test Log (log #6)
+int ataReadSelfTestLog (int device, struct ata_smart_selftestlog *data){
+
+  // get data from device
+  if (smartcommandhandler(device, READ_LOG, 0x06, (char *)data)){
+    syserror("Error SMART Error Self-Test Log Read failed");
+    return -1;
+  }
+
+  // compute its checksum, and issue a warning if needed
+  if (checksum((unsigned char *)data))
+    checksumwarning("SMART Self-Test Log Structure");
+  
+  // fix firmware bugs in self-test log
+  if (con->fixfirmwarebug == FIX_SAMSUNG)
+    fixsamsungselftestlog(data);
+
+  // fix endian order, if needed
+  if (isbigendian()){
+    int i;
+    swap2((char*)&(data->revnumber));
+    for (i=0; i<21; i++){
+      struct ata_smart_selftestlog_struct *x=data->selftest_struct+i;
+      swap2((char *)&(x->timestamp));
+      swap4((char *)&(x->lbafirstfailure));
+    }
+  }
+
+  return 0;
+}
+
+
+// Reads the Log Directory (log #0).  Note: NO CHECKSUM!!
+int ataReadLogDirectory (int device, struct ata_smart_log_directory *data){     
+  
+  // get data from device
+  if (smartcommandhandler(device, READ_LOG, 0x00, (char *)data)){
+    return -1;
+  }
+
+  // swap endian order if needed
+  if (isbigendian()){
+    swap2((char *)&(data->logversion));
+  }
+  
+  return 0;
+}
+
+
+// Reads the selective self-test log (log #9)
+int ataReadSelectiveSelfTestLog(int device, struct ata_selective_self_test_log *data){  
+  
+  // get data from device
+  if (smartcommandhandler(device, READ_LOG, 0x09, (char *)data)){
+    syserror("Error SMART Read Selective Self-Test Log failed");
+    return -1;
+  }
+   
+  // compute its checksum, and issue a warning if needed
+  if (checksum((unsigned char *)data))
+    checksumwarning("SMART Selective Self-Test Log Structure");
+  
+  // swap endian order if needed
+  if (isbigendian()){
+    int i;
+    swap2((char *)&(data->logversion));
+    for (i=0;i<5;i++){
+      swap8((char *)&(data->span[i].start));
+      swap8((char *)&(data->span[i].end));
+    }
+    swap8((char *)&(data->currentlba));
+    swap2((char *)&(data->currentspan));
+    swap2((char *)&(data->flags));
+    swap2((char *)&(data->pendingtime));
+  }
+  
+  if (data->logversion != 1)
+    pout("SMART Selective Self-Test Log Data Structure Revision Number (%d) should be 1\n", data->logversion);
+  
+  return 0;
+}
+
+// Writes the selective self-test log (log #9)
+int ataWriteSelectiveSelfTestLog(int device, struct ata_smart_values *sv){   
+  int i;
+  struct ata_selective_self_test_log sstlog, *data=&sstlog;
+  unsigned char cksum=0;
+  unsigned char *ptr=(unsigned char *)data;
+  
+  // Read log
+  if (ataReadSelectiveSelfTestLog(device, data)) {
+    pout("Since Read failed, will not attempt to WRITE Selective Self-test Log\n");
+    return -1;
+  }
+  
+  // Fix logversion if needed
+  if (data->logversion !=1) {
+    pout("Error SMART Selective Self-Test Log Data Structure Revision not recognized\n"
+        "Revision number should be 1 but is %d.  To be safe, aborting WRITE LOG\n", data->logversion);
+    return -2;
+  }
+
+  // Host is NOT allowed to write selective self-test log if a selective
+  // self-test is in progress.
+  if (0<data->currentspan && data->currentspan<6 && ((sv->self_test_exec_status)>>4)==15) {
+    pout("Error SMART Selective or other Self-Test in progress.\n");
+    return -4;
+  }
+  
+  // Clear spans
+  for (i=0; i<5; i++)
+    memset(data->span+i, 0, sizeof(struct test_span));
+  
+  // Set spans for testing 
+  for (i=0; i<con->smartselectivenumspans; i++){
+    data->span[i].start = con->smartselectivespan[i][0];
+    data->span[i].end   = con->smartselectivespan[i][1];
+  }
+
+  // host must initialize to zero before initiating selective self-test
+  data->currentlba=0;
+  data->currentspan=0;
+  
+  // Perform off-line scan after selective test?
+  if (1 == con->scanafterselect)
+    // NO
+    data->flags &= ~SELECTIVE_FLAG_DOSCAN;
+  else if (2 == con->scanafterselect)
+    // YES
+    data->flags |= SELECTIVE_FLAG_DOSCAN;
+  
+  // Must clear active and pending flags before writing
+  data->flags &= ~(SELECTIVE_FLAG_ACTIVE);  
+  data->flags &= ~(SELECTIVE_FLAG_PENDING);
+
+  // modify pending time?
+  if (con->pendingtime)
+    data->pendingtime=(unsigned short)(con->pendingtime-1);
+
+  // Set checksum to zero, then compute checksum
+  data->checksum=0;
+  for (i=0; i<512; i++)
+    cksum+=ptr[i];
+  cksum=~cksum;
+  cksum+=1;
+  data->checksum=cksum;
+
+    // swap endian order if needed
+  if (isbigendian()){
+    int i;
+    swap2((char *)&(data->logversion));
+    for (i=0;i<5;i++){
+      swap8((char *)&(data->span[i].start));
+      swap8((char *)&(data->span[i].end));
+    }
+    swap8((char *)&(data->currentlba));
+    swap2((char *)&(data->currentspan));
+    swap2((char *)&(data->flags));
+    swap2((char *)&(data->pendingtime));
+  }
+
+  // write new selective self-test log
+  if (smartcommandhandler(device, WRITE_LOG, 0x09, (char *)data)){
+    syserror("Error Write Selective Self-Test Log failed");
+    return -3;
+  }
+
+  return 0;
+}
+
+// This corrects some quantities that are byte reversed in the SMART
+// ATA ERROR LOG.
+void fixsamsungerrorlog(struct ata_smart_errorlog *data){
+  int i,j;
+  
+  // FIXED IN SAMSUNG -25 FIRMWARE???
+  // Device error count in bytes 452-3
+  swap2((char *)&(data->ata_error_count));
+  
+  // FIXED IN SAMSUNG -22a FIRMWARE
+  // step through 5 error log data structures
+  for (i=0; i<5; i++){
+    // step through 5 command data structures
+    for (j=0; j<5; j++)
+      // Command data structure 4-byte millisec timestamp.  These are
+      // bytes (N+8, N+9, N+10, N+11).
+      swap4((char *)&(data->errorlog_struct[i].commands[j].timestamp));
+    // Error data structure two-byte hour life timestamp.  These are
+    // bytes (N+28, N+29).
+    swap2((char *)&(data->errorlog_struct[i].error_struct.timestamp));
+  }
+  return;
+}
+
+// NEEDED ONLY FOR SAMSUNG -22 (some) -23 AND -24?? FIRMWARE
+void fixsamsungerrorlog2(struct ata_smart_errorlog *data){
+  // Device error count in bytes 452-3
+  swap2((char *)&(data->ata_error_count));
+  return;
+}
+
+// Reads the Summary SMART Error Log (log #1). The Comprehensive SMART
+// Error Log is #2, and the Extended Comprehensive SMART Error log is
+// #3
+int ataReadErrorLog (int device, struct ata_smart_errorlog *data){      
+  
+  // get data from device
+  if (smartcommandhandler(device, READ_LOG, 0x01, (char *)data)){
+    syserror("Error SMART Error Log Read failed");
+    return -1;
+  }
+  
+  // compute its checksum, and issue a warning if needed
+  if (checksum((unsigned char *)data))
+    checksumwarning("SMART ATA Error Log Structure");
+  
+  // Some disks have the byte order reversed in some SMART Summary
+  // Error log entries
+  if (con->fixfirmwarebug == FIX_SAMSUNG)
+    fixsamsungerrorlog(data);
+  else if (con->fixfirmwarebug == FIX_SAMSUNG2)
+    fixsamsungerrorlog2(data);
+
+  // Correct endian order if necessary
+  if (isbigendian()){
+    int i,j;
+    
+    // Device error count in bytes 452-3
+    swap2((char *)&(data->ata_error_count));
+    
+    // step through 5 error log data structures
+    for (i=0; i<5; i++){
+      // step through 5 command data structures
+      for (j=0; j<5; j++)
+        // Command data structure 4-byte millisec timestamp
+        swap4((char *)&(data->errorlog_struct[i].commands[j].timestamp));
+      // Error data structure life timestamp
+      swap2((char *)&(data->errorlog_struct[i].error_struct.timestamp));
+    }
+  }
+  
+  return 0;
+}
+
+int ataReadSmartThresholds (int device, struct ata_smart_thresholds_pvt *data){
+  
+  // get data from device
+  if (smartcommandhandler(device, READ_THRESHOLDS, 0, (char *)data)){
+    syserror("Error SMART Thresholds Read failed");
+    return -1;
+  }
+  
+  // compute its checksum, and issue a warning if needed
+  if (checksum((unsigned char *)data))
+    checksumwarning("SMART Attribute Thresholds Structure");
+  
+  // byte swap if needed
+  if (isbigendian())
+    swap2((char *)&(data->revnumber));
+
+  return 0;
+}
+
+int ataEnableSmart (int device ){       
+  if (smartcommandhandler(device, ENABLE, 0, NULL)){
+    syserror("Error SMART Enable failed");
+    return -1;
+  }
+  return 0;
+}
+
+int ataDisableSmart (int device ){      
+  
+  if (smartcommandhandler(device, DISABLE, 0, NULL)){
+    syserror("Error SMART Disable failed");
+    return -1;
+  }  
+  return 0;
+}
+
+int ataEnableAutoSave(int device){  
+  if (smartcommandhandler(device, AUTOSAVE, 241, NULL)){
+    syserror("Error SMART Enable Auto-save failed");
+    return -1;
+  }
+  return 0;
+}
+
+int ataDisableAutoSave(int device){
+  
+  if (smartcommandhandler(device, AUTOSAVE, 0, NULL)){
+    syserror("Error SMART Disable Auto-save failed");
+    return -1;
+  }
+  return 0;
+}
+
+// In *ALL* ATA standards the Enable/Disable AutoOffline command is
+// marked "OBSOLETE". It is defined in SFF-8035i Revision 2, and most
+// vendors still support it for backwards compatibility. IBM documents
+// it for some drives.
+int ataEnableAutoOffline (int device ){ 
+  
+  /* timer hard coded to 4 hours */  
+  if (smartcommandhandler(device, AUTO_OFFLINE, 248, NULL)){
+    syserror("Error SMART Enable Automatic Offline failed");
+    return -1;
+  }
+  return 0;
+}
+
+// Another Obsolete Command.  See comments directly above, associated
+// with the corresponding Enable command.
+int ataDisableAutoOffline (int device ){        
+  
+  if (smartcommandhandler(device, AUTO_OFFLINE, 0, NULL)){
+    syserror("Error SMART Disable Automatic Offline failed");
+    return -1;
+  }
+  return 0;
+}
+
+// If SMART is enabled, supported, and working, then this call is
+// guaranteed to return 1, else zero.  Note that it should return 1
+// regardless of whether the disk's SMART status is 'healthy' or
+// 'failing'.
+int ataDoesSmartWork(int device){
+  int retval=smartcommandhandler(device, STATUS, 0, NULL);
+
+  if (-1 == retval)
+    return 0;
+
+  return 1;
+}
+
+// This function uses a different interface (DRIVE_TASK) than the
+// other commands in this file.
+int ataSmartStatus2(int device){
+  return smartcommandhandler(device, STATUS_CHECK, 0, NULL);  
+}
+
+// This is the way to execute ALL tests: offline, short self-test,
+// extended self test, with and without captive mode, etc.
+int ataSmartTest(int device, int testtype, struct ata_smart_values *sv) {     
+  char cmdmsg[128],*type,*captive;
+  int errornum, cap, retval, select=0;
+
+  // Boolean, if set, says test is captive
+  cap=testtype & CAPTIVE_MASK;
+
+  // Set up strings that describe the type of test
+  if (cap)
+    captive="captive";
+  else
+    captive="off-line";
+  
+  if (testtype==OFFLINE_FULL_SCAN)
+    type="off-line";
+  else  if (testtype==SHORT_SELF_TEST || testtype==SHORT_CAPTIVE_SELF_TEST)
+    type="Short self-test";
+  else if (testtype==EXTEND_SELF_TEST || testtype==EXTEND_CAPTIVE_SELF_TEST)
+    type="Extended self-test";
+  else if (testtype==CONVEYANCE_SELF_TEST || testtype==CONVEYANCE_CAPTIVE_SELF_TEST)
+    type="Conveyance self-test";
+  else if ((select=(testtype==SELECTIVE_SELF_TEST || testtype==SELECTIVE_CAPTIVE_SELF_TEST)))
+    type="Selective self-test";
+  else
+    type="[Unrecognized] self-test";
+  
+  // If doing a selective self-test, first use WRITE_LOG to write the
+  // selective self-test log.
+  if (select && (retval=ataWriteSelectiveSelfTestLog(device, sv))) {
+    if (retval==-4)
+      pout("Can't start selective self-test without aborting current test: use '-X' option to smartctl.\n");
+    return retval;
+  }
+
+  //  Print ouf message that we are sending the command to test
+  if (testtype==ABORT_SELF_TEST)
+    sprintf(cmdmsg,"Abort SMART off-line mode self-test routine");
+  else
+    sprintf(cmdmsg,"Execute SMART %s routine immediately in %s mode",type,captive);
+  pout("Sending command: \"%s\".\n",cmdmsg);
+
+  if (select) {
+    int i;
+    pout("SPAN         STARTING_LBA           ENDING_LBA\n");
+    for (i = 0; i < con->smartselectivenumspans; i++)
+      pout("   %d %20"PRId64" %20"PRId64"\n", i,
+           con->smartselectivespan[i][0],
+           con->smartselectivespan[i][1]);
+  }
+  
+  // Now send the command to test
+  errornum=smartcommandhandler(device, IMMEDIATE_OFFLINE, testtype, NULL);
+  
+  if (errornum && !(cap && errno==EIO)){
+    char errormsg[128];
+    sprintf(errormsg,"Command \"%s\" failed",cmdmsg); 
+    syserror(errormsg);
+    pout("\n");
+    return -1;
+  }
+  
+  // Since the command succeeded, tell user
+  if (testtype==ABORT_SELF_TEST)
+    pout("Self-testing aborted!\n");
+  else
+    pout("Drive command \"%s\" successful.\nTesting has begun.\n",cmdmsg);
+  return 0;
+}
+
+/* Test Time Functions */
+int TestTime(struct ata_smart_values *data,int testtype){
+  switch (testtype){
+  case OFFLINE_FULL_SCAN:
+    return (int) data->total_time_to_complete_off_line;
+  case SHORT_SELF_TEST:
+  case SHORT_CAPTIVE_SELF_TEST:
+    return (int) data->short_test_completion_time;
+  case EXTEND_SELF_TEST:
+  case EXTEND_CAPTIVE_SELF_TEST:
+    return (int) data->extend_test_completion_time;
+  case CONVEYANCE_SELF_TEST:
+  case CONVEYANCE_CAPTIVE_SELF_TEST:
+    return (int) data->conveyance_test_completion_time;
+  default:
+    return 0;
+  }
+}
+
+// This function tells you both about the ATA error log and the
+// self-test error log capability (introduced in ATA-5).  The bit is
+// poorly documented in the ATA/ATAPI standard.  Starting with ATA-6,
+// SMART error logging is also indicated in bit 0 of DEVICE IDENTIFY
+// word 84 and 87.  Top two bits must match the pattern 01. BEFORE
+// ATA-6 these top two bits still had to match the pattern 01, but the
+// remaining bits were reserved (==0).
+int isSmartErrorLogCapable (struct ata_smart_values *data, struct ata_identify_device *identity){
+
+  unsigned short word84=identity->command_set_extension;
+  unsigned short word87=identity->csf_default;
+  int isata6=identity->major_rev_num & (0x01<<6);
+  int isata7=identity->major_rev_num & (0x01<<7);
+
+  if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x01))
+    return 1;
+  
+  if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x01))
+    return 1;
+  
+  // otherwise we'll use the poorly documented capability bit
+  return data->errorlog_capability & 0x01;
+}
+
+// See previous function.  If the error log exists then the self-test
+// log should (must?) also exist.
+int isSmartTestLogCapable (struct ata_smart_values *data, struct ata_identify_device *identity){
+
+  unsigned short word84=identity->command_set_extension;
+  unsigned short word87=identity->csf_default;
+  int isata6=identity->major_rev_num & (0x01<<6);
+  int isata7=identity->major_rev_num & (0x01<<7);
+
+  if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x02))
+    return 1;
+  
+  if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x02))
+    return 1;
+
+
+  // otherwise we'll use the poorly documented capability bit
+    return data->errorlog_capability & 0x01;
+}
+
+
+int isGeneralPurposeLoggingCapable(struct ata_identify_device *identity){
+  unsigned short word84=identity->command_set_extension;
+  unsigned short word87=identity->csf_default;
+
+  // If bit 14 of word 84 is set to one and bit 15 of word 84 is
+  // cleared to zero, the contents of word 84 contains valid support
+  // information. If not, support information is not valid in this
+  // word.
+  if ((word84>>14) == 0x01)
+    // If bit 5 of word 84 is set to one, the device supports the
+    // General Purpose Logging feature set.
+    return (word84 & (0x01 << 5));
+  
+  // If bit 14 of word 87 is set to one and bit 15 of word 87 is
+  // cleared to zero, the contents of words (87:85) contain valid
+  // information. If not, information is not valid in these words.  
+  if ((word87>>14) == 0x01)
+    // If bit 5 of word 87 is set to one, the device supports
+    // the General Purpose Logging feature set.
+    return (word87 & (0x01 << 5));
+
+  // not capable
+  return 0;
+}
+
+
+// SMART self-test capability is also indicated in bit 1 of DEVICE
+// IDENTIFY word 87 (if top two bits of word 87 match pattern 01).
+// However this was only introduced in ATA-6 (but self-test log was in
+// ATA-5).
+int isSupportExecuteOfflineImmediate(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x01;
+}
+// Note in the ATA-5 standard, the following bit is listed as "Vendor
+// Specific".  So it may not be reliable. The only use of this that I
+// have found is in IBM drives, where it is well-documented.  See for
+// example page 170, section 13.32.1.18 of the IBM Travelstar 40GNX
+// hard disk drive specifications page 164 Revision 1.1 22 Apr 2002.
+int isSupportAutomaticTimer(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x02;
+}
+int isSupportOfflineAbort(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x04;
+}
+int isSupportOfflineSurfaceScan(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x08;
+}
+int isSupportSelfTest (struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x10;
+}
+int isSupportConveyanceSelfTest(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x20;
+}
+int isSupportSelectiveSelfTest(struct ata_smart_values *data){
+   return data->offline_data_collection_capability & 0x40;
+}
+
+
+
+// Loop over all valid attributes.  If they are prefailure attributes
+// and are at or below the threshold value, then return the ID of the
+// first failing attribute found.  Return 0 if all prefailure
+// attributes are in bounds.  The spec says "Bit 0
+// -Pre-failure/advisory - If the value of this bit equals zero, an
+// attribute value less than or equal to its corresponding attribute
+// threshold indicates an advisory condition where the usage or age of
+// the device has exceeded its intended design life period. If the
+// value of this bit equals one, an atribute value less than or equal
+// to its corresponding attribute threshold indicates a pre-failure
+// condition where imminent loss of data is being predicted."
+
+
+// onlyfailed=0 : are or were any age or prefailure attributes <= threshold
+// onlyfailed=1:  are any prefailure attributes <= threshold now
+int ataCheckSmart(struct ata_smart_values *data,
+                  struct ata_smart_thresholds_pvt *thresholds,
+                  int onlyfailed){
+  int i;
+  
+  // loop over all attributes
+  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
+
+    // pointers to disk's values and vendor's thresholds
+    struct ata_smart_attribute *disk=data->vendor_attributes+i;
+    struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i;
+    // consider only valid attributes
+    if (disk->id && thre->id){
+      int failednow,failedever;
+      
+      failednow =disk->current <= thre->threshold;
+      failedever=disk->worst   <= thre->threshold;
+      
+      if (!onlyfailed && failedever)
+        return disk->id;
+      
+      if (onlyfailed && failednow && ATTRIBUTE_FLAGS_PREFAILURE(disk->flags))
+        return disk->id;      
+    }
+  }
+  return 0;
+}
+
+
+
+// This checks the n'th attribute in the attribute list, NOT the
+// attribute with id==n.  If the attribute does not exist, or the
+// attribute is > threshold, then returns zero.  If the attribute is
+// <= threshold (failing) then we the attribute number if it is a
+// prefail attribute.  Else we return minus the attribute number if it
+// is a usage attribute.
+int ataCheckAttribute(struct ata_smart_values *data,
+                      struct ata_smart_thresholds_pvt *thresholds,
+                      int n){
+  struct ata_smart_attribute *disk;
+  struct ata_smart_threshold_entry *thre;
+  
+  if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !data || !thresholds)
+    return 0;
+  
+  // pointers to disk's values and vendor's thresholds
+  disk=data->vendor_attributes+n;
+  thre=thresholds->thres_entries+n;
+
+  if (!disk || !thre)
+    return 0;
+  
+  // consider only valid attributes, check for failure
+  if (!disk->id || !thre->id || (disk->id != thre->id) || disk->current> thre->threshold)
+    return 0;
+  
+  // We have found a failed attribute.  Return positive or negative? 
+  if (ATTRIBUTE_FLAGS_PREFAILURE(disk->flags))
+    return disk->id;
+  else
+    return -1*(disk->id);
+}
+
+
+// This routine prints the raw value of an attribute as a text string
+// into out. It also returns this 48-bit number as a long long.  The
+// array defs[] contains non-zero values if particular attributes have
+// non-default interpretations.
+
+int64_t ataPrintSmartAttribRawValue(char *out, 
+                                    struct ata_smart_attribute *attribute,
+                                    unsigned char *defs){
+  int64_t rawvalue;
+  unsigned word[3];
+  int j;
+  unsigned char select;
+  
+  // convert the six individual bytes to a long long (8 byte) integer.
+  // This is the value that we'll eventually return.
+  rawvalue = 0;
+  for (j=0; j<6; j++) {
+    // This looks a bit roundabout, but is necessary.  Don't
+    // succumb to the temptation to use raw[j]<<(8*j) since under
+    // the normal rules this will be promoted to the native type.
+    // On a 32 bit machine this might then overflow.
+    int64_t temp;
+    temp = attribute->raw[j];
+    temp <<= 8*j;
+    rawvalue |= temp;
+  }
+
+  // convert quantities to three two-byte words
+  for (j=0; j<3; j++){
+    word[j] = attribute->raw[2*j+1];
+    word[j] <<= 8;
+    word[j] |= attribute->raw[2*j];
+  }
+  
+  // if no data array, Attributes have default interpretations
+  if (defs)
+    select=defs[attribute->id];
+  else
+    select=0;
+
+  // Print six one-byte quantities.
+  if (select==253){
+    for (j=0; j<5; j++)
+      out+=sprintf(out, "%d ", attribute->raw[5-j]);
+    out+=sprintf(out, "%d ", attribute->raw[0]);
+    return rawvalue;
+  } 
+  
+  // Print three two-byte quantities
+  if (select==254){
+    out+=sprintf(out, "%d %d %d", word[2], word[1], word[0]); 
+    return rawvalue;
+  } 
+  
+  // Print one six-byte quantity
+  if (select==255){
+    out+=sprintf(out, "%"PRIu64, rawvalue);
+    return rawvalue;
+  }
+
+  // This switch statement is where we handle Raw attributes
+  // that are stored in an unusual vendor-specific format,
+  switch (attribute->id){
+    // Spin-up time
+  case 3:
+    out+=sprintf(out, "%d", word[0]);
+    // if second nonzero then it stores the average spin-up time
+    if (word[1])
+      out+=sprintf(out, " (Average %d)", word[1]);
+    break;
+    // Power on time
+  case 9:
+    if (select==1){
+      // minutes
+      int64_t tmp1=rawvalue/60;
+      int64_t tmp2=rawvalue%60;
+      out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m", tmp1, tmp2);
+    }
+    else if (select==3){
+      // seconds
+      int64_t hours=rawvalue/3600;
+      int64_t minutes=(rawvalue-3600*hours)/60;
+      int64_t seconds=rawvalue%60;
+      out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m+%02"PRIu64"s", hours, minutes, seconds);
+    }
+    else if (select==4){
+      // 30-second counter
+      int64_t tmp1=rawvalue/120;
+      int64_t tmp2=(rawvalue-120*tmp1)/2;
+      out+=sprintf(out, "%"PRIu64"h+%02"PRIu64"m", tmp1, tmp2);
+    }
+    else
+      // hours
+      out+=sprintf(out, "%"PRIu64, rawvalue);  //stored in hours
+    break;
+   // Load unload cycles
+  case 193:
+    if (select==1){
+      // loadunload
+      long load  =attribute->raw[0] + (attribute->raw[1]<<8) + (attribute->raw[2]<<16);
+      long unload=attribute->raw[3] + (attribute->raw[4]<<8) + (attribute->raw[5]<<16);
+      out+=sprintf(out, "%lu/%lu", load, unload);
+    }
+    else
+      // associated
+      out+=sprintf(out, "%"PRIu64, rawvalue);
+    break;
+    // Temperature
+  case 194:
+    if (select==1){
+      // ten times temperature in Celsius
+      int deg=word[0]/10;
+      int tenths=word[0]%10;
+      out+=sprintf(out, "%d.%d", deg, tenths);
+    }
+    else if (select==2)
+      // unknown attribute
+      out+=sprintf(out, "%"PRIu64, rawvalue);
+    else {
+      out+=sprintf(out, "%d", word[0]);
+      if (!(rawvalue==word[0])) {
+       int min=word[1]<word[2]?word[1]:word[2];
+       int max=word[1]>word[2]?word[1]:word[2];
+        // The other bytes are in use. Try IBM's model
+        out+=sprintf(out, " (Lifetime Min/Max %d/%d)", min, max);
+      }
+    }
+    break;
+  default:
+    out+=sprintf(out, "%"PRIu64, rawvalue);
+  }
+  
+  // Return the full value
+  return rawvalue;
+}
+
+
+// Note some attribute names appear redundant because different
+// manufacturers use different attribute IDs for an attribute with the
+// same name.  The variable val should contain a non-zero value if a particular
+// attributes has a non-default interpretation.
+void ataPrintSmartAttribName(char *out, unsigned char id, unsigned char *definitions){
+  char *name;
+  unsigned char val;
+
+  // If no data array, use default interpretations
+  if (definitions)
+    val=definitions[id];
+  else
+    val=0;
+
+  switch (id){
+    
+  case 1:
+    name="Raw_Read_Error_Rate";
+    break;
+  case 2:
+    name="Throughput_Performance";
+    break;
+  case 3:
+    name="Spin_Up_Time";
+    break;
+  case 4:
+    name="Start_Stop_Count";
+    break;
+  case 5:
+    name="Reallocated_Sector_Ct";
+    break;
+  case 6:
+    name="Read_Channel_Margin";
+    break;
+  case 7:
+    name="Seek_Error_Rate";
+    break;
+  case 8:
+    name="Seek_Time_Performance";
+    break;
+  case 9:
+    switch (val) {
+    case 1:
+      name="Power_On_Minutes";
+      break;
+    case 2:
+      name="Temperature_Celsius";
+      break;
+    case 3:
+      name="Power_On_Seconds";
+      break;
+    case 4:
+      name="Power_On_Half_Minutes";
+      break;
+    default:
+      name="Power_On_Hours";
+      break;
+    }
+    break;
+  case 10:
+    name="Spin_Retry_Count";
+    break;
+  case 11:
+    name="Calibration_Retry_Count";
+    break;
+  case 12:
+    name="Power_Cycle_Count";
+    break;
+  case 13:
+    name="Read_Soft_Error_Rate";
+    break;
+  case 191:
+    name="G-Sense_Error_Rate";
+    break;
+  case 192:
+    switch (val) {
+    case 1:
+      // Fujitsu
+      name="Emergency_Retract_Cycle_Ct";
+      break;
+    default:
+      name="Power-Off_Retract_Count";
+      break;
+    }
+    break;
+  case 193:
+    name="Load_Cycle_Count";
+    break;
+  case 194:
+    switch (val){
+    case 1:
+      // Samsung SV1204H with RK100-13 firmware
+      name="Temperature_Celsius_x10";
+      break;
+    case 2:
+      // for disks with no temperature Attribute
+      name="Unknown_Attribute";
+      break;
+    default:
+      name="Temperature_Celsius";
+      break;
+    }
+    break;
+  case 195:
+    // Fujitsu name="ECC_On_The_Fly_Count";
+    name="Hardware_ECC_Recovered";
+    break;
+  case 196:
+    name="Reallocated_Event_Count";
+    break;
+  case 197:
+    name="Current_Pending_Sector";
+    break;
+  case 198:
+    switch (val){
+    case 1:
+      // Fujitsu
+      name="Off-line_Scan_UNC_Sector_Ct";
+      break;
+    default:
+      name="Offline_Uncorrectable";
+      break;
+    }
+    break;
+  case 199:
+    name="UDMA_CRC_Error_Count";
+    break;
+  case 200:
+    switch (val) {
+    case 1:
+      // Fujitsu MHS2020AT
+      name="Write_Error_Count";
+      break;
+    default:
+      // Western Digital
+      name="Multi_Zone_Error_Rate";
+      break;
+    }
+    break;
+  case 201:
+    switch (val) {
+    case 1:
+      // Fujitsu
+      name="Detected_TA_Count";
+      break;
+    default:
+      name="Soft_Read_Error_Rate";
+      break;
+    }
+    break;
+  case 202:
+    // Fujitsu
+    name="TA_Increase_Count";
+    // Maxtor: Data Address Mark Errors
+    break;
+  case 203:
+    // Fujitsu
+    name="Run_Out_Cancel";
+    // Maxtor: ECC Errors
+    break;
+  case 204:
+    // Fujitsu
+    name="Shock_Count_Write_Opern";
+    // Maxtor: Soft ECC Correction
+    break;
+  case 205:
+    // Fujitsu
+    name="Shock_Rate_Write_Opern";
+    // Maxtor: Thermal Aspirates
+    break;
+  case 206:
+    // Fujitsu
+    name="Flying_Height";
+    break;
+  case 207:
+    // Maxtor
+    name="Spin_High_Current";
+    break;
+  case 208:
+    // Maxtor
+    name="Spin_Buzz";
+    break;
+  case 209:
+    // Maxtor
+    name="Offline_Seek_Performnce";
+    break;
+  case 220:
+    switch (val) {
+    case 1:
+      name="Temperature_Celsius";
+      break;
+    default:
+      name="Disk_Shift";
+      break;
+    }
+    break;
+  case 221:
+    name="G-Sense_Error_Rate";
+    break;
+  case 222:
+    name="Loaded_Hours";
+    break;
+  case 223:
+    name="Load_Retry_Count";
+    break;
+  case 224:
+    name="Load_Friction";
+    break;
+  case 225:
+    name="Load_Cycle_Count";
+    break;
+  case 226:
+    name="Load-in_Time";
+    break;
+  case 227:
+    name="Torq-amp_Count";
+    break;
+  case 228:
+    name="Power-off_Retract_Count";
+    break;
+  case 230:
+    // seen in IBM DTPA-353750
+    name="Head_Amplitude";
+    break;
+  case 231:
+    name="Temperature_Celsius";
+    break;
+  case 240:
+    name="Head_Flying_Hours";
+    break;
+  case 250:
+    name="Read_Error_Retry_Rate";
+    break;
+  default:
+    name="Unknown_Attribute";
+    break;
+  }
+  sprintf(out,"%3hu %s",(short int)id,name);
+  return;
+}
+
+// Returns raw value of Attribute with ID==id. This will be in the
+// range 0 to 2^48-1 inclusive.  If the Attribute does not exist,
+// return -1.
+int64_t ATAReturnAttributeRawValue(unsigned char id, struct ata_smart_values *data) {
+  int i;
+
+  // valid Attribute IDs are in the range 1 to 255 inclusive.
+  if (!id || !data)
+    return -1;
+  
+  // loop over Attributes to see if there is one with the desired ID
+  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+    struct ata_smart_attribute *this = data->vendor_attributes + i;
+    if (this->id == id) {
+      // we've found the desired Attribute.  Return its value
+      int64_t rawvalue=0;
+      int j;
+
+      for (j=0; j<6; j++) {
+       // This looks a bit roundabout, but is necessary.  Don't
+       // succumb to the temptation to use raw[j]<<(8*j) since under
+       // the normal rules this will be promoted to the native type.
+       // On a 32 bit machine this might then overflow.
+       int64_t temp;
+       temp = this->raw[j];
+       temp <<= 8*j;
+       rawvalue |= temp;
+      } // loop over j
+      return rawvalue;
+    } // found desired Attribute
+  } // loop over Attributes
+  
+  // fall-through: no such Attribute found
+  return -1;
+}
+
+
diff --git a/atacmds.h b/atacmds.h
new file mode 100644 (file)
index 0000000..01698d2
--- /dev/null
+++ b/atacmds.h
@@ -0,0 +1,537 @@
+/*
+ * atacmds.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+#ifndef ATACMDS_H_
+#define ATACMDS_H_
+
+#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.81 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+// Macro to check expected size of struct at compile time using a
+// dummy typedef.  On size mismatch, compiler reports a negative array
+// size.  If you see an error message of this form, it means that the
+// #pragma pack(1) pragma below is not having the desired effect on
+// your compiler.
+#define ASSERT_SIZEOF_STRUCT(s, n) \
+  typedef char assert_sizeof_struct_##s[(sizeof(struct s) == (n)) ? 1 : -1]
+
+// Add __attribute__((packed)) if compiler supports it
+// because some gcc versions (at least ARM) lack support of #pragma pack()
+#ifdef HAVE_ATTR_PACKED
+#define ATTR_PACKED __attribute__((packed))
+#else
+#define ATTR_PACKED
+#endif
+
+typedef enum {
+  // returns no data, just succeeds or fails
+  ENABLE,
+  DISABLE,
+  AUTOSAVE,
+  IMMEDIATE_OFFLINE,
+  AUTO_OFFLINE,
+  STATUS,       // just says if SMART is working or not
+  STATUS_CHECK, // says if disk's SMART status is healthy, or failing
+  // return 512 bytes of data:
+  READ_VALUES,
+  READ_THRESHOLDS,
+  READ_LOG,
+  IDENTIFY,
+  PIDENTIFY,
+  // returns 1 byte of data
+  CHECK_POWER_MODE,
+  // writes 512 bytes of data:
+  WRITE_LOG
+} smart_command_set;
+
+// ATA Specification Command Register Values (Commands)
+#define ATA_IDENTIFY_DEVICE             0xec                                              
+#define ATA_IDENTIFY_PACKET_DEVICE      0xa1
+#define ATA_SMART_CMD                   0xb0
+#define ATA_CHECK_POWER_MODE            0xe5
+
+// ATA Specification Feature Register Values (SMART Subcommands).
+// Note that some are obsolete as of ATA-7.
+#define ATA_SMART_READ_VALUES           0xd0
+#define ATA_SMART_READ_THRESHOLDS       0xd1
+#define ATA_SMART_AUTOSAVE              0xd2
+#define ATA_SMART_SAVE                  0xd3
+#define ATA_SMART_IMMEDIATE_OFFLINE     0xd4
+#define ATA_SMART_READ_LOG_SECTOR       0xd5
+#define ATA_SMART_WRITE_LOG_SECTOR      0xd6
+#define ATA_SMART_WRITE_THRESHOLDS      0xd7
+#define ATA_SMART_ENABLE                0xd8
+#define ATA_SMART_DISABLE               0xd9
+#define ATA_SMART_STATUS                0xda
+// SFF 8035i Revision 2 Specification Feature Register Value (SMART
+// Subcommand)
+#define ATA_SMART_AUTO_OFFLINE          0xdb
+
+// Sector Number values for ATA_SMART_IMMEDIATE_OFFLINE Subcommand
+#define OFFLINE_FULL_SCAN               0
+#define SHORT_SELF_TEST                 1
+#define EXTEND_SELF_TEST                2
+#define CONVEYANCE_SELF_TEST            3
+#define SELECTIVE_SELF_TEST             4
+#define ABORT_SELF_TEST                 127
+#define SHORT_CAPTIVE_SELF_TEST         129
+#define EXTEND_CAPTIVE_SELF_TEST        130
+#define CONVEYANCE_CAPTIVE_SELF_TEST    131
+#define SELECTIVE_CAPTIVE_SELF_TEST     132
+#define CAPTIVE_MASK                    (0x01<<7)
+
+// Maximum allowed number of SMART Attributes
+#define NUMBER_ATA_SMART_ATTRIBUTES     30
+
+// Needed parts of the ATA DRIVE IDENTIFY Structure. Those labeled
+// word* are NOT used.
+#pragma pack(1)
+struct ata_identify_device {
+  unsigned short words000_009[10];
+  unsigned char  serial_no[20];
+  unsigned short words020_022[3];
+  unsigned char  fw_rev[8];
+  unsigned char  model[40];
+  unsigned short words047_079[33];
+  unsigned short major_rev_num;
+  unsigned short minor_rev_num;
+  unsigned short command_set_1;
+  unsigned short command_set_2;
+  unsigned short command_set_extension;
+  unsigned short cfs_enable_1;
+  unsigned short word086;
+  unsigned short csf_default;
+  unsigned short words088_255[168];
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_identify_device, 512);
+
+/* ata_smart_attribute is the vendor specific in SFF-8035 spec */ 
+#pragma pack(1)
+struct ata_smart_attribute {
+  unsigned char id;
+  // meaning of flag bits: see MACROS just below
+  // WARNING: MISALIGNED!
+  unsigned short flags; 
+  unsigned char current;
+  unsigned char worst;
+  unsigned char raw[6];
+  unsigned char reserv;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_attribute, 12);
+
+// MACROS to interpret the flags bits in the previous structure.
+// These have not been implemented using bitflags and a union, to make
+// it portable across bit/little endian and different platforms.
+
+// 0: Prefailure bit
+
+// From SFF 8035i Revision 2 page 19: Bit 0 (pre-failure/advisory bit)
+// - If the value of this bit equals zero, an attribute value less
+// than or equal to its corresponding attribute threshold indicates an
+// advisory condition where the usage or age of the device has
+// exceeded its intended design life period. If the value of this bit
+// equals one, an attribute value less than or equal to its
+// corresponding attribute threshold indicates a prefailure condition
+// where imminent loss of data is being predicted.
+#define ATTRIBUTE_FLAGS_PREFAILURE(x) (x & 0x01)
+
+// 1: Online bit 
+
+//  From SFF 8035i Revision 2 page 19: Bit 1 (on-line data collection
+// bit) - If the value of this bit equals zero, then the attribute
+// value is updated only during off-line data collection
+// activities. If the value of this bit equals one, then the attribute
+// value is updated during normal operation of the device or during
+// both normal operation and off-line testing.
+#define ATTRIBUTE_FLAGS_ONLINE(x) (x & 0x02)
+
+
+// The following are (probably) IBM's, Maxtors and  Quantum's definitions for the
+// vendor-specific bits:
+// 2: Performance type bit
+#define ATTRIBUTE_FLAGS_PERFORMANCE(x) (x & 0x04)
+
+// 3: Errorrate type bit
+#define ATTRIBUTE_FLAGS_ERRORRATE(x) (x & 0x08)
+
+// 4: Eventcount bit
+#define ATTRIBUTE_FLAGS_EVENTCOUNT(x) (x & 0x10)
+
+// 5: Selfpereserving bit
+#define ATTRIBUTE_FLAGS_SELFPRESERVING(x) (x & 0x20)
+
+
+// Last ten bits are reserved for future use
+
+/* ata_smart_values is format of the read drive Attribute command */
+/* see Table 34 of T13/1321D Rev 1 spec (Device SMART data structure) for *some* info */
+#pragma pack(1)
+struct ata_smart_values {
+  unsigned short int revnumber;
+  struct ata_smart_attribute vendor_attributes [NUMBER_ATA_SMART_ATTRIBUTES];
+  unsigned char offline_data_collection_status;
+  unsigned char self_test_exec_status;  //IBM # segments for offline collection
+  unsigned short int total_time_to_complete_off_line; // IBM different
+  unsigned char vendor_specific_366; // Maxtor & IBM curent segment pointer
+  unsigned char offline_data_collection_capability;
+  unsigned short int smart_capability;
+  unsigned char errorlog_capability;
+  unsigned char vendor_specific_371;  // Maxtor, IBM: self-test failure checkpoint see below!
+  unsigned char short_test_completion_time;
+  unsigned char extend_test_completion_time;
+  unsigned char conveyance_test_completion_time;
+  unsigned char reserved_375_385[11];
+  unsigned char vendor_specific_386_510[125]; // Maxtor bytes 508-509 Attribute/Threshold Revision #
+  unsigned char chksum;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_values, 512);
+
+/* Maxtor, IBM: self-test failure checkpoint byte meaning:
+ 00 - write test
+ 01 - servo basic
+ 02 - servo random
+ 03 - G-list scan
+ 04 - Handling damage
+ 05 - Read scan
+*/
+
+/* Vendor attribute of SMART Threshold (compare to ata_smart_attribute above) */
+#pragma pack(1)
+struct ata_smart_threshold_entry {
+  unsigned char id;
+  unsigned char threshold;
+  unsigned char reserved[10];
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_threshold_entry, 12);
+
+/* Format of Read SMART THreshold Command */
+/* Compare to ata_smart_values above */
+#pragma pack(1)
+struct ata_smart_thresholds_pvt {
+  unsigned short int revnumber;
+  struct ata_smart_threshold_entry thres_entries[NUMBER_ATA_SMART_ATTRIBUTES];
+  unsigned char reserved[149];
+  unsigned char chksum;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_thresholds_pvt, 512);
+
+
+// Table 42 of T13/1321D Rev 1 spec (Error Data Structure)
+#pragma pack(1)
+struct ata_smart_errorlog_error_struct {
+  unsigned char reserved;
+  unsigned char error_register;
+  unsigned char sector_count;
+  unsigned char sector_number;
+  unsigned char cylinder_low;
+  unsigned char cylinder_high;
+  unsigned char drive_head;
+  unsigned char status;
+  unsigned char extended_error[19];
+  unsigned char state;
+  unsigned short timestamp;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_errorlog_error_struct, 30);
+
+
+// Table 41 of T13/1321D Rev 1 spec (Command Data Structure)
+#pragma pack(1)
+struct ata_smart_errorlog_command_struct {
+  unsigned char devicecontrolreg;
+  unsigned char featuresreg;
+  unsigned char sector_count;
+  unsigned char sector_number;
+  unsigned char cylinder_low;
+  unsigned char cylinder_high;
+  unsigned char drive_head;
+  unsigned char commandreg;
+  unsigned int timestamp;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_errorlog_command_struct, 12);
+
+// Table 40 of T13/1321D Rev 1 spec (Error log data structure)
+#pragma pack(1)
+struct ata_smart_errorlog_struct {
+  struct ata_smart_errorlog_command_struct commands[5];
+  struct ata_smart_errorlog_error_struct error_struct;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_errorlog_struct, 90);
+
+// Table 39 of T13/1321D Rev 1 spec (SMART error log sector)
+#pragma pack(1)
+struct ata_smart_errorlog {
+  unsigned char revnumber;
+  unsigned char error_log_pointer;
+  struct ata_smart_errorlog_struct errorlog_struct[5];
+  unsigned short int ata_error_count;
+  unsigned char reserved[57];
+  unsigned char checksum;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_errorlog, 512);
+
+// Table 45 of T13/1321D Rev 1 spec (Self-test log descriptor entry)
+#pragma pack(1)
+struct ata_smart_selftestlog_struct {
+  unsigned char selftestnumber; // Sector number register
+  unsigned char selfteststatus;
+  unsigned short int timestamp;
+  unsigned char selftestfailurecheckpoint;
+  unsigned int lbafirstfailure;
+  unsigned char vendorspecific[15];
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_selftestlog_struct, 24);
+
+// Table 44 of T13/1321D Rev 1 spec (Self-test log data structure)
+#pragma pack(1)
+struct ata_smart_selftestlog {
+  unsigned short int revnumber;
+  struct ata_smart_selftestlog_struct selftest_struct[21];
+  unsigned char vendorspecific[2];
+  unsigned char mostrecenttest;
+  unsigned char reserved[2];
+  unsigned char chksum;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_selftestlog, 512);
+
+// SMART LOG DIRECTORY Table 52 of T13/1532D Vol 1 Rev 1a
+#pragma pack(1)
+struct ata_smart_log_entry {
+  unsigned char numsectors;
+  unsigned char reserved;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_log_entry, 2);
+
+#pragma pack(1)
+struct ata_smart_log_directory {
+  unsigned short int logversion;
+  struct ata_smart_log_entry entry[255];
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_smart_log_directory, 512);
+
+// SMART SELECTIVE SELF-TEST LOG Table 61 of T13/1532D Volume 1
+// Revision 3
+#pragma pack(1)
+struct test_span {
+  uint64_t start;
+  uint64_t end;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(test_span, 16);
+
+#pragma pack(1)
+struct ata_selective_self_test_log {
+  unsigned short     logversion;
+  struct test_span   span[5];
+  unsigned char      reserved1[337-82+1];
+  unsigned char      vendor_specific1[491-338+1];
+  uint64_t           currentlba;
+  unsigned short     currentspan;
+  unsigned short     flags;
+  unsigned char      vendor_specific2[507-504+1];
+  unsigned short     pendingtime;
+  unsigned char      reserved2;
+  unsigned char      checksum;
+} ATTR_PACKED;
+#pragma pack()
+ASSERT_SIZEOF_STRUCT(ata_selective_self_test_log, 512);
+
+#define SELECTIVE_FLAG_DOSCAN  (0x0002)
+#define SELECTIVE_FLAG_PENDING (0x0008)
+#define SELECTIVE_FLAG_ACTIVE  (0x0010)
+
+// Get information from drive
+int ataReadHDIdentity(int device, struct ata_identify_device *buf);
+int ataCheckPowerMode(int 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 *);  
+
+/* Enable/Disable SMART on device */
+int ataEnableSmart ( int device );
+int ataDisableSmart (int device );
+int ataEnableAutoSave(int device);
+int ataDisableAutoSave(int device);
+
+/* Automatic Offline Testing */
+int ataEnableAutoOffline ( int device );
+int ataDisableAutoOffline (int 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);
+
+// 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);
+
+// If SMART supported, this is guaranteed to return 1 if SMART is enabled, else 0.
+int ataDoesSmartWork(int device);
+
+// returns 1 if SMART supported, 0 if not supported or can't tell
+int ataSmartSupport ( struct 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);
+
+/* 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 isExtendedSelfTestTime ( struct ata_smart_values *data);
+
+int isSmartErrorLogCapable(struct ata_smart_values *data, struct ata_identify_device *identity);
+
+int isSmartTestLogCapable(struct ata_smart_values *data, struct ata_identify_device *identity);
+
+int isGeneralPurposeLoggingCapable(struct ata_identify_device *identity);
+
+int isSupportExecuteOfflineImmediate ( struct ata_smart_values *data);
+
+int isSupportAutomaticTimer ( struct ata_smart_values *data);
+
+int isSupportOfflineAbort ( struct ata_smart_values *data);
+
+int isSupportOfflineSurfaceScan ( struct ata_smart_values *data);
+
+int isSupportSelfTest (struct ata_smart_values *data);
+
+int isSupportConveyanceSelfTest(struct ata_smart_values *data);
+
+int isSupportSelectiveSelfTest(struct ata_smart_values *data);
+
+int ataSmartTest(int device, int testtype, struct ata_smart_values *data);
+
+int TestTime(struct 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);
+
+// 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);
+
+// This checks the n'th attribute in the attribute list, NOT the
+// attribute with id==n.  If the attribute does not exist, or the
+// attribute is > threshold, then returns zero.  If the attribute is
+// <= threshold (failing) then we the attribute number if it is a
+// prefail attribute.  Else we return minus the attribute number if it
+// is a usage attribute.
+int ataCheckAttribute(struct ata_smart_values *data,
+                      struct ata_smart_thresholds_pvt *thresholds,
+                      int n);
+
+// External handler function, for when a checksum is not correct.  Can
+// simply return if no action is desired, or can print error messages
+// as needed, or exit.  Is passed a string with the name of the Data
+// Structure with the incorrect checksum.
+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);
+
+
+// This are the meanings of the Self-test failure checkpoint byte.
+// This is in the self-test log at offset 4 bytes into the self-test
+// descriptor and in the SMART READ DATA structure at byte offset
+// 371. These codes are not well documented.  The meanings returned by
+// this routine are used (at least) by Maxtor and IBM. Returns NULL if
+// not recognized.
+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);
+
+// 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);
+
+
+// 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);
+// 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);
+#endif
+
+// This function is exported to give low-level capability
+int smartcommandhandler(int device, smart_command_set command, int select, char *data);
+
+// Utility routines.
+void swap2(char *location);
+void swap4(char *location);
+void swap8(char *location);
+#endif /* ATACMDS_H_ */
diff --git a/ataprint.c b/ataprint.c
new file mode 100644 (file)
index 0000000..09cbe4d
--- /dev/null
@@ -0,0 +1,1864 @@
+/*
+ * ataprint.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+#include "config.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif // #ifdef HAVE_LOCALE_H
+
+#include "int64.h"
+#include "atacmdnames.h"
+#include "atacmds.h"
+#include "ataprint.h"
+#include "smartctl.h"
+#include "extern.h"
+#include "utility.h"
+#include "knowndrives.h"
+
+const char *ataprint_c_cvsid="$Id: ataprint.c,v 1.164 2006/04/12 14:54:28 ballen4705 Exp $"
+ATACMDNAMES_H_CVSID ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
+
+// for passing global control variables
+extern smartmonctrl *con;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// Copies n bytes (or n-1 if n is odd) from in to out, but swaps adjacents
+// bytes.
+void swapbytes(char *out, const char *in, size_t n)
+{
+  size_t i;
+
+  for (i = 0; i < n; i += 2) {
+    out[i]   = in[i+1];
+    out[i+1] = in[i];
+  }
+}
+
+// Copies in to out, but removes leading and trailing whitespace.
+void trim(char *out, const char *in)
+{
+  int i, first, last;
+
+  // Find the first non-space character (maybe none).
+  first = -1;
+  for (i = 0; in[i]; i++)
+    if (!isspace((int)in[i])) {
+      first = i;
+      break;
+    }
+
+  if (first == -1) {
+    // There are no non-space characters.
+    out[0] = '\0';
+    return;
+  }
+
+  // Find the last non-space character.
+  for (i = strlen(in)-1; i >= first && isspace((int)in[i]); i--)
+    ;
+  last = i;
+
+  strncpy(out, in+first, last-first+1);
+  out[last-first+1] = '\0';
+}
+
+// Convenience function for formatting strings from ata_identify_device
+void formatdriveidstring(char *out, const char *in, int n)
+{
+  char tmp[65];
+
+  n = n > 64 ? 64 : n;
+#ifndef __NetBSD__
+  swapbytes(tmp, in, n);
+#else
+  strncpy(tmp, in, n); /* NetBSD delivers host byte order strings */  
+#endif
+  tmp[n] = '\0';
+  trim(out, tmp);
+}
+
+void infofound(char *output) {
+  if (*output)
+    pout("%s\n", output);
+  else
+    pout("[No Information Found]\n");
+}
+
+
+/* For the given Command Register (CR) and Features Register (FR), attempts
+ * to construct a string that describes the contents of the Status
+ * Register (ST) and Error Register (ER).  The string is dynamically allocated
+ * memory and the return value is a pointer to this string.  It is up to the
+ * caller to free this memory.  If there is insufficient memory or if the
+ * meanings of the flags of the error register are not known for the given
+ * command then it returns NULL.
+ *
+ * The meanings of the flags of the error register for all commands are
+ * described in the ATA spec and could all be supported here in theory.
+ * Currently, only a few commands are supported (those that have been seen
+ * to produce errors).  If many more are to be added then this function
+ * should probably be redesigned.
+ */
+char *construct_st_er_desc(struct ata_smart_errorlog_struct *data) {
+  unsigned char CR=data->commands[4].commandreg;
+  unsigned char FR=data->commands[4].featuresreg;
+  unsigned char ST=data->error_struct.status;
+  unsigned char ER=data->error_struct.error_register;
+  char *s;
+  const char *error_flag[8];
+  int i, print_lba=0, print_sector=0;
+
+  // Set of character strings corresponding to different error codes.
+  // Please keep in alphabetic order if you add more.
+  const char  *abrt  = "ABRT";  // ABORTED
+ const char   *amnf  = "AMNF";  // ADDRESS MARK NOT FOUND
+ const char   *ccto  = "CCTO";  // COMMAND COMPLETTION TIMED OUT
+ const char   *eom   = "EOM";   // END OF MEDIA
+ const char   *icrc  = "ICRC";  // INTERFACE CRC ERROR
+ const char   *idnf  = "IDNF";  // ID NOT FOUND
+ const char   *ili   = "ILI";   // MEANING OF THIS BIT IS COMMAND-SET SPECIFIC
+ const char   *mc    = "MC";    // MEDIA CHANGED 
+ const char   *mcr   = "MCR";   // MEDIA CHANGE REQUEST
+ const char   *nm    = "NM";    // NO MEDIA
+ const char   *obs   = "obs";   // OBSOLETE
+ const char   *tk0nf = "TK0NF"; // TRACK 0 NOT FOUND
+ const char   *unc   = "UNC";   // UNCORRECTABLE
+ const char   *wp    = "WP";    // WRITE PROTECTED
+
+  /* If for any command the Device Fault flag of the status register is
+   * not used then used_device_fault should be set to 0 (in the CR switch
+   * below)
+   */
+  int uses_device_fault = 1;
+
+  /* A value of NULL means that the error flag isn't used */
+  for (i = 0; i < 8; i++)
+    error_flag[i] = NULL;
+
+  switch (CR) {
+  case 0x10:  // RECALIBRATE
+    error_flag[2] = abrt;
+    error_flag[1] = tk0nf;
+    break;
+  case 0x20:  /* READ SECTOR(S) */
+  case 0x21:  // READ SECTOR(S)
+  case 0x24:  // READ SECTOR(S) EXT
+  case 0xC4:  /* READ MULTIPLE */
+  case 0x29:  // READ MULTIPLE EXT
+    error_flag[6] = unc;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = amnf;
+    print_lba=1;
+    break;
+  case 0x22:  // READ LONG (with retries)
+  case 0x23:  // READ LONG (without retries)
+    error_flag[4] = idnf;
+    error_flag[2] = abrt;
+    error_flag[0] = amnf;
+    print_lba=1;
+    break;
+  case 0x2a:  // READ STREAM DMA
+  case 0x2b:  // READ STREAM PIO
+    if (CR==0x2a)
+      error_flag[7] = icrc;
+    error_flag[6] = unc;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = ccto;
+    print_lba=1;
+    print_sector=(int)data->error_struct.sector_count;
+    break;
+  case 0x3A:  // WRITE STREAM DMA
+  case 0x3B:  // WRITE STREAM PIO
+    if (CR==0x3A)
+      error_flag[7] = icrc;
+    error_flag[6] = wp;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = ccto;
+    print_lba=1;
+    print_sector=(int)data->error_struct.sector_count;
+    break;
+  case 0x25:  /* READ DMA EXT */
+  case 0x26:  // READ DMA QUEUED EXT
+  case 0xC7:  // READ DMA QUEUED
+  case 0xC8:  /* READ DMA */
+  case 0xC9:
+    error_flag[7] = icrc;
+    error_flag[6] = unc;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = amnf;
+    print_lba=1;
+    if (CR==0x25 || CR==0xC8)
+      print_sector=(int)data->error_struct.sector_count;
+    break;
+  case 0x30:  /* WRITE SECTOR(S) */
+  case 0x31:  // WRITE SECTOR(S)
+  case 0x34:  // WRITE SECTOR(S) EXT
+  case 0xC5:  /* WRITE MULTIPLE */
+  case 0x39:  // WRITE MULTIPLE EXT
+  case 0xCE:  // WRITE MULTIPLE FUA EXT
+    error_flag[6] = wp;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    print_lba=1;
+    break;
+  case 0x32:  // WRITE LONG (with retries)
+  case 0x33:  // WRITE LONG (without retries)
+    error_flag[4] = idnf;
+    error_flag[2] = abrt;
+    print_lba=1;
+    break;
+  case 0x3C:  // WRITE VERIFY
+    error_flag[6] = unc;
+    error_flag[4] = idnf;
+    error_flag[2] = abrt;
+    error_flag[0] = amnf;
+    print_lba=1;
+    break;
+  case 0x40: // READ VERIFY SECTOR(S) with retries
+  case 0x41: // READ VERIFY SECTOR(S) without retries
+  case 0x42: // READ VERIFY SECTOR(S) EXT
+    error_flag[6] = unc;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = amnf;
+    print_lba=1;
+    break;
+  case 0xA0:  /* PACKET */
+    /* Bits 4-7 are all used for sense key (a 'command packet set specific error
+     * indication' according to the ATA/ATAPI-7 standard), so "Sense key" will
+     * be repeated in the error description string if more than one of those
+     * bits is set.
+     */
+    error_flag[7] = "Sense key (bit 3)",
+    error_flag[6] = "Sense key (bit 2)",
+    error_flag[5] = "Sense key (bit 1)",
+    error_flag[4] = "Sense key (bit 0)",
+    error_flag[2] = abrt;
+    error_flag[1] = eom;
+    error_flag[0] = ili;
+    break;
+  case 0xA1:  /* IDENTIFY PACKET DEVICE */
+  case 0xEF:  /* SET FEATURES */
+  case 0x00:  /* NOP */
+  case 0xC6:  /* SET MULTIPLE MODE */
+    error_flag[2] = abrt;
+    break;
+  case 0x2F:  // READ LOG EXT
+    error_flag[6] = unc;
+    error_flag[4] = idnf;
+    error_flag[2] = abrt;
+    error_flag[0] = obs;
+    break;
+  case 0x3F:  // WRITE LOG EXT
+    error_flag[4] = idnf;
+    error_flag[2] = abrt;
+    error_flag[0] = obs;
+    break;
+  case 0xB0:  /* SMART */
+    switch(FR) {
+    case 0xD0:  // SMART READ DATA
+    case 0xD1:  // SMART READ ATTRIBUTE THRESHOLDS
+    case 0xD5:  /* SMART READ LOG */
+      error_flag[6] = unc;
+      error_flag[4] = idnf;
+      error_flag[2] = abrt;
+      error_flag[0] = obs;
+      break;
+    case 0xD6:  /* SMART WRITE LOG */
+      error_flag[4] = idnf;
+      error_flag[2] = abrt;
+      error_flag[0] = obs;
+      break;
+    case 0xD2:  // Enable/Disable Attribute Autosave
+    case 0xD3:  // SMART SAVE ATTRIBUTE VALUES (ATA-3)
+    case 0xD8:  // SMART ENABLE OPERATIONS
+    case 0xD9:  /* SMART DISABLE OPERATIONS */
+    case 0xDA:  /* SMART RETURN STATUS */
+    case 0xDB:  // Enable/Disable Auto Offline (SFF)
+      error_flag[2] = abrt;
+      break;
+    case 0xD4:  // SMART EXECUTE IMMEDIATE OFFLINE
+      error_flag[4] = idnf;
+      error_flag[2] = abrt;
+      break;
+    default:
+      return NULL;
+      break;
+    }
+    break;
+  case 0xB1:  /* DEVICE CONFIGURATION */
+    switch (FR) {
+    case 0xC0:  /* DEVICE CONFIGURATION RESTORE */
+      error_flag[2] = abrt;
+      break;
+    default:
+      return NULL;
+      break;
+    }
+    break;
+  case 0xCA:  /* WRITE DMA */
+  case 0xCB:
+  case 0x35:  // WRITE DMA EXT
+  case 0x3D:  // WRITE DMA FUA EXT
+  case 0xCC:  // WRITE DMA QUEUED
+  case 0x36:  // WRITE DMA QUEUED EXT
+  case 0x3E:  // WRITE DMA QUEUED FUA EXT
+    error_flag[7] = icrc;
+    error_flag[6] = wp;
+    error_flag[5] = mc;
+    error_flag[4] = idnf;
+    error_flag[3] = mcr;
+    error_flag[2] = abrt;
+    error_flag[1] = nm;
+    error_flag[0] = amnf;
+    print_lba=1;
+    if (CR==0x35)
+      print_sector=(int)data->error_struct.sector_count;
+    break;
+  case 0xE4: // READ BUFFER
+  case 0xE8: // WRITE BUFFER
+    error_flag[2] = abrt;
+    break;
+  default:
+    return NULL;
+  }
+
+  /* 256 bytes -- that'll be plenty (OK, this is lazy!) */
+  if (!(s = (char *)malloc(256)))
+    return s;
+
+  s[0] = '\0';
+
+  /* We ignore any status flags other than Device Fault and Error */
+
+  if (uses_device_fault && (ST & (1 << 5))) {
+    strcat(s, "Device Fault");
+    if (ST & 1)  // Error flag
+      strcat(s, "; ");
+  }
+  if (ST & 1) {  // Error flag
+    int count = 0;
+
+    strcat(s, "Error: ");
+    for (i = 7; i >= 0; i--)
+      if ((ER & (1 << i)) && (error_flag[i])) {
+        if (count++ > 0)
+           strcat(s, ", ");
+        strcat(s, error_flag[i]);
+      }
+  }
+
+  // If the error was a READ or WRITE error, print the Logical Block
+  // Address (LBA) at which the read or write failed.
+  if (print_lba) {
+    char tmp[128];
+    int lba;
+
+    // bits 24-27: bits 0-3 of DH
+    lba   = 0xf & data->error_struct.drive_head;
+    lba <<= 8;
+    // bits 16-23: CH
+    lba  |= data->error_struct.cylinder_high;
+    lba <<= 8;
+    // bits 8-15:  CL
+    lba  |= data->error_struct.cylinder_low;
+    lba <<= 8;
+    // bits 0-7:   SN
+    lba  |= data->error_struct.sector_number;
+
+    // print number of sectors, if known, and append to print string
+    if (print_sector) {
+      snprintf(tmp, 128, " %d sectors", print_sector);
+      strcat(s, tmp);
+    }
+
+    // print LBA, and append to print string
+    snprintf(tmp, 128, " at LBA = 0x%08x = %d", lba, lba);
+    strcat(s, tmp);
+  }
+
+  return s;
+}
+
+// This returns the capacity of a disk drive and also prints this into
+// a string, using comma separators to make it easier to read.  If the
+// drive doesn't support LBA addressing or has no user writable
+// sectors (eg, CDROM or DVD) then routine returns zero.
+uint64_t determine_capacity(struct ata_identify_device *drive, char *pstring){
+
+  unsigned short command_set_2  = drive->command_set_2;
+  unsigned short capabilities_0 = drive->words047_079[49-47];
+  unsigned short sects_16       = drive->words047_079[60-47];
+  unsigned short sects_32       = drive->words047_079[61-47];
+  unsigned short lba_16         = drive->words088_255[100-88];
+  unsigned short lba_32         = drive->words088_255[101-88];
+  unsigned short lba_48         = drive->words088_255[102-88];
+  unsigned short lba_64         = drive->words088_255[103-88];
+  uint64_t capacity_short=0, capacity=0, threedigits, power_of_ten;
+  int started=0,k=1000000000;
+  char separator=',';
+
+  // get correct character to use as thousands separator
+#ifdef HAVE_LOCALE_H
+  struct lconv *currentlocale=NULL;
+  setlocale (LC_ALL, "");
+  currentlocale=localeconv();
+  if (*(currentlocale->thousands_sep))
+    separator=*(currentlocale->thousands_sep);
+#endif // #ifdef HAVE_LOCALE_H
+
+  // if drive supports LBA addressing, determine 32-bit LBA capacity
+  if (capabilities_0 & 0x0200) {
+    capacity_short = (unsigned int)sects_32 << 16 | 
+                     (unsigned int)sects_16 << 0  ;
+    
+    // if drive supports 48-bit addressing, determine THAT capacity
+    if ((command_set_2 & 0xc000) == 0x4000 && (command_set_2 & 0x0400))
+      capacity = (uint64_t)lba_64 << 48 | 
+                (uint64_t)lba_48 << 32 |
+                (uint64_t)lba_32 << 16 | 
+                (uint64_t)lba_16 << 0  ;
+    
+    // choose the larger of the two possible capacities
+    if (capacity_short>capacity)
+      capacity=capacity_short;
+  }
+
+  // turn sectors into bytes
+  capacity_short = (capacity *= 512);
+  
+  // print with locale-specific separators (default is comma)
+  power_of_ten =  k;
+  power_of_ten *= k;
+  
+  for (k=0; k<7; k++) {
+    threedigits = capacity/power_of_ten;
+    capacity   -= threedigits*power_of_ten;
+    if (started)
+      // we have already printed some digits
+      pstring += sprintf(pstring, "%c%03"PRIu64, separator, threedigits);
+    else if (threedigits || k==6) {
+      // these are the first digits that we are printing
+      pstring += sprintf(pstring, "%"PRIu64, threedigits);
+      started = 1;
+    }
+    if (k!=6)
+      power_of_ten /= 1000;
+  }
+  
+  return capacity_short;
+}
+
+void ataPrintDriveInfo (struct ata_identify_device *drive){
+  int version, drivetype;
+  const char *description;
+  char unknown[64], timedatetz[DATEANDEPOCHLEN];
+  unsigned short minorrev;
+  char model[64], serial[64], firm[64], capacity[64];
+
+  // format drive information (with byte swapping as needed)
+  formatdriveidstring(model, (char *)drive->model,40);
+  formatdriveidstring(serial, (char *)drive->serial_no,20);
+  formatdriveidstring(firm, (char *)drive->fw_rev,8);
+
+  // print out model, serial # and firmware versions  (byte-swap ASCI strings)
+  drivetype=lookupdrive(model, firm);
+
+  // Print model family if known
+  if (drivetype>=0 && knowndrives[drivetype].modelfamily)
+    pout("Model Family:     %s\n", knowndrives[drivetype].modelfamily);
+
+  pout("Device Model:     ");
+  infofound(model);
+  pout("Serial Number:    ");
+  infofound(serial);
+  pout("Firmware Version: ");
+  infofound(firm);
+
+  if (determine_capacity(drive, capacity))
+    pout("User Capacity:    %s bytes\n", capacity);
+  
+  // See if drive is recognized
+  pout("Device is:        %s\n", drivetype<0?
+       "Not in smartctl database [for details use: -P showall]":
+       "In smartctl database [for details use: -P show]");
+
+  // now get ATA version info
+  version=ataVersionInfo(&description,drive, &minorrev);
+
+  // unrecognized minor revision code
+  if (!description){
+    if (!minorrev)
+      sprintf(unknown, "Exact ATA specification draft version not indicated");
+    else
+      sprintf(unknown,"Not recognized. Minor revision code: 0x%02hx", minorrev);
+    description=unknown;
+  }
+  
+  
+  // SMART Support was first added into the ATA/ATAPI-3 Standard with
+  // Revision 3 of the document, July 25, 1995.  Look at the "Document
+  // Status" revision commands at the beginning of
+  // http://www.t13.org/project/d2008r6.pdf to see this.  So it's not
+  // enough to check if we are ATA-3.  Version=-3 indicates ATA-3
+  // BEFORE Revision 3.
+  pout("ATA Version is:   %d\n",(int)abs(version));
+  pout("ATA Standard is:  %s\n",description);
+  
+  // print current time and date and timezone
+  dateandtimezone(timedatetz);
+  pout("Local Time is:    %s\n", timedatetz);
+
+  // Print warning message, if there is one
+  if (drivetype>=0 && knowndrives[drivetype].warningmsg)
+    pout("\n==> WARNING: %s\n\n", knowndrives[drivetype].warningmsg);
+
+  if (version>=3)
+    return;
+  
+  pout("SMART is only available in ATA Version 3 Revision 3 or greater.\n");
+  pout("We will try to proceed in spite of this.\n");
+  return;
+}
+
+
+const char *OfflineDataCollectionStatus(unsigned char status_byte){
+  unsigned char stat=status_byte & 0x7f;
+  
+  switch(stat){
+  case 0x00:
+    return "was never started";
+  case 0x02:
+    return "was completed without error";
+  case 0x03:
+    if (status_byte == 0x03)
+      return "is in progress";
+    else
+      return "is in a Reserved state";
+  case 0x04:
+    return "was suspended by an interrupting command from host";
+  case 0x05:
+    return "was aborted by an interrupting command from host";
+  case 0x06:
+    return "was aborted by the device with a fatal error";
+  default:
+    if (stat >= 0x40)
+      return "is in a Vendor Specific state\n";
+    else
+      return "is in a Reserved state\n";
+  }
+}
+  
+  
+  /*  prints verbose value Off-line data collection status byte */
+  void PrintSmartOfflineStatus(struct ata_smart_values *data){
+  
+  pout("Offline data collection status:  (0x%02x)\t",
+       (int)data->offline_data_collection_status);
+    
+  // Off-line data collection status byte is not a reserved
+  // or vendor specific value
+  pout("Offline data collection activity\n"
+       "\t\t\t\t\t%s.\n", OfflineDataCollectionStatus(data->offline_data_collection_status));
+  
+  // Report on Automatic Data Collection Status.  Only IBM documents
+  // this bit.  See SFF 8035i Revision 2 for details.
+  if (data->offline_data_collection_status & 0x80)
+    pout("\t\t\t\t\tAuto Offline Data Collection: Enabled.\n");
+  else
+    pout("\t\t\t\t\tAuto Offline Data Collection: Disabled.\n");
+  
+  return;
+}
+
+void PrintSmartSelfExecStatus(struct ata_smart_values *data)
+{
+   pout("Self-test execution status:      ");
+   
+   switch (data->self_test_exec_status >> 4)
+   {
+      case 0:
+        pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t",
+                (int)data->self_test_exec_status);
+        pout("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n");
+        break;
+       case 1:
+         pout("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t",
+                 (int)data->self_test_exec_status);
+         pout("the host.\n");
+         break;
+       case 2:
+         pout("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t",
+                 (int)data->self_test_exec_status);
+         pout("by the host with a hard or soft reset.\n");
+         break;
+       case 3:
+          pout("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("occurred while the device was executing\n\t\t\t\t\t");
+          pout("its self-test routine and the device \n\t\t\t\t\t");
+          pout("was unable to complete the self-test \n\t\t\t\t\t");
+          pout("routine.\n");
+          break;
+       case 4:
+          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("a test element that failed and the test\n\t\t\t\t\t");
+          pout("element that failed is not known.\n");
+          break;
+       case 5:
+          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("the electrical element of the test\n\t\t\t\t\t");
+          pout("failed.\n");
+          break;
+       case 6:
+          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("the servo (and/or seek) element of the \n\t\t\t\t\t");
+          pout("test failed.\n");
+          break;
+       case 7:
+          pout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("the read element of the test failed.\n");
+          break;
+       case 15:
+          pout("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t",
+                  (int)data->self_test_exec_status);
+          pout("%1d0%% of test remaining.\n", 
+                  (int)(data->self_test_exec_status & 0x0f));
+          break;
+       default:
+          pout("(%4d)\tReserved.\n",
+                  (int)data->self_test_exec_status);
+          break;
+   }
+        
+}
+
+
+
+void PrintSmartTotalTimeCompleteOffline ( struct ata_smart_values *data){
+  pout("Total time to complete Offline \n");
+  pout("data collection: \t\t (%4d) seconds.\n", 
+       (int)data->total_time_to_complete_off_line);
+}
+
+
+
+void PrintSmartOfflineCollectCap(struct ata_smart_values *data){
+  pout("Offline data collection\n");
+  pout("capabilities: \t\t\t (0x%02x) ",
+       (int)data->offline_data_collection_capability);
+  
+  if (data->offline_data_collection_capability == 0x00){
+    pout("\tOffline data collection not supported.\n");
+  } 
+  else {
+    pout( "%s\n", isSupportExecuteOfflineImmediate(data)?
+          "SMART execute Offline immediate." :
+          "No SMART execute Offline immediate.");
+    
+    pout( "\t\t\t\t\t%s\n", isSupportAutomaticTimer(data)? 
+          "Auto Offline data collection on/off support.":
+          "No Auto Offline data collection support.");
+    
+    pout( "\t\t\t\t\t%s\n", isSupportOfflineAbort(data)? 
+          "Abort Offline collection upon new\n\t\t\t\t\tcommand.":
+          "Suspend Offline collection upon new\n\t\t\t\t\tcommand.");
+    
+    pout( "\t\t\t\t\t%s\n", isSupportOfflineSurfaceScan(data)? 
+          "Offline surface scan supported.":
+          "No Offline surface scan supported.");
+    
+    pout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)? 
+          "Self-test supported.":
+          "No Self-test supported.");
+
+    pout( "\t\t\t\t\t%s\n", isSupportConveyanceSelfTest(data)? 
+          "Conveyance Self-test supported.":
+          "No Conveyance Self-test supported.");
+
+    pout( "\t\t\t\t\t%s\n", isSupportSelectiveSelfTest(data)? 
+          "Selective Self-test supported.":
+          "No Selective Self-test supported.");
+  }
+}
+
+
+
+void PrintSmartCapability ( struct ata_smart_values *data)
+{
+   pout("SMART capabilities:            ");
+   pout("(0x%04x)\t", (int)data->smart_capability);
+   
+   if (data->smart_capability == 0x00)
+   {
+       pout("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n");
+   } 
+   else 
+   {
+        
+      pout( "%s\n", (data->smart_capability & 0x01)? 
+              "Saves SMART data before entering\n\t\t\t\t\tpower-saving mode.":
+              "Does not save SMART data before\n\t\t\t\t\tentering power-saving mode.");
+                
+      if ( data->smart_capability & 0x02 )
+      {
+          pout("\t\t\t\t\tSupports SMART auto save timer.\n");
+      }
+   }
+}
+
+void PrintSmartErrorLogCapability (struct ata_smart_values *data, struct ata_identify_device *identity)
+{
+
+   pout("Error logging capability:       ");
+    
+   if ( isSmartErrorLogCapable(data, identity) )
+   {
+      pout(" (0x%02x)\tError logging supported.\n",
+               (int)data->errorlog_capability);
+   }
+   else {
+       pout(" (0x%02x)\tError logging NOT supported.\n",
+                (int)data->errorlog_capability);
+   }
+}
+
+void PrintSmartShortSelfTestPollingTime(struct ata_smart_values *data){
+  pout("Short self-test routine \n");
+  if (isSupportSelfTest(data))
+    pout("recommended polling time: \t (%4d) minutes.\n", 
+         (int)data->short_test_completion_time);
+  else
+    pout("recommended polling time: \t        Not Supported.\n");
+}
+
+void PrintSmartExtendedSelfTestPollingTime(struct ata_smart_values *data){
+  pout("Extended self-test routine\n");
+  if (isSupportSelfTest(data))
+    pout("recommended polling time: \t (%4d) minutes.\n", 
+         (int)data->extend_test_completion_time);
+  else
+    pout("recommended polling time: \t        Not Supported.\n");
+}
+
+void PrintSmartConveyanceSelfTestPollingTime(struct ata_smart_values *data){
+  pout("Conveyance self-test routine\n");
+  if (isSupportConveyanceSelfTest(data))
+    pout("recommended polling time: \t (%4d) minutes.\n", 
+         (int)data->conveyance_test_completion_time);
+  else
+    pout("recommended polling time: \t        Not Supported.\n");
+}
+
+// onlyfailed=0 : print all attribute values
+// onlyfailed=1:  just ones that are currently failed and have prefailure bit set
+// onlyfailed=2:  ones that are failed, or have failed with or without prefailure bit set
+void PrintSmartAttribWithThres (struct ata_smart_values *data, 
+                                struct ata_smart_thresholds_pvt *thresholds,
+                                int onlyfailed){
+  int i;
+  int needheader=1;
+  char rawstring[64];
+    
+  // step through all vendor attributes
+  for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
+    char *status;
+    struct ata_smart_attribute *disk=data->vendor_attributes+i;
+    struct ata_smart_threshold_entry *thre=thresholds->thres_entries+i;
+    
+    // consider only valid attributes (allowing some screw-ups in the
+    // thresholds page data to slip by)
+    if (disk->id){
+      char *type, *update;
+      int failednow,failedever;
+      char attributename[64];
+
+      failednow = (disk->current <= thre->threshold);
+      failedever= (disk->worst   <= thre->threshold);
+      
+      // These break out of the loop if we are only printing certain entries...
+      if (onlyfailed==1 && (!ATTRIBUTE_FLAGS_PREFAILURE(disk->flags) || !failednow))
+        continue;
+      
+      if (onlyfailed==2 && !failedever)
+        continue;
+      
+      // print header only if needed
+      if (needheader){
+        if (!onlyfailed){
+          pout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber);
+          pout("Vendor Specific SMART Attributes with Thresholds:\n");
+        }
+        pout("ID# ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE\n");
+        needheader=0;
+      }
+      
+      // is this Attribute currently failed, or has it ever failed?
+      if (failednow)
+        status="FAILING_NOW";
+      else if (failedever)
+        status="In_the_past";
+      else
+        status="    -";
+
+      // Print name of attribute
+      ataPrintSmartAttribName(attributename,disk->id, con->attributedefs);
+      pout("%-28s",attributename);
+
+      // printing line for each valid attribute
+      type=ATTRIBUTE_FLAGS_PREFAILURE(disk->flags)?"Pre-fail":"Old_age";
+      update=ATTRIBUTE_FLAGS_ONLINE(disk->flags)?"Always":"Offline";
+
+      pout("0x%04x   %.3d   %.3d   %.3d    %-10s%-9s%-12s", 
+             (int)disk->flags, (int)disk->current, (int)disk->worst,
+             (int)thre->threshold, type, update, status);
+
+      // print raw value of attribute
+      ataPrintSmartAttribRawValue(rawstring, disk, con->attributedefs);
+      pout("%s\n", rawstring);
+      
+      // print a warning if there is inconsistency here!
+      if (disk->id != thre->id){
+        char atdat[64],atthr[64];
+        ataPrintSmartAttribName(atdat, disk->id, con->attributedefs);
+        ataPrintSmartAttribName(atthr, thre->id, con->attributedefs);
+        pout("%-28s<== Data Page      |  WARNING: PREVIOUS ATTRIBUTE HAS TWO\n",atdat);
+        pout("%-28s<== Threshold Page |  INCONSISTENT IDENTITIES IN THE DATA\n",atthr);
+      }
+    }
+  }
+  if (!needheader) pout("\n");
+}
+
+void ataPrintGeneralSmartValues(struct ata_smart_values *data, struct ata_identify_device *drive){
+  pout("General SMART Values:\n");
+  
+  PrintSmartOfflineStatus(data); 
+  
+  if (isSupportSelfTest(data)){
+    PrintSmartSelfExecStatus (data);
+  }
+  
+  PrintSmartTotalTimeCompleteOffline(data);
+  PrintSmartOfflineCollectCap(data);
+  PrintSmartCapability(data);
+  
+  PrintSmartErrorLogCapability(data, drive);
+
+  pout( "\t\t\t\t\t%s\n", isGeneralPurposeLoggingCapable(drive)?
+        "General Purpose Logging supported.":
+        "No General Purpose Logging support.");
+
+  if (isSupportSelfTest(data)){
+    PrintSmartShortSelfTestPollingTime (data);
+    PrintSmartExtendedSelfTestPollingTime (data);
+  }
+  if (isSupportConveyanceSelfTest(data))
+    PrintSmartConveyanceSelfTestPollingTime (data);
+  
+  pout("\n");
+}
+
+int ataPrintLogDirectory(struct ata_smart_log_directory *data){
+  int i;
+  char *name;
+
+  pout("SMART Log Directory Logging Version %d%s\n",
+       data->logversion, data->logversion==1?" [multi-sector log support]":"");
+  for (i=0; i<=255; i++){
+    int numsect;
+    
+    // Directory log length
+    numsect = i? data->entry[i-1].numsectors : 1;
+    
+    // If the log is not empty, what is it's name
+    if (numsect){
+      switch (i) {
+      case 0:
+        name="Log Directory"; break;
+      case 1:
+        name="Summary SMART error log"; break;
+      case 2:
+        name="Comprehensive SMART error log"; break;
+      case 3:
+        name="Extended Comprehensive SMART error log"; break;
+      case 6:
+        name="SMART self-test log"; break;
+      case 7:
+        name="Extended self-test log"; break;
+      case 9:
+        name="Selective self-test log"; break;
+      case 0x20:
+        name="Streaming performance log"; break;
+      case 0x21:
+        name="Write stream error log"; break;
+      case 0x22:
+        name="Read stream error log"; break;
+      case 0x23:
+        name="Delayed sector log"; break;
+      default:
+        if (0xa0<=i && i<=0xbf) 
+          name="Device vendor specific log";
+        else if (0x80<=i && i<=0x9f)
+          name="Host vendor specific log";
+        else
+          name="Reserved log";
+        break;
+      }
+
+      // print name and length of log
+      pout("Log at address 0x%02x has %03d sectors [%s]\n",
+           i, numsect, name);
+    }
+  }
+  return 0;
+}
+
+// returns number of errors
+int ataPrintSmartErrorlog(struct ata_smart_errorlog *data){
+  int k;
+
+  pout("SMART Error Log Version: %d\n", (int)data->revnumber);
+  
+  // if no errors logged, return
+  if (!data->error_log_pointer){
+    pout("No Errors Logged\n\n");
+    return 0;
+  }
+  PRINT_ON(con);
+  // If log pointer out of range, return
+  if (data->error_log_pointer>5){
+    pout("Invalid Error Log index = 0x%02x (T13/1321D rev 1c "
+         "Section 8.41.6.8.2.2 gives valid range from 1 to 5)\n\n",
+         (int)data->error_log_pointer);
+    return 0;
+  }
+
+  // Some internal consistency checking of the data structures
+  if ((data->ata_error_count-data->error_log_pointer)%5 && con->fixfirmwarebug != FIX_SAMSUNG2) {
+    pout("Warning: ATA error count %d inconsistent with error log pointer %d\n\n",
+         data->ata_error_count,data->error_log_pointer);
+  }
+  
+  // starting printing error log info
+  if (data->ata_error_count<=5)
+    pout( "ATA Error Count: %d\n", (int)data->ata_error_count);
+  else
+    pout( "ATA Error Count: %d (device log contains only the most recent five errors)\n",
+           (int)data->ata_error_count);
+  PRINT_OFF(con);
+  pout("\tCR = Command Register [HEX]\n"
+       "\tFR = Features Register [HEX]\n"
+       "\tSC = Sector Count Register [HEX]\n"
+       "\tSN = Sector Number Register [HEX]\n"
+       "\tCL = Cylinder Low Register [HEX]\n"
+       "\tCH = Cylinder High Register [HEX]\n"
+       "\tDH = Device/Head Register [HEX]\n"
+       "\tDC = Device Command Register [HEX]\n"
+       "\tER = Error register [HEX]\n"
+       "\tST = Status register [HEX]\n"
+       "Powered_Up_Time is measured from power on, and printed as\n"
+       "DDd+hh:mm:SS.sss where DD=days, hh=hours, mm=minutes,\n"
+       "SS=sec, and sss=millisec. It \"wraps\" after 49.710 days.\n\n");
+  
+  // now step through the five error log data structures (table 39 of spec)
+  for (k = 4; k >= 0; k-- ) {
+    char *st_er_desc;
+
+    // The error log data structure entries are a circular buffer
+    int j, i=(data->error_log_pointer+k)%5;
+    struct ata_smart_errorlog_struct *elog=data->errorlog_struct+i;
+    struct ata_smart_errorlog_error_struct *summary=&(elog->error_struct);
+
+    // Spec says: unused error log structures shall be zero filled
+    if (nonempty((unsigned char*)elog,sizeof(*elog))){
+      // Table 57 of T13/1532D Volume 1 Revision 3
+      char *msgstate;
+      int bits=summary->state & 0x0f;
+      int days = (int)summary->timestamp/24;
+
+      switch (bits){
+      case 0x00: msgstate="in an unknown state";break;
+      case 0x01: msgstate="sleeping"; break;
+      case 0x02: msgstate="in standby mode"; break;
+      case 0x03: msgstate="active or idle"; break;
+      case 0x04: msgstate="doing SMART Offline or Self-test"; break;
+      default:   
+        if (bits<0x0b)
+          msgstate="in a reserved state";
+        else
+          msgstate="in a vendor specific state";
+      }
+
+      // See table 42 of ATA5 spec
+      PRINT_ON(con);
+      pout("Error %d occurred at disk power-on lifetime: %d hours (%d days + %d hours)\n",
+             (int)(data->ata_error_count+k-4), (int)summary->timestamp, days, (int)(summary->timestamp-24*days));
+      PRINT_OFF(con);
+      pout("  When the command that caused the error occurred, the device was %s.\n\n",msgstate);
+      pout("  After command completion occurred, registers were:\n"
+           "  ER ST SC SN CL CH DH\n"
+           "  -- -- -- -- -- -- --\n"
+           "  %02x %02x %02x %02x %02x %02x %02x",
+           (int)summary->error_register,
+           (int)summary->status,
+           (int)summary->sector_count,
+           (int)summary->sector_number,
+           (int)summary->cylinder_low,
+           (int)summary->cylinder_high,
+           (int)summary->drive_head);
+      // Add a description of the contents of the status and error registers
+      // if possible
+      st_er_desc = construct_st_er_desc(elog);
+      if (st_er_desc) {
+        pout("  %s", st_er_desc);
+        free(st_er_desc);
+      }
+      pout("\n\n");
+      pout("  Commands leading to the command that caused the error were:\n"
+           "  CR FR SC SN CL CH DH DC   Powered_Up_Time  Command/Feature_Name\n"
+           "  -- -- -- -- -- -- -- --  ----------------  --------------------\n");
+      for ( j = 4; j >= 0; j--){
+        struct ata_smart_errorlog_command_struct *thiscommand=elog->commands+j;
+
+        // Spec says: unused data command structures shall be zero filled
+        if (nonempty((unsigned char*)thiscommand,sizeof(*thiscommand))) {
+         char timestring[32];
+         
+         // Convert integer milliseconds to a text-format string
+         MsecToText(thiscommand->timestamp, timestring);
+         
+          pout("  %02x %02x %02x %02x %02x %02x %02x %02x  %16s  %s\n",
+               (int)thiscommand->commandreg,
+               (int)thiscommand->featuresreg,
+               (int)thiscommand->sector_count,
+               (int)thiscommand->sector_number,
+               (int)thiscommand->cylinder_low,
+               (int)thiscommand->cylinder_high,
+               (int)thiscommand->drive_head,
+               (int)thiscommand->devicecontrolreg,
+              timestring,
+               look_up_ata_command(thiscommand->commandreg, thiscommand->featuresreg));
+       }
+      }
+      pout("\n");
+    }
+  }
+  PRINT_ON(con);
+  if (con->printing_switchable)
+    pout("\n");
+  PRINT_OFF(con);
+  return data->ata_error_count;  
+}
+
+void ataPrintSelectiveSelfTestLog(struct ata_selective_self_test_log *log, struct ata_smart_values *sv) {
+  int i,field1,field2;
+  char *msg;
+  char tmp[64];
+  uint64_t maxl=0,maxr=0;
+  uint64_t current=log->currentlba;
+  uint64_t currentend=current+65535;
+
+  // print data structure revision number
+  pout("SMART Selective self-test log data structure revision number %d\n",(int)log->logversion);
+  if (1 != log->logversion)
+    pout("Warning: ATA Specification requires selective self-test log data structure revision number = 1\n");
+  
+  switch((sv->self_test_exec_status)>>4){
+  case  0:msg="Completed";
+    break;
+  case  1:msg="Aborted_by_host";
+    break;
+  case  2:msg="Interrupted";
+    break;
+  case  3:msg="Fatal_error";
+    break;
+  case  4:msg="Completed_unknown_failure";
+    break;
+  case  5:msg="Completed_electrical_failure";
+    break;
+  case  6:msg="Completed_servo/seek_failure";
+    break;
+  case  7:msg="Completed_read_failure";
+    break;
+  case  8:msg="Completed_handling_damage??";
+    break;
+  case 15:msg="Self_test_in_progress";
+    break;
+  default:msg="Unknown_status ";
+    break;
+  }
+
+  // find the number of columns needed for printing. If in use, the
+  // start/end of span being read-scanned...
+  if (log->currentspan>5) {
+    maxl=current;
+    maxr=currentend;
+  }
+  for (i=0; i<5; i++) {
+    uint64_t start=log->span[i].start;
+    uint64_t end  =log->span[i].end; 
+    // ... plus max start/end of each of the five test spans.
+    if (start>maxl)
+      maxl=start;
+    if (end > maxr)
+      maxr=end;
+  }
+  
+  // we need at least 7 characters wide fields to accomodate the
+  // labels
+  if ((field1=snprintf(tmp,64, "%"PRIu64, maxl))<7)
+    field1=7;
+  if ((field2=snprintf(tmp,64, "%"PRIu64, maxr))<7)
+    field2=7;
+
+  // now print the five test spans
+  pout(" SPAN  %*s  %*s  CURRENT_TEST_STATUS\n", field1, "MIN_LBA", field2, "MAX_LBA");
+
+  for (i=0; i<5; i++) {
+    uint64_t start=log->span[i].start;
+    uint64_t end=log->span[i].end;
+    
+    if ((i+1)==(int)log->currentspan)
+      // this span is currently under test
+      pout("    %d  %*"PRIu64"  %*"PRIu64"  %s [%01d0%% left] (%"PRIu64"-%"PRIu64")\n",
+          i+1, field1, start, field2, end, msg,
+          (int)(sv->self_test_exec_status & 0x7), current, currentend);
+    else
+      // this span is not currently under test
+      pout("    %d  %*"PRIu64"  %*"PRIu64"  Not_testing\n",
+          i+1, field1, start, field2, end);
+  }  
+  
+  // if we are currently read-scanning, print LBAs and the status of
+  // the read scan
+  if (log->currentspan>5)
+    pout("%5d  %*"PRIu64"  %*"PRIu64"  Read_scanning %s\n",
+        (int)log->currentspan, field1, current, field2, currentend,
+        OfflineDataCollectionStatus(sv->offline_data_collection_status));
+  
+  /* Print selective self-test flags.  Possible flag combinations are
+     (numbering bits from 0-15):
+     Bit-1 Bit-3   Bit-4
+     Scan  Pending Active
+     0     *       *       Don't scan
+     1     0       0       Will carry out scan after selective test
+     1     1       0       Waiting to carry out scan after powerup
+     1     0       1       Currently scanning       
+     1     1       1       Currently scanning
+  */
+  
+  pout("Selective self-test flags (0x%x):\n", (unsigned int)log->flags);
+  if (log->flags & SELECTIVE_FLAG_DOSCAN) {
+    if (log->flags & SELECTIVE_FLAG_ACTIVE)
+      pout("  Currently read-scanning the remainder of the disk.\n");
+    else if (log->flags & SELECTIVE_FLAG_PENDING)
+      pout("  Read-scan of remainder of disk interrupted; will resume %d min after power-up.\n",
+          (int)log->pendingtime);
+    else
+      pout("  After scanning selected spans, read-scan remainder of disk.\n");
+  }
+  else
+    pout("  After scanning selected spans, do NOT read-scan remainder of disk.\n");
+  
+  // print pending time
+  pout("If Selective self-test is pending on power-up, resume after %d minute delay.\n",
+       (int)log->pendingtime);
+
+  return; 
+}
+
+// return value is:
+// bottom 8 bits: number of entries found where self-test showed an error
+// remaining bits: if nonzero, power on hours of last self-test where error was found
+int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog *data,int allentries){
+  int i,j,noheaderprinted=1;
+  int retval=0, hours=0, testno=0;
+
+  if (allentries)
+    pout("SMART Self-test log structure revision number %d\n",(int)data->revnumber);
+  if ((data->revnumber!=0x0001) && allentries && con->fixfirmwarebug != FIX_SAMSUNG)
+    pout("Warning: ATA Specification requires self-test log structure revision number = 1\n");
+  if (data->mostrecenttest==0){
+    if (allentries)
+      pout("No self-tests have been logged.  [To run self-tests, use: smartctl -t]\n\n");
+    return 0;
+  }
+
+  // print log      
+  for (i=20;i>=0;i--){    
+    struct ata_smart_selftestlog_struct *log;
+
+    // log is a circular buffer
+    j=(i+data->mostrecenttest)%21;
+    log=data->selftest_struct+j;
+
+    if (nonempty((unsigned char*)log,sizeof(*log))){
+      char *msgtest,*msgstat,percent[64],firstlba[64];
+      int errorfound=0;
+      
+      // count entry based on non-empty structures -- needed for
+      // Seagate only -- other vendors don't have blank entries 'in
+      // the middle'
+      testno++;
+
+      // test name
+      switch(log->selftestnumber){
+      case   0: msgtest="Offline            "; break;
+      case   1: msgtest="Short offline      "; break;
+      case   2: msgtest="Extended offline   "; break;
+      case   3: msgtest="Conveyance offline "; break;
+      case   4: msgtest="Selective offline  "; break;
+      case 127: msgtest="Abort offline test "; break;
+      case 129: msgtest="Short captive      "; break;
+      case 130: msgtest="Extended captive   "; break;
+      case 131: msgtest="Conveyance captive "; break;
+      case 132: msgtest="Selective captive  "; break;
+      default:  
+        if ( log->selftestnumber>=192 ||
+            (log->selftestnumber>= 64 && log->selftestnumber<=126))
+          msgtest="Vendor offline     ";
+        else
+          msgtest="Reserved offline   ";
+      }
+      
+      // test status
+      switch((log->selfteststatus)>>4){
+      case  0:msgstat="Completed without error      "; break;
+      case  1:msgstat="Aborted by host              "; break;
+      case  2:msgstat="Interrupted (host reset)     "; break;
+      case  3:msgstat="Fatal or unknown error       "; errorfound=1; break;
+      case  4:msgstat="Completed: unknown failure   "; errorfound=1; break;
+      case  5:msgstat="Completed: electrical failure"; errorfound=1; break;
+      case  6:msgstat="Completed: servo/seek failure"; errorfound=1; break;
+      case  7:msgstat="Completed: read failure      "; errorfound=1; break;
+      case  8:msgstat="Completed: handling damage?? "; errorfound=1; break;
+      case 15:msgstat="Self-test routine in progress"; break;
+      default:msgstat="Unknown/reserved test status ";
+      }
+
+      retval+=errorfound;
+      sprintf(percent,"%1d0%%",(log->selfteststatus)&0xf);
+
+      // T13/1321D revision 1c: (Data structure Rev #1)
+
+      //The failing LBA shall be the LBA of the uncorrectable sector
+      //that caused the test to fail. If the device encountered more
+      //than one uncorrectable sector during the test, this field
+      //shall indicate the LBA of the first uncorrectable sector
+      //encountered. If the test passed or the test failed for some
+      //reason other than an uncorrectable sector, the value of this
+      //field is undefined.
+
+      // This is true in ALL ATA-5 specs
+      
+      if (!errorfound || log->lbafirstfailure==0xffffffff || log->lbafirstfailure==0x00000000)
+        sprintf(firstlba,"%s","-");
+      else      
+        sprintf(firstlba,"%u",log->lbafirstfailure);
+
+      // print out a header if needed
+      if (noheaderprinted && (allentries || errorfound)){
+        pout("Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error\n");
+        noheaderprinted=0;
+      }
+      
+      // print out an entry, either if we are printing all entries OR
+      // if an error was found
+      if (allentries || errorfound)
+        pout("#%2d  %s %s %s  %8d         %s\n", testno, msgtest, msgstat, percent, (int)log->timestamp, firstlba);
+
+      // keep track of time of most recent error
+      if (errorfound && !hours)
+        hours=log->timestamp;
+    }
+  }
+  if (!allentries && retval)
+    pout("\n");
+
+  hours = hours << 8;
+  return (retval | hours);
+}
+
+void ataPseudoCheckSmart ( struct ata_smart_values *data, 
+                           struct ata_smart_thresholds_pvt *thresholds) {
+  int i;
+  int failed = 0;
+  for (i = 0 ; i < NUMBER_ATA_SMART_ATTRIBUTES ; i++) {
+    if (data->vendor_attributes[i].id &&   
+        thresholds->thres_entries[i].id &&
+        ATTRIBUTE_FLAGS_PREFAILURE(data->vendor_attributes[i].flags) &&
+        (data->vendor_attributes[i].current <= thresholds->thres_entries[i].threshold) &&
+        (thresholds->thres_entries[i].threshold != 0xFE)){
+      pout("Attribute ID %d Failed\n",(int)data->vendor_attributes[i].id);
+      failed = 1;
+    } 
+  }   
+  pout("%s\n", ( failed )?
+         "SMART overall-health self-assessment test result: FAILED!\n"
+         "Drive failure expected in less than 24 hours. SAVE ALL DATA":
+         "SMART overall-health self-assessment test result: PASSED");
+}
+
+
+// Compares failure type to policy in effect, and either exits or
+// simply returns to the calling routine.
+void failuretest(int type, int returnvalue){
+
+  // If this is an error in an "optional" SMART command
+  if (type==OPTIONAL_CMD){
+    if (con->conservative){
+      pout("An optional SMART command failed: exiting.  Remove '-T conservative' option to continue.\n");
+      EXIT(returnvalue);
+    }
+    return;
+  }
+
+  // If this is an error in a "mandatory" SMART command
+  if (type==MANDATORY_CMD){
+    if (con->permissive--)
+      return;
+    pout("A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.\n");
+    EXIT(returnvalue);
+  }
+
+  pout("Smartctl internal error in failuretest(type=%d). Please contact developers at " PACKAGE_HOMEPAGE "\n",type);
+  EXIT(returnvalue|FAILCMD);
+}
+
+// Used to warn users about invalid checksums.  Action to be taken may be
+// altered by the user.
+void checksumwarning(const char *string){
+  // user has asked us to ignore checksum errors
+  if (con->checksumignore)
+        return;
+
+  pout("Warning! %s error: invalid SMART checksum.\n",string);
+
+  // user has asked us to fail on checksum errors
+  if (con->checksumfail)
+    EXIT(FAILSMART);
+
+  return;
+}
+
+// Initialize to zero just in case some SMART routines don't work
+struct ata_identify_device drive;
+struct ata_smart_values smartval;
+struct ata_smart_thresholds_pvt smartthres;
+struct ata_smart_errorlog smarterror;
+struct ata_smart_selftestlog smartselftest;
+
+int ataPrintMain (int fd){
+  int timewait,code;
+  int returnval=0, retid=0, supported=0, needupdate=0;
+
+  // Start by getting Drive ID information.  We need this, to know if SMART is supported.
+  if ((retid=ataReadHDIdentity(fd,&drive))<0){
+    pout("Smartctl: Device Read Identity Failed (not an ATA/ATAPI device)\n\n");
+    failuretest(MANDATORY_CMD, returnval|=FAILID);
+  }
+
+  // If requested, show which presets would be used for this drive and exit.
+  if (con->showpresets) {
+    showpresets(&drive);
+    EXIT(0);
+  }
+
+  // Use preset vendor attribute options unless user has requested otherwise.
+  if (!con->ignorepresets){
+    unsigned char *charptr;
+    if ((charptr=con->attributedefs))
+      applypresets(&drive, &charptr, con);
+    else {
+      pout("Fatal internal error in ataPrintMain()\n");
+      EXIT(returnval|=FAILCMD);
+    }
+  }
+
+  // Print most drive identity information if requested
+  if (con->driveinfo){
+    pout("=== START OF INFORMATION SECTION ===\n");
+    ataPrintDriveInfo(&drive);
+  }
+
+  // Was this a packet device?
+  if (retid>0){
+    pout("SMART support is: Unavailable - Packet Interface Devices [this device: %s] don't support ATA SMART\n", packetdevicetype(retid-1));
+    failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+  }
+  
+  // if drive does not supports SMART it's time to exit
+  supported=ataSmartSupport(&drive);
+  if (supported != 1){
+    if (supported==0) {
+      pout("SMART support is: Unavailable - device lacks SMART capability.\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+      pout("                  Checking to be sure by trying SMART ENABLE command.\n");
+    }
+    else {
+      pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 82-83 don't show if SMART supported.\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+      pout("                  Checking for SMART support by trying SMART ENABLE command.\n");
+    }
+
+    if (ataEnableSmart(fd)){
+      pout("                  SMART ENABLE failed - this establishes that this device lacks SMART functionality.\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+      supported=0;
+    }
+    else {
+      pout("                  SMART ENABLE appeared to work!  Continuing.\n");
+      supported=1;
+    }
+    if (!con->driveinfo) pout("\n");
+  }
+  
+  // Now print remaining drive info: is SMART enabled?    
+  if (con->driveinfo){
+    int ison=ataIsSmartEnabled(&drive),isenabled=ison;
+    
+    if (ison==-1) {
+      pout("SMART support is: Ambiguous - ATA IDENTIFY DEVICE words 85-87 don't show if SMART is enabled.\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+      // check SMART support by trying a command
+      pout("                  Checking to be sure by trying SMART RETURN STATUS command.\n");
+      isenabled=ataDoesSmartWork(fd);
+    }
+    else {
+      pout("SMART support is: Available - device has SMART capability.\n");
+#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
+      if (ata_identify_is_cached(fd)) {
+        pout("                  %sabled status cached by OS, trying SMART RETURN STATUS cmd.\n",
+                    (isenabled?"En":"Dis"));
+        isenabled=ataDoesSmartWork(fd);
+      }
+#endif
+    }
+
+    if (isenabled)
+      pout("SMART support is: Enabled\n");
+    else {
+      if (ison==-1)
+        pout("SMART support is: Unavailable\n");
+      else
+        pout("SMART support is: Disabled\n");
+    }
+    pout("\n");
+  }
+  
+  // START OF THE ENABLE/DISABLE SECTION OF THE CODE
+  if (con->smartenable || con->smartdisable || 
+      con->smartautosaveenable || con->smartautosavedisable || 
+      con->smartautoofflineenable || con->smartautoofflinedisable)
+    pout("=== START OF ENABLE/DISABLE COMMANDS SECTION ===\n");
+  
+  // Enable/Disable SMART commands
+  if (con->smartenable){
+    if (ataEnableSmart(fd)) {
+      pout("Smartctl: SMART Enable Failed.\n\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+    }
+    else
+      pout("SMART Enabled.\n");
+  }
+  
+  // From here on, every command requires that SMART be enabled...
+  if (!ataDoesSmartWork(fd)) {
+    pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n");
+    return returnval;
+  }
+  
+  // Turn off SMART on device
+  if (con->smartdisable){    
+    if (ataDisableSmart(fd)) {
+      pout( "Smartctl: SMART Disable Failed.\n\n");
+      failuretest(MANDATORY_CMD,returnval|=FAILSMART);
+    }
+    pout("SMART Disabled. Use option -s with argument 'on' to enable it.\n");
+    return returnval;           
+  }
+  
+  // Let's ALWAYS issue this command to get the SMART status
+  code=ataSmartStatus2(fd);
+  if (code==-1)
+    failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+
+  // Enable/Disable Auto-save attributes
+  if (con->smartautosaveenable){
+    if (ataEnableAutoSave(fd)){
+      pout( "Smartctl: SMART Enable Attribute Autosave Failed.\n\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+    }
+    else
+      pout("SMART Attribute Autosave Enabled.\n");
+  }
+  if (con->smartautosavedisable){
+    if (ataDisableAutoSave(fd)){
+      pout( "Smartctl: SMART Disable Attribute Autosave Failed.\n\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+    }
+    else
+      pout("SMART Attribute Autosave Disabled.\n");
+  }
+  
+  // for everything else read values and thresholds are needed
+  if (ataReadSmartValues(fd, &smartval)){
+    pout("Smartctl: SMART Read Values failed.\n\n");
+    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+  }
+  if (ataReadSmartThresholds(fd, &smartthres)){
+    pout("Smartctl: SMART Read Thresholds failed.\n\n");
+    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+  }
+
+  // Enable/Disable Off-line testing
+  if (con->smartautoofflineenable){
+    if (!isSupportAutomaticTimer(&smartval)){
+      pout("Warning: device does not support SMART Automatic Timers.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    needupdate=1;
+    if (ataEnableAutoOffline(fd)){
+      pout( "Smartctl: SMART Enable Automatic Offline Failed.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else
+      pout("SMART Automatic Offline Testing Enabled every four hours.\n");
+  }
+  if (con->smartautoofflinedisable){
+    if (!isSupportAutomaticTimer(&smartval)){
+      pout("Warning: device does not support SMART Automatic Timers.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    needupdate=1;
+    if (ataDisableAutoOffline(fd)){
+      pout("Smartctl: SMART Disable Automatic Offline Failed.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else
+      pout("SMART Automatic Offline Testing Disabled.\n");
+  }
+
+  if (needupdate && ataReadSmartValues(fd, &smartval)){
+    pout("Smartctl: SMART Read Values failed.\n\n");
+    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+  }
+
+  // all this for a newline!
+  if (con->smartenable || con->smartdisable || 
+      con->smartautosaveenable || con->smartautosavedisable || 
+      con->smartautoofflineenable || con->smartautoofflinedisable)
+    pout("\n");
+
+  // START OF READ-ONLY OPTIONS APART FROM -V and -i
+  if (con->checksmart || con->generalsmartvalues || con->smartvendorattrib || con->smarterrorlog || con->smartselftestlog)
+    pout("=== START OF READ SMART DATA SECTION ===\n");
+  
+  // Check SMART status (use previously returned value)
+  if (con->checksmart){
+    switch (code) {
+
+    case 0:
+      // The case where the disk health is OK
+      pout("SMART overall-health self-assessment test result: PASSED\n");
+      if (ataCheckSmart(&smartval, &smartthres,0)){
+        if (con->smartvendorattrib)
+          pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
+        else {
+          PRINT_ON(con);
+          pout("Please note the following marginal Attributes:\n");
+          PrintSmartAttribWithThres(&smartval, &smartthres,2);
+        } 
+        returnval|=FAILAGE;
+      }
+      else
+        pout("\n");
+      break;
+      
+    case 1:
+      // The case where the disk health is NOT OK
+      PRINT_ON(con);
+      pout("SMART overall-health self-assessment test result: FAILED!\n"
+           "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
+      PRINT_OFF(con);
+      if (ataCheckSmart(&smartval, &smartthres,1)){
+        returnval|=FAILATTR;
+        if (con->smartvendorattrib)
+          pout("See vendor-specific Attribute list for failed Attributes.\n\n");
+        else {
+          PRINT_ON(con);
+          pout("Failed Attributes:\n");
+          PrintSmartAttribWithThres(&smartval, &smartthres,1);
+        }
+      }
+      else
+        pout("No failed Attributes found.\n\n");   
+      returnval|=FAILSTATUS;
+      PRINT_OFF(con);
+      break;
+
+    case -1:
+    default:
+      // The case where something went wrong with HDIO_DRIVE_TASK ioctl()
+      if (ataCheckSmart(&smartval, &smartthres,1)){
+        PRINT_ON(con);
+        pout("SMART overall-health self-assessment test result: FAILED!\n"
+             "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
+        PRINT_OFF(con);
+        returnval|=FAILATTR;
+        returnval|=FAILSTATUS;
+        if (con->smartvendorattrib)
+          pout("See vendor-specific Attribute list for failed Attributes.\n\n");
+        else {
+          PRINT_ON(con);
+          pout("Failed Attributes:\n");
+          PrintSmartAttribWithThres(&smartval, &smartthres,1);
+        }
+      }
+      else {
+        pout("SMART overall-health self-assessment test result: PASSED\n");
+        if (ataCheckSmart(&smartval, &smartthres,0)){
+          if (con->smartvendorattrib)
+            pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
+          else {
+            PRINT_ON(con);
+            pout("Please note the following marginal Attributes:\n");
+            PrintSmartAttribWithThres(&smartval, &smartthres,2);
+          } 
+          returnval|=FAILAGE;
+        }
+        else
+          pout("\n");
+      } 
+      PRINT_OFF(con);
+      break;
+    } // end of switch statement
+    
+    PRINT_OFF(con);
+  } // end of checking SMART Status
+  
+  // Print general SMART values
+  if (con->generalsmartvalues)
+    ataPrintGeneralSmartValues(&smartval, &drive); 
+
+  // Print vendor-specific attributes
+  if (con->smartvendorattrib){
+    PRINT_ON(con);
+    PrintSmartAttribWithThres(&smartval, &smartthres,con->printing_switchable?2:0);
+    PRINT_OFF(con);
+  }
+
+  // Print SMART log Directory
+  if (con->smartlogdirectory){
+    struct ata_smart_log_directory smartlogdirectory;
+    if (!isGeneralPurposeLoggingCapable(&drive)){
+      pout("Warning: device does not support General Purpose Logging\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else {
+      PRINT_ON(con);
+      pout("Log Directory Supported\n");
+      if (ataReadLogDirectory(fd, &smartlogdirectory)){
+        PRINT_OFF(con);
+        pout("Read Log Directory failed.\n\n");
+        failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+      }
+      else
+        ataPrintLogDirectory( &smartlogdirectory);
+    }
+    PRINT_OFF(con);
+  }
+  
+  // Print SMART error log
+  if (con->smarterrorlog){
+    if (!isSmartErrorLogCapable(&smartval, &drive)){
+      pout("Warning: device does not support Error Logging\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    if (ataReadErrorLog(fd, &smarterror)){
+      pout("Smartctl: SMART Error Log Read Failed\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else {
+      // quiet mode is turned on inside ataPrintSmartErrorLog()
+      if (ataPrintSmartErrorlog(&smarterror))
+       returnval|=FAILERR;
+      PRINT_OFF(con);
+    }
+  }
+  
+  // Print SMART self-test log
+  if (con->smartselftestlog){
+    if (!isSmartTestLogCapable(&smartval, &drive)){
+      pout("Warning: device does not support Self Test Logging\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }    
+    if(ataReadSelfTestLog(fd, &smartselftest)){
+      pout("Smartctl: SMART Self Test Log Read Failed\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else {
+      PRINT_ON(con);
+      if (ataPrintSmartSelfTestlog(&smartselftest,!con->printing_switchable))
+       returnval|=FAILLOG;
+      PRINT_OFF(con);
+      pout("\n");
+    }
+  }
+
+  // Print SMART selective self-test log
+  if (con->selectivetestlog){
+    struct ata_selective_self_test_log log;
+    
+    if (!isSupportSelectiveSelfTest(&smartval))
+      pout("Device does not support Selective Self Tests/Logging\n");
+    else if(ataReadSelectiveSelfTestLog(fd, &log)) {
+      pout("Smartctl: SMART Selective Self Test Log Read Failed\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    else {
+      PRINT_ON(con);
+      ataPrintSelectiveSelfTestLog(&log, &smartval);
+      PRINT_OFF(con);
+      pout("\n");
+    }
+  }
+
+  // START OF THE TESTING SECTION OF THE CODE.  IF NO TESTING, RETURN
+  if (con->testcase==-1)
+    return returnval;
+  
+  pout("=== START OF OFFLINE IMMEDIATE AND SELF-TEST SECTION ===\n");
+  // if doing a self-test, be sure it's supported by the hardware
+  switch (con->testcase){
+  case OFFLINE_FULL_SCAN:
+    if (!isSupportExecuteOfflineImmediate(&smartval)){
+      pout("Warning: device does not support Execute Offline Immediate function.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    break;
+  case ABORT_SELF_TEST:
+  case SHORT_SELF_TEST:
+  case EXTEND_SELF_TEST:
+  case SHORT_CAPTIVE_SELF_TEST:
+  case EXTEND_CAPTIVE_SELF_TEST:
+    if (!isSupportSelfTest(&smartval)){
+      pout("Warning: device does not support Self-Test functions.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    break;
+  case CONVEYANCE_SELF_TEST:
+  case CONVEYANCE_CAPTIVE_SELF_TEST:
+    if (!isSupportConveyanceSelfTest(&smartval)){
+      pout("Warning: device does not support Conveyance Self-Test functions.\n\n");
+      failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+    }
+    break;
+  case SELECTIVE_SELF_TEST:
+  case SELECTIVE_CAPTIVE_SELF_TEST:
+    if (!isSupportSelectiveSelfTest(&smartval)){
+      pout("Warning: device does not support Selective Self-Test functions.\n\n");
+      failuretest(MANDATORY_CMD, returnval|=FAILSMART);
+    }
+    break;
+  default:
+    pout("Internal error in smartctl: con->testcase==%d not recognized\n", (int)con->testcase);
+    pout("Please contact smartmontools developers at %s.\n", PACKAGE_BUGREPORT);
+    EXIT(returnval|=FAILCMD);
+  }
+
+  // Now do the test.  Note ataSmartTest prints its own error/success
+  // messages
+  if (ataSmartTest(fd, con->testcase, &smartval))
+    failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+  else {  
+    // Tell user how long test will take to complete.  This is tricky
+    // because in the case of an Offline Full Scan, the completion
+    // timer is volatile, and needs to be read AFTER the command is
+    // given. If this will interrupt the Offline Full Scan, we don't
+    // do it, just warn user.
+    if (con->testcase==OFFLINE_FULL_SCAN){
+      if (isSupportOfflineAbort(&smartval))
+       pout("Note: giving further SMART commands will abort Offline testing\n");
+      else if (ataReadSmartValues(fd, &smartval)){
+       pout("Smartctl: SMART Read Values failed.\n");
+       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+      }
+    }
+    
+    // Now say how long the test will take to complete
+    if ((timewait=TestTime(&smartval,con->testcase))){ 
+      time_t t=time(NULL);
+      if (con->testcase==OFFLINE_FULL_SCAN) {
+       t+=timewait;
+       pout("Please wait %d seconds for test to complete.\n", (int)timewait);
+      } else {
+       t+=timewait*60;
+       pout("Please wait %d minutes for test to complete.\n", (int)timewait);
+      }
+      pout("Test will complete after %s\n", ctime(&t));
+      
+      if (con->testcase!=SHORT_CAPTIVE_SELF_TEST && 
+         con->testcase!=EXTEND_CAPTIVE_SELF_TEST && 
+         con->testcase!=CONVEYANCE_CAPTIVE_SELF_TEST && 
+         con->testcase!=SELECTIVE_CAPTIVE_SELF_TEST)
+       pout("Use smartctl -X to abort test.\n"); 
+    }
+  }
+
+  return returnval;
+}
diff --git a/ataprint.h b/ataprint.h
new file mode 100644 (file)
index 0000000..d2e1e46
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * ataprint.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+#ifndef ATAPRINT_H_
+#define ATAPRINT_H_
+
+#define ATAPRINT_H_CVSID "$Id: ataprint.h,v 1.28 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Prints ATA Drive Information and S.M.A.R.T. Capability */
+void ataPrintDriveInfo(struct ata_identify_device *);
+
+void ataPrintGeneralSmartValues(struct ata_smart_values *, struct ata_identify_device *);
+
+void ataPrintSmartThresholds(struct ata_smart_thresholds_pvt *);
+
+// returns number of errors in Errorlog
+int  ataPrintSmartErrorlog(struct ata_smart_errorlog *);
+
+int ataPrintLogDirectory(struct ata_smart_log_directory *);
+
+void PrintSmartAttributes(struct ata_smart_values *);
+
+void PrintSmartAttribWithThres(struct ata_smart_values *,
+                                struct ata_smart_thresholds_pvt *,
+                                int onlyfailed);
+
+// returns number of entries that had logged errors
+int ataPrintSmartSelfTestlog(struct ata_smart_selftestlog *, int allentries);
+
+void ataPseudoCheckSmart(struct ata_smart_values *, struct ata_smart_thresholds_pvt *);
+
+// Convenience function for formatting strings from ata_identify_device.
+void formatdriveidstring(char *out, const char *in, int n);
+
+int ataPrintMain(int fd);
+
+#endif
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..3fc1c45
--- /dev/null
@@ -0,0 +1,88 @@
+#!/bin/sh
+# $Id: autogen.sh,v 1.13 2005/09/19 09:28:11 chrfranke Exp $
+#
+# Generate ./configure from config.in and Makefile.in from Makefile.am.
+# This also adds files like missing,depcomp,install-sh to the source
+# direcory. To update these files at a later date use:
+#      autoreconf -f -i -v
+
+
+# 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 ||
+        echo "Warning: DOS text file type set, 'make dist' and related targets will not work."
+    rm -f dostest.tmp
+}
+
+typep()
+{
+    cmd=$1 ; TMP=$IFS ; IFS=: ; set $PATH
+    for dir
+    do
+       if [ -x "$dir/$cmd" ]; then
+           echo "$dir/$cmd"
+           IFS=$TMP
+           return 0
+        fi
+    done
+    IFS=$TMP
+    return 1
+}
+
+test -x "$AUTOMAKE" || 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 "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 "the bleeding edge, you should know what you're doing, mainly how"
+echo "to test it before the developers. Be patient."
+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."
+exit 2;
+}
+
+# Warn if Automake version is unknown
+ver=
+case "$AUTOMAKE" in
+  *automake-1.[78]|*automake17)
+    ;;
+  *)
+    ver="`$AUTOMAKE --version | head -1 | sed -n 's,^.*\([12]\.[.0-9]*[-pl0-9]*\).*$,\1,p'`"
+    ver="${ver:-?.?.?}"
+    case "$ver" in
+      1.[78]*|1.9.[1-6]) 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."
+}
+
+set -e # stops on error status
+
+${ACLOCAL}
+autoheader
+${AUTOMAKE} --add-missing --copy --foreign
+autoconf
diff --git a/config.guess b/config.guess
new file mode 100755 (executable)
index 0000000..c28419d
--- /dev/null
@@ -0,0 +1,1450 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+timestamp='2004-10-25'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Per Bothner <per@bothner.com>.
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# This script attempts to guess a canonical system name similar to
+# config.sub.  If it succeeds, it prints the system name on stdout, and
+# exits with 0.  Otherwise, it exits with 1.
+#
+# The plan is that this can be called by configure scripts if you
+# don't specify an explicit build system type.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+trap 'exit 1' 1 2 15
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+set_cc_for_build='
+trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
+trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
+: ${TMPDIR=/tmp} ;
+ { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
+dummy=$tmp/dummy ;
+tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
+case $CC_FOR_BUILD,$HOST_CC,$CC in
+ ,,)    echo "int x;" > $dummy.c ;
+       for c in cc gcc c89 c99 ; do
+         if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
+            CC_FOR_BUILD="$c"; break ;
+         fi ;
+       done ;
+       if test x"$CC_FOR_BUILD" = x ; then
+         CC_FOR_BUILD=no_compiler_found ;
+       fi
+       ;;
+ ,,*)   CC_FOR_BUILD=$CC ;;
+ ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+esac ;'
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
+       PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
+    *:NetBSD:*:*)
+       # NetBSD (nbsd) targets should (where applicable) match one or
+       # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+       # switched to ELF, *-*-netbsd* would select the old
+       # object file format.  This provides both forward
+       # compatibility and a consistent mechanism for selecting the
+       # object file format.
+       #
+       # Note: NetBSD doesn't particularly care about the vendor
+       # portion of the name.  We always set it to "unknown".
+       sysctl="sysctl -n hw.machine_arch"
+       UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
+           /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+       case "${UNAME_MACHINE_ARCH}" in
+           armeb) machine=armeb-unknown ;;
+           arm*) machine=arm-unknown ;;
+           sh3el) machine=shl-unknown ;;
+           sh3eb) machine=sh-unknown ;;
+           *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
+       esac
+       # The Operating System including object format, if it has switched
+       # to ELF recently, or will in the future.
+       case "${UNAME_MACHINE_ARCH}" in
+           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+               eval $set_cc_for_build
+               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+                       | grep __ELF__ >/dev/null
+               then
+                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+                   # Return netbsd for either.  FIX?
+                   os=netbsd
+               else
+                   os=netbsdelf
+               fi
+               ;;
+           *)
+               os=netbsd
+               ;;
+       esac
+       # The OS release
+       # Debian GNU/NetBSD machines have a different userland, and
+       # thus, need a distinct triplet. However, they do not need
+       # kernel version information, so it can be replaced with a
+       # suitable tag, in the style of linux-gnu.
+       case "${UNAME_VERSION}" in
+           Debian*)
+               release='-gnu'
+               ;;
+           *)
+               release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+               ;;
+       esac
+       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+       # contains redundant information, the shorter form:
+       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+       echo "${machine}-${os}${release}"
+       exit 0 ;;
+    amd64:OpenBSD:*:*)
+       echo x86_64-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    amiga:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    cats:OpenBSD:*:*)
+       echo arm-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    hp300:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    luna88k:OpenBSD:*:*)
+       echo m88k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mac68k:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    macppc:OpenBSD:*:*)
+       echo powerpc-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvme68k:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvme88k:OpenBSD:*:*)
+       echo m88k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    mvmeppc:OpenBSD:*:*)
+       echo powerpc-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sgi:OpenBSD:*:*)
+       echo mips64-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    sun3:OpenBSD:*:*)
+       echo m68k-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    *:OpenBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE}
+       exit 0 ;;
+    *:ekkoBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
+       exit 0 ;;
+    macppc:MirBSD:*:*)
+       echo powerppc-unknown-mirbsd${UNAME_RELEASE}
+       exit 0 ;;
+    *:MirBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+       exit 0 ;;
+    alpha:OSF1:*:*)
+       case $UNAME_RELEASE in
+       *4.0)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+               ;;
+       *5.*)
+               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+               ;;
+       esac
+       # According to Compaq, /usr/sbin/psrinfo has been available on
+       # OSF/1 and Tru64 systems produced since 1995.  I hope that
+       # covers most systems running today.  This code pipes the CPU
+       # types through head -n 1, so we only detect the type of CPU 0.
+       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+       case "$ALPHA_CPU_TYPE" in
+           "EV4 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "EV4.5 (21064)")
+               UNAME_MACHINE="alpha" ;;
+           "LCA4 (21066/21068)")
+               UNAME_MACHINE="alpha" ;;
+           "EV5 (21164)")
+               UNAME_MACHINE="alphaev5" ;;
+           "EV5.6 (21164A)")
+               UNAME_MACHINE="alphaev56" ;;
+           "EV5.6 (21164PC)")
+               UNAME_MACHINE="alphapca56" ;;
+           "EV5.7 (21164PC)")
+               UNAME_MACHINE="alphapca57" ;;
+           "EV6 (21264)")
+               UNAME_MACHINE="alphaev6" ;;
+           "EV6.7 (21264A)")
+               UNAME_MACHINE="alphaev67" ;;
+           "EV6.8CB (21264C)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8AL (21264B)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.8CX (21264D)")
+               UNAME_MACHINE="alphaev68" ;;
+           "EV6.9A (21264/EV69A)")
+               UNAME_MACHINE="alphaev69" ;;
+           "EV7 (21364)")
+               UNAME_MACHINE="alphaev7" ;;
+           "EV7.9 (21364A)")
+               UNAME_MACHINE="alphaev79" ;;
+       esac
+       # A Pn.n version is a patched version.
+       # A Vn.n version is a released version.
+       # A Tn.n version is a released field test version.
+       # A Xn.n version is an unreleased experimental baselevel.
+       # 1.2 uses "1.2" for uname -r.
+       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+       exit 0 ;;
+    Alpha\ *:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # Should we change UNAME_MACHINE based on the output of uname instead
+       # of the specific Alpha model?
+       echo alpha-pc-interix
+       exit 0 ;;
+    21064:Windows_NT:50:3)
+       echo alpha-dec-winnt3.5
+       exit 0 ;;
+    Amiga*:UNIX_System_V:4.0:*)
+       echo m68k-unknown-sysv4
+       exit 0;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-amigaos
+       exit 0 ;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+       echo ${UNAME_MACHINE}-unknown-morphos
+       exit 0 ;;
+    *:OS/390:*:*)
+       echo i370-ibm-openedition
+       exit 0 ;;
+    *:OS400:*:*)
+        echo powerpc-ibm-os400
+       exit 0 ;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+       echo arm-acorn-riscix${UNAME_RELEASE}
+       exit 0;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+       echo hppa1.1-hitachi-hiuxmpp
+       exit 0;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+       if test "`(/bin/universe) 2>/dev/null`" = att ; then
+               echo pyramid-pyramid-sysv3
+       else
+               echo pyramid-pyramid-bsd
+       fi
+       exit 0 ;;
+    NILE*:*:*:dcosx)
+       echo pyramid-pyramid-svr4
+       exit 0 ;;
+    DRS?6000:unix:4.0:6*)
+       echo sparc-icl-nx6
+       exit 0 ;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+       case `/usr/bin/uname -p` in
+           sparc) echo sparc-icl-nx7 && exit 0 ;;
+       esac ;;
+    sun4H:SunOS:5.*:*)
+       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    i86pc:SunOS:5.*:*)
+       echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:6*:*)
+       # According to config.sub, this is the proper way to canonicalize
+       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+       # it's likely to be more like Solaris than SunOS4.
+       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    sun4*:SunOS:*:*)
+       case "`/usr/bin/arch -k`" in
+           Series*|S4*)
+               UNAME_RELEASE=`uname -v`
+               ;;
+       esac
+       # Japanese Language versions have a version number like `4.1.3-JL'.
+       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
+       exit 0 ;;
+    sun3*:SunOS:*:*)
+       echo m68k-sun-sunos${UNAME_RELEASE}
+       exit 0 ;;
+    sun*:*:4.2BSD:*)
+       UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+       case "`/bin/arch`" in
+           sun3)
+               echo m68k-sun-sunos${UNAME_RELEASE}
+               ;;
+           sun4)
+               echo sparc-sun-sunos${UNAME_RELEASE}
+               ;;
+       esac
+       exit 0 ;;
+    aushp:SunOS:*:*)
+       echo sparc-auspex-sunos${UNAME_RELEASE}
+       exit 0 ;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit 0 ;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+       echo m68k-atari-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+        echo m68k-atari-mint${UNAME_RELEASE}
+       exit 0 ;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+        echo m68k-milan-mint${UNAME_RELEASE}
+        exit 0 ;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+        echo m68k-hades-mint${UNAME_RELEASE}
+        exit 0 ;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+        echo m68k-unknown-mint${UNAME_RELEASE}
+        exit 0 ;;
+    m68k:machten:*:*)
+       echo m68k-apple-machten${UNAME_RELEASE}
+       exit 0 ;;
+    powerpc:machten:*:*)
+       echo powerpc-apple-machten${UNAME_RELEASE}
+       exit 0 ;;
+    RISC*:Mach:*:*)
+       echo mips-dec-mach_bsd4.3
+       exit 0 ;;
+    RISC*:ULTRIX:*:*)
+       echo mips-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    VAX*:ULTRIX*:*:*)
+       echo vax-dec-ultrix${UNAME_RELEASE}
+       exit 0 ;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+       echo clipper-intergraph-clix${UNAME_RELEASE}
+       exit 0 ;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+       int main (int argc, char *argv[]) {
+#else
+       int main (argc, argv) int argc; char *argv[]; {
+#endif
+       #if defined (host_mips) && defined (MIPSEB)
+       #if defined (SYSTYPE_SYSV)
+         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_SVR4)
+         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
+       #endif
+       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
+       #endif
+       #endif
+         exit (-1);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c \
+         && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \
+         && exit 0
+       echo mips-mips-riscos${UNAME_RELEASE}
+       exit 0 ;;
+    Motorola:PowerMAX_OS:*:*)
+       echo powerpc-motorola-powermax
+       exit 0 ;;
+    Motorola:*:4.3:PL8-*)
+       echo powerpc-harris-powermax
+       exit 0 ;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+       echo powerpc-harris-powermax
+       exit 0 ;;
+    Night_Hawk:Power_UNIX:*:*)
+       echo powerpc-harris-powerunix
+       exit 0 ;;
+    m88k:CX/UX:7*:*)
+       echo m88k-harris-cxux7
+       exit 0 ;;
+    m88k:*:4*:R4*)
+       echo m88k-motorola-sysv4
+       exit 0 ;;
+    m88k:*:3*:R3*)
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    AViiON:dgux:*:*)
+        # DG/UX returns AViiON for all architectures
+        UNAME_PROCESSOR=`/usr/bin/uname -p`
+       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+       then
+           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
+              [ ${TARGET_BINARY_INTERFACE}x = x ]
+           then
+               echo m88k-dg-dgux${UNAME_RELEASE}
+           else
+               echo m88k-dg-dguxbcs${UNAME_RELEASE}
+           fi
+       else
+           echo i586-dg-dgux${UNAME_RELEASE}
+       fi
+       exit 0 ;;
+    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
+       echo m88k-dolphin-sysv3
+       exit 0 ;;
+    M88*:*:R3*:*)
+       # Delta 88k system running SVR3
+       echo m88k-motorola-sysv3
+       exit 0 ;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+       echo m88k-tektronix-sysv3
+       exit 0 ;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+       echo m68k-tektronix-bsd
+       exit 0 ;;
+    *:IRIX*:*:*)
+       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+       exit 0 ;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+       echo romp-ibm-aix      # uname -m gives an 8 hex-code CPU id
+       exit 0 ;;              # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+       echo i386-ibm-aix
+       exit 0 ;;
+    ia64:AIX:*:*)
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+       exit 0 ;;
+    *:AIX:2:3)
+       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+               eval $set_cc_for_build
+               sed 's/^                //' << EOF >$dummy.c
+               #include <sys/systemcfg.h>
+
+               main()
+                       {
+                       if (!__power_pc())
+                               exit(1);
+                       puts("powerpc-ibm-aix3.2.5");
+                       exit(0);
+                       }
+EOF
+               $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+               echo rs6000-ibm-aix3.2.5
+       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+               echo rs6000-ibm-aix3.2.4
+       else
+               echo rs6000-ibm-aix3.2
+       fi
+       exit 0 ;;
+    *:AIX:*:[45])
+       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+       if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
+               IBM_ARCH=rs6000
+       else
+               IBM_ARCH=powerpc
+       fi
+       if [ -x /usr/bin/oslevel ] ; then
+               IBM_REV=`/usr/bin/oslevel`
+       else
+               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+       fi
+       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+       exit 0 ;;
+    *:AIX:*:*)
+       echo rs6000-ibm-aix
+       exit 0 ;;
+    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+       echo romp-ibm-bsd4.4
+       exit 0 ;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
+       exit 0 ;;                           # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+       echo rs6000-bull-bosx
+       exit 0 ;;
+    DPX/2?00:B.O.S.:*:*)
+       echo m68k-bull-sysv3
+       exit 0 ;;
+    9000/[34]??:4.3bsd:1.*:*)
+       echo m68k-hp-bsd
+       exit 0 ;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+       echo m68k-hp-bsd4.4
+       exit 0 ;;
+    9000/[34678]??:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       case "${UNAME_MACHINE}" in
+           9000/31? )            HP_ARCH=m68000 ;;
+           9000/[34]?? )         HP_ARCH=m68k ;;
+           9000/[678][0-9][0-9])
+               if [ -x /usr/bin/getconf ]; then
+                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+                    case "${sc_cpu_version}" in
+                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
+                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
+                      532)                      # CPU_PA_RISC2_0
+                        case "${sc_kernel_bits}" in
+                          32) HP_ARCH="hppa2.0n" ;;
+                          64) HP_ARCH="hppa2.0w" ;;
+                         '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
+                        esac ;;
+                    esac
+               fi
+               if [ "${HP_ARCH}" = "" ]; then
+                   eval $set_cc_for_build
+                   sed 's/^              //' << EOF >$dummy.c
+
+              #define _HPUX_SOURCE
+              #include <stdlib.h>
+              #include <unistd.h>
+
+              int main ()
+              {
+              #if defined(_SC_KERNEL_BITS)
+                  long bits = sysconf(_SC_KERNEL_BITS);
+              #endif
+                  long cpu  = sysconf (_SC_CPU_VERSION);
+
+                  switch (cpu)
+               {
+               case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+               case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+               case CPU_PA_RISC2_0:
+              #if defined(_SC_KERNEL_BITS)
+                   switch (bits)
+                       {
+                       case 64: puts ("hppa2.0w"); break;
+                       case 32: puts ("hppa2.0n"); break;
+                       default: puts ("hppa2.0"); break;
+                       } break;
+              #else  /* !defined(_SC_KERNEL_BITS) */
+                   puts ("hppa2.0"); break;
+              #endif
+               default: puts ("hppa1.0"); break;
+               }
+                  exit (0);
+              }
+EOF
+                   (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+                   test -z "$HP_ARCH" && HP_ARCH=hppa
+               fi ;;
+       esac
+       if [ ${HP_ARCH} = "hppa2.0w" ]
+       then
+           # avoid double evaluation of $set_cc_for_build
+           test -n "$CC_FOR_BUILD" || eval $set_cc_for_build
+           if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null
+           then
+               HP_ARCH="hppa2.0w"
+           else
+               HP_ARCH="hppa64"
+           fi
+       fi
+       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+       exit 0 ;;
+    ia64:HP-UX:*:*)
+       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
+       echo ia64-hp-hpux${HPUX_REV}
+       exit 0 ;;
+    3050*:HI-UX:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <unistd.h>
+       int
+       main ()
+       {
+         long cpu = sysconf (_SC_CPU_VERSION);
+         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+            results, however.  */
+         if (CPU_IS_PA_RISC (cpu))
+           {
+             switch (cpu)
+               {
+                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+                 default: puts ("hppa-hitachi-hiuxwe2"); break;
+               }
+           }
+         else if (CPU_IS_HP_MC68K (cpu))
+           puts ("m68k-hitachi-hiuxwe2");
+         else puts ("unknown-hitachi-hiuxwe2");
+         exit (0);
+       }
+EOF
+       $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0
+       echo unknown-hitachi-hiuxwe2
+       exit 0 ;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
+       echo hppa1.1-hp-bsd
+       exit 0 ;;
+    9000/8??:4.3bsd:*:*)
+       echo hppa1.0-hp-bsd
+       exit 0 ;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+       echo hppa1.0-hp-mpeix
+       exit 0 ;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+       echo hppa1.1-hp-osf
+       exit 0 ;;
+    hp8??:OSF1:*:*)
+       echo hppa1.0-hp-osf
+       exit 0 ;;
+    i*86:OSF1:*:*)
+       if [ -x /usr/sbin/sysversion ] ; then
+           echo ${UNAME_MACHINE}-unknown-osf1mk
+       else
+           echo ${UNAME_MACHINE}-unknown-osf1
+       fi
+       exit 0 ;;
+    parisc*:Lites*:*:*)
+       echo hppa1.1-hp-lites
+       exit 0 ;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+       echo c1-convex-bsd
+        exit 0 ;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+        exit 0 ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+       echo c34-convex-bsd
+        exit 0 ;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+       echo c38-convex-bsd
+        exit 0 ;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+       echo c4-convex-bsd
+        exit 0 ;;
+    CRAY*Y-MP:*:*:*)
+       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*[A-Z]90:*:*:*)
+       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+             -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*TS:*:*:*)
+       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*T3E:*:*:*)
+       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    CRAY*SV1:*:*:*)
+       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    *:UNICOS/mp:*:*)
+       echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+       exit 0 ;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+       FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+        exit 0 ;;
+    5000:UNIX_System_V:4.*:*)
+        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
+        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
+        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+       exit 0 ;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    sparc*:BSD/OS:*:*)
+       echo sparc-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    *:BSD/OS:*:*)
+       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+       exit 0 ;;
+    *:FreeBSD:*:*)
+       echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit 0 ;;
+    i*:CYGWIN*:*)
+       echo ${UNAME_MACHINE}-pc-cygwin
+       exit 0 ;;
+    i*:MINGW*:*)
+       echo ${UNAME_MACHINE}-pc-mingw32
+       exit 0 ;;
+    i*:PW*:*)
+       echo ${UNAME_MACHINE}-pc-pw32
+       exit 0 ;;
+    x86:Interix*:[34]*)
+       echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//'
+       exit 0 ;;
+    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
+       echo i${UNAME_MACHINE}-pc-mks
+       exit 0 ;;
+    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
+       # How do we know it's Interix rather than the generic POSIX subsystem?
+       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
+       # UNAME_MACHINE based on the output of uname instead of i386?
+       echo i586-pc-interix
+       exit 0 ;;
+    i*:UWIN*:*)
+       echo ${UNAME_MACHINE}-pc-uwin
+       exit 0 ;;
+    p*:CYGWIN*:*)
+       echo powerpcle-unknown-cygwin
+       exit 0 ;;
+    prep*:SunOS:5.*:*)
+       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+       exit 0 ;;
+    *:GNU:*:*)
+       # the GNU system
+       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+       exit 0 ;;
+    *:GNU/*:*:*)
+       # other systems with GNU libc and userland
+       echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+       exit 0 ;;
+    i*86:Minix:*:*)
+       echo ${UNAME_MACHINE}-pc-minix
+       exit 0 ;;
+    arm*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    cris:Linux:*:*)
+       echo cris-axis-linux-gnu
+       exit 0 ;;
+    crisv32:Linux:*:*)
+       echo crisv32-axis-linux-gnu
+       exit 0 ;;
+    frv:Linux:*:*)
+       echo frv-unknown-linux-gnu
+       exit 0 ;;
+    ia64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    m32r*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    m68*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    mips:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef mips
+       #undef mipsel
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=mipsel
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=mips
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+       test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+       ;;
+    mips64:Linux:*:*)
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #undef CPU
+       #undef mips64
+       #undef mips64el
+       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+       CPU=mips64el
+       #else
+       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+       CPU=mips64
+       #else
+       CPU=
+       #endif
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=`
+       test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0
+       ;;
+    ppc:Linux:*:*)
+       echo powerpc-unknown-linux-gnu
+       exit 0 ;;
+    ppc64:Linux:*:*)
+       echo powerpc64-unknown-linux-gnu
+       exit 0 ;;
+    alpha:Linux:*:*)
+       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+         EV5)   UNAME_MACHINE=alphaev5 ;;
+         EV56)  UNAME_MACHINE=alphaev56 ;;
+         PCA56) UNAME_MACHINE=alphapca56 ;;
+         PCA57) UNAME_MACHINE=alphapca56 ;;
+         EV6)   UNAME_MACHINE=alphaev6 ;;
+         EV67)  UNAME_MACHINE=alphaev67 ;;
+         EV68*) UNAME_MACHINE=alphaev68 ;;
+        esac
+       objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
+       if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
+       echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+       exit 0 ;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+       # Look for CPU level
+       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+         PA7*) echo hppa1.1-unknown-linux-gnu ;;
+         PA8*) echo hppa2.0-unknown-linux-gnu ;;
+         *)    echo hppa-unknown-linux-gnu ;;
+       esac
+       exit 0 ;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+       echo hppa64-unknown-linux-gnu
+       exit 0 ;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+       echo ${UNAME_MACHINE}-ibm-linux
+       exit 0 ;;
+    sh64*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    sh*:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+       echo ${UNAME_MACHINE}-unknown-linux-gnu
+       exit 0 ;;
+    x86_64:Linux:*:*)
+       echo x86_64-unknown-linux-gnu
+       exit 0 ;;
+    i*86:Linux:*:*)
+       # The BFD linker knows what the default object file format is, so
+       # first see if it will tell us. cd to the root directory to prevent
+       # problems with other programs or directories called `ld' in the path.
+       # Set LC_ALL=C to ensure ld outputs messages in English.
+       ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
+                        | sed -ne '/supported targets:/!d
+                                   s/[         ][      ]*/ /g
+                                   s/.*supported targets: *//
+                                   s/ .*//
+                                   p'`
+        case "$ld_supported_targets" in
+         elf32-i386)
+               TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
+               ;;
+         a.out-i386-linux)
+               echo "${UNAME_MACHINE}-pc-linux-gnuaout"
+               exit 0 ;;
+         coff-i386)
+               echo "${UNAME_MACHINE}-pc-linux-gnucoff"
+               exit 0 ;;
+         "")
+               # Either a pre-BFD a.out linker (linux-gnuoldld) or
+               # one that does not give us useful --help.
+               echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
+               exit 0 ;;
+       esac
+       # Determine whether the default compiler is a.out or elf
+       eval $set_cc_for_build
+       sed 's/^        //' << EOF >$dummy.c
+       #include <features.h>
+       #ifdef __ELF__
+       # ifdef __GLIBC__
+       #  if __GLIBC__ >= 2
+       LIBC=gnu
+       #  else
+       LIBC=gnulibc1
+       #  endif
+       # else
+       LIBC=gnulibc1
+       # endif
+       #else
+       #ifdef __INTEL_COMPILER
+       LIBC=gnu
+       #else
+       LIBC=gnuaout
+       #endif
+       #endif
+       #ifdef __dietlibc__
+       LIBC=dietlibc
+       #endif
+EOF
+       eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=`
+       test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0
+       test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0
+       ;;
+    i*86:DYNIX/ptx:4*:*)
+       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+       # earlier versions are messed up and put the nodename in both
+       # sysname and nodename.
+       echo i386-sequent-sysv4
+       exit 0 ;;
+    i*86:UNIX_SV:4.2MP:2.*)
+        # Unixware is an offshoot of SVR4, but it has its own version
+        # number series starting with 2...
+        # I am not positive that other SVR4 systems won't match this,
+       # I just have to hope.  -- rms.
+        # Use sysv4.2uw... so that sysv4* matches it.
+       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
+       exit 0 ;;
+    i*86:OS/2:*:*)
+       # If we were able to find `uname', then EMX Unix compatibility
+       # is probably installed.
+       echo ${UNAME_MACHINE}-pc-os2-emx
+       exit 0 ;;
+    i*86:XTS-300:*:STOP)
+       echo ${UNAME_MACHINE}-unknown-stop
+       exit 0 ;;
+    i*86:atheos:*:*)
+       echo ${UNAME_MACHINE}-unknown-atheos
+       exit 0 ;;
+       i*86:syllable:*:*)
+       echo ${UNAME_MACHINE}-pc-syllable
+       exit 0 ;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
+       echo i386-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    i*86:*DOS:*:*)
+       echo ${UNAME_MACHINE}-pc-msdosdjgpp
+       exit 0 ;;
+    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
+       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
+       else
+               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+       fi
+       exit 0 ;;
+    i*86:*:5:[78]*)
+       case `/bin/uname -X | grep "^Machine"` in
+           *486*)           UNAME_MACHINE=i486 ;;
+           *Pentium)        UNAME_MACHINE=i586 ;;
+           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+       esac
+       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+       exit 0 ;;
+    i*86:*:3.2:*)
+       if test -f /usr/options/cb.name; then
+               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
+       elif /bin/uname -X 2>/dev/null >/dev/null ; then
+               UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+                       && UNAME_MACHINE=i586
+               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+                       && UNAME_MACHINE=i686
+               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+       else
+               echo ${UNAME_MACHINE}-pc-sysv32
+       fi
+       exit 0 ;;
+    pc:*:*:*)
+       # Left here for compatibility:
+        # uname -m prints for DJGPP always 'pc', but it prints nothing about
+        # the processor, so we play safe by assuming i386.
+       echo i386-pc-msdosdjgpp
+        exit 0 ;;
+    Intel:Mach:3*:*)
+       echo i386-pc-mach3
+       exit 0 ;;
+    paragon:*:*:*)
+       echo i860-intel-osf1
+       exit 0 ;;
+    i860:*:4.*:*) # i860-SVR4
+       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
+       else # Add other i860-SVR4 vendors below as they are discovered.
+         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
+       fi
+       exit 0 ;;
+    mini*:CTIX:SYS*5:*)
+       # "miniframe"
+       echo m68010-convergent-sysv
+       exit 0 ;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+       echo m68k-convergent-sysv
+       exit 0 ;;
+    M680?0:D-NIX:5.3:*)
+       echo m68k-diab-dnix
+       exit 0 ;;
+    M68*:*:R3V[5678]*:*)
+       test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+       OS_REL=''
+       test -r /etc/.relid \
+       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+         && echo i486-ncr-sysv4.3${OS_REL} && exit 0
+       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+         && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+          && echo i486-ncr-sysv4 && exit 0 ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+       echo m68k-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    mc68030:UNIX_System_V:4.*:*)
+       echo m68k-atari-sysv4
+       exit 0 ;;
+    TSUNAMI:LynxOS:2.*:*)
+       echo sparc-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    rs6000:LynxOS:2.*:*)
+       echo rs6000-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
+       echo powerpc-unknown-lynxos${UNAME_RELEASE}
+       exit 0 ;;
+    SM[BE]S:UNIX_SV:*:*)
+       echo mips-dde-sysv${UNAME_RELEASE}
+       exit 0 ;;
+    RM*:ReliantUNIX-*:*:*)
+       echo mips-sni-sysv4
+       exit 0 ;;
+    RM*:SINIX-*:*:*)
+       echo mips-sni-sysv4
+       exit 0 ;;
+    *:SINIX-*:*:*)
+       if uname -p 2>/dev/null >/dev/null ; then
+               UNAME_MACHINE=`(uname -p) 2>/dev/null`
+               echo ${UNAME_MACHINE}-sni-sysv4
+       else
+               echo ns32k-sni-sysv
+       fi
+       exit 0 ;;
+    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+                      # says <Richard.M.Bartel@ccMail.Census.GOV>
+        echo i586-unisys-sysv4
+        exit 0 ;;
+    *:UNIX_System_V:4*:FTX*)
+       # From Gerald Hewes <hewes@openmarket.com>.
+       # How about differentiating between stratus architectures? -djm
+       echo hppa1.1-stratus-sysv4
+       exit 0 ;;
+    *:*:*:FTX*)
+       # From seanf@swdc.stratus.com.
+       echo i860-stratus-sysv4
+       exit 0 ;;
+    *:VOS:*:*)
+       # From Paul.Green@stratus.com.
+       echo hppa1.1-stratus-vos
+       exit 0 ;;
+    mc68*:A/UX:*:*)
+       echo m68k-apple-aux${UNAME_RELEASE}
+       exit 0 ;;
+    news*:NEWS-OS:6*:*)
+       echo mips-sony-newsos6
+       exit 0 ;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+       if [ -d /usr/nec ]; then
+               echo mips-nec-sysv${UNAME_RELEASE}
+       else
+               echo mips-unknown-sysv${UNAME_RELEASE}
+       fi
+        exit 0 ;;
+    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
+       echo powerpc-be-beos
+       exit 0 ;;
+    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
+       echo powerpc-apple-beos
+       exit 0 ;;
+    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
+       echo i586-pc-beos
+       exit 0 ;;
+    SX-4:SUPER-UX:*:*)
+       echo sx4-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    SX-5:SUPER-UX:*:*)
+       echo sx5-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    SX-6:SUPER-UX:*:*)
+       echo sx6-nec-superux${UNAME_RELEASE}
+       exit 0 ;;
+    Power*:Rhapsody:*:*)
+       echo powerpc-apple-rhapsody${UNAME_RELEASE}
+       exit 0 ;;
+    *:Rhapsody:*:*)
+       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+       exit 0 ;;
+    *:Darwin:*:*)
+       UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
+       case $UNAME_PROCESSOR in
+           *86) UNAME_PROCESSOR=i686 ;;
+           unknown) UNAME_PROCESSOR=powerpc ;;
+       esac
+       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+       exit 0 ;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+       UNAME_PROCESSOR=`uname -p`
+       if test "$UNAME_PROCESSOR" = "x86"; then
+               UNAME_PROCESSOR=i386
+               UNAME_MACHINE=pc
+       fi
+       echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
+       exit 0 ;;
+    *:QNX:*:4*)
+       echo i386-pc-qnx
+       exit 0 ;;
+    NSR-?:NONSTOP_KERNEL:*:*)
+       echo nsr-tandem-nsk${UNAME_RELEASE}
+       exit 0 ;;
+    *:NonStop-UX:*:*)
+       echo mips-compaq-nonstopux
+       exit 0 ;;
+    BS2000:POSIX*:*:*)
+       echo bs2000-siemens-sysv
+       exit 0 ;;
+    DS/*:UNIX_System_V:*:*)
+       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+       exit 0 ;;
+    *:Plan9:*:*)
+       # "uname -m" is not consistent, so use $cputype instead. 386
+       # is converted to i386 for consistency with other x86
+       # operating systems.
+       if test "$cputype" = "386"; then
+           UNAME_MACHINE=i386
+       else
+           UNAME_MACHINE="$cputype"
+       fi
+       echo ${UNAME_MACHINE}-unknown-plan9
+       exit 0 ;;
+    *:TOPS-10:*:*)
+       echo pdp10-unknown-tops10
+       exit 0 ;;
+    *:TENEX:*:*)
+       echo pdp10-unknown-tenex
+       exit 0 ;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+       echo pdp10-dec-tops20
+       exit 0 ;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+       echo pdp10-xkl-tops20
+       exit 0 ;;
+    *:TOPS-20:*:*)
+       echo pdp10-unknown-tops20
+       exit 0 ;;
+    *:ITS:*:*)
+       echo pdp10-unknown-its
+       exit 0 ;;
+    SEI:*:*:SEIUX)
+        echo mips-sei-seiux${UNAME_RELEASE}
+       exit 0 ;;
+    *:DragonFly:*:*)
+       echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+       exit 0 ;;
+    *:*VMS:*:*)
+       UNAME_MACHINE=`(uname -p) 2>/dev/null`
+       case "${UNAME_MACHINE}" in
+           A*) echo alpha-dec-vms && exit 0 ;;
+           I*) echo ia64-dec-vms && exit 0 ;;
+           V*) echo vax-dec-vms && exit 0 ;;
+       esac ;;
+    *:XENIX:*:SysV)
+       echo i386-pc-xenix
+       exit 0 ;;
+esac
+
+#echo '(No uname command or uname output not recognized.)' 1>&2
+#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
+
+eval $set_cc_for_build
+cat >$dummy.c <<EOF
+#ifdef _SEQUENT_
+# include <sys/types.h>
+# include <sys/utsname.h>
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+          "4"
+#else
+         ""
+#endif
+         ); exit (0);
+#endif
+#endif
+
+#if defined (__arm) && defined (__acorn) && defined (__unix)
+  printf ("arm-acorn-riscix"); exit (0);
+#endif
+
+#if defined (hp300) && !defined (hpux)
+  printf ("m68k-hp-bsd\n"); exit (0);
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+    struct utsname un;
+
+    uname(&un);
+
+    if (strncmp(un.version, "V2", 2) == 0) {
+       printf ("i386-sequent-ptx2\n"); exit (0);
+    }
+    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+       printf ("i386-sequent-ptx1\n"); exit (0);
+    }
+    printf ("i386-sequent-ptx\n"); exit (0);
+
+#endif
+
+#if defined (vax)
+# if !defined (ultrix)
+#  include <sys/param.h>
+#  if defined (BSD)
+#   if BSD == 43
+      printf ("vax-dec-bsd4.3\n"); exit (0);
+#   else
+#    if BSD == 199006
+      printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#    else
+      printf ("vax-dec-bsd\n"); exit (0);
+#    endif
+#   endif
+#  else
+    printf ("vax-dec-bsd\n"); exit (0);
+#  endif
+# else
+    printf ("vax-dec-ultrix\n"); exit (0);
+# endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0
+
+# Apollos put the system type in the environment.
+
+test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; }
+
+# Convex versions that predate uname can use getsysinfo(1)
+
+if [ -x /usr/convex/getsysinfo ]
+then
+    case `getsysinfo -f cpu_type` in
+    c1*)
+       echo c1-convex-bsd
+       exit 0 ;;
+    c2*)
+       if getsysinfo -f scalar_acc
+       then echo c32-convex-bsd
+       else echo c2-convex-bsd
+       fi
+       exit 0 ;;
+    c34*)
+       echo c34-convex-bsd
+       exit 0 ;;
+    c38*)
+       echo c38-convex-bsd
+       exit 0 ;;
+    c4*)
+       echo c4-convex-bsd
+       exit 0 ;;
+    esac
+fi
+
+cat >&2 <<EOF
+$0: unable to guess system type
+
+This script, last modified $timestamp, has failed to recognize
+the operating system you are using. It is advised that you
+download the most up to date version of the config scripts from
+
+    ftp://ftp.gnu.org/pub/gnu/config/
+
+If the version you run ($0) is already up to date, please
+send the following data and any information you think might be
+pertinent to <config-patches@gnu.org> in order to provide the needed
+information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = ${UNAME_MACHINE}
+UNAME_RELEASE = ${UNAME_RELEASE}
+UNAME_SYSTEM  = ${UNAME_SYSTEM}
+UNAME_VERSION = ${UNAME_VERSION}
+EOF
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..348c066
--- /dev/null
@@ -0,0 +1,148 @@
+/* config.h.in.  Generated from configure.in by autoheader.  */
+
+/* smartmontools CVS Tag */
+#undef CONFIG_H_CVSID
+
+/* use mailx as default mailer */
+#undef DEFAULT_MAILER
+
+/* Define to 1 if you have the `ata_identify_is_cached' function in os_*.c. */
+#undef HAVE_ATA_IDENTIFY_IS_CACHED
+
+/* Define to 1 if C compiler supports __attribute__((packed)) */
+#undef HAVE_ATTR_PACKED
+
+/* Define to 1 if you have the <dev/ata/atavar.h> header file. */
+#undef HAVE_DEV_ATA_ATAVAR_H
+
+/* Define to 1 if you have the `getdomainname' function. */
+#undef HAVE_GETDOMAINNAME
+
+/* Define to 1 if you have the `gethostbyname' function. */
+#undef HAVE_GETHOSTBYNAME
+
+/* Define to 1 if you have the `gethostname' function. */
+#undef HAVE_GETHOSTNAME
+
+/* Define to 1 if you have the `getopt' function. */
+#undef HAVE_GETOPT
+
+/* Define to 1 if you have the <getopt.h> header file. */
+#undef HAVE_GETOPT_H
+
+/* Define to 1 if you have the `getopt_long' function. */
+#undef HAVE_GETOPT_LONG
+
+/* Define to 1 if you have the `get_os_version_str' function in os_*.c. */
+#undef HAVE_GET_OS_VERSION_STR
+
+/* Define to 1 if the system has the type `int64_t'. */
+#undef HAVE_INT64_T
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <locale.h> header file. */
+#undef HAVE_LOCALE_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#undef HAVE_NETDB_H
+
+/* Define to 1 if you have the `sigset' function. */
+#undef HAVE_SIGSET
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strtoull' function. */
+#undef HAVE_STRTOULL
+
+/* Define to 1 if you have the <sys/inttypes.h> header file. */
+#undef HAVE_SYS_INTTYPES_H
+
+/* Define to 1 if you have the <sys/int_types.h> header file. */
+#undef HAVE_SYS_INT_TYPES_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/tweio.h> header file. */
+#undef HAVE_SYS_TWEIO_H
+
+/* Define to 1 if you have the <sys/twereg.h> header file. */
+#undef HAVE_SYS_TWEREG_H
+
+/* Define to 1 if you have the <sys/tw_osl_ioctl.h> header file. */
+#undef HAVE_SYS_TW_OSL_IOCTL_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if the system has the type `uint64_t'. */
+#undef HAVE_UINT64_T
+
+/* Define to 1 if you have the `uname' function. */
+#undef HAVE_UNAME
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if the `snprintf' function is sane */
+#undef HAVE_WORKING_SNPRINTF
+
+/* need assembly code os_solaris_ata.s */
+#undef NEED_SOLARIS_ATA_CODE
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* smartmontools Home Page */
+#undef PACKAGE_HOMEPAGE
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* smartmontools Build Host */
+#undef SMARTMONTOOLS_BUILD_HOST
+
+/* smartmontools Configure Arguments */
+#undef SMARTMONTOOLS_CONFIGURE_ARGS
+
+/* smartmontools Configure Date */
+#undef SMARTMONTOOLS_CONFIGURE_DATE
+
+/* smartmontools Release Date */
+#undef SMARTMONTOOLS_RELEASE_DATE
+
+/* smartmontools Release Time */
+#undef SMARTMONTOOLS_RELEASE_TIME
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
diff --git a/config.sub b/config.sub
new file mode 100755 (executable)
index 0000000..edb6b66
--- /dev/null
@@ -0,0 +1,1555 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
+#   2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+timestamp='2004-08-29'
+
+# This file is (in principle) common to ALL GNU software.
+# The presence of a machine in this file suggests that SOME GNU software
+# can handle that machine.  It does not imply ALL GNU software can.
+#
+# This file is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Please send patches to <config-patches@gnu.org>.  Submit a context
+# diff and a properly formatted ChangeLog entry.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS
+       $0 [OPTION] ALIAS
+
+Canonicalize a configuration name.
+
+Operation modes:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit 0 ;;
+    --version | -v )
+       echo "$version" ; exit 0 ;;
+    --help | --h* | -h )
+       echo "$usage"; exit 0 ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )        # Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help"
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo $1
+       exit 0;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
+# Here we must recognize all the valid KERNEL-OS combinations.
+maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
+case $maybe_os in
+  nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \
+  kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*)
+    os=-$maybe_os
+    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
+    ;;
+  *)
+    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
+    if [ $basic_machine != $1 ]
+    then os=`echo $1 | sed 's/.*-/-/'`
+    else os=; fi
+    ;;
+esac
+
+### Let's recognize common machines as not being operating systems so
+### that things like config.sub decstation-3100 work.  We also
+### recognize some manufacturers as not being operating systems, so we
+### can provide default operating systems below.
+case $os in
+       -sun*os*)
+               # Prevent following clause from handling this invalid input.
+               ;;
+       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
+       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
+       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
+       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
+       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
+       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
+       -apple | -axis | -knuth | -cray)
+               os=
+               basic_machine=$1
+               ;;
+       -sim | -cisco | -oki | -wec | -winbond)
+               os=
+               basic_machine=$1
+               ;;
+       -scout)
+               ;;
+       -wrs)
+               os=-vxworks
+               basic_machine=$1
+               ;;
+       -chorusos*)
+               os=-chorusos
+               basic_machine=$1
+               ;;
+       -chorusrdb)
+               os=-chorusrdb
+               basic_machine=$1
+               ;;
+       -hiux*)
+               os=-hiuxwe2
+               ;;
+       -sco5)
+               os=-sco3.2v5
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco4)
+               os=-sco3.2v4
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2.[4-9]*)
+               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco3.2v[4-9]*)
+               # Don't forget version if it is 3.2v4 or newer.
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -sco*)
+               os=-sco3.2v2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -udk*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -isc)
+               os=-isc2.2
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -clix*)
+               basic_machine=clipper-intergraph
+               ;;
+       -isc*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
+               ;;
+       -lynx*)
+               os=-lynxos
+               ;;
+       -ptx*)
+               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+               ;;
+       -windowsnt*)
+               os=`echo $os | sed -e 's/windowsnt/winnt/'`
+               ;;
+       -psos*)
+               os=-psos
+               ;;
+       -mint | -mint[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+esac
+
+# Decode aliases for certain CPU-COMPANY combinations.
+case $basic_machine in
+       # Recognize the basic CPU types without company name.
+       # Some are omitted here because they have special meanings below.
+       1750a | 580 \
+       | a29k \
+       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
+       | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
+       | am33_2.0 \
+       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \
+       | c4x | clipper \
+       | d10v | d30v | dlx | dsp16xx \
+       | fr30 | frv \
+       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+       | i370 | i860 | i960 | ia64 \
+       | ip2k | iq2000 \
+       | m32r | m32rle | m68000 | m68k | m88k | mcore \
+       | mips | mipsbe | mipseb | mipsel | mipsle \
+       | mips16 \
+       | mips64 | mips64el \
+       | mips64vr | mips64vrel \
+       | mips64orion | mips64orionel \
+       | mips64vr4100 | mips64vr4100el \
+       | mips64vr4300 | mips64vr4300el \
+       | mips64vr5000 | mips64vr5000el \
+       | mipsisa32 | mipsisa32el \
+       | mipsisa32r2 | mipsisa32r2el \
+       | mipsisa64 | mipsisa64el \
+       | mipsisa64r2 | mipsisa64r2el \
+       | mipsisa64sb1 | mipsisa64sb1el \
+       | mipsisa64sr71k | mipsisa64sr71kel \
+       | mipstx39 | mipstx39el \
+       | mn10200 | mn10300 \
+       | msp430 \
+       | ns16k | ns32k \
+       | openrisc | or32 \
+       | pdp10 | pdp11 | pj | pjl \
+       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+       | pyramid \
+       | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \
+       | sh64 | sh64le \
+       | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv8 | sparcv9 | sparcv9b \
+       | strongarm \
+       | tahoe | thumb | tic4x | tic80 | tron \
+       | v850 | v850e \
+       | we32k \
+       | x86 | xscale | xstormy16 | xtensa \
+       | z8k)
+               basic_machine=$basic_machine-unknown
+               ;;
+       m6811 | m68hc11 | m6812 | m68hc12)
+               # Motorola 68HC11/12.
+               basic_machine=$basic_machine-unknown
+               os=-none
+               ;;
+       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+               ;;
+
+       # We use `pc' rather than `unknown'
+       # because (1) that's what they normally are, and
+       # (2) the word "unknown" tends to confuse beginning users.
+       i*86 | x86_64)
+         basic_machine=$basic_machine-pc
+         ;;
+       # Object if more than one company name word.
+       *-*-*)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+       # Recognize the basic CPU types with company name.
+       580-* \
+       | a29k-* \
+       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
+       | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
+       | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+       | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
+       | avr-* \
+       | bs2000-* \
+       | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
+       | clipper-* | craynv-* | cydra-* \
+       | d10v-* | d30v-* | dlx-* \
+       | elxsi-* \
+       | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
+       | h8300-* | h8500-* \
+       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
+       | i*86-* | i860-* | i960-* | ia64-* \
+       | ip2k-* | iq2000-* \
+       | m32r-* | m32rle-* \
+       | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
+       | m88110-* | m88k-* | mcore-* \
+       | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
+       | mips16-* \
+       | mips64-* | mips64el-* \
+       | mips64vr-* | mips64vrel-* \
+       | mips64orion-* | mips64orionel-* \
+       | mips64vr4100-* | mips64vr4100el-* \
+       | mips64vr4300-* | mips64vr4300el-* \
+       | mips64vr5000-* | mips64vr5000el-* \
+       | mipsisa32-* | mipsisa32el-* \
+       | mipsisa32r2-* | mipsisa32r2el-* \
+       | mipsisa64-* | mipsisa64el-* \
+       | mipsisa64r2-* | mipsisa64r2el-* \
+       | mipsisa64sb1-* | mipsisa64sb1el-* \
+       | mipsisa64sr71k-* | mipsisa64sr71kel-* \
+       | mipstx39-* | mipstx39el-* \
+       | mmix-* \
+       | msp430-* \
+       | none-* | np1-* | ns16k-* | ns32k-* \
+       | orion-* \
+       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
+       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+       | pyramid-* \
+       | romp-* | rs6000-* \
+       | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \
+       | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
+       | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \
+       | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \
+       | tahoe-* | thumb-* \
+       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+       | tron-* \
+       | v850-* | v850e-* | vax-* \
+       | we32k-* \
+       | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \
+       | xtensa-* \
+       | ymp-* \
+       | z8k-*)
+               ;;
+       # Recognize the various machine names and aliases which stand
+       # for a CPU type and a company and sometimes even an OS.
+       386bsd)
+               basic_machine=i386-unknown
+               os=-bsd
+               ;;
+       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+               basic_machine=m68000-att
+               ;;
+       3b*)
+               basic_machine=we32k-att
+               ;;
+       a29khif)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       abacus)
+               basic_machine=abacus-unknown
+               ;;
+       adobe68k)
+               basic_machine=m68010-adobe
+               os=-scout
+               ;;
+       alliant | fx80)
+               basic_machine=fx80-alliant
+               ;;
+       altos | altos3068)
+               basic_machine=m68k-altos
+               ;;
+       am29k)
+               basic_machine=a29k-none
+               os=-bsd
+               ;;
+       amd64)
+               basic_machine=x86_64-pc
+               ;;
+       amd64-*)
+               basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       amdahl)
+               basic_machine=580-amdahl
+               os=-sysv
+               ;;
+       amiga | amiga-*)
+               basic_machine=m68k-unknown
+               ;;
+       amigaos | amigados)
+               basic_machine=m68k-unknown
+               os=-amigaos
+               ;;
+       amigaunix | amix)
+               basic_machine=m68k-unknown
+               os=-sysv4
+               ;;
+       apollo68)
+               basic_machine=m68k-apollo
+               os=-sysv
+               ;;
+       apollo68bsd)
+               basic_machine=m68k-apollo
+               os=-bsd
+               ;;
+       aux)
+               basic_machine=m68k-apple
+               os=-aux
+               ;;
+       balance)
+               basic_machine=ns32k-sequent
+               os=-dynix
+               ;;
+       c90)
+               basic_machine=c90-cray
+               os=-unicos
+               ;;
+       convex-c1)
+               basic_machine=c1-convex
+               os=-bsd
+               ;;
+       convex-c2)
+               basic_machine=c2-convex
+               os=-bsd
+               ;;
+       convex-c32)
+               basic_machine=c32-convex
+               os=-bsd
+               ;;
+       convex-c34)
+               basic_machine=c34-convex
+               os=-bsd
+               ;;
+       convex-c38)
+               basic_machine=c38-convex
+               os=-bsd
+               ;;
+       cray | j90)
+               basic_machine=j90-cray
+               os=-unicos
+               ;;
+       craynv)
+               basic_machine=craynv-cray
+               os=-unicosmp
+               ;;
+       cr16c)
+               basic_machine=cr16c-unknown
+               os=-elf
+               ;;
+       crds | unos)
+               basic_machine=m68k-crds
+               ;;
+       crisv32 | crisv32-* | etraxfs*)
+               basic_machine=crisv32-axis
+               ;;
+       cris | cris-* | etrax*)
+               basic_machine=cris-axis
+               ;;
+       crx)
+               basic_machine=crx-unknown
+               os=-elf
+               ;;
+       da30 | da30-*)
+               basic_machine=m68k-da30
+               ;;
+       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
+               basic_machine=mips-dec
+               ;;
+       decsystem10* | dec10*)
+               basic_machine=pdp10-dec
+               os=-tops10
+               ;;
+       decsystem20* | dec20*)
+               basic_machine=pdp10-dec
+               os=-tops20
+               ;;
+       delta | 3300 | motorola-3300 | motorola-delta \
+             | 3300-motorola | delta-motorola)
+               basic_machine=m68k-motorola
+               ;;
+       delta88)
+               basic_machine=m88k-motorola
+               os=-sysv3
+               ;;
+       dpx20 | dpx20-*)
+               basic_machine=rs6000-bull
+               os=-bosx
+               ;;
+       dpx2* | dpx2*-bull)
+               basic_machine=m68k-bull
+               os=-sysv3
+               ;;
+       ebmon29k)
+               basic_machine=a29k-amd
+               os=-ebmon
+               ;;
+       elxsi)
+               basic_machine=elxsi-elxsi
+               os=-bsd
+               ;;
+       encore | umax | mmax)
+               basic_machine=ns32k-encore
+               ;;
+       es1800 | OSE68k | ose68k | ose | OSE)
+               basic_machine=m68k-ericsson
+               os=-ose
+               ;;
+       fx2800)
+               basic_machine=i860-alliant
+               ;;
+       genix)
+               basic_machine=ns32k-ns
+               ;;
+       gmicro)
+               basic_machine=tron-gmicro
+               os=-sysv
+               ;;
+       go32)
+               basic_machine=i386-pc
+               os=-go32
+               ;;
+       h3050r* | hiux*)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       h8300hms)
+               basic_machine=h8300-hitachi
+               os=-hms
+               ;;
+       h8300xray)
+               basic_machine=h8300-hitachi
+               os=-xray
+               ;;
+       h8500hms)
+               basic_machine=h8500-hitachi
+               os=-hms
+               ;;
+       harris)
+               basic_machine=m88k-harris
+               os=-sysv3
+               ;;
+       hp300-*)
+               basic_machine=m68k-hp
+               ;;
+       hp300bsd)
+               basic_machine=m68k-hp
+               os=-bsd
+               ;;
+       hp300hpux)
+               basic_machine=m68k-hp
+               os=-hpux
+               ;;
+       hp3k9[0-9][0-9] | hp9[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k2[0-9][0-9] | hp9k31[0-9])
+               basic_machine=m68000-hp
+               ;;
+       hp9k3[2-9][0-9])
+               basic_machine=m68k-hp
+               ;;
+       hp9k6[0-9][0-9] | hp6[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hp9k7[0-79][0-9] | hp7[0-79][0-9])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k78[0-9] | hp78[0-9])
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+               # FIXME: really hppa2.0-hp
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][13679] | hp8[0-9][13679])
+               basic_machine=hppa1.1-hp
+               ;;
+       hp9k8[0-9][0-9] | hp8[0-9][0-9])
+               basic_machine=hppa1.0-hp
+               ;;
+       hppa-next)
+               os=-nextstep3
+               ;;
+       hppaosf)
+               basic_machine=hppa1.1-hp
+               os=-osf
+               ;;
+       hppro)
+               basic_machine=hppa1.1-hp
+               os=-proelf
+               ;;
+       i370-ibm* | ibm*)
+               basic_machine=i370-ibm
+               ;;
+# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
+       i*86v32)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv32
+               ;;
+       i*86v4*)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv4
+               ;;
+       i*86v)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-sysv
+               ;;
+       i*86sol2)
+               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
+               os=-solaris2
+               ;;
+       i386mach)
+               basic_machine=i386-mach
+               os=-mach
+               ;;
+       i386-vsta | vsta)
+               basic_machine=i386-unknown
+               os=-vsta
+               ;;
+       iris | iris4d)
+               basic_machine=mips-sgi
+               case $os in
+                   -irix*)
+                       ;;
+                   *)
+                       os=-irix4
+                       ;;
+               esac
+               ;;
+       isi68 | isi)
+               basic_machine=m68k-isi
+               os=-sysv
+               ;;
+       m88k-omron*)
+               basic_machine=m88k-omron
+               ;;
+       magnum | m3230)
+               basic_machine=mips-mips
+               os=-sysv
+               ;;
+       merlin)
+               basic_machine=ns32k-utek
+               os=-sysv
+               ;;
+       mingw32)
+               basic_machine=i386-pc
+               os=-mingw32
+               ;;
+       miniframe)
+               basic_machine=m68000-convergent
+               ;;
+       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
+               basic_machine=m68k-atari
+               os=-mint
+               ;;
+       mips3*-*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
+               ;;
+       mips3*)
+               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
+               ;;
+       monitor)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       morphos)
+               basic_machine=powerpc-unknown
+               os=-morphos
+               ;;
+       msdos)
+               basic_machine=i386-pc
+               os=-msdos
+               ;;
+       mvs)
+               basic_machine=i370-ibm
+               os=-mvs
+               ;;
+       ncr3000)
+               basic_machine=i486-ncr
+               os=-sysv4
+               ;;
+       netbsd386)
+               basic_machine=i386-unknown
+               os=-netbsd
+               ;;
+       netwinder)
+               basic_machine=armv4l-rebel
+               os=-linux
+               ;;
+       news | news700 | news800 | news900)
+               basic_machine=m68k-sony
+               os=-newsos
+               ;;
+       news1000)
+               basic_machine=m68030-sony
+               os=-newsos
+               ;;
+       news-3600 | risc-news)
+               basic_machine=mips-sony
+               os=-newsos
+               ;;
+       necv70)
+               basic_machine=v70-nec
+               os=-sysv
+               ;;
+       next | m*-next )
+               basic_machine=m68k-next
+               case $os in
+                   -nextstep* )
+                       ;;
+                   -ns2*)
+                     os=-nextstep2
+                       ;;
+                   *)
+                     os=-nextstep3
+                       ;;
+               esac
+               ;;
+       nh3000)
+               basic_machine=m68k-harris
+               os=-cxux
+               ;;
+       nh[45]000)
+               basic_machine=m88k-harris
+               os=-cxux
+               ;;
+       nindy960)
+               basic_machine=i960-intel
+               os=-nindy
+               ;;
+       mon960)
+               basic_machine=i960-intel
+               os=-mon960
+               ;;
+       nonstopux)
+               basic_machine=mips-compaq
+               os=-nonstopux
+               ;;
+       np1)
+               basic_machine=np1-gould
+               ;;
+       nsr-tandem)
+               basic_machine=nsr-tandem
+               ;;
+       op50n-* | op60c-*)
+               basic_machine=hppa1.1-oki
+               os=-proelf
+               ;;
+       or32 | or32-*)
+               basic_machine=or32-unknown
+               os=-coff
+               ;;
+       os400)
+               basic_machine=powerpc-ibm
+               os=-os400
+               ;;
+       OSE68000 | ose68000)
+               basic_machine=m68000-ericsson
+               os=-ose
+               ;;
+       os68k)
+               basic_machine=m68k-none
+               os=-os68k
+               ;;
+       pa-hitachi)
+               basic_machine=hppa1.1-hitachi
+               os=-hiuxwe2
+               ;;
+       paragon)
+               basic_machine=i860-intel
+               os=-osf
+               ;;
+       pbd)
+               basic_machine=sparc-tti
+               ;;
+       pbb)
+               basic_machine=m68k-tti
+               ;;
+       pc532 | pc532-*)
+               basic_machine=ns32k-pc532
+               ;;
+       pentium | p5 | k5 | k6 | nexgen | viac3)
+               basic_machine=i586-pc
+               ;;
+       pentiumpro | p6 | 6x86 | athlon | athlon_*)
+               basic_machine=i686-pc
+               ;;
+       pentiumii | pentium2 | pentiumiii | pentium3)
+               basic_machine=i686-pc
+               ;;
+       pentium4)
+               basic_machine=i786-pc
+               ;;
+       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumpro-* | p6-* | 6x86-* | athlon-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pentium4-*)
+               basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       pn)
+               basic_machine=pn-gould
+               ;;
+       power)  basic_machine=power-ibm
+               ;;
+       ppc)    basic_machine=powerpc-unknown
+               ;;
+       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppcle | powerpclittle | ppc-le | powerpc-little)
+               basic_machine=powerpcle-unknown
+               ;;
+       ppcle-* | powerpclittle-*)
+               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64)  basic_machine=powerpc64-unknown
+               ;;
+       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+               basic_machine=powerpc64le-unknown
+               ;;
+       ppc64le-* | powerpc64little-*)
+               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+               ;;
+       ps2)
+               basic_machine=i386-ibm
+               ;;
+       pw32)
+               basic_machine=i586-unknown
+               os=-pw32
+               ;;
+       rom68k)
+               basic_machine=m68k-rom68k
+               os=-coff
+               ;;
+       rm[46]00)
+               basic_machine=mips-siemens
+               ;;
+       rtpc | rtpc-*)
+               basic_machine=romp-ibm
+               ;;
+       s390 | s390-*)
+               basic_machine=s390-ibm
+               ;;
+       s390x | s390x-*)
+               basic_machine=s390x-ibm
+               ;;
+       sa29200)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       sb1)
+               basic_machine=mipsisa64sb1-unknown
+               ;;
+       sb1el)
+               basic_machine=mipsisa64sb1el-unknown
+               ;;
+       sei)
+               basic_machine=mips-sei
+               os=-seiux
+               ;;
+       sequent)
+               basic_machine=i386-sequent
+               ;;
+       sh)
+               basic_machine=sh-hitachi
+               os=-hms
+               ;;
+       sh64)
+               basic_machine=sh64-unknown
+               ;;
+       sparclite-wrs | simso-wrs)
+               basic_machine=sparclite-wrs
+               os=-vxworks
+               ;;
+       sps7)
+               basic_machine=m68k-bull
+               os=-sysv2
+               ;;
+       spur)
+               basic_machine=spur-unknown
+               ;;
+       st2000)
+               basic_machine=m68k-tandem
+               ;;
+       stratus)
+               basic_machine=i860-stratus
+               os=-sysv4
+               ;;
+       sun2)
+               basic_machine=m68000-sun
+               ;;
+       sun2os3)
+               basic_machine=m68000-sun
+               os=-sunos3
+               ;;
+       sun2os4)
+               basic_machine=m68000-sun
+               os=-sunos4
+               ;;
+       sun3os3)
+               basic_machine=m68k-sun
+               os=-sunos3
+               ;;
+       sun3os4)
+               basic_machine=m68k-sun
+               os=-sunos4
+               ;;
+       sun4os3)
+               basic_machine=sparc-sun
+               os=-sunos3
+               ;;
+       sun4os4)
+               basic_machine=sparc-sun
+               os=-sunos4
+               ;;
+       sun4sol2)
+               basic_machine=sparc-sun
+               os=-solaris2
+               ;;
+       sun3 | sun3-*)
+               basic_machine=m68k-sun
+               ;;
+       sun4)
+               basic_machine=sparc-sun
+               ;;
+       sun386 | sun386i | roadrunner)
+               basic_machine=i386-sun
+               ;;
+       sv1)
+               basic_machine=sv1-cray
+               os=-unicos
+               ;;
+       symmetry)
+               basic_machine=i386-sequent
+               os=-dynix
+               ;;
+       t3e)
+               basic_machine=alphaev5-cray
+               os=-unicos
+               ;;
+       t90)
+               basic_machine=t90-cray
+               os=-unicos
+               ;;
+       tic54x | c54x*)
+               basic_machine=tic54x-unknown
+               os=-coff
+               ;;
+       tic55x | c55x*)
+               basic_machine=tic55x-unknown
+               os=-coff
+               ;;
+       tic6x | c6x*)
+               basic_machine=tic6x-unknown
+               os=-coff
+               ;;
+       tx39)
+               basic_machine=mipstx39-unknown
+               ;;
+       tx39el)
+               basic_machine=mipstx39el-unknown
+               ;;
+       toad1)
+               basic_machine=pdp10-xkl
+               os=-tops20
+               ;;
+       tower | tower-32)
+               basic_machine=m68k-ncr
+               ;;
+       tpf)
+               basic_machine=s390x-ibm
+               os=-tpf
+               ;;
+       udi29k)
+               basic_machine=a29k-amd
+               os=-udi
+               ;;
+       ultra3)
+               basic_machine=a29k-nyu
+               os=-sym1
+               ;;
+       v810 | necv810)
+               basic_machine=v810-nec
+               os=-none
+               ;;
+       vaxv)
+               basic_machine=vax-dec
+               os=-sysv
+               ;;
+       vms)
+               basic_machine=vax-dec
+               os=-vms
+               ;;
+       vpp*|vx|vx-*)
+               basic_machine=f301-fujitsu
+               ;;
+       vxworks960)
+               basic_machine=i960-wrs
+               os=-vxworks
+               ;;
+       vxworks68)
+               basic_machine=m68k-wrs
+               os=-vxworks
+               ;;
+       vxworks29k)
+               basic_machine=a29k-wrs
+               os=-vxworks
+               ;;
+       w65*)
+               basic_machine=w65-wdc
+               os=-none
+               ;;
+       w89k-*)
+               basic_machine=hppa1.1-winbond
+               os=-proelf
+               ;;
+       xps | xps100)
+               basic_machine=xps100-honeywell
+               ;;
+       ymp)
+               basic_machine=ymp-cray
+               os=-unicos
+               ;;
+       z8k-*-coff)
+               basic_machine=z8k-unknown
+               os=-sim
+               ;;
+       none)
+               basic_machine=none-none
+               os=-none
+               ;;
+
+# Here we handle the default manufacturer of certain CPU types.  It is in
+# some cases the only manufacturer, in others, it is the most popular.
+       w89k)
+               basic_machine=hppa1.1-winbond
+               ;;
+       op50n)
+               basic_machine=hppa1.1-oki
+               ;;
+       op60c)
+               basic_machine=hppa1.1-oki
+               ;;
+       romp)
+               basic_machine=romp-ibm
+               ;;
+       mmix)
+               basic_machine=mmix-knuth
+               ;;
+       rs6000)
+               basic_machine=rs6000-ibm
+               ;;
+       vax)
+               basic_machine=vax-dec
+               ;;
+       pdp10)
+               # there are many clones, so DEC is not a safe bet
+               basic_machine=pdp10-unknown
+               ;;
+       pdp11)
+               basic_machine=pdp11-dec
+               ;;
+       we32k)
+               basic_machine=we32k-att
+               ;;
+       sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele)
+               basic_machine=sh-unknown
+               ;;
+       sh64)
+               basic_machine=sh64-unknown
+               ;;
+       sparc | sparcv8 | sparcv9 | sparcv9b)
+               basic_machine=sparc-sun
+               ;;
+       cydra)
+               basic_machine=cydra-cydrome
+               ;;
+       orion)
+               basic_machine=orion-highlevel
+               ;;
+       orion105)
+               basic_machine=clipper-highlevel
+               ;;
+       mac | mpw | mac-mpw)
+               basic_machine=m68k-apple
+               ;;
+       pmac | pmac-mpw)
+               basic_machine=powerpc-apple
+               ;;
+       *-unknown)
+               # Make sure to match an already-canonicalized machine name.
+               ;;
+       *)
+               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $basic_machine in
+       *-digital*)
+               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+               ;;
+       *-commodore*)
+               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+               ;;
+       *)
+               ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if [ x"$os" != x"" ]
+then
+case $os in
+        # First match some system type aliases
+        # that might get confused with valid system types.
+       # -solaris* is a basic system type, with this one exception.
+       -solaris1 | -solaris1.*)
+               os=`echo $os | sed -e 's|solaris1|sunos4|'`
+               ;;
+       -solaris)
+               os=-solaris2
+               ;;
+       -svr4*)
+               os=-sysv4
+               ;;
+       -unixware*)
+               os=-sysv4.2uw
+               ;;
+       -gnu/linux*)
+               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+               ;;
+       # First accept the basic system types.
+       # The portable systems comes first.
+       # Each alternative MUST END IN A *, to match a version number.
+       # -sysv* is not here because it comes later, after sysvr4.
+       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
+             | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
+             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
+             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
+             | -aos* \
+             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
+             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
+             | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \
+             | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
+             | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
+             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
+             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
+             | -chorusos* | -chorusrdb* \
+             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+             | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \
+             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
+             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
+             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
+             | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
+             | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
+             | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly*)
+       # Remember, each alternative MUST END IN *, to match a version number.
+               ;;
+       -qnx*)
+               case $basic_machine in
+                   x86-* | i*86-*)
+                       ;;
+                   *)
+                       os=-nto$os
+                       ;;
+               esac
+               ;;
+       -nto-qnx*)
+               ;;
+       -nto*)
+               os=`echo $os | sed -e 's|nto|nto-qnx|'`
+               ;;
+       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
+             | -windows* | -osx | -abug | -netware* | -os9* | -beos* \
+             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+               ;;
+       -mac*)
+               os=`echo $os | sed -e 's|mac|macos|'`
+               ;;
+       -linux-dietlibc)
+               os=-linux-dietlibc
+               ;;
+       -linux*)
+               os=`echo $os | sed -e 's|linux|linux-gnu|'`
+               ;;
+       -sunos5*)
+               os=`echo $os | sed -e 's|sunos5|solaris2|'`
+               ;;
+       -sunos6*)
+               os=`echo $os | sed -e 's|sunos6|solaris3|'`
+               ;;
+       -opened*)
+               os=-openedition
+               ;;
+        -os400*)
+               os=-os400
+               ;;
+       -wince*)
+               os=-wince
+               ;;
+       -osfrose*)
+               os=-osfrose
+               ;;
+       -osf*)
+               os=-osf
+               ;;
+       -utek*)
+               os=-bsd
+               ;;
+       -dynix*)
+               os=-bsd
+               ;;
+       -acis*)
+               os=-aos
+               ;;
+       -atheos*)
+               os=-atheos
+               ;;
+       -syllable*)
+               os=-syllable
+               ;;
+       -386bsd)
+               os=-bsd
+               ;;
+       -ctix* | -uts*)
+               os=-sysv
+               ;;
+       -nova*)
+               os=-rtmk-nova
+               ;;
+       -ns2 )
+               os=-nextstep2
+               ;;
+       -nsk*)
+               os=-nsk
+               ;;
+       # Preserve the version number of sinix5.
+       -sinix5.*)
+               os=`echo $os | sed -e 's|sinix|sysv|'`
+               ;;
+       -sinix*)
+               os=-sysv4
+               ;;
+        -tpf*)
+               os=-tpf
+               ;;
+       -triton*)
+               os=-sysv3
+               ;;
+       -oss*)
+               os=-sysv3
+               ;;
+       -svr4)
+               os=-sysv4
+               ;;
+       -svr3)
+               os=-sysv3
+               ;;
+       -sysvr4)
+               os=-sysv4
+               ;;
+       # This must come after -sysvr4.
+       -sysv*)
+               ;;
+       -ose*)
+               os=-ose
+               ;;
+       -es1800*)
+               os=-ose
+               ;;
+       -xenix)
+               os=-xenix
+               ;;
+       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+               os=-mint
+               ;;
+       -aros*)
+               os=-aros
+               ;;
+       -kaos*)
+               os=-kaos
+               ;;
+       -none)
+               ;;
+       *)
+               # Get rid of the `-' at the beginning of $os.
+               os=`echo $os | sed 's/[^-]*-//'`
+               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
+               exit 1
+               ;;
+esac
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+case $basic_machine in
+       *-acorn)
+               os=-riscix1.2
+               ;;
+       arm*-rebel)
+               os=-linux
+               ;;
+       arm*-semi)
+               os=-aout
+               ;;
+    c4x-* | tic4x-*)
+        os=-coff
+        ;;
+       # This must come before the *-dec entry.
+       pdp10-*)
+               os=-tops20
+               ;;
+       pdp11-*)
+               os=-none
+               ;;
+       *-dec | vax-*)
+               os=-ultrix4.2
+               ;;
+       m68*-apollo)
+               os=-domain
+               ;;
+       i386-sun)
+               os=-sunos4.0.2
+               ;;
+       m68000-sun)
+               os=-sunos3
+               # This also exists in the configure program, but was not the
+               # default.
+               # os=-sunos4
+               ;;
+       m68*-cisco)
+               os=-aout
+               ;;
+       mips*-cisco)
+               os=-elf
+               ;;
+       mips*-*)
+               os=-elf
+               ;;
+       or32-*)
+               os=-coff
+               ;;
+       *-tti)  # must be before sparc entry or we get the wrong os.
+               os=-sysv3
+               ;;
+       sparc-* | *-sun)
+               os=-sunos4.1.1
+               ;;
+       *-be)
+               os=-beos
+               ;;
+       *-ibm)
+               os=-aix
+               ;;
+       *-knuth)
+               os=-mmixware
+               ;;
+       *-wec)
+               os=-proelf
+               ;;
+       *-winbond)
+               os=-proelf
+               ;;
+       *-oki)
+               os=-proelf
+               ;;
+       *-hp)
+               os=-hpux
+               ;;
+       *-hitachi)
+               os=-hiux
+               ;;
+       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+               os=-sysv
+               ;;
+       *-cbm)
+               os=-amigaos
+               ;;
+       *-dg)
+               os=-dgux
+               ;;
+       *-dolphin)
+               os=-sysv3
+               ;;
+       m68k-ccur)
+               os=-rtu
+               ;;
+       m88k-omron*)
+               os=-luna
+               ;;
+       *-next )
+               os=-nextstep
+               ;;
+       *-sequent)
+               os=-ptx
+               ;;
+       *-crds)
+               os=-unos
+               ;;
+       *-ns)
+               os=-genix
+               ;;
+       i370-*)
+               os=-mvs
+               ;;
+       *-next)
+               os=-nextstep3
+               ;;
+       *-gould)
+               os=-sysv
+               ;;
+       *-highlevel)
+               os=-bsd
+               ;;
+       *-encore)
+               os=-bsd
+               ;;
+       *-sgi)
+               os=-irix
+               ;;
+       *-siemens)
+               os=-sysv4
+               ;;
+       *-masscomp)
+               os=-rtu
+               ;;
+       f30[01]-fujitsu | f700-fujitsu)
+               os=-uxpv
+               ;;
+       *-rom68k)
+               os=-coff
+               ;;
+       *-*bug)
+               os=-coff
+               ;;
+       *-apple)
+               os=-macos
+               ;;
+       *-atari*)
+               os=-mint
+               ;;
+       *)
+               os=-none
+               ;;
+esac
+fi
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+vendor=unknown
+case $basic_machine in
+       *-unknown)
+               case $os in
+                       -riscix*)
+                               vendor=acorn
+                               ;;
+                       -sunos*)
+                               vendor=sun
+                               ;;
+                       -aix*)
+                               vendor=ibm
+                               ;;
+                       -beos*)
+                               vendor=be
+                               ;;
+                       -hpux*)
+                               vendor=hp
+                               ;;
+                       -mpeix*)
+                               vendor=hp
+                               ;;
+                       -hiux*)
+                               vendor=hitachi
+                               ;;
+                       -unos*)
+                               vendor=crds
+                               ;;
+                       -dgux*)
+                               vendor=dg
+                               ;;
+                       -luna*)
+                               vendor=omron
+                               ;;
+                       -genix*)
+                               vendor=ns
+                               ;;
+                       -mvs* | -opened*)
+                               vendor=ibm
+                               ;;
+                       -os400*)
+                               vendor=ibm
+                               ;;
+                       -ptx*)
+                               vendor=sequent
+                               ;;
+                       -tpf*)
+                               vendor=ibm
+                               ;;
+                       -vxsim* | -vxworks* | -windiss*)
+                               vendor=wrs
+                               ;;
+                       -aux*)
+                               vendor=apple
+                               ;;
+                       -hms*)
+                               vendor=hitachi
+                               ;;
+                       -mpw* | -macos*)
+                               vendor=apple
+                               ;;
+                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+                               vendor=atari
+                               ;;
+                       -vos*)
+                               vendor=stratus
+                               ;;
+               esac
+               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
+               ;;
+esac
+
+echo $basic_machine$os
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/configure b/configure
new file mode 100755 (executable)
index 0000000..c5f73d5
--- /dev/null
+++ b/configure
@@ -0,0 +1,8268 @@
+#! /bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.59 for smartmontools 5.36.
+#
+# Report bugs to <smartmontools-support@lists.sourceforge.net>.
+#
+# Copyright (C) 2003 Free Software Foundation, Inc.
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)$' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+         /^X\/\(\/\/\)$/{ s//\1/; q; }
+         /^X\/\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+        case $as_dir in
+        /*)
+          if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+            $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+            $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+            CONFIG_SHELL=$as_dir/$as_base
+            export CONFIG_SHELL
+            exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+          fi;;
+        esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='     ' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS="  $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+
+# Name of the host.
+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+exec 6>&1
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_config_libobj_dir=.
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+
+# Maximum number of lines to put in a shell here document.
+# This variable seems obsolete.  It should probably be removed, and
+# only ac_max_sed_lines should be used.
+: ${ac_max_here_lines=38}
+
+# Identity of this package.
+PACKAGE_NAME='smartmontools'
+PACKAGE_TARNAME='smartmontools'
+PACKAGE_VERSION='5.36'
+PACKAGE_STRING='smartmontools 5.36'
+PACKAGE_BUGREPORT='smartmontools-support@lists.sourceforge.net'
+
+ac_unique_file="smartctl.c"
+# Factoring default headers for most tests.
+ac_includes_default="\
+#include <stdio.h>
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# if HAVE_STDLIB_H
+#  include <stdlib.h>
+# endif
+#endif
+#if HAVE_STRING_H
+# if !STDC_HEADERS && HAVE_MEMORY_H
+#  include <memory.h>
+# endif
+# include <string.h>
+#endif
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_INTTYPES_H
+# include <inttypes.h>
+#else
+# if HAVE_STDINT_H
+#  include <stdint.h>
+# endif
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif"
+
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar MAINTAINER_MODE_TRUE MAINTAINER_MODE_FALSE MAINT CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE CCAS CCASFLAGS build build_cpu build_vendor build_os host host_cpu host_vendor host_os CPP EGREP libc_have_working_snprintf gcc_have_attr_packed ASFLAGS exampledir initddir docdir smartd_suffix SMARTD_SUFFIX_TRUE SMARTD_SUFFIX_FALSE releaseversion smartmontools_release_date smartmontools_release_time os_deps os_libs OS_DARWIN_TRUE OS_DARWIN_FALSE OS_SOLARIS_TRUE OS_SOLARIS_FALSE OS_WIN32_MINGW_TRUE OS_WIN32_MINGW_FALSE LIBOBJS LTLIBOBJS'
+ac_subst_files=''
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+ac_prev=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval "$ac_prev=\$ac_option"
+    ac_prev=
+    continue
+  fi
+
+  ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'`
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_option in
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+  | --da=*)
+    datadir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    eval "enable_$ac_feature=no" ;;
+
+  -enable-* | --enable-*)
+    ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid feature name: $ac_feature" >&2
+   { (exit 1); exit 1; }; }
+    ac_feature=`echo $ac_feature | sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "enable_$ac_feature='$ac_optarg'" ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst \
+  | --locals | --local | --loca | --loc | --lo)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+  | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package| sed 's/-/_/g'`
+    case $ac_option in
+      *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;;
+      *) ac_optarg=yes ;;
+    esac
+    eval "with_$ac_package='$ac_optarg'" ;;
+
+  -without-* | --without-*)
+    ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid package name: $ac_package" >&2
+   { (exit 1); exit 1; }; }
+    ac_package=`echo $ac_package | sed 's/-/_/g'`
+    eval "with_$ac_package=no" ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) { echo "$as_me: error: unrecognized option: $ac_option
+Try \`$0 --help' for more information." >&2
+   { (exit 1); exit 1; }; }
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&
+      { echo "$as_me: error: invalid variable name: $ac_envvar" >&2
+   { (exit 1); exit 1; }; }
+    ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`
+    eval "$ac_envvar='$ac_optarg'"
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  { echo "$as_me: error: missing argument to $ac_option" >&2
+   { (exit 1); exit 1; }; }
+fi
+
+# Be sure to have absolute paths.
+for ac_var in exec_prefix prefix
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* | NONE | '' ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# Be sure to have absolute paths.
+for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \
+             localstatedir libdir includedir oldincludedir infodir mandir
+do
+  eval ac_val=$`echo $ac_var`
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* ) ;;
+    *)  { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+    echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
+    If a cross compiler is detected then cross compile mode will be used." >&2
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then its parent.
+  ac_confdir=`(dirname "$0") 2>/dev/null ||
+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$0" : 'X\(//\)[^/]' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$0" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r $srcdir/$ac_unique_file; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+  if test "$ac_srcdir_defaulted" = yes; then
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2
+   { (exit 1); exit 1; }; }
+  else
+    { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2
+   { (exit 1); exit 1; }; }
+  fi
+fi
+(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null ||
+  { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2
+   { (exit 1); exit 1; }; }
+srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'`
+ac_env_build_alias_set=${build_alias+set}
+ac_env_build_alias_value=$build_alias
+ac_cv_env_build_alias_set=${build_alias+set}
+ac_cv_env_build_alias_value=$build_alias
+ac_env_host_alias_set=${host_alias+set}
+ac_env_host_alias_value=$host_alias
+ac_cv_env_host_alias_set=${host_alias+set}
+ac_cv_env_host_alias_value=$host_alias
+ac_env_target_alias_set=${target_alias+set}
+ac_env_target_alias_value=$target_alias
+ac_cv_env_target_alias_set=${target_alias+set}
+ac_cv_env_target_alias_value=$target_alias
+ac_env_CC_set=${CC+set}
+ac_env_CC_value=$CC
+ac_cv_env_CC_set=${CC+set}
+ac_cv_env_CC_value=$CC
+ac_env_CFLAGS_set=${CFLAGS+set}
+ac_env_CFLAGS_value=$CFLAGS
+ac_cv_env_CFLAGS_set=${CFLAGS+set}
+ac_cv_env_CFLAGS_value=$CFLAGS
+ac_env_LDFLAGS_set=${LDFLAGS+set}
+ac_env_LDFLAGS_value=$LDFLAGS
+ac_cv_env_LDFLAGS_set=${LDFLAGS+set}
+ac_cv_env_LDFLAGS_value=$LDFLAGS
+ac_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_env_CPPFLAGS_value=$CPPFLAGS
+ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set}
+ac_cv_env_CPPFLAGS_value=$CPPFLAGS
+ac_env_CCAS_set=${CCAS+set}
+ac_env_CCAS_value=$CCAS
+ac_cv_env_CCAS_set=${CCAS+set}
+ac_cv_env_CCAS_value=$CCAS
+ac_env_CCASFLAGS_set=${CCASFLAGS+set}
+ac_env_CCASFLAGS_value=$CCASFLAGS
+ac_cv_env_CCASFLAGS_set=${CCASFLAGS+set}
+ac_cv_env_CCASFLAGS_value=$CCASFLAGS
+ac_env_CPP_set=${CPP+set}
+ac_env_CPP_value=$CPP
+ac_cv_env_CPP_set=${CPP+set}
+ac_cv_env_CPP_value=$CPP
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures smartmontools 5.36 to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+_ACEOF
+
+  cat <<_ACEOF
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                         [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                         [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR           user executables [EPREFIX/bin]
+  --sbindir=DIR          system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR       program executables [EPREFIX/libexec]
+  --datadir=DIR          read-only architecture-independent data [PREFIX/share]
+  --sysconfdir=DIR       read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR   modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR    modifiable single-machine data [PREFIX/var]
+  --libdir=DIR           object code libraries [EPREFIX/lib]
+  --includedir=DIR       C header files [PREFIX/include]
+  --oldincludedir=DIR    C header files for non-gcc [/usr/include]
+  --infodir=DIR          info documentation [PREFIX/info]
+  --mandir=DIR           man documentation [PREFIX/man]
+_ACEOF
+
+  cat <<\_ACEOF
+
+Program names:
+  --program-prefix=PREFIX            prepend PREFIX to installed program names
+  --program-suffix=SUFFIX            append SUFFIX to installed program names
+  --program-transform-name=PROGRAM   run sed PROGRAM on installed program names
+
+System types:
+  --build=BUILD     configure for building on BUILD [guessed]
+  --host=HOST       cross-compile to build programs to run on HOST [BUILD]
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+  case $ac_init_help in
+     short | recursive ) echo "Configuration of smartmontools 5.36:";;
+   esac
+  cat <<\_ACEOF
+
+Optional Features:
+  --disable-FEATURE       do not include FEATURE (same as --enable-FEATURE=no)
+  --enable-FEATURE[=ARG]  include FEATURE [ARG=yes]
+  --enable-maintainer-mode  enable make rules and dependencies not useful
+                         (and sometimes confusing) to the casual installer
+  --disable-dependency-tracking  speeds up one-time build
+  --enable-dependency-tracking   do not reject slow dependency extractors
+  --enable-sample         Enables appending .sample to the installed smartd rc
+                          script and configuration file
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-initscriptdir=dir
+                          Location of init scripts (default is
+                          ${sysconfdir}/rc.d/init.d)
+  --with-docdir=dir       Location of documentation (default is
+                          ${prefix}/share/doc/smartmontools-5.X)
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+              nonstandard directory <lib dir>
+  CPPFLAGS    C/C++ preprocessor flags, e.g. -I<include dir> if you have
+              headers in a nonstandard directory <include dir>
+  CCAS        assembler compiler command (defaults to CC)
+  CCASFLAGS   assembler compiler flags (defaults to CFLAGS)
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to <smartmontools-support@lists.sourceforge.net>.
+_ACEOF
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  ac_popdir=`pwd`
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d $ac_dir || continue
+    ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+    cd $ac_dir
+    # Check for guested configure; otherwise get Cygnus style configure.
+    if test -f $ac_srcdir/configure.gnu; then
+      echo
+      $SHELL $ac_srcdir/configure.gnu  --help=recursive
+    elif test -f $ac_srcdir/configure; then
+      echo
+      $SHELL $ac_srcdir/configure  --help=recursive
+    elif test -f $ac_srcdir/configure.ac ||
+          test -f $ac_srcdir/configure.in; then
+      echo
+      $ac_configure --help
+    else
+      echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi
+    cd $ac_popdir
+  done
+fi
+
+test -n "$ac_init_help" && exit 0
+if $ac_init_version; then
+  cat <<\_ACEOF
+smartmontools configure 5.36
+generated by GNU Autoconf 2.59
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit 0
+fi
+exec 5>config.log
+cat >&5 <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by smartmontools $as_me 5.36, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+hostinfo               = `(hostinfo) 2>/dev/null               || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  echo "PATH: $as_dir"
+done
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_sep=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *" "*|*"   "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;
+    2)
+      ac_configure_args1="$ac_configure_args1 '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'"
+      # Get rid of the leading space.
+      ac_sep=" "
+      ;;
+    esac
+  done
+done
+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }
+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Be sure not to use single quotes in there, as some shells,
+# such as our DU 5.0 friend, will then `close' the trap.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    cat <<\_ASBOX
+## ---------------- ##
+## Cache variables. ##
+## ---------------- ##
+_ASBOX
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+{
+  (set) 2>&1 |
+    case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      sed -n \
+       "s/'"'"'/'"'"'\\\\'"'"''"'"'/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p"
+      ;;
+    *)
+      sed -n \
+       "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+}
+    echo
+
+    cat <<\_ASBOX
+## ----------------- ##
+## Output variables. ##
+## ----------------- ##
+_ASBOX
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=$`echo $ac_var`
+      echo "$ac_var='"'"'$ac_val'"'"'"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      cat <<\_ASBOX
+## ------------- ##
+## Output files. ##
+## ------------- ##
+_ASBOX
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=$`echo $ac_var`
+       echo "$ac_var='"'"'$ac_val'"'"'"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      cat <<\_ASBOX
+## ----------- ##
+## confdefs.h. ##
+## ----------- ##
+_ASBOX
+      echo
+      sed "/^$/d" confdefs.h | sort
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      echo "$as_me: caught signal $ac_signal"
+    echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core &&
+  rm -rf conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+     ' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo >confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+  if test "x$prefix" != xNONE; then
+    CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+  else
+    CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+  fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+  if test -r "$ac_site_file"; then
+    { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5
+echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file"
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special
+  # files actually), so we avoid doing that.
+  if test -f "$cache_file"; then
+    { echo "$as_me:$LINENO: loading cache $cache_file" >&5
+echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . $cache_file;;
+      *)                      . ./$cache_file;;
+    esac
+  fi
+else
+  { echo "$as_me:$LINENO: creating cache $cache_file" >&5
+echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in `(set) 2>&1 |
+              sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val="\$ac_cv_env_${ac_var}_value"
+  eval ac_new_val="\$ac_env_${ac_var}_value"
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5
+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5
+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+       { echo "$as_me:$LINENO:   former value:  $ac_old_val" >&5
+echo "$as_me:   former value:  $ac_old_val" >&2;}
+       { echo "$as_me:$LINENO:   current value: $ac_new_val" >&5
+echo "$as_me:   current value: $ac_new_val" >&2;}
+       ac_cache_corrupted=:
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *" "*|*"   "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*)
+      ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5
+echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5
+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+smartmontools_configure_date=`date -u +"%Y/%m/%d %T %Z"`
+smartmontools_cvs_tag=`echo '$Id: configure.in,v 1.113 2005/11/27 20:01:29 chrfranke Exp $'`
+smartmontools_release_date=2006/04/12
+smartmontools_release_time="17:39:01 UTC"
+
+
+cat >>confdefs.h <<_ACEOF
+#define SMARTMONTOOLS_CONFIGURE_ARGS "$ac_configure_args"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SMARTMONTOOLS_CONFIGURE_DATE "$smartmontools_configure_date"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SMARTMONTOOLS_RELEASE_DATE "$smartmontools_release_date"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define SMARTMONTOOLS_RELEASE_TIME "$smartmontools_release_time"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define CONFIG_H_CVSID "$smartmontools_cvs_tag"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_HOMEPAGE "http://smartmontools.sourceforge.net/"
+_ACEOF
+
+
+          ac_config_headers="$ac_config_headers config.h"
+
+
+am__api_version="1.9"
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+  if test -f $ac_dir/install-sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install-sh -c"
+    break
+  elif test -f $ac_dir/install.sh; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/install.sh -c"
+    break
+  elif test -f $ac_dir/shtool; then
+    ac_aux_dir=$ac_dir
+    ac_install_sh="$ac_aux_dir/shtool install -c"
+    break
+  fi
+done
+if test -z "$ac_aux_dir"; then
+  { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5
+echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+ac_config_guess="$SHELL $ac_aux_dir/config.guess"
+ac_config_sub="$SHELL $ac_aux_dir/config.sub"
+ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure.
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+       if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+         if test $ac_prog = install &&
+           grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         elif test $ac_prog = install &&
+           grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # program-specific install script used by HP pwplus--don't use.
+           :
+         else
+           ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+           break 3
+         fi
+       fi
+      done
+    done
+    ;;
+esac
+done
+
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL=$ac_install_sh
+  fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+echo "$as_me:$LINENO: checking whether build environment is sane" >&5
+echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6
+# Just in case
+sleep 1
+echo timestamp > conftest.file
+# Do `set' in a subshell so we don't clobber the current shell's
+# arguments.  Must try -L first in case configure is actually a
+# symlink; some systems play weird games with the mod time of symlinks
+# (eg FreeBSD returns the mod time of the symlink's containing
+# directory).
+if (
+   set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null`
+   if test "$*" = "X"; then
+      # -L didn't work.
+      set X `ls -t $srcdir/configure conftest.file`
+   fi
+   rm -f conftest.file
+   if test "$*" != "X $srcdir/configure conftest.file" \
+      && test "$*" != "X conftest.file $srcdir/configure"; then
+
+      # If neither matched, then we have a broken ls.  This can happen
+      # if, for instance, CONFIG_SHELL is bash and it inherits a
+      # broken ls alias from the environment.  This has actually
+      # happened.  Such a system could not be considered "sane".
+      { { echo "$as_me:$LINENO: error: ls -t appears to fail.  Make sure there is not a broken
+alias in your environment" >&5
+echo "$as_me: error: ls -t appears to fail.  Make sure there is not a broken
+alias in your environment" >&2;}
+   { (exit 1); exit 1; }; }
+   fi
+
+   test "$2" = conftest.file
+   )
+then
+   # Ok.
+   :
+else
+   { { echo "$as_me:$LINENO: error: newly created file is older than distributed files!
+Check your system clock" >&5
+echo "$as_me: error: newly created file is older than distributed files!
+Check your system clock" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+test "$program_prefix" != NONE &&
+  program_transform_name="s,^,$program_prefix,;$program_transform_name"
+# Use a double $ so make ignores it.
+test "$program_suffix" != NONE &&
+  program_transform_name="s,\$,$program_suffix,;$program_transform_name"
+# Double any \ or $.  echo might interpret backslashes.
+# By default was `s,x,x', remove it if useless.
+cat <<\_ACEOF >conftest.sed
+s/[\\$]/&&/g;s/;s,x,x,$//
+_ACEOF
+program_transform_name=`echo $program_transform_name | sed -f conftest.sed`
+rm conftest.sed
+
+# expand $ac_aux_dir to an absolute path
+am_aux_dir=`cd $ac_aux_dir && pwd`
+
+test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
+# Use eval to expand $SHELL
+if eval "$MISSING --run true"; then
+  am_missing_run="$MISSING --run "
+else
+  am_missing_run=
+  { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5
+echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+fi
+
+if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
+  # We used to keeping the `.' as first argument, in order to
+  # allow $(mkdir_p) to be used without argument.  As in
+  #   $(mkdir_p) $(somedir)
+  # where $(somedir) is conditionally defined.  However this is wrong
+  # for two reasons:
+  #  1. if the package is installed by a user who cannot write `.'
+  #     make install will fail,
+  #  2. the above comment should most certainly read
+  #     $(mkdir_p) $(DESTDIR)$(somedir)
+  #     so it does not work when $(somedir) is undefined and
+  #     $(DESTDIR) is not.
+  #  To support the latter case, we have to write
+  #     test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
+  #  so the `.' trick is pointless.
+  mkdir_p='mkdir -p --'
+else
+  # On NextStep and OpenStep, the `mkdir' command does not
+  # recognize any option.  It will interpret all options as
+  # directories to create, and then abort because `.' already
+  # exists.
+  for d in ./-p ./--version;
+  do
+    test -d $d && rmdir $d
+  done
+  # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
+  if test -f "$ac_aux_dir/mkinstalldirs"; then
+    mkdir_p='$(mkinstalldirs)'
+  else
+    mkdir_p='$(install_sh) -d'
+  fi
+fi
+
+for ac_prog in gawk mawk nawk awk
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_AWK+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$AWK"; then
+  ac_cv_prog_AWK="$AWK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_AWK="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+AWK=$ac_cv_prog_AWK
+if test -n "$AWK"; then
+  echo "$as_me:$LINENO: result: $AWK" >&5
+echo "${ECHO_T}$AWK" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$AWK" && break
+done
+
+echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'`
+if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.make <<\_ACEOF
+all:
+       @echo 'ac_maketemp="$(MAKE)"'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftest.make
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  SET_MAKE=
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+rm -rf .tst 2>/dev/null
+mkdir .tst 2>/dev/null
+if test -d .tst; then
+  am__leading_dot=.
+else
+  am__leading_dot=_
+fi
+rmdir .tst 2>/dev/null
+
+# test to see if srcdir already configured
+if test "`cd $srcdir && pwd`" != "`pwd`" &&
+   test -f $srcdir/config.status; then
+  { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5
+echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+# test whether we have cygpath
+if test -z "$CYGPATH_W"; then
+  if (cygpath --version) >/dev/null 2>/dev/null; then
+    CYGPATH_W='cygpath -w'
+  else
+    CYGPATH_W=echo
+  fi
+fi
+
+
+# Define the identity of the package.
+ PACKAGE='smartmontools'
+ VERSION='5.36'
+
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE "$PACKAGE"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define VERSION "$VERSION"
+_ACEOF
+
+# Some tools Automake needs.
+
+ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"}
+
+
+AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"}
+
+
+AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"}
+
+
+AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
+
+
+MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
+
+install_sh=${install_sh-"$am_aux_dir/install-sh"}
+
+# Installed binaries are usually stripped using `strip' when the user
+# run `make install-strip'.  However `strip' might not be the right
+# tool to use in cross-compilation environments, therefore Automake
+# will honor the `STRIP' environment variable to overrule this program.
+if test "$cross_compiling" != no; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
+set dummy ${ac_tool_prefix}strip; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_STRIP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$STRIP"; then
+  ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_STRIP="${ac_tool_prefix}strip"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+STRIP=$ac_cv_prog_STRIP
+if test -n "$STRIP"; then
+  echo "$as_me:$LINENO: result: $STRIP" >&5
+echo "${ECHO_T}$STRIP" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_STRIP"; then
+  ac_ct_STRIP=$STRIP
+  # Extract the first word of "strip", so it can be a program name with args.
+set dummy strip; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_STRIP"; then
+  ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_STRIP="strip"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  test -z "$ac_cv_prog_ac_ct_STRIP" && ac_cv_prog_ac_ct_STRIP=":"
+fi
+fi
+ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
+if test -n "$ac_ct_STRIP"; then
+  echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5
+echo "${ECHO_T}$ac_ct_STRIP" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  STRIP=$ac_ct_STRIP
+else
+  STRIP="$ac_cv_prog_STRIP"
+fi
+
+fi
+INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
+
+# We need awk for the "check" target.  The system "awk" is bad on
+# some platforms.
+# Always define AMTAR for backward compatibility.
+
+AMTAR=${AMTAR-"${am_missing_run}tar"}
+
+am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
+
+
+
+
+
+
+echo "$as_me:$LINENO: checking whether to enable maintainer-specific portions of Makefiles" >&5
+echo $ECHO_N "checking whether to enable maintainer-specific portions of Makefiles... $ECHO_C" >&6
+    # Check whether --enable-maintainer-mode or --disable-maintainer-mode was given.
+if test "${enable_maintainer_mode+set}" = set; then
+  enableval="$enable_maintainer_mode"
+  USE_MAINTAINER_MODE=$enableval
+else
+  USE_MAINTAINER_MODE=no
+fi;
+  echo "$as_me:$LINENO: result: $USE_MAINTAINER_MODE" >&5
+echo "${ECHO_T}$USE_MAINTAINER_MODE" >&6
+
+
+if test $USE_MAINTAINER_MODE = yes; then
+  MAINTAINER_MODE_TRUE=
+  MAINTAINER_MODE_FALSE='#'
+else
+  MAINTAINER_MODE_TRUE='#'
+  MAINTAINER_MODE_FALSE=
+fi
+
+  MAINT=$MAINTAINER_MODE_TRUE
+
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  CC=$ac_ct_CC
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  echo "$as_me:$LINENO: result: $CC" >&5
+echo "${ECHO_T}$CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_ac_ct_CC+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  echo "$as_me:$LINENO: result: $ac_ct_CC" >&5
+echo "${ECHO_T}$ac_ct_CC" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$ac_ct_CC" && break
+done
+
+  CC=$ac_ct_CC
+fi
+
+fi
+
+
+test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&5
+echo "$as_me: error: no acceptable C compiler found in \$PATH
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+
+# Provide some information about the compiler.
+echo "$as_me:$LINENO:" \
+     "checking for C compiler version" >&5
+ac_compiler=`set X $ac_compile; echo $2`
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
+  (eval $ac_compiler --version </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5
+  (eval $ac_compiler -v </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5
+  (eval $ac_compiler -V </dev/null >&5) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+echo "$as_me:$LINENO: checking for C compiler default output file name" >&5
+echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6
+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5
+  (eval $ac_link_default) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # Find the output, starting from the most likely.  This scheme is
+# not robust to junk in `.', hence go to wildcards (a.*) only as a last
+# resort.
+
+# Be careful to initialize this variable, since it used to be cached.
+# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile.
+ac_cv_exeext=
+# b.out is created by i960 compilers.
+for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj )
+       ;;
+    conftest.$ac_ext )
+       # This is the source file.
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+       ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       # FIXME: I believe we export ac_cv_exeext for Libtool,
+       # but it would be cool to find out if it's true.  Does anybody
+       # maintain Libtool? --akim.
+       export ac_cv_exeext
+       break;;
+    * )
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: C compiler cannot create executables
+See \`config.log' for more details." >&5
+echo "$as_me: error: C compiler cannot create executables
+See \`config.log' for more details." >&2;}
+   { (exit 77); exit 77; }; }
+fi
+
+ac_exeext=$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_file" >&5
+echo "${ECHO_T}$ac_file" >&6
+
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether the C compiler works" >&5
+echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6
+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0
+# If not cross compiling, check that we can run a simple program.
+if test "$cross_compiling" != yes; then
+  if { ac_try='./$ac_file'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { echo "$as_me:$LINENO: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+    fi
+  fi
+fi
+echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+
+rm -f a.out a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+# Check the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+echo "$as_me:$LINENO: checking whether we are cross compiling" >&5
+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6
+echo "$as_me:$LINENO: result: $cross_compiling" >&5
+echo "${ECHO_T}$cross_compiling" >&6
+
+echo "$as_me:$LINENO: checking for suffix of executables" >&5
+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         export ac_cv_exeext
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest$ac_cv_exeext
+echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5
+echo "${ECHO_T}$ac_cv_exeext" >&6
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+echo "$as_me:$LINENO: checking for suffix of object files" >&5
+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6
+if test "${ac_cv_objext+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; then
+  for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot compute suffix of object files: cannot compile
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_objext" >&5
+echo "${ECHO_T}$ac_cv_objext" >&6
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5
+echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6
+if test "${ac_cv_c_compiler_gnu+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_compiler_gnu=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_compiler_gnu=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5
+echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6
+GCC=`test $ac_compiler_gnu = yes && echo yes`
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+CFLAGS="-g"
+echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5
+echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_g+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_g=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_prog_cc_g=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_g" >&6
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5
+echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6
+if test "${ac_cv_prog_cc_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_prog_cc_stdc=no
+ac_save_CC=$CC
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std1 is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std1.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+# Don't try gcc -ansi; that turns off useful extensions and
+# breaks some systems' header files.
+# AIX                  -qlanglvl=ansi
+# Ultrix and OSF/1     -std1
+# HP-UX 10.20 and later        -Ae
+# HP-UX older versions -Aa -D_HPUX_SOURCE
+# SVR4                 -Xc -D__EXTENSIONS__
+for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_prog_cc_stdc=$ac_arg
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext
+done
+rm -f conftest.$ac_ext conftest.$ac_objext
+CC=$ac_save_CC
+
+fi
+
+case "x$ac_cv_prog_cc_stdc" in
+  x|xno)
+    echo "$as_me:$LINENO: result: none needed" >&5
+echo "${ECHO_T}none needed" >&6 ;;
+  *)
+    echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5
+echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6
+    CC="$CC $ac_cv_prog_cc_stdc" ;;
+esac
+
+# Some people use a C++ compiler to compile C.  Since we use `exit',
+# in C++ we need to declare it.  In case someone uses the same compiler
+# for both compiling C and C++ we need to have the C++ compiler decide
+# the declaration of exit, since it's the most demanding environment.
+cat >conftest.$ac_ext <<_ACEOF
+#ifndef __cplusplus
+  choke me
+#endif
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  for ac_declaration in \
+   '' \
+   'extern "C" void std::exit (int) throw (); using std::exit;' \
+   'extern "C" void std::exit (int); using std::exit;' \
+   'extern "C" void exit (int) throw ();' \
+   'extern "C" void exit (int);' \
+   'void exit (int);'
+do
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+#include <stdlib.h>
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+continue
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_declaration
+int
+main ()
+{
+exit (42);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+done
+rm -f conftest*
+if test -n "$ac_declaration"; then
+  echo '#ifdef __cplusplus' >>confdefs.h
+  echo $ac_declaration      >>confdefs.h
+  echo '#endif'             >>confdefs.h
+fi
+
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+DEPDIR="${am__leading_dot}deps"
+
+          ac_config_commands="$ac_config_commands depfiles"
+
+
+am_make=${MAKE-make}
+cat > confinc << 'END'
+am__doit:
+       @echo done
+.PHONY: am__doit
+END
+# If we don't find an include directive, just comment out the code.
+echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5
+echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6
+am__include="#"
+am__quote=
+_am_result=none
+# First try GNU make style include.
+echo "include confinc" > confmf
+# We grep out `Entering directory' and `Leaving directory'
+# messages which can occur if `w' ends up in MAKEFLAGS.
+# In particular we don't look at `^make:' because GNU make might
+# be invoked under some other name (usually "gmake"), in which
+# case it prints its new name instead of `make'.
+if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then
+   am__include=include
+   am__quote=
+   _am_result=GNU
+fi
+# Now try BSD make style include.
+if test "$am__include" = "#"; then
+   echo '.include "confinc"' > confmf
+   if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then
+      am__include=.include
+      am__quote="\""
+      _am_result=BSD
+   fi
+fi
+
+
+echo "$as_me:$LINENO: result: $_am_result" >&5
+echo "${ECHO_T}$_am_result" >&6
+rm -f confinc confmf
+
+# Check whether --enable-dependency-tracking or --disable-dependency-tracking was given.
+if test "${enable_dependency_tracking+set}" = set; then
+  enableval="$enable_dependency_tracking"
+
+fi;
+if test "x$enable_dependency_tracking" != xno; then
+  am_depcomp="$ac_aux_dir/depcomp"
+  AMDEPBACKSLASH='\'
+fi
+
+
+if test "x$enable_dependency_tracking" != xno; then
+  AMDEP_TRUE=
+  AMDEP_FALSE='#'
+else
+  AMDEP_TRUE='#'
+  AMDEP_FALSE=
+fi
+
+
+
+
+depcc="$CC"   am_compiler_list=
+
+echo "$as_me:$LINENO: checking dependency style of $depcc" >&5
+echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6
+if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
+  # We make a subdir and do the tests there.  Otherwise we can end up
+  # making bogus files that we don't know about and never remove.  For
+  # instance it was reported that on HP-UX the gcc test will end up
+  # making a dummy file named `D' -- because `-MD' means `put the output
+  # in D'.
+  mkdir conftest.dir
+  # Copy depcomp to subdir because otherwise we won't find it if we're
+  # using a relative directory.
+  cp "$am_depcomp" conftest.dir
+  cd conftest.dir
+  # We will build objects and dependencies in a subdirectory because
+  # it helps to detect inapplicable dependency modes.  For instance
+  # both Tru64's cc and ICC support -MD to output dependencies as a
+  # side effect of compilation, but ICC will put the dependencies in
+  # the current directory while Tru64 will put them in the object
+  # directory.
+  mkdir sub
+
+  am_cv_CC_dependencies_compiler_type=none
+  if test "$am_compiler_list" = ""; then
+     am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp`
+  fi
+  for depmode in $am_compiler_list; do
+    # Setup a source with many dependencies, because some compilers
+    # like to wrap large dependency lists on column 80 (with \), and
+    # we should not choose a depcomp mode which is confused by this.
+    #
+    # We need to recreate these files for each test, as the compiler may
+    # overwrite some of them when testing with obscure command lines.
+    # This happens at least with the AIX C compiler.
+    : > sub/conftest.c
+    for i in 1 2 3 4 5 6; do
+      echo '#include "conftst'$i'.h"' >> sub/conftest.c
+      # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
+      # Solaris 8's {/usr,}/bin/sh.
+      touch sub/conftst$i.h
+    done
+    echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
+
+    case $depmode in
+    nosideeffect)
+      # after this tag, mechanisms are not by side-effect, so they'll
+      # only be used when explicitly requested
+      if test "x$enable_dependency_tracking" = xyes; then
+       continue
+      else
+       break
+      fi
+      ;;
+    none) break ;;
+    esac
+    # We check with `-c' and `-o' for the sake of the "dashmstdout"
+    # mode.  It turns out that the SunPro C++ compiler does not properly
+    # handle `-M -o', and we need to detect this.
+    if depmode=$depmode \
+       source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \
+       depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
+       $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
+         >/dev/null 2>conftest.err &&
+       grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
+       grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
+       ${MAKE-make} -s -f confmf > /dev/null 2>&1; then
+      # icc doesn't choke on unknown options, it will just issue warnings
+      # or remarks (even with -Werror).  So we grep stderr for any message
+      # that says an option was ignored or not supported.
+      # When given -MP, icc 7.0 and 7.1 complain thusly:
+      #   icc: Command line warning: ignoring option '-M'; no argument required
+      # The diagnosis changed in icc 8.0:
+      #   icc: Command line remark: option '-MP' not supported
+      if (grep 'ignoring option' conftest.err ||
+          grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else
+        am_cv_CC_dependencies_compiler_type=$depmode
+        break
+      fi
+    fi
+  done
+
+  cd ..
+  rm -rf conftest.dir
+else
+  am_cv_CC_dependencies_compiler_type=none
+fi
+
+fi
+echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5
+echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6
+CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type
+
+
+
+if
+  test "x$enable_dependency_tracking" != xno \
+  && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then
+  am__fastdepCC_TRUE=
+  am__fastdepCC_FALSE='#'
+else
+  am__fastdepCC_TRUE='#'
+  am__fastdepCC_FALSE=
+fi
+
+
+# By default we simply use the C compiler to build assembly code.
+
+test "${CCAS+set}" = set || CCAS=$CC
+test "${CCASFLAGS+set}" = set || CCASFLAGS=$CFLAGS
+
+
+
+# Find a good install program.  We prefer a C program (faster),
+# so one script is as good as another.  But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AmigaOS /C/install, which installs bootblocks on floppy discs
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# OS/2's system install, which has a completely different semantic
+# ./install, which can be erroneously created by make from ./install.sh.
+echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5
+echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6
+if test -z "$INSTALL"; then
+if test "${ac_cv_path_install+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  # Account for people who put trailing slashes in PATH elements.
+case $as_dir/ in
+  ./ | .// | /cC/* | \
+  /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
+  ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \
+  /usr/ucb/* ) ;;
+  *)
+    # OSF1 and SCO ODT 3.0 have their own names for install.
+    # Don't use installbsd from OSF since it installs stuff as root
+    # by default.
+    for ac_prog in ginstall scoinst install; do
+      for ac_exec_ext in '' $ac_executable_extensions; do
+       if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+         if test $ac_prog = install &&
+           grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # AIX install.  It has an incompatible calling convention.
+           :
+         elif test $ac_prog = install &&
+           grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+           # program-specific install script used by HP pwplus--don't use.
+           :
+         else
+           ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+           break 3
+         fi
+       fi
+      done
+    done
+    ;;
+esac
+done
+
+
+fi
+  if test "${ac_cv_path_install+set}" = set; then
+    INSTALL=$ac_cv_path_install
+  else
+    # As a last resort, use the slow shell script.  We don't cache a
+    # path for INSTALL within a source directory, because that will
+    # break other packages using the cache if that directory is
+    # removed, or if the path is relative.
+    INSTALL=$ac_install_sh
+  fi
+fi
+echo "$as_me:$LINENO: result: $INSTALL" >&5
+echo "${ECHO_T}$INSTALL" >&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+# Make sure we can run config.sub.
+$ac_config_sub sun4 >/dev/null 2>&1 ||
+  { { echo "$as_me:$LINENO: error: cannot run $ac_config_sub" >&5
+echo "$as_me: error: cannot run $ac_config_sub" >&2;}
+   { (exit 1); exit 1; }; }
+
+echo "$as_me:$LINENO: checking build system type" >&5
+echo $ECHO_N "checking build system type... $ECHO_C" >&6
+if test "${ac_cv_build+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_build_alias=$build_alias
+test -z "$ac_cv_build_alias" &&
+  ac_cv_build_alias=`$ac_config_guess`
+test -z "$ac_cv_build_alias" &&
+  { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5
+echo "$as_me: error: cannot guess build type; you must specify one" >&2;}
+   { (exit 1); exit 1; }; }
+ac_cv_build=`$ac_config_sub $ac_cv_build_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_build_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_build_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_build" >&5
+echo "${ECHO_T}$ac_cv_build" >&6
+build=$ac_cv_build
+build_cpu=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $ac_cv_build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+echo "$as_me:$LINENO: checking host system type" >&5
+echo $ECHO_N "checking host system type... $ECHO_C" >&6
+if test "${ac_cv_host+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_cv_host_alias=$host_alias
+test -z "$ac_cv_host_alias" &&
+  ac_cv_host_alias=$ac_cv_build_alias
+ac_cv_host=`$ac_config_sub $ac_cv_host_alias` ||
+  { { echo "$as_me:$LINENO: error: $ac_config_sub $ac_cv_host_alias failed" >&5
+echo "$as_me: error: $ac_config_sub $ac_cv_host_alias failed" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+echo "$as_me:$LINENO: result: $ac_cv_host" >&5
+echo "${ECHO_T}$ac_cv_host" >&6
+host=$ac_cv_host
+host_cpu=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $ac_cv_host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+
+case "${host}" in
+       *-*-mingw*)
+               CPPFLAGS="$CPPFLAGS -mno-cygwin"
+               LDFLAGS="$LDFLAGS -mno-cygwin"
+               CPPFLAGS="$CPPFLAGS -idirafter ${srcdir}/posix -idirafter ${srcdir}/os_win32"
+esac
+
+#  AC_SEARCH_LIBS (FUNCTION, SEARCH-LIBS, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND], [OTHER-LIBRARIES])
+echo "$as_me:$LINENO: checking for library containing gethostbyname" >&5
+echo $ECHO_N "checking for library containing gethostbyname... $ECHO_C" >&6
+if test "${ac_cv_search_gethostbyname+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+ac_cv_search_gethostbyname=no
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char gethostbyname ();
+int
+main ()
+{
+gethostbyname ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_gethostbyname="none required"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_gethostbyname" = no; then
+  for ac_lib in nsl; do
+    LIBS="-l$ac_lib  $ac_func_search_save_LIBS"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char gethostbyname ();
+int
+main ()
+{
+gethostbyname ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_gethostbyname="-l$ac_lib"
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+  done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_search_gethostbyname" >&6
+if test "$ac_cv_search_gethostbyname" != no; then
+  test "$ac_cv_search_gethostbyname" = "none required" || LIBS="$ac_cv_search_gethostbyname $LIBS"
+
+else
+  echo "$as_me:$LINENO: checking for library containing gethostbyname" >&5
+echo $ECHO_N "checking for library containing gethostbyname... $ECHO_C" >&6
+if test "${ac_cv_search_gethostbyname+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_func_search_save_LIBS=$LIBS
+ac_cv_search_gethostbyname=no
+
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char gethostbyname ();
+int
+main ()
+{
+gethostbyname ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_gethostbyname="none required"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+if test "$ac_cv_search_gethostbyname" = no; then
+  for ac_lib in nsl; do
+    LIBS="-l$ac_lib -lsocket $ac_func_search_save_LIBS"
+    cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char gethostbyname ();
+int
+main ()
+{
+gethostbyname ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_search_gethostbyname="-l$ac_lib"
+break
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+  done
+fi
+LIBS=$ac_func_search_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_search_gethostbyname" >&5
+echo "${ECHO_T}$ac_cv_search_gethostbyname" >&6
+if test "$ac_cv_search_gethostbyname" != no; then
+  test "$ac_cv_search_gethostbyname" = "none required" || LIBS="$ac_cv_search_gethostbyname $LIBS"
+
+fi
+
+fi
+
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5
+echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if test "${ac_cv_prog_CPP+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+echo "$as_me:$LINENO: result: $CPP" >&5
+echo "${ECHO_T}$CPP" >&6
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  :
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether non-existent headers
+  # can be detected and how.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  # Broken: success on invalid input.
+continue
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then
+  :
+else
+  { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&5
+echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+echo "$as_me:$LINENO: checking for egrep" >&5
+echo $ECHO_N "checking for egrep... $ECHO_C" >&6
+if test "${ac_cv_prog_egrep+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if echo a | (grep -E '(a|b)') >/dev/null 2>&1
+    then ac_cv_prog_egrep='grep -E'
+    else ac_cv_prog_egrep='egrep'
+    fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5
+echo "${ECHO_T}$ac_cv_prog_egrep" >&6
+ EGREP=$ac_cv_prog_egrep
+
+
+echo "$as_me:$LINENO: checking for ANSI C header files" >&5
+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6
+if test "${ac_cv_header_stdc+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_header_stdc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_header_stdc=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test $ac_cv_header_stdc = yes; then
+  # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <string.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "memchr" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdlib.h>
+
+_ACEOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+  $EGREP "free" >/dev/null 2>&1; then
+  :
+else
+  ac_cv_header_stdc=no
+fi
+rm -f conftest*
+
+fi
+
+if test $ac_cv_header_stdc = yes; then
+  # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+  if test "$cross_compiling" = yes; then
+  :
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <ctype.h>
+#if ((' ' & 0x0FF) == 0x020)
+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#else
+# define ISLOWER(c) \
+                  (('a' <= (c) && (c) <= 'i') \
+                    || ('j' <= (c) && (c) <= 'r') \
+                    || ('s' <= (c) && (c) <= 'z'))
+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
+#endif
+
+#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
+int
+main ()
+{
+  int i;
+  for (i = 0; i < 256; i++)
+    if (XOR (islower (i), ISLOWER (i))
+       || toupper (i) != TOUPPER (i))
+      exit(2);
+  exit (0);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  :
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_header_stdc=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+fi
+echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5
+echo "${ECHO_T}$ac_cv_header_stdc" >&6
+if test $ac_cv_header_stdc = yes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define STDC_HEADERS 1
+_ACEOF
+
+fi
+
+# On IRIX 5.3, sys/types and inttypes.h are conflicting.
+
+
+
+
+
+
+
+
+
+for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
+                 inttypes.h stdint.h unistd.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_Header=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_Header=no"
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+
+for ac_header in locale.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------------------------- ##
+## Report this to smartmontools-support@lists.sourceforge.net ##
+## ---------------------------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in getopt.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------------------------- ##
+## Report this to smartmontools-support@lists.sourceforge.net ##
+## ---------------------------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in dev/ata/atavar.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------------------------- ##
+## Report this to smartmontools-support@lists.sourceforge.net ##
+## ---------------------------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in netdb.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------------------------- ##
+## Report this to smartmontools-support@lists.sourceforge.net ##
+## ---------------------------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in inttypes.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------------------------- ##
+## Report this to smartmontools-support@lists.sourceforge.net ##
+## ---------------------------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+for ac_header in stdint.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------------------------- ##
+## Report this to smartmontools-support@lists.sourceforge.net ##
+## ---------------------------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+for ac_header in sys/inttypes.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------------------------- ##
+## Report this to smartmontools-support@lists.sourceforge.net ##
+## ---------------------------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+for ac_header in sys/int_types.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------------------------- ##
+## Report this to smartmontools-support@lists.sourceforge.net ##
+## ---------------------------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+for ac_header in sys/tweio.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------------------------- ##
+## Report this to smartmontools-support@lists.sourceforge.net ##
+## ---------------------------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in sys/twereg.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------------------------- ##
+## Report this to smartmontools-support@lists.sourceforge.net ##
+## ---------------------------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+for ac_header in sys/tw_osl_ioctl.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ---------------------------------------------------------- ##
+## Report this to smartmontools-support@lists.sourceforge.net ##
+## ---------------------------------------------------------- ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+
+echo "$as_me:$LINENO: checking for int64_t" >&5
+echo $ECHO_N "checking for int64_t... $ECHO_C" >&6
+if test "${ac_cv_type_int64_t+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if ((int64_t *) 0)
+  return 0;
+if (sizeof (int64_t))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_type_int64_t=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_int64_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_int64_t" >&5
+echo "${ECHO_T}$ac_cv_type_int64_t" >&6
+if test $ac_cv_type_int64_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_INT64_T 1
+_ACEOF
+
+
+fi
+echo "$as_me:$LINENO: checking for uint64_t" >&5
+echo $ECHO_N "checking for uint64_t... $ECHO_C" >&6
+if test "${ac_cv_type_uint64_t+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+int
+main ()
+{
+if ((uint64_t *) 0)
+  return 0;
+if (sizeof (uint64_t))
+  return 0;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_type_uint64_t=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_type_uint64_t=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_type_uint64_t" >&5
+echo "${ECHO_T}$ac_cv_type_uint64_t" >&6
+if test $ac_cv_type_uint64_t = yes; then
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_UINT64_T 1
+_ACEOF
+
+
+fi
+
+
+
+for ac_func in getopt
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in getopt_long
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in getdomainname
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in gethostname
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in gethostbyname
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in sigset
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in strtoull
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+for ac_func in uname
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+# Check whether snprintf appends null char and returns expected length on overflow
+
+
+echo "$as_me:$LINENO: checking for working snprintf" >&5
+echo $ECHO_N "checking for working snprintf... $ECHO_C" >&6
+if test "$cross_compiling" = yes; then
+  { { echo "$as_me:$LINENO: error: cannot run test program while cross compiling
+See \`config.log' for more details." >&5
+echo "$as_me: error: cannot run test program while cross compiling
+See \`config.log' for more details." >&2;}
+   { (exit 1); exit 1; }; }
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+ char buf[]="ABCDEFGHI";
+               int i=snprintf(buf,8,"12345678"); return !(!buf[7] && i==8);
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  libc_have_working_snprintf=yes
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+libc_have_working_snprintf=no
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+
+if test "$libc_have_working_snprintf" = "yes"; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_WORKING_SNPRINTF 1
+_ACEOF
+
+fi
+echo "$as_me:$LINENO: result: $libc_have_working_snprintf" >&5
+echo "${ECHO_T}$libc_have_working_snprintf" >&6
+
+# check for __attribute__((packed))
+
+
+echo "$as_me:$LINENO: checking whether C compiler supports __attribute__((packed))" >&5
+echo $ECHO_N "checking whether C compiler supports __attribute__((packed))... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+int
+main ()
+{
+struct a { int b; } __attribute__((packed));
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+        { ac_try='test -z "$ac_c_werror_flag"
+                        || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+        { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  gcc_have_attr_packed=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+gcc_have_attr_packed=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+
+if test "$gcc_have_attr_packed" = "yes"; then
+  cat >>confdefs.h <<\_ACEOF
+#define HAVE_ATTR_PACKED 1
+_ACEOF
+
+fi
+echo "$as_me:$LINENO: result: $gcc_have_attr_packed" >&5
+echo "${ECHO_T}$gcc_have_attr_packed" >&6
+
+
+
+
+
+exampledir='${docdir}/examplescripts'
+
+
+
+# Check whether --with-initscriptdir or --without-initscriptdir was given.
+if test "${with_initscriptdir+set}" = set; then
+  withval="$with_initscriptdir"
+  initddir="$withval"
+else
+  initddir='${sysconfdir}/rc.d/init.d'
+fi;
+
+
+
+# Check whether --with-docdir or --without-docdir was given.
+if test "${with_docdir+set}" = set; then
+  withval="$with_docdir"
+  docdir="$withval"
+else
+  docdir='${prefix}/share/doc/${PACKAGE}-${VERSION}'
+fi;
+
+
+# Check whether --enable-sample or --disable-sample was given.
+if test "${enable_sample+set}" = set; then
+  enableval="$enable_sample"
+  smartd_suffix='.sample'
+else
+  smartd_suffix=''
+fi;
+
+
+
+if test $smartd_suffix; then
+  SMARTD_SUFFIX_TRUE=
+  SMARTD_SUFFIX_FALSE='#'
+else
+  SMARTD_SUFFIX_TRUE='#'
+  SMARTD_SUFFIX_FALSE=
+fi
+
+
+if test "$prefix" = "NONE"; then
+        if test "$mandir" = '${prefix}/man'; then
+       mandir='${prefix}/share/man'
+
+    fi
+fi
+
+releaseversion='${PACKAGE}-${VERSION}'
+
+
+
+
+case "${host}" in
+       *-*-linux-gnu*)
+               os_deps='os_linux.o'
+
+               os_libs=''
+ ;;
+       *-*-linux*)
+               os_deps='os_linux.o'
+
+               os_libs=''
+ ;;
+       *-*-freebsd*)
+               os_deps='os_freebsd.o'
+
+               os_libs='-lcam'
+;;
+       sparc-*-solaris*)
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_MAILER "mailx"
+_ACEOF
+
+
+cat >>confdefs.h <<_ACEOF
+#define NEED_SOLARIS_ATA_CODE "os_solaris_ata.s"
+_ACEOF
+
+               os_deps='os_solaris.o os_solaris_ata.o'
+
+               os_libs=''
+ ;;
+       *-pc-solaris*)
+
+cat >>confdefs.h <<_ACEOF
+#define DEFAULT_MAILER "mailx"
+_ACEOF
+
+               os_deps='os_solaris.o'
+
+               os_libs=''
+ ;;
+       *-*-netbsd*)
+               os_deps='os_netbsd.o'
+
+               os_libs='-lutil'
+ ;;
+       *-*-openbsd*)
+               os_deps='os_openbsd.o'
+
+               os_libs='-lutil'
+ ;;
+       *-*-cygwin*)
+               os_deps='os_win32.o'
+
+               os_libs=''
+ ;;
+       *-*-mingw*)
+               os_deps='os_win32.o'
+
+               os_libs=''
+ ;;
+       *-*-darwin*)
+               os_deps='os_darwin.o'
+
+               os_libs='-framework CoreFoundation -framework IOKit'
+ ;;
+       *)
+               os_deps='os_generic.o'
+
+               os_libs=''
+ ;;
+esac
+
+# Define symbols for optional functions in OS specific module
+case "${os_deps}" in
+  os_win32*)
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_ATA_IDENTIFY_IS_CACHED 1
+_ACEOF
+ ;;
+esac
+case "${os_deps}" in
+  os_win32*)
+
+cat >>confdefs.h <<\_ACEOF
+#define HAVE_GET_OS_VERSION_STR 1
+_ACEOF
+ ;;
+esac
+
+
+
+if echo $host_os | grep '^darwin' > /dev/null; then
+  OS_DARWIN_TRUE=
+  OS_DARWIN_FALSE='#'
+else
+  OS_DARWIN_TRUE='#'
+  OS_DARWIN_FALSE=
+fi
+
+
+
+if echo $host_os | grep '^solaris' > /dev/null; then
+  OS_SOLARIS_TRUE=
+  OS_SOLARIS_FALSE='#'
+else
+  OS_SOLARIS_TRUE='#'
+  OS_SOLARIS_FALSE=
+fi
+
+
+
+if echo $host_os | grep '^mingw' > /dev/null; then
+  OS_WIN32_MINGW_TRUE=
+  OS_WIN32_MINGW_FALSE='#'
+else
+  OS_WIN32_MINGW_TRUE='#'
+  OS_WIN32_MINGW_FALSE=
+fi
+
+
+if test "x$GCC" = "xyes"; then
+  if test -z "`echo "$CFLAGS" | grep "\-Wall" 2> /dev/null`" ; then
+      CFLAGS="$CFLAGS -Wall"
+  fi
+# In the next line, do NOT delete the 2 spaces inside double quotes.
+  if test -z "`echo "$CFLAGS " | grep "\-W " 2> /dev/null`" ; then
+      CFLAGS="$CFLAGS -W"
+  fi
+  case "${host}" in
+    *-*-mingw*)
+      # MinGW uses MSVCRT.DLL which uses printf format "%I64d" and not "%lld" for int64_t
+      CFLAGS="$CFLAGS -Wno-format";;
+  esac
+else
+  case "${host}" in
+       *-*-solaris*)
+                    if test -z "`echo "$CFLAGS" | grep "\-xmemalign" 2> /dev/null`" ; then
+                        CFLAGS="-xmemalign=1i $CFLAGS"
+          fi
+          if test -z "`echo "$CFLAGS" | grep "\-xCC" 2> /dev/null`" ; then
+                        CFLAGS="-xCC $CFLAGS"
+          fi
+          if test -z "`echo "$CFLAGS" | grep "\-xO" 2> /dev/null`" ; then
+                        CFLAGS="-xO2 $CFLAGS"
+          fi
+ esac
+fi
+
+
+cat >>confdefs.h <<_ACEOF
+#define SMARTMONTOOLS_BUILD_HOST "${host}"
+_ACEOF
+
+
+
+
+                    ac_config_files="$ac_config_files Makefile examplescripts/Makefile"
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+{
+  (set) 2>&1 |
+    case `(ac_space=' '; set | grep ac_space) 2>&1` in
+    *ac_space=\ *)
+      # `set' does not quote correctly, so add quotes (double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \).
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;;
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n \
+       "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p"
+      ;;
+    esac;
+} |
+  sed '
+     t clear
+     : clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     : end' >>confcache
+if diff $cache_file confcache >/dev/null 2>&1; then :; else
+  if test -w $cache_file; then
+    test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file"
+    cat confcache >$cache_file
+  else
+    echo "not updating unwritable cache $cache_file"
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# VPATH may cause trouble with some makes, so we remove $(srcdir),
+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=/{
+s/:*\$(srcdir):*/:/;
+s/:*\${srcdir}:*/:/;
+s/:*@srcdir@:*/:/;
+s/^\([^=]*=[    ]*\):*/\1/;
+s/:*$//;
+s/^[^=]*=[      ]*$//;
+}'
+fi
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_i=`echo "$ac_i" |
+        sed 's/\$U\././;s/\.o$//;s/\.obj$//'`
+  # 2. Add them.
+  ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext"
+  ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"MAINTAINER_MODE\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"AMDEP\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"am__fastdepCC\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${SMARTD_SUFFIX_TRUE}" && test -z "${SMARTD_SUFFIX_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"SMARTD_SUFFIX\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"SMARTD_SUFFIX\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${OS_DARWIN_TRUE}" && test -z "${OS_DARWIN_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"OS_DARWIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"OS_DARWIN\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${OS_SOLARIS_TRUE}" && test -z "${OS_SOLARIS_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"OS_SOLARIS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"OS_SOLARIS\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+if test -z "${OS_WIN32_MINGW_TRUE}" && test -z "${OS_WIN32_MINGW_FALSE}"; then
+  { { echo "$as_me:$LINENO: error: conditional \"OS_WIN32_MINGW\" was never defined.
+Usually this means the macro was only invoked conditionally." >&5
+echo "$as_me: error: conditional \"OS_WIN32_MINGW\" was never defined.
+Usually this means the macro was only invoked conditionally." >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+: ${CONFIG_STATUS=./config.status}
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5
+echo "$as_me: creating $CONFIG_STATUS" >&6;}
+cat >$CONFIG_STATUS <<_ACEOF
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+SHELL=\${CONFIG_SHELL-$SHELL}
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+## --------------------- ##
+## M4sh Initialization.  ##
+## --------------------- ##
+
+# Be Bourne compatible
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+  emulate sh
+  NULLCMD=:
+  # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
+  set -o posix
+fi
+DUALCASE=1; export DUALCASE # for MKS sh
+
+# Support unset when possible.
+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+  as_unset=unset
+else
+  as_unset=false
+fi
+
+
+# Work around bugs in pre-3.0 UWIN ksh.
+$as_unset ENV MAIL MAILPATH
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+for as_var in \
+  LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \
+  LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \
+  LC_TELEPHONE LC_TIME
+do
+  if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then
+    eval $as_var=C; export $as_var
+  else
+    $as_unset $as_var
+  fi
+done
+
+# Required to use basename.
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+
+# Name of the executable.
+as_me=`$as_basename "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)$' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; }
+         /^X\/\(\/\/\)$/{ s//\1/; q; }
+         /^X\/\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+
+
+# PATH needs CR, and LINENO needs CR and PATH.
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  echo "#! /bin/sh" >conf$$.sh
+  echo  "exit 0"   >>conf$$.sh
+  chmod +x conf$$.sh
+  if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then
+    PATH_SEPARATOR=';'
+  else
+    PATH_SEPARATOR=:
+  fi
+  rm -f conf$$.sh
+fi
+
+
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2"  || {
+  # Find who we are.  Look in the path if we contain no path at all
+  # relative or not.
+  case $0 in
+    *[\\/]* ) as_myself=$0 ;;
+    *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+done
+
+       ;;
+  esac
+  # We did not find ourselves, most probably we were run as `sh COMMAND'
+  # in which case we are not to be found in the path.
+  if test "x$as_myself" = x; then
+    as_myself=$0
+  fi
+  if test ! -f "$as_myself"; then
+    { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5
+echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;}
+   { (exit 1); exit 1; }; }
+  fi
+  case $CONFIG_SHELL in
+  '')
+    as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for as_base in sh bash ksh sh5; do
+        case $as_dir in
+        /*)
+          if ("$as_dir/$as_base" -c '
+  as_lineno_1=$LINENO
+  as_lineno_2=$LINENO
+  as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null`
+  test "x$as_lineno_1" != "x$as_lineno_2" &&
+  test "x$as_lineno_3"  = "x$as_lineno_2" ') 2>/dev/null; then
+            $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; }
+            $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; }
+            CONFIG_SHELL=$as_dir/$as_base
+            export CONFIG_SHELL
+            exec "$CONFIG_SHELL" "$0" ${1+"$@"}
+          fi;;
+        esac
+       done
+done
+;;
+  esac
+
+  # Create $as_me.lineno as a copy of $as_myself, but with $LINENO
+  # uniformly replaced by the line number.  The first 'sed' inserts a
+  # line-number line before each line; the second 'sed' does the real
+  # work.  The second script uses 'N' to pair each line-number line
+  # with the numbered line, and appends trailing '-' during
+  # substitution so that $LINENO is not a special case at line end.
+  # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the
+  # second 'sed' script.  Blame Lee E. McMahon for sed's syntax.  :-)
+  sed '=' <$as_myself |
+    sed '
+      N
+      s,$,-,
+      : loop
+      s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3,
+      t loop
+      s,-$,,
+      s,^['$as_cr_digits']*\n,,
+    ' >$as_me.lineno &&
+  chmod +x $as_me.lineno ||
+    { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5
+echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;}
+   { (exit 1); exit 1; }; }
+
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensible to this).
+  . ./$as_me.lineno
+  # Exit status is that of the last command.
+  exit
+}
+
+
+case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in
+  *c*,-n*) ECHO_N= ECHO_C='
+' ECHO_T='     ' ;;
+  *c*,*  ) ECHO_N=-n ECHO_C= ECHO_T= ;;
+  *)       ECHO_N= ECHO_C='\c' ECHO_T= ;;
+esac
+
+if expr a : '\(a\)' >/dev/null 2>&1; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+rm -f conf$$ conf$$.exe conf$$.file
+echo >conf$$.file
+if ln -s conf$$.file conf$$ 2>/dev/null; then
+  # We could just check for DJGPP; but this test a) works b) is more generic
+  # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04).
+  if test -f conf$$.exe; then
+    # Don't use ln at all; we don't have any links
+    as_ln_s='cp -p'
+  else
+    as_ln_s='ln -s'
+  fi
+elif ln conf$$.file conf$$ 2>/dev/null; then
+  as_ln_s=ln
+else
+  as_ln_s='cp -p'
+fi
+rm -f conf$$ conf$$.exe conf$$.file
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p=:
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_executable_p="test -f"
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.
+as_nl='
+'
+IFS="  $as_nl"
+
+# CDPATH.
+$as_unset CDPATH
+
+exec 6>&1
+
+# Open the log real soon, to keep \$[0] and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.  Logging --version etc. is OK.
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+cat >&5 <<_CSEOF
+
+This file was extended by smartmontools $as_me 5.36, which was
+generated by GNU Autoconf 2.59.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+_CSEOF
+echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5
+echo >&5
+_ACEOF
+
+# Files that config.status was made for.
+if test -n "$ac_config_files"; then
+  echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_headers"; then
+  echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_links"; then
+  echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS
+fi
+
+if test -n "$ac_config_commands"; then
+  echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+ac_cs_usage="\
+\`$as_me' instantiates files from templates according to the
+current configuration.
+
+Usage: $0 [OPTIONS] [FILE]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number, then exit
+  -q, --quiet      do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+  --file=FILE[:TEMPLATE]
+                  instantiate the configuration file FILE
+  --header=FILE[:TEMPLATE]
+                  instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Configuration commands:
+$config_commands
+
+Report bugs to <bug-autoconf@gnu.org>."
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+ac_cs_version="\\
+smartmontools config.status 5.36
+configured by $0, generated by GNU Autoconf 2.59,
+  with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\"
+
+Copyright (C) 2003 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+srcdir=$srcdir
+INSTALL="$INSTALL"
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+# If no file are specified by the user, then we need to provide default
+# value.  By we need to know if files were specified by the user.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=*)
+    ac_option=`expr "x$1" : 'x\([^=]*\)='`
+    ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  -*)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  *) # This is not an option, so the user has probably given explicit
+     # arguments.
+     ac_option=$1
+     ac_need_defaults=false;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --vers* | -V )
+    echo "$ac_cs_version"; exit 0 ;;
+  --he | --h)
+    # Conflict between --help and --header
+    { { echo "$as_me:$LINENO: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: ambiguous option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; };;
+  --help | --hel | -h )
+    echo "$ac_cs_usage"; exit 0 ;;
+  --debug | --d* | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    CONFIG_FILES="$CONFIG_FILES $ac_optarg"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg"
+    ac_need_defaults=false;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&5
+echo "$as_me: error: unrecognized option: $1
+Try \`$0 --help' for more information." >&2;}
+   { (exit 1); exit 1; }; } ;;
+
+  *) ac_config_targets="$ac_config_targets $1" ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+if \$ac_cs_recheck; then
+  echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6
+  exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+fi
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+#
+# INIT-COMMANDS section.
+#
+
+AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"
+
+_ACEOF
+
+
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_config_target in $ac_config_targets
+do
+  case "$ac_config_target" in
+  # Handling of arguments.
+  "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
+  "examplescripts/Makefile" ) CONFIG_FILES="$CONFIG_FILES examplescripts/Makefile" ;;
+  "depfiles" ) CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;;
+  "config.h" ) CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+  *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5
+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}
+   { (exit 1); exit 1; }; };;
+  esac
+done
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+  test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason to put it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Create a temporary directory, and hook for its removal unless debugging.
+$debug ||
+{
+  trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0
+  trap '{ (exit 1); exit 1; }' 1 2 13 15
+}
+
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` &&
+  test -n "$tmp" && test -d "$tmp"
+}  ||
+{
+  tmp=./confstat$$-$RANDOM
+  (umask 077 && mkdir $tmp)
+} ||
+{
+   echo "$me: cannot create a temporary directory in ." >&2
+   { (exit 1); exit 1; }
+}
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<_ACEOF
+
+#
+# CONFIG_FILES section.
+#
+
+# No need to generate the scripts if there are no CONFIG_FILES.
+# This happens for instance when ./config.status config.h
+if test -n "\$CONFIG_FILES"; then
+  # Protect against being on the right side of a sed subst in config.status.
+  sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g;
+   s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF
+s,@SHELL@,$SHELL,;t t
+s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t
+s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t
+s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t
+s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t
+s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t
+s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t
+s,@exec_prefix@,$exec_prefix,;t t
+s,@prefix@,$prefix,;t t
+s,@program_transform_name@,$program_transform_name,;t t
+s,@bindir@,$bindir,;t t
+s,@sbindir@,$sbindir,;t t
+s,@libexecdir@,$libexecdir,;t t
+s,@datadir@,$datadir,;t t
+s,@sysconfdir@,$sysconfdir,;t t
+s,@sharedstatedir@,$sharedstatedir,;t t
+s,@localstatedir@,$localstatedir,;t t
+s,@libdir@,$libdir,;t t
+s,@includedir@,$includedir,;t t
+s,@oldincludedir@,$oldincludedir,;t t
+s,@infodir@,$infodir,;t t
+s,@mandir@,$mandir,;t t
+s,@build_alias@,$build_alias,;t t
+s,@host_alias@,$host_alias,;t t
+s,@target_alias@,$target_alias,;t t
+s,@DEFS@,$DEFS,;t t
+s,@ECHO_C@,$ECHO_C,;t t
+s,@ECHO_N@,$ECHO_N,;t t
+s,@ECHO_T@,$ECHO_T,;t t
+s,@LIBS@,$LIBS,;t t
+s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t
+s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t
+s,@INSTALL_DATA@,$INSTALL_DATA,;t t
+s,@CYGPATH_W@,$CYGPATH_W,;t t
+s,@PACKAGE@,$PACKAGE,;t t
+s,@VERSION@,$VERSION,;t t
+s,@ACLOCAL@,$ACLOCAL,;t t
+s,@AUTOCONF@,$AUTOCONF,;t t
+s,@AUTOMAKE@,$AUTOMAKE,;t t
+s,@AUTOHEADER@,$AUTOHEADER,;t t
+s,@MAKEINFO@,$MAKEINFO,;t t
+s,@install_sh@,$install_sh,;t t
+s,@STRIP@,$STRIP,;t t
+s,@ac_ct_STRIP@,$ac_ct_STRIP,;t t
+s,@INSTALL_STRIP_PROGRAM@,$INSTALL_STRIP_PROGRAM,;t t
+s,@mkdir_p@,$mkdir_p,;t t
+s,@AWK@,$AWK,;t t
+s,@SET_MAKE@,$SET_MAKE,;t t
+s,@am__leading_dot@,$am__leading_dot,;t t
+s,@AMTAR@,$AMTAR,;t t
+s,@am__tar@,$am__tar,;t t
+s,@am__untar@,$am__untar,;t t
+s,@MAINTAINER_MODE_TRUE@,$MAINTAINER_MODE_TRUE,;t t
+s,@MAINTAINER_MODE_FALSE@,$MAINTAINER_MODE_FALSE,;t t
+s,@MAINT@,$MAINT,;t t
+s,@CC@,$CC,;t t
+s,@CFLAGS@,$CFLAGS,;t t
+s,@LDFLAGS@,$LDFLAGS,;t t
+s,@CPPFLAGS@,$CPPFLAGS,;t t
+s,@ac_ct_CC@,$ac_ct_CC,;t t
+s,@EXEEXT@,$EXEEXT,;t t
+s,@OBJEXT@,$OBJEXT,;t t
+s,@DEPDIR@,$DEPDIR,;t t
+s,@am__include@,$am__include,;t t
+s,@am__quote@,$am__quote,;t t
+s,@AMDEP_TRUE@,$AMDEP_TRUE,;t t
+s,@AMDEP_FALSE@,$AMDEP_FALSE,;t t
+s,@AMDEPBACKSLASH@,$AMDEPBACKSLASH,;t t
+s,@CCDEPMODE@,$CCDEPMODE,;t t
+s,@am__fastdepCC_TRUE@,$am__fastdepCC_TRUE,;t t
+s,@am__fastdepCC_FALSE@,$am__fastdepCC_FALSE,;t t
+s,@CCAS@,$CCAS,;t t
+s,@CCASFLAGS@,$CCASFLAGS,;t t
+s,@build@,$build,;t t
+s,@build_cpu@,$build_cpu,;t t
+s,@build_vendor@,$build_vendor,;t t
+s,@build_os@,$build_os,;t t
+s,@host@,$host,;t t
+s,@host_cpu@,$host_cpu,;t t
+s,@host_vendor@,$host_vendor,;t t
+s,@host_os@,$host_os,;t t
+s,@CPP@,$CPP,;t t
+s,@EGREP@,$EGREP,;t t
+s,@libc_have_working_snprintf@,$libc_have_working_snprintf,;t t
+s,@gcc_have_attr_packed@,$gcc_have_attr_packed,;t t
+s,@ASFLAGS@,$ASFLAGS,;t t
+s,@exampledir@,$exampledir,;t t
+s,@initddir@,$initddir,;t t
+s,@docdir@,$docdir,;t t
+s,@smartd_suffix@,$smartd_suffix,;t t
+s,@SMARTD_SUFFIX_TRUE@,$SMARTD_SUFFIX_TRUE,;t t
+s,@SMARTD_SUFFIX_FALSE@,$SMARTD_SUFFIX_FALSE,;t t
+s,@releaseversion@,$releaseversion,;t t
+s,@smartmontools_release_date@,$smartmontools_release_date,;t t
+s,@smartmontools_release_time@,$smartmontools_release_time,;t t
+s,@os_deps@,$os_deps,;t t
+s,@os_libs@,$os_libs,;t t
+s,@OS_DARWIN_TRUE@,$OS_DARWIN_TRUE,;t t
+s,@OS_DARWIN_FALSE@,$OS_DARWIN_FALSE,;t t
+s,@OS_SOLARIS_TRUE@,$OS_SOLARIS_TRUE,;t t
+s,@OS_SOLARIS_FALSE@,$OS_SOLARIS_FALSE,;t t
+s,@OS_WIN32_MINGW_TRUE@,$OS_WIN32_MINGW_TRUE,;t t
+s,@OS_WIN32_MINGW_FALSE@,$OS_WIN32_MINGW_FALSE,;t t
+s,@LIBOBJS@,$LIBOBJS,;t t
+s,@LTLIBOBJS@,$LTLIBOBJS,;t t
+CEOF
+
+_ACEOF
+
+  cat >>$CONFIG_STATUS <<\_ACEOF
+  # Split the substitutions into bite-sized pieces for seds with
+  # small command number limits, like on Digital OSF/1 and HP-UX.
+  ac_max_sed_lines=48
+  ac_sed_frag=1 # Number of current file.
+  ac_beg=1 # First line for current file.
+  ac_end=$ac_max_sed_lines # Line after last line for current file.
+  ac_more_lines=:
+  ac_sed_cmds=
+  while $ac_more_lines; do
+    if test $ac_beg -gt 1; then
+      sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    else
+      sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag
+    fi
+    if test ! -s $tmp/subs.frag; then
+      ac_more_lines=false
+    else
+      # The purpose of the label and of the branching condition is to
+      # speed up the sed processing (if there are no `@' at all, there
+      # is no need to browse any of the substitutions).
+      # These are the two extra sed commands mentioned above.
+      (echo ':t
+  /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed
+      if test -z "$ac_sed_cmds"; then
+       ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed"
+      else
+       ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed"
+      fi
+      ac_sed_frag=`expr $ac_sed_frag + 1`
+      ac_beg=$ac_end
+      ac_end=`expr $ac_end + $ac_max_sed_lines`
+    fi
+  done
+  if test -z "$ac_sed_cmds"; then
+    ac_sed_cmds=cat
+  fi
+fi # test -n "$CONFIG_FILES"
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+       cat >$tmp/stdin
+       ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+       ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+       ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories.
+  ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+  case $INSTALL in
+  [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;;
+  *) ac_INSTALL=$ac_top_builddir$INSTALL ;;
+  esac
+
+  if test x"$ac_file" != x-; then
+    { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+    rm -f "$ac_file"
+  fi
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    configure_input=
+  else
+    configure_input="$ac_file.  "
+  fi
+  configure_input=$configure_input"Generated from `echo $ac_file_in |
+                                    sed 's,.*/,,'` by configure."
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+        # Absolute (can't be DOS-style, as IFS=:)
+        test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+        echo "$f";;
+      *) # Relative
+        if test -f "$f"; then
+          # Build tree
+          echo "$f"
+        elif test -f "$srcdir/$f"; then
+          # Source tree
+          echo "$srcdir/$f"
+        else
+          # /dev/null tree
+          { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+        fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF
+  sed "$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s,@configure_input@,$configure_input,;t t
+s,@srcdir@,$ac_srcdir,;t t
+s,@abs_srcdir@,$ac_abs_srcdir,;t t
+s,@top_srcdir@,$ac_top_srcdir,;t t
+s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t
+s,@builddir@,$ac_builddir,;t t
+s,@abs_builddir@,$ac_abs_builddir,;t t
+s,@top_builddir@,$ac_top_builddir,;t t
+s,@abs_top_builddir@,$ac_abs_top_builddir,;t t
+s,@INSTALL@,$ac_INSTALL,;t t
+" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out
+  rm -f $tmp/stdin
+  if test x"$ac_file" != x-; then
+    mv $tmp/out $ac_file
+  else
+    cat $tmp/out
+    rm -f $tmp/out
+  fi
+
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_HEADER section.
+#
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s,^\([   ]*\)#\([        ]*define[       ][      ]*\)'
+ac_dB='[        ].*$,\1#\2'
+ac_dC=' '
+ac_dD=',;t'
+# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_uA='s,^\([   ]*\)#\([        ]*\)undef\([    ][      ]*\)'
+ac_uB='$,\1#\2define\3'
+ac_uC=' '
+ac_uD=',;t'
+
+for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue
+  # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+  case $ac_file in
+  - | *:- | *:-:* ) # input from stdin
+       cat >$tmp/stdin
+       ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+       ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'`
+       ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;;
+  * )   ac_file_in=$ac_file.in ;;
+  esac
+
+  test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5
+echo "$as_me: creating $ac_file" >&6;}
+
+  # First look for the input files in the build tree, otherwise in the
+  # src tree.
+  ac_file_inputs=`IFS=:
+    for f in $ac_file_in; do
+      case $f in
+      -) echo $tmp/stdin ;;
+      [\\/$]*)
+        # Absolute (can't be DOS-style, as IFS=:)
+        test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+        # Do quote $f, to prevent DOS paths from being IFS'd.
+        echo "$f";;
+      *) # Relative
+        if test -f "$f"; then
+          # Build tree
+          echo "$f"
+        elif test -f "$srcdir/$f"; then
+          # Source tree
+          echo "$srcdir/$f"
+        else
+          # /dev/null tree
+          { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5
+echo "$as_me: error: cannot find input file: $f" >&2;}
+   { (exit 1); exit 1; }; }
+        fi;;
+      esac
+    done` || { (exit 1); exit 1; }
+  # Remove the trailing spaces.
+  sed 's/[      ]*$//' $ac_file_inputs >$tmp/in
+
+_ACEOF
+
+# Transform confdefs.h into two sed scripts, `conftest.defines' and
+# `conftest.undefs', that substitutes the proper values into
+# config.h.in to produce config.h.  The first handles `#define'
+# templates, and the second `#undef' templates.
+# And first: Protect against being on the right side of a sed subst in
+# config.status.  Protect against being in an unquoted here document
+# in config.status.
+rm -f conftest.defines conftest.undefs
+# Using a here document instead of a string reduces the quoting nightmare.
+# Putting comments in sed scripts is not portable.
+#
+# `end' is used to avoid that the second main sed command (meant for
+# 0-ary CPP macros) applies to n-ary macro definitions.
+# See the Autoconf documentation for `clear'.
+cat >confdef2sed.sed <<\_ACEOF
+s/[\\&,]/\\&/g
+s,[\\$`],\\&,g
+t clear
+: clear
+s,^[    ]*#[    ]*define[       ][      ]*\([^  (][^    (]*\)\(([^)]*)\)[       ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp
+t end
+s,^[    ]*#[    ]*define[       ][      ]*\([^  ][^     ]*\)[   ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp
+: end
+_ACEOF
+# If some macros were called several times there might be several times
+# the same #defines, which is useless.  Nevertheless, we may not want to
+# sort them, since we want the *last* AC-DEFINE to be honored.
+uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines
+sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs
+rm -f confdef2sed.sed
+
+# This sed command replaces #undef with comments.  This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >>conftest.undefs <<\_ACEOF
+s,^[    ]*#[    ]*undef[        ][      ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+_ACEOF
+
+# Break up conftest.defines because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS
+echo '  if grep "^[     ]*#[    ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS
+echo '  # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS
+echo '  :' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.defines >/dev/null
+do
+  # Write a limited-size here document to $tmp/defines.sed.
+  echo '  cat >$tmp/defines.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#define' lines.
+  echo '/^[     ]*#[    ]*define/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/defines.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail
+  rm -f conftest.defines
+  mv conftest.tail conftest.defines
+done
+rm -f conftest.defines
+echo '  fi # grep' >>$CONFIG_STATUS
+echo >>$CONFIG_STATUS
+
+# Break up conftest.undefs because some shells have a limit on the size
+# of here documents, and old seds have small limits too (100 cmds).
+echo '  # Handle all the #undef templates' >>$CONFIG_STATUS
+rm -f conftest.tail
+while grep . conftest.undefs >/dev/null
+do
+  # Write a limited-size here document to $tmp/undefs.sed.
+  echo '  cat >$tmp/undefs.sed <<CEOF' >>$CONFIG_STATUS
+  # Speed up: don't consider the non `#undef'
+  echo '/^[     ]*#[    ]*undef/!b' >>$CONFIG_STATUS
+  # Work around the forget-to-reset-the-flag bug.
+  echo 't clr' >>$CONFIG_STATUS
+  echo ': clr' >>$CONFIG_STATUS
+  sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS
+  echo 'CEOF
+  sed -f $tmp/undefs.sed $tmp/in >$tmp/out
+  rm -f $tmp/in
+  mv $tmp/out $tmp/in
+' >>$CONFIG_STATUS
+  sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail
+  rm -f conftest.undefs
+  mv conftest.tail conftest.undefs
+done
+rm -f conftest.undefs
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+  # Let's still pretend it is `configure' which instantiates (i.e., don't
+  # use $as_me), people would be surprised to read:
+  #    /* config.h.  Generated by config.status.  */
+  if test x"$ac_file" = x-; then
+    echo "/* Generated by configure.  */" >$tmp/config.h
+  else
+    echo "/* $ac_file.  Generated by configure.  */" >$tmp/config.h
+  fi
+  cat $tmp/in >>$tmp/config.h
+  rm -f $tmp/in
+  if test x"$ac_file" != x-; then
+    if diff $ac_file $tmp/config.h >/dev/null 2>&1; then
+      { echo "$as_me:$LINENO: $ac_file is unchanged" >&5
+echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      ac_dir=`(dirname "$ac_file") 2>/dev/null ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+      { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+      rm -f $ac_file
+      mv $tmp/config.h $ac_file
+    fi
+  else
+    cat $tmp/config.h
+    rm -f $tmp/config.h
+  fi
+# Compute $ac_file's index in $config_headers.
+_am_stamp_count=1
+for _am_header in $config_headers :; do
+  case $_am_header in
+    $ac_file | $ac_file:* )
+      break ;;
+    * )
+      _am_stamp_count=`expr $_am_stamp_count + 1` ;;
+  esac
+done
+echo "timestamp for $ac_file" >`(dirname $ac_file) 2>/dev/null ||
+$as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X$ac_file : 'X\(//\)[^/]' \| \
+        X$ac_file : 'X\(//\)$' \| \
+        X$ac_file : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X$ac_file |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`/stamp-h$_am_stamp_count
+done
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+#
+# CONFIG_COMMANDS section.
+#
+for ac_file in : $CONFIG_COMMANDS; do test "x$ac_file" = x: && continue
+  ac_dest=`echo "$ac_file" | sed 's,:.*,,'`
+  ac_source=`echo "$ac_file" | sed 's,[^:]*:,,'`
+  ac_dir=`(dirname "$ac_dest") 2>/dev/null ||
+$as_expr X"$ac_dest" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_dest" : 'X\(//\)[^/]' \| \
+        X"$ac_dest" : 'X\(//\)$' \| \
+        X"$ac_dest" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$ac_dest" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+  { if $as_mkdir_p; then
+    mkdir -p "$ac_dir"
+  else
+    as_dir="$ac_dir"
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5
+echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+  ac_builddir=.
+
+if test "$ac_dir" != .; then
+  ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`
+  # A "../" for each directory in $ac_dir_suffix.
+  ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'`
+else
+  ac_dir_suffix= ac_top_builddir=
+fi
+
+case $srcdir in
+  .)  # No --srcdir option.  We are building in place.
+    ac_srcdir=.
+    if test -z "$ac_top_builddir"; then
+       ac_top_srcdir=.
+    else
+       ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'`
+    fi ;;
+  [\\/]* | ?:[\\/]* )  # Absolute path.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir ;;
+  *) # Relative path.
+    ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_builddir$srcdir ;;
+esac
+
+# Do not use `cd foo && pwd` to compute absolute paths, because
+# the directories may not exist.
+case `pwd` in
+.) ac_abs_builddir="$ac_dir";;
+*)
+  case "$ac_dir" in
+  .) ac_abs_builddir=`pwd`;;
+  [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";;
+  *) ac_abs_builddir=`pwd`/"$ac_dir";;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_builddir=${ac_top_builddir}.;;
+*)
+  case ${ac_top_builddir}. in
+  .) ac_abs_top_builddir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;;
+  *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_srcdir=$ac_srcdir;;
+*)
+  case $ac_srcdir in
+  .) ac_abs_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;;
+  *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;;
+  esac;;
+esac
+case $ac_abs_builddir in
+.) ac_abs_top_srcdir=$ac_top_srcdir;;
+*)
+  case $ac_top_srcdir in
+  .) ac_abs_top_srcdir=$ac_abs_builddir;;
+  [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;;
+  *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;;
+  esac;;
+esac
+
+
+  { echo "$as_me:$LINENO: executing $ac_dest commands" >&5
+echo "$as_me: executing $ac_dest commands" >&6;}
+  case $ac_dest in
+    depfiles ) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do
+  # Strip MF so we end up with the name of the file.
+  mf=`echo "$mf" | sed -e 's/:.*$//'`
+  # Check whether this is an Automake generated Makefile or not.
+  # We used to match only the files named `Makefile.in', but
+  # some people rename them; so instead we look at the file content.
+  # Grep'ing the first line is not enough: some people post-process
+  # each Makefile.in and add a new line on top of each file to say so.
+  # So let's grep whole file.
+  if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
+    dirpart=`(dirname "$mf") 2>/dev/null ||
+$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$mf" : 'X\(//\)[^/]' \| \
+        X"$mf" : 'X\(//\)$' \| \
+        X"$mf" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$mf" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+  else
+    continue
+  fi
+  # Extract the definition of DEPDIR, am__include, and am__quote
+  # from the Makefile without running `make'.
+  DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
+  test -z "$DEPDIR" && continue
+  am__include=`sed -n 's/^am__include = //p' < "$mf"`
+  test -z "am__include" && continue
+  am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
+  # When using ansi2knr, U may be empty or an underscore; expand it
+  U=`sed -n 's/^U = //p' < "$mf"`
+  # Find all dependency output files, they are included files with
+  # $(DEPDIR) in their names.  We invoke sed twice because it is the
+  # simplest approach to changing $(DEPDIR) to its actual value in the
+  # expansion.
+  for file in `sed -n "
+    s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
+       sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+    # Make sure the directory exists.
+    test -f "$dirpart/$file" && continue
+    fdir=`(dirname "$file") 2>/dev/null ||
+$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$file" : 'X\(//\)[^/]' \| \
+        X"$file" : 'X\(//\)$' \| \
+        X"$file" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+    { if $as_mkdir_p; then
+    mkdir -p $dirpart/$fdir
+  else
+    as_dir=$dirpart/$fdir
+    as_dirs=
+    while test ! -d "$as_dir"; do
+      as_dirs="$as_dir $as_dirs"
+      as_dir=`(dirname "$as_dir") 2>/dev/null ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| \
+        .     : '\(.\)' 2>/dev/null ||
+echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; }
+         /^X\(\/\/\)[^/].*/{ s//\1/; q; }
+         /^X\(\/\/\)$/{ s//\1/; q; }
+         /^X\(\/\).*/{ s//\1/; q; }
+         s/.*/./; q'`
+    done
+    test ! -n "$as_dirs" || mkdir $as_dirs
+  fi || { { echo "$as_me:$LINENO: error: cannot create directory $dirpart/$fdir" >&5
+echo "$as_me: error: cannot create directory $dirpart/$fdir" >&2;}
+   { (exit 1); exit 1; }; }; }
+
+    # echo "creating $dirpart/$file"
+    echo '# dummy' > "$dirpart/$file"
+  done
+done
+ ;;
+  esac
+done
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF
+
+{ (exit 0); exit 0; }
+_ACEOF
+chmod +x $CONFIG_STATUS
+ac_clean_files=$ac_clean_files_save
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || { (exit 1); exit 1; }
+fi
+
+echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5
+echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'`
+if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.make <<\_ACEOF
+all:
+       @echo 'ac_maketemp="$(MAKE)"'
+_ACEOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+  eval ac_cv_prog_make_${ac_make}_set=yes
+else
+  eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftest.make
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+  SET_MAKE=
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+  SET_MAKE="MAKE=${MAKE-make}"
+fi
+
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..e427448
--- /dev/null
@@ -0,0 +1,220 @@
+#
+# $Id: configure.in,v 1.114 2006/04/12 17:39:32 ballen4705 Exp $
+#
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.50)
+AC_INIT(smartmontools, 5.36, smartmontools-support@lists.sourceforge.net)
+AC_CONFIG_SRCDIR(smartctl.c)
+
+smartmontools_configure_date=`date -u +"%Y/%m/%d %T %Z"`
+smartmontools_cvs_tag=`echo '$Id: configure.in,v 1.114 2006/04/12 17:39:32 ballen4705 Exp $'`
+smartmontools_release_date=2006/04/12
+smartmontools_release_time="17:39:01 UTC"
+
+AC_DEFINE_UNQUOTED(SMARTMONTOOLS_CONFIGURE_ARGS, "$ac_configure_args",            [smartmontools Configure Arguments])
+AC_DEFINE_UNQUOTED(SMARTMONTOOLS_CONFIGURE_DATE, "$smartmontools_configure_date", [smartmontools Configure Date])
+AC_DEFINE_UNQUOTED(SMARTMONTOOLS_RELEASE_DATE,   "$smartmontools_release_date",   [smartmontools Release Date])
+AC_DEFINE_UNQUOTED(SMARTMONTOOLS_RELEASE_TIME,   "$smartmontools_release_time",   [smartmontools Release Time])
+AC_DEFINE_UNQUOTED(CONFIG_H_CVSID,               "$smartmontools_cvs_tag",        [smartmontools CVS Tag])
+AC_DEFINE_UNQUOTED(PACKAGE_HOMEPAGE,             "http://smartmontools.sourceforge.net/", [smartmontools Home Page])
+
+AM_CONFIG_HEADER(config.h)
+
+AM_INIT_AUTOMAKE
+
+AM_MAINTAINER_MODE
+
+AC_LANG_C
+dnl Checks for programs.
+AC_PROG_CC
+AM_PROG_AS
+AC_PROG_INSTALL
+
+AC_CANONICAL_HOST
+dnl Set flags which may affect AC_CHECK_*.
+case "${host}" in
+       *-*-mingw*)
+               CPPFLAGS="$CPPFLAGS -mno-cygwin"
+               LDFLAGS="$LDFLAGS -mno-cygwin"
+               CPPFLAGS="$CPPFLAGS -idirafter ${srcdir}/posix -idirafter ${srcdir}/os_win32"
+esac
+
+dnl Checks for libraries.needed for gethostbyname (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), , )
+
+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.
+AC_CHECK_HEADERS([inttypes.h])         dnl C99, UNIX98, solaris 2.6+
+AC_CHECK_HEADERS([stdint.h])           dnl C99
+AC_CHECK_HEADERS([sys/inttypes.h])     dnl pre-UNIX98
+AC_CHECK_HEADERS([sys/int_types.h])    dnl pre-UNIX98, solaris 2.6+
+dnl Check for FreeBSD twe include files...currently missing on 5.2, but should be there
+AC_CHECK_HEADERS([sys/tweio.h])
+AC_CHECK_HEADERS([sys/twereg.h])
+dnl Check for FreeBSD twa include files...
+AC_CHECK_HEADERS([sys/tw_osl_ioctl.h])
+
+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([getdomainname])
+AC_CHECK_FUNCS([gethostname])
+AC_CHECK_FUNCS([gethostbyname])
+AC_CHECK_FUNCS([sigset])
+AC_CHECK_FUNCS([strtoull])
+AC_CHECK_FUNCS([uname])
+
+# Check whether snprintf appends null char and returns expected length on overflow
+AH_TEMPLATE(HAVE_WORKING_SNPRINTF, [Define to 1 if the `snprintf' function is sane])
+AC_MSG_CHECKING([for working snprintf])
+AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include <stdio.h>]], [[ char buf[]="ABCDEFGHI";
+               int i=snprintf(buf,8,"12345678"); return !(!buf[7] && i==8); ]])],
+              [libc_have_working_snprintf=yes], [libc_have_working_snprintf=no])
+AC_SUBST(libc_have_working_snprintf)
+if test "$libc_have_working_snprintf" = "yes"; then
+  AC_DEFINE(HAVE_WORKING_SNPRINTF)
+fi
+AC_MSG_RESULT([$libc_have_working_snprintf])
+
+# check for __attribute__((packed))
+AH_TEMPLATE(HAVE_ATTR_PACKED, [Define to 1 if C compiler supports __attribute__((packed))])
+AC_MSG_CHECKING([whether C compiler supports __attribute__((packed))])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM(, [[struct a { int b; } __attribute__((packed));]])],
+                  [gcc_have_attr_packed=yes], [gcc_have_attr_packed=no])
+AC_SUBST(gcc_have_attr_packed)
+if test "$gcc_have_attr_packed" = "yes"; then
+  AC_DEFINE(HAVE_ATTR_PACKED)
+fi
+AC_MSG_RESULT([$gcc_have_attr_packed])
+
+AC_SUBST(CPPFLAGS)
+AC_SUBST(LDFLAGS)
+AC_SUBST(ASFLAGS)
+
+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)
+
+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(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)
+
+if test "$prefix" = "NONE"; then
+    dnl no prefix and no mandir, so use ${prefix}/share/man as default
+    if test "$mandir" = '${prefix}/man'; then
+       AC_SUBST([mandir], ['${prefix}/share/man'])
+    fi
+fi
+
+AC_SUBST(releaseversion,['${PACKAGE}-${VERSION}'])
+AC_SUBST(smartmontools_release_date)
+AC_SUBST(smartmontools_release_time)
+
+dnl if OS not recognized, then use the os_generic modules
+case "${host}" in
+       *-*-linux-gnu*) 
+               AC_SUBST([os_deps], ['os_linux.o']) 
+               AC_SUBST([os_libs], ['']) ;;
+       *-*-linux*)
+               AC_SUBST([os_deps], ['os_linux.o']) 
+               AC_SUBST([os_libs], ['']) ;;
+       *-*-freebsd*)
+               AC_SUBST([os_deps], ['os_freebsd.o']) 
+               AC_SUBST([os_libs], ['-lcam']);;
+       sparc-*-solaris*) 
+               AC_DEFINE_UNQUOTED(DEFAULT_MAILER, "mailx", [use mailx as default mailer])
+               AC_DEFINE_UNQUOTED(NEED_SOLARIS_ATA_CODE, "os_solaris_ata.s", [need assembly code os_solaris_ata.s])
+               AC_SUBST([os_deps], ['os_solaris.o os_solaris_ata.o']) 
+               AC_SUBST([os_libs], ['']) ;;
+       *-pc-solaris*) 
+               AC_DEFINE_UNQUOTED(DEFAULT_MAILER, "mailx", [use mailx as default mailer]) 
+               AC_SUBST([os_deps], ['os_solaris.o']) 
+               AC_SUBST([os_libs], ['']) ;;
+       *-*-netbsd*)
+               AC_SUBST([os_deps], ['os_netbsd.o']) 
+               AC_SUBST([os_libs], ['-lutil']) ;;
+       *-*-openbsd*)
+               AC_SUBST([os_deps], ['os_openbsd.o'])
+               AC_SUBST([os_libs], ['-lutil']) ;;
+       *-*-cygwin*)
+               AC_SUBST([os_deps], ['os_win32.o'])
+               AC_SUBST([os_libs], ['']) ;;
+       *-*-mingw*)
+               AC_SUBST([os_deps], ['os_win32.o'])
+               AC_SUBST([os_libs], ['']) ;;
+       *-*-darwin*)
+               AC_SUBST([os_deps], ['os_darwin.o'])
+               AC_SUBST([os_libs], ['-framework CoreFoundation -framework IOKit']) ;;
+       *)
+               AC_SUBST([os_deps], ['os_generic.o']) 
+               AC_SUBST([os_libs], ['']) ;;
+esac
+
+# Define symbols for optional functions in OS specific module
+case "${os_deps}" in
+  os_win32*)
+    AC_DEFINE(HAVE_ATA_IDENTIFY_IS_CACHED, 1, [Define to 1 if you have the `ata_identify_is_cached' function in os_*.c.]) ;;
+esac
+case "${os_deps}" in
+  os_win32*)
+    AC_DEFINE(HAVE_GET_OS_VERSION_STR, 1, [Define to 1 if you have the `get_os_version_str' function in os_*.c.]) ;;
+esac
+
+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])
+
+dnl Add -Wall and -W if using gcc and its not already specified.
+if test "x$GCC" = "xyes"; then
+  if test -z "`echo "$CFLAGS" | grep "\-Wall" 2> /dev/null`" ; then
+      CFLAGS="$CFLAGS -Wall"
+  fi
+# In the next line, do NOT delete the 2 spaces inside double quotes.
+  if test -z "`echo "$CFLAGS " | grep "\-W " 2> /dev/null`" ; then
+      CFLAGS="$CFLAGS -W"
+  fi
+  case "${host}" in
+    *-*-mingw*)
+      # MinGW uses MSVCRT.DLL which uses printf format "%I64d" and not "%lld" for int64_t
+      CFLAGS="$CFLAGS -Wno-format";;
+  esac
+else
+ dnl We are NOT using gcc, so enable host-specific compiler flags
+ case "${host}" in
+       *-*-solaris*) 
+          dnl set CFLAGS for Solaris C compiler
+          if test -z "`echo "$CFLAGS" | grep "\-xmemalign" 2> /dev/null`" ; then
+            dnl we have to tell the compilers about packed ATA structures
+            CFLAGS="-xmemalign=1i $CFLAGS"
+          fi
+          if test -z "`echo "$CFLAGS" | grep "\-xCC" 2> /dev/null`" ; then
+            dnl we have to tell the compiler to ignore C++ style comments
+            CFLAGS="-xCC $CFLAGS"
+          fi
+          if test -z "`echo "$CFLAGS" | grep "\-xO" 2> /dev/null`" ; then
+            dnl turn on optimization if user has not explicitly set its value
+            CFLAGS="-xO2 $CFLAGS"
+          fi
+ esac
+fi
+
+AC_DEFINE_UNQUOTED(SMARTMONTOOLS_BUILD_HOST,     "${host}",                       [smartmontools Build Host])
+
+AC_SUBST(CFLAGS)
+
+AC_OUTPUT(Makefile examplescripts/Makefile)
+AC_PROG_MAKE_SET
diff --git a/depcomp b/depcomp
new file mode 100755 (executable)
index 0000000..11e2d3b
--- /dev/null
+++ b/depcomp
@@ -0,0 +1,522 @@
+#! /bin/sh
+# depcomp - compile a program generating dependencies as side-effects
+
+scriptversion=2004-05-31.23
+
+# Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
+
+case $1 in
+  '')
+     echo "$0: No command.  Try \`$0 --help' for more information." 1>&2
+     exit 1;
+     ;;
+  -h | --h*)
+    cat <<\EOF
+Usage: depcomp [--help] [--version] PROGRAM [ARGS]
+
+Run PROGRAMS ARGS to compile a file, generating dependencies
+as side-effects.
+
+Environment variables:
+  depmode     Dependency tracking mode.
+  source      Source file read by `PROGRAMS ARGS'.
+  object      Object file output by `PROGRAMS ARGS'.
+  DEPDIR      directory where to store dependencies.
+  depfile     Dependency file to output.
+  tmpdepfile  Temporary file to use when outputing dependencies.
+  libtool     Whether libtool is used (yes/no).
+
+Report bugs to <bug-automake@gnu.org>.
+EOF
+    exit 0
+    ;;
+  -v | --v*)
+    echo "depcomp $scriptversion"
+    exit 0
+    ;;
+esac
+
+if test -z "$depmode" || test -z "$source" || test -z "$object"; then
+  echo "depcomp: Variables source, object and depmode must be set" 1>&2
+  exit 1
+fi
+
+# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
+depfile=${depfile-`echo "$object" |
+  sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
+tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
+
+rm -f "$tmpdepfile"
+
+# Some modes work just like other modes, but use different flags.  We
+# parameterize here, but still list the modes in the big case below,
+# to make depend.m4 easier to write.  Note that we *cannot* use a case
+# here, because this file can only contain one case statement.
+if test "$depmode" = hp; then
+  # HP compiler uses -M and no extra arg.
+  gccflag=-M
+  depmode=gcc
+fi
+
+if test "$depmode" = dashXmstdout; then
+   # This is just like dashmstdout with a different argument.
+   dashmflag=-xM
+   depmode=dashmstdout
+fi
+
+case "$depmode" in
+gcc3)
+## gcc 3 implements dependency tracking that does exactly what
+## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like
+## it if -MD -MP comes after the -MF stuff.  Hmm.
+  "$@" -MT "$object" -MD -MP -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  mv "$tmpdepfile" "$depfile"
+  ;;
+
+gcc)
+## There are various ways to get dependency output from gcc.  Here's
+## why we pick this rather obscure method:
+## - Don't want to use -MD because we'd like the dependencies to end
+##   up in a subdir.  Having to rename by hand is ugly.
+##   (We might end up doing this anyway to support other compilers.)
+## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
+##   -MM, not -M (despite what the docs say).
+## - Using -M directly means running the compiler twice (even worse
+##   than renaming).
+  if test -z "$gccflag"; then
+    gccflag=-MD,
+  fi
+  "$@" -Wp,"$gccflag$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+## The second -e expression handles DOS-style file names with drive letters.
+  sed -e 's/^[^:]*: / /' \
+      -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
+## This next piece of magic avoids the `deleted header file' problem.
+## The problem is that when a header file which appears in a .P file
+## is deleted, the dependency causes make to die (because there is
+## typically no way to rebuild the header).  We avoid this by adding
+## dummy dependencies for each header file.  Too bad gcc doesn't do
+## this for us directly.
+  tr ' ' '
+' < "$tmpdepfile" |
+## Some versions of gcc put a space before the `:'.  On the theory
+## that the space means something, we add a space to the output as
+## well.
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+hp)
+  # This case exists only to let depend.m4 do its work.  It works by
+  # looking at the text of this script.  This case will never be run,
+  # since it is checked for above.
+  exit 1
+  ;;
+
+sgi)
+  if test "$libtool" = yes; then
+    "$@" "-Wp,-MDupdate,$tmpdepfile"
+  else
+    "$@" -MDupdate "$tmpdepfile"
+  fi
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+
+  if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files
+    echo "$object : \\" > "$depfile"
+
+    # Clip off the initial element (the dependent).  Don't try to be
+    # clever and replace this with sed code, as IRIX sed won't handle
+    # lines with more than a fixed number of characters (4096 in
+    # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines;
+    # the IRIX cc adds comments like `#:fec' to the end of the
+    # dependency line.
+    tr ' ' '
+' < "$tmpdepfile" \
+    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
+    tr '
+' ' ' >> $depfile
+    echo >> $depfile
+
+    # The second pass generates a dummy entry for each header file.
+    tr ' ' '
+' < "$tmpdepfile" \
+   | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
+   >> $depfile
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+aix)
+  # The C for AIX Compiler uses -M and outputs the dependencies
+  # in a .u file.  In older versions, this file always lives in the
+  # current directory.  Also, the AIX compiler puts `$object:' at the
+  # start of each line; $object doesn't have directory information.
+  # Version 6 uses the directory in both cases.
+  stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
+  tmpdepfile="$stripped.u"
+  if test "$libtool" = yes; then
+    "$@" -Wc,-M
+  else
+    "$@" -M
+  fi
+  stat=$?
+
+  if test -f "$tmpdepfile"; then :
+  else
+    stripped=`echo "$stripped" | sed 's,^.*/,,'`
+    tmpdepfile="$stripped.u"
+  fi
+
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+
+  if test -f "$tmpdepfile"; then
+    outname="$stripped.o"
+    # Each line is of the form `foo.o: dependent.h'.
+    # Do two passes, one to just change these to
+    # `$object: dependent.h' and one to simply `dependent.h:'.
+    sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
+    sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
+  else
+    # The sourcefile does not contain any dependencies, so just
+    # store a dummy comment line, to avoid errors with the Makefile
+    # "include basename.Plo" scheme.
+    echo "#dummy" > "$depfile"
+  fi
+  rm -f "$tmpdepfile"
+  ;;
+
+icc)
+  # Intel's C compiler understands `-MD -MF file'.  However on
+  #    icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
+  # ICC 7.0 will fill foo.d with something like
+  #    foo.o: sub/foo.c
+  #    foo.o: sub/foo.h
+  # which is wrong.  We want:
+  #    sub/foo.o: sub/foo.c
+  #    sub/foo.o: sub/foo.h
+  #    sub/foo.c:
+  #    sub/foo.h:
+  # ICC 7.1 will output
+  #    foo.o: sub/foo.c sub/foo.h
+  # and will wrap long lines using \ :
+  #    foo.o: sub/foo.c ... \
+  #     sub/foo.h ... \
+  #     ...
+
+  "$@" -MD -MF "$tmpdepfile"
+  stat=$?
+  if test $stat -eq 0; then :
+  else
+    rm -f "$tmpdepfile"
+    exit $stat
+  fi
+  rm -f "$depfile"
+  # Each line is of the form `foo.o: dependent.h',
+  # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
+  # Do two passes, one to just change these to
+  # `$object: dependent.h' and one to simply `dependent.h:'.
+  sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
+  # Some versions of the HPUX 10.20 sed can't process this invocation
+  # correctly.  Breaking it into two sed invocations is a workaround.
+  sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
+    sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+tru64)
+   # The Tru64 compiler uses -MD to generate dependencies as a side
+   # effect.  `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
+   # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
+   # dependencies in `foo.d' instead, so we check for that too.
+   # Subdirectories are respected.
+   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
+   test "x$dir" = "x$object" && dir=
+   base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
+
+   if test "$libtool" = yes; then
+      # Dependencies are output in .lo.d with libtool 1.4.
+      # With libtool 1.5 they are output both in $dir.libs/$base.o.d
+      # and in $dir.libs/$base.o.d and $dir$base.o.d.  We process the
+      # latter, because the former will be cleaned when $dir.libs is
+      # erased.
+      tmpdepfile1="$dir.libs/$base.lo.d"
+      tmpdepfile2="$dir$base.o.d"
+      tmpdepfile3="$dir.libs/$base.d"
+      "$@" -Wc,-MD
+   else
+      tmpdepfile1="$dir$base.o.d"
+      tmpdepfile2="$dir$base.d"
+      tmpdepfile3="$dir$base.d"
+      "$@" -MD
+   fi
+
+   stat=$?
+   if test $stat -eq 0; then :
+   else
+      rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+      exit $stat
+   fi
+
+   if test -f "$tmpdepfile1"; then
+      tmpdepfile="$tmpdepfile1"
+   elif test -f "$tmpdepfile2"; then
+      tmpdepfile="$tmpdepfile2"
+   else
+      tmpdepfile="$tmpdepfile3"
+   fi
+   if test -f "$tmpdepfile"; then
+      sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
+      # That's a tab and a space in the [].
+      sed -e 's,^.*\.[a-z]*:[   ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
+   else
+      echo "#dummy" > "$depfile"
+   fi
+   rm -f "$tmpdepfile"
+   ;;
+
+#nosideeffect)
+  # This comment above is used by automake to tell side-effect
+  # dependency tracking mechanisms from slower ones.
+
+dashmstdout)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout, regardless of -o.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test $1 != '--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove `-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  test -z "$dashmflag" && dashmflag=-M
+  # Require at least two characters before searching for `:'
+  # in the target name.  This is to cope with DOS-style filenames:
+  # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
+  "$@" $dashmflag |
+    sed 's:^[  ]*[^: ][^:][^:]*\:[    ]*:'"$object"'\: :' > "$tmpdepfile"
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  tr ' ' '
+' < "$tmpdepfile" | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+dashXmstdout)
+  # This case only exists to satisfy depend.m4.  It is never actually
+  # run, as this mode is specially recognized in the preamble.
+  exit 1
+  ;;
+
+makedepend)
+  "$@" || exit $?
+  # Remove any Libtool call
+  if test "$libtool" = yes; then
+    while test $1 != '--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+  # X makedepend
+  shift
+  cleared=no
+  for arg in "$@"; do
+    case $cleared in
+    no)
+      set ""; shift
+      cleared=yes ;;
+    esac
+    case "$arg" in
+    -D*|-I*)
+      set fnord "$@" "$arg"; shift ;;
+    # Strip any option that makedepend may not understand.  Remove
+    # the object too, otherwise makedepend will parse it as a source file.
+    -*|$object)
+      ;;
+    *)
+      set fnord "$@" "$arg"; shift ;;
+    esac
+  done
+  obj_suffix="`echo $object | sed 's/^.*\././'`"
+  touch "$tmpdepfile"
+  ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
+  rm -f "$depfile"
+  cat < "$tmpdepfile" > "$depfile"
+  sed '1,2d' "$tmpdepfile" | tr ' ' '
+' | \
+## Some versions of the HPUX 10.20 sed can't process this invocation
+## correctly.  Breaking it into two sed invocations is a workaround.
+    sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile" "$tmpdepfile".bak
+  ;;
+
+cpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout.
+  "$@" || exit $?
+
+  # Remove the call to Libtool.
+  if test "$libtool" = yes; then
+    while test $1 != '--mode=compile'; do
+      shift
+    done
+    shift
+  fi
+
+  # Remove `-o $object'.
+  IFS=" "
+  for arg
+  do
+    case $arg in
+    -o)
+      shift
+      ;;
+    $object)
+      shift
+      ;;
+    *)
+      set fnord "$@" "$arg"
+      shift # fnord
+      shift # $arg
+      ;;
+    esac
+  done
+
+  "$@" -E |
+    sed -n '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
+    sed '$ s: \\$::' > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  cat < "$tmpdepfile" >> "$depfile"
+  sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+msvisualcpp)
+  # Important note: in order to support this mode, a compiler *must*
+  # always write the preprocessed file to stdout, regardless of -o,
+  # because we must use -o when running libtool.
+  "$@" || exit $?
+  IFS=" "
+  for arg
+  do
+    case "$arg" in
+    "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
+       set fnord "$@"
+       shift
+       shift
+       ;;
+    *)
+       set fnord "$@" "$arg"
+       shift
+       shift
+       ;;
+    esac
+  done
+  "$@" -E |
+  sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
+  rm -f "$depfile"
+  echo "$object : \\" > "$depfile"
+  . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::   \1 \\:p' >> "$depfile"
+  echo "       " >> "$depfile"
+  . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
+  rm -f "$tmpdepfile"
+  ;;
+
+none)
+  exec "$@"
+  ;;
+
+*)
+  echo "Unknown depmode $depmode" 1>&2
+  exit 1
+  ;;
+esac
+
+exit 0
+
+# Local Variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/examplescripts/Example1 b/examplescripts/Example1
new file mode 100755 (executable)
index 0000000..9f4726f
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/bash 
+#
+# This is a script from the smartmontools examplescripts/ directory.
+# It can be used as an argument to the -M exec Directive in
+# /etc/smartd.conf, in a line like 
+# -m root@localhost -M exec /path/to/this/file
+#
+# Please see man 8 smartd or man 5 smartd.conf for further
+# information.
+#
+# $Id: Example1,v 1.7 2004/08/29 02:33:17 ballen4705 Exp $
+
+# Save standard input into a temp file
+cat > /root/tempfile
+
+# Echo command line arguments into temp file
+echo "Command line argument 1:" >> /root/tempfile
+echo $1 >> /root/tempfile
+echo "Command line argument 2:" >> /root/tempfile
+echo $2 >> /root/tempfile
+echo "Command line argument 3:" >> /root/tempfile
+echo $3 >> /root/tempfile
+
+# Echo environment variables into a temp file
+echo "Variables are":       >> /root/tempfile
+echo "$SMARTD_DEVICE"       >> /root/tempfile
+echo "$SMARTD_DEVICESTRING" >> /root/tempfile
+echo "$SMARTD_DEVICETYPE"   >> /root/tempfile
+echo "$SMARTD_MESSAGE"      >> /root/tempfile
+echo "$SMARTD_FULLMESSAGE"  >> /root/tempfile
+echo "$SMARTD_ADDRESS"      >> /root/tempfile
+echo "$SMARTD_SUBJECT"      >> /root/tempfile
+echo "$SMARTD_TFIRST"       >> /root/tempfile
+echo "$SMARTD_TFIRSTEPOCH"  >> /root/tempfile
+
+# Run smartctl -a and save output in temp file
+/usr/sbin/smartctl -a -d $SMARTD_DEVICETYPE $SMARTD_DEVICE >> /root/tempfile
+
+# Email the contents of the temp file. Solaris and other OSes
+# may need to use /bin/mailx not /bin/mail.
+/bin/mail -s "SMART errors detected on host: `hostname`" $SMARTD_ADDRESS < /root/tempfile
+
+# And exit
+exit 0
diff --git a/examplescripts/Example2 b/examplescripts/Example2
new file mode 100755 (executable)
index 0000000..f653433
--- /dev/null
@@ -0,0 +1,22 @@
+#! /bin/bash
+#
+# This is a script from the smartmontools examplescripts/ directory.
+# It can be used as an argument to the -M exec Directive in
+# /etc/smartd.conf, in a line like 
+# -m root@localhost -M exec /path/to/this/file
+#
+# Please see man 8 smartd or man 5 smartd.conf for further
+# information.
+#
+# $Id: Example2,v 1.4 2004/01/07 16:49:56 ballen4705 Exp $
+
+# Save the email message (STDIN) to a file:
+cat > /root/msg
+
+# Append the output of smartctl -a to the message:
+/usr/sbin/smartctl -a -d $SMARTD_DEVICETYPE $SMARTD_DEVICE >> /root/msg
+
+# Now email the message to the user at address ADD.  Solaris and
+# other OSes may need to use /bin/mailx below.
+/bin/mail -s "$SMARTD_SUBJECT" $SMARTD_ADDRESS < /root/msg
+
diff --git a/examplescripts/Example3 b/examplescripts/Example3
new file mode 100755 (executable)
index 0000000..6a1b11c
--- /dev/null
@@ -0,0 +1,25 @@
+#! /bin/bash
+#
+# This is a script from the smartmontools examplescripts/ directory.
+# It can be used as an argument to the -M exec Directive in
+# /etc/smartd.conf, in a line like 
+# -m <nomailer> -M exec /path/to/this/file
+#
+# Please see man 8 smartd or man 5 smartd.conf for further
+# information.
+#
+# $Id: Example3,v 1.4 2003/08/17 09:15:56 ballen4705 Exp $
+
+# Warn all users of a problem     
+wall 'Problem detected with disk: ' "$SMARTD_DEVICESTRING"    
+wall 'Warning message from smartd is: ' "$SMARTD_MESSAGE"      
+wall 'Shutting down machine in 30 seconds... '
+
+# Wait half a minute
+sleep 30 
+
+# Power down the machine (uncomment the shutdown command if you really
+# want to do this!)
+
+# /sbin/shutdown -hf now
+
diff --git a/examplescripts/Makefile.am b/examplescripts/Makefile.am
new file mode 100644 (file)
index 0000000..d37c17d
--- /dev/null
@@ -0,0 +1,10 @@
+## Process this file with automake to produce Makefile.in
+examplesdir=$(exampledir)
+
+examples_DATA = README
+
+examples_SCRIPTS = Example1            \
+                       Example2        \
+                       Example3
+
+EXTRA_DIST = $(examples_SCRIPTS)
diff --git a/examplescripts/Makefile.in b/examplescripts/Makefile.in
new file mode 100644 (file)
index 0000000..01ea4fa
--- /dev/null
@@ -0,0 +1,369 @@
+# Makefile.in generated by automake 1.9.3 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004  Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = examplescripts
+DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+       $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(examplesdir)" \
+       "$(DESTDIR)$(examplesdir)"
+examplesSCRIPT_INSTALL = $(INSTALL_SCRIPT)
+SCRIPTS = $(examples_SCRIPTS)
+SOURCES =
+DIST_SOURCES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+examplesDATA_INSTALL = $(INSTALL_DATA)
+DATA = $(examples_DATA)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+ASFLAGS = @ASFLAGS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCAS = @CCAS@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+OBJEXT = @OBJEXT@
+OS_DARWIN_FALSE = @OS_DARWIN_FALSE@
+OS_DARWIN_TRUE = @OS_DARWIN_TRUE@
+OS_SOLARIS_FALSE = @OS_SOLARIS_FALSE@
+OS_SOLARIS_TRUE = @OS_SOLARIS_TRUE@
+OS_WIN32_MINGW_FALSE = @OS_WIN32_MINGW_FALSE@
+OS_WIN32_MINGW_TRUE = @OS_WIN32_MINGW_TRUE@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SMARTD_SUFFIX_FALSE = @SMARTD_SUFFIX_FALSE@
+SMARTD_SUFFIX_TRUE = @SMARTD_SUFFIX_TRUE@
+STRIP = @STRIP@
+VERSION = @VERSION@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_STRIP = @ac_ct_STRIP@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+docdir = @docdir@
+exampledir = @exampledir@
+exec_prefix = @exec_prefix@
+gcc_have_attr_packed = @gcc_have_attr_packed@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+includedir = @includedir@
+infodir = @infodir@
+initddir = @initddir@
+install_sh = @install_sh@
+libc_have_working_snprintf = @libc_have_working_snprintf@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+os_deps = @os_deps@
+os_libs = @os_libs@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+releaseversion = @releaseversion@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+smartd_suffix = @smartd_suffix@
+smartmontools_release_date = @smartmontools_release_date@
+smartmontools_release_time = @smartmontools_release_time@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+examplesdir = $(exampledir)
+examples_DATA = README
+examples_SCRIPTS = Example1            \
+                       Example2        \
+                       Example3
+
+EXTRA_DIST = $(examples_SCRIPTS)
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+       @for dep in $?; do \
+         case '$(am__configure_deps)' in \
+           *$$dep*) \
+             cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+               && exit 0; \
+             exit 1;; \
+         esac; \
+       done; \
+       echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign  examplescripts/Makefile'; \
+       cd $(top_srcdir) && \
+         $(AUTOMAKE) --foreign  examplescripts/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+       @case '$?' in \
+         *config.status*) \
+           cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+         *) \
+           echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+           cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+       esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+       cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-examplesSCRIPTS: $(examples_SCRIPTS)
+       @$(NORMAL_INSTALL)
+       test -z "$(examplesdir)" || $(mkdir_p) "$(DESTDIR)$(examplesdir)"
+       @list='$(examples_SCRIPTS)'; for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         if test -f $$d$$p; then \
+           f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
+           echo " $(examplesSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(examplesdir)/$$f'"; \
+           $(examplesSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(examplesdir)/$$f"; \
+         else :; fi; \
+       done
+
+uninstall-examplesSCRIPTS:
+       @$(NORMAL_UNINSTALL)
+       @list='$(examples_SCRIPTS)'; for p in $$list; do \
+         f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
+         echo " rm -f '$(DESTDIR)$(examplesdir)/$$f'"; \
+         rm -f "$(DESTDIR)$(examplesdir)/$$f"; \
+       done
+uninstall-info-am:
+install-examplesDATA: $(examples_DATA)
+       @$(NORMAL_INSTALL)
+       test -z "$(examplesdir)" || $(mkdir_p) "$(DESTDIR)$(examplesdir)"
+       @list='$(examples_DATA)'; for p in $$list; do \
+         if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+         f=$(am__strip_dir) \
+         echo " $(examplesDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(examplesdir)/$$f'"; \
+         $(examplesDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(examplesdir)/$$f"; \
+       done
+
+uninstall-examplesDATA:
+       @$(NORMAL_UNINSTALL)
+       @list='$(examples_DATA)'; for p in $$list; do \
+         f=$(am__strip_dir) \
+         echo " rm -f '$(DESTDIR)$(examplesdir)/$$f'"; \
+         rm -f "$(DESTDIR)$(examplesdir)/$$f"; \
+       done
+tags: TAGS
+TAGS:
+
+ctags: CTAGS
+CTAGS:
+
+
+distdir: $(DISTFILES)
+       @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+       topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+       list='$(DISTFILES)'; for file in $$list; do \
+         case $$file in \
+           $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+           $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+         esac; \
+         if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+         dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+         if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+           dir="/$$dir"; \
+           $(mkdir_p) "$(distdir)$$dir"; \
+         else \
+           dir=''; \
+         fi; \
+         if test -d $$d/$$file; then \
+           if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+             cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+           fi; \
+           cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+         else \
+           test -f $(distdir)/$$file \
+           || cp -p $$d/$$file $(distdir)/$$file \
+           || exit 1; \
+         fi; \
+       done
+check-am: all-am
+check: check-am
+all-am: Makefile $(SCRIPTS) $(DATA)
+installdirs:
+       for dir in "$(DESTDIR)$(examplesdir)" "$(DESTDIR)$(examplesdir)"; do \
+         test -z "$$dir" || $(mkdir_p) "$$dir"; \
+       done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+       @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+       $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+         install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+         `test -z '$(STRIP)' || \
+           echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+       -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+       @echo "This command is intended for maintainers to use"
+       @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic mostlyclean-am
+
+distclean: distclean-am
+       -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-examplesDATA install-examplesSCRIPTS
+
+install-exec-am:
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+       -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-examplesDATA uninstall-examplesSCRIPTS \
+       uninstall-info-am
+
+.PHONY: all all-am check check-am clean clean-generic distclean \
+       distclean-generic distdir dvi dvi-am html html-am info info-am \
+       install install-am install-data install-data-am \
+       install-examplesDATA install-examplesSCRIPTS install-exec \
+       install-exec-am install-info install-info-am install-man \
+       install-strip installcheck installcheck-am installdirs \
+       maintainer-clean maintainer-clean-generic mostlyclean \
+       mostlyclean-generic pdf pdf-am ps ps-am uninstall uninstall-am \
+       uninstall-examplesDATA uninstall-examplesSCRIPTS \
+       uninstall-info-am
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/examplescripts/README b/examplescripts/README
new file mode 100644 (file)
index 0000000..404feca
--- /dev/null
@@ -0,0 +1,50 @@
+# Home page: http://smartmontools.sourceforge.net
+#
+# $Id: README,v 1.5 2006/04/12 14:54:28 ballen4705 Exp $
+#
+# Copyright (C) 2003-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+# 
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 2, or (at your option) any later
+# version.
+# 
+# You should have received a copy of the GNU General Public License (for
+# example COPYING); if not, write to the Free Software Foundation, Inc., 675
+# Mass Ave, Cambridge, MA 02139, USA.
+#
+# This code was originally developed as a Senior Thesis by Michael Cornwell
+# at the Concurrent Systems Laboratory (now part of the Storage Systems
+# Research Center), Jack Baskin School of Engineering, University of
+# California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+
+This directory contains executable bash scripts, that are intended for
+use with the
+  -m address -M exec /path/to/an/executable
+Directive in /etc/smartd.conf.
+
+Details about how to use this Directive may be found in the man pages for
+smartd and smartd.conf.
+  man 8 smartd
+  man 5 smartd.conf
+should display those pages on your system.
+
+If you wish to contribute additional scripts to this collection,
+please email them to <smartmontools-support@lists.sourceforge.net>,
+and include a brief description to use below.
+
+The files contained in this directory are:
+
+Example1: appends values of $SMARTD_* environment variables and the output
+          of smartctl -a to the normal email message, and sends that
+          to the email address listed as the argument to the -m
+          Directive.
+
+Example2: Appends output of smartctl -a to the normal email message
+         and sends that to the email address listed as the argument
+         to the -m Directive.
+
+Example3: Uses wall(1) to send a warning message to all users, then powers
+          down the machine.
+
+
diff --git a/extern.h b/extern.h
new file mode 100644 (file)
index 0000000..5eabed7
--- /dev/null
+++ b/extern.h
@@ -0,0 +1,102 @@
+/*
+ * extern.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+#ifndef EXTERN_H_
+#define EXTERN_H_
+
+#define EXTERN_H_CVSID "$Id: extern.h,v 1.41 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+// Possible values for fixfirmwarebug.  If use 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
+
+// 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];
+  // number of spans
+  int smartselectivenumspans;
+  int           testcase;
+  // 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;
+  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 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 permissive;
+  unsigned char conservative;
+  unsigned char checksumfail;
+  unsigned char checksumignore;
+  unsigned char reportataioctl;
+  unsigned char reportscsiioctl;
+  unsigned char fixfirmwarebug;
+  // 3Ware controller type, but also extensible to other contoller types
+  unsigned char controller_type;
+  // For 3Ware controllers, nonzero value is 1 plus the disk number
+  unsigned char controller_port;
+  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];
+} smartmonctrl;
+
+#endif
diff --git a/install-sh b/install-sh
new file mode 100755 (executable)
index 0000000..0b65ee8
--- /dev/null
@@ -0,0 +1,323 @@
+#!/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2004-10-22.00
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.  It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+chmodcmd="$chmodprog 0755"
+chowncmd=
+chgrpcmd=
+stripcmd=
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=
+dst=
+dir_arg=
+dstarg=
+no_target_directory=
+
+usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+   or: $0 [OPTION]... SRCFILES... DIRECTORY
+   or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+   or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+-c         (ignored)
+-d         create directories instead of installing files.
+-g GROUP   $chgrpprog installed files to GROUP.
+-m MODE    $chmodprog installed files to MODE.
+-o USER    $chownprog installed files to USER.
+-s         $stripprog installed files.
+-t DIRECTORY  install into DIRECTORY.
+-T         report an error if DSTFILE is a directory.
+--help     display this help and exit.
+--version  display version info and exit.
+
+Environment variables override the default commands:
+  CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
+"
+
+while test -n "$1"; do
+  case $1 in
+    -c) shift
+        continue;;
+
+    -d) dir_arg=true
+        shift
+        continue;;
+
+    -g) chgrpcmd="$chgrpprog $2"
+        shift
+        shift
+        continue;;
+
+    --help) echo "$usage"; exit 0;;
+
+    -m) chmodcmd="$chmodprog $2"
+        shift
+        shift
+        continue;;
+
+    -o) chowncmd="$chownprog $2"
+        shift
+        shift
+        continue;;
+
+    -s) stripcmd=$stripprog
+        shift
+        continue;;
+
+    -t) dstarg=$2
+       shift
+       shift
+       continue;;
+
+    -T) no_target_directory=true
+       shift
+       continue;;
+
+    --version) echo "$0 $scriptversion"; exit 0;;
+
+    *)  # When -d is used, all remaining arguments are directories to create.
+       # When -t is used, the destination is already specified.
+       test -n "$dir_arg$dstarg" && break
+        # Otherwise, the last argument is the destination.  Remove it from $@.
+       for arg
+       do
+          if test -n "$dstarg"; then
+           # $@ is not empty: it contains at least $arg.
+           set fnord "$@" "$dstarg"
+           shift # fnord
+         fi
+         shift # arg
+         dstarg=$arg
+       done
+       break;;
+  esac
+done
+
+if test -z "$1"; then
+  if test -z "$dir_arg"; then
+    echo "$0: no input file specified." >&2
+    exit 1
+  fi
+  # It's OK to call `install-sh -d' without argument.
+  # This can happen when creating conditional directories.
+  exit 0
+fi
+
+for src
+do
+  # Protect names starting with `-'.
+  case $src in
+    -*) src=./$src ;;
+  esac
+
+  if test -n "$dir_arg"; then
+    dst=$src
+    src=
+
+    if test -d "$dst"; then
+      mkdircmd=:
+      chmodcmd=
+    else
+      mkdircmd=$mkdirprog
+    fi
+  else
+    # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+    # might cause directories to be created, which would be especially bad
+    # if $src (and thus $dsttmp) contains '*'.
+    if test ! -f "$src" && test ! -d "$src"; then
+      echo "$0: $src does not exist." >&2
+      exit 1
+    fi
+
+    if test -z "$dstarg"; then
+      echo "$0: no destination specified." >&2
+      exit 1
+    fi
+
+    dst=$dstarg
+    # Protect names starting with `-'.
+    case $dst in
+      -*) dst=./$dst ;;
+    esac
+
+    # If destination is a directory, append the input filename; won't work
+    # if double slashes aren't ignored.
+    if test -d "$dst"; then
+      if test -n "$no_target_directory"; then
+       echo "$0: $dstarg: Is a directory" >&2
+       exit 1
+      fi
+      dst=$dst/`basename "$src"`
+    fi
+  fi
+
+  # This sed command emulates the dirname command.
+  dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'`
+
+  # Make sure that the destination directory exists.
+
+  # Skip lots of stat calls in the usual case.
+  if test ! -d "$dstdir"; then
+    defaultIFS='
+        '
+    IFS="${IFS-$defaultIFS}"
+
+    oIFS=$IFS
+    # Some sh's can't handle IFS=/ for some reason.
+    IFS='%'
+    set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'`
+    shift
+    IFS=$oIFS
+
+    pathcomp=
+
+    while test $# -ne 0 ; do
+      pathcomp=$pathcomp$1
+      shift
+      if test ! -d "$pathcomp"; then
+        $mkdirprog "$pathcomp"
+       # mkdir can fail with a `File exist' error in case several
+       # install-sh are creating the directory concurrently.  This
+       # is OK.
+       test -d "$pathcomp" || exit
+      fi
+      pathcomp=$pathcomp/
+    done
+  fi
+
+  if test -n "$dir_arg"; then
+    $doit $mkdircmd "$dst" \
+      && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \
+      && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \
+      && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \
+      && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; }
+
+  else
+    dstfile=`basename "$dst"`
+
+    # Make a couple of temp file names in the proper directory.
+    dsttmp=$dstdir/_inst.$$_
+    rmtmp=$dstdir/_rm.$$_
+
+    # Trap to clean up those temp files at exit.
+    trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+    trap '(exit $?); exit' 1 2 13 15
+
+    # Copy the file name to the temp name.
+    $doit $cpprog "$src" "$dsttmp" &&
+
+    # and set any options; do chmod last to preserve setuid bits.
+    #
+    # If any of these fail, we abort the whole thing.  If we want to
+    # ignore errors from any of these, just make sure not to ignore
+    # errors from the above "$doit $cpprog $src $dsttmp" command.
+    #
+    { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
+      && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
+      && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
+      && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } &&
+
+    # Now rename the file to the real destination.
+    { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \
+      || {
+          # The rename failed, perhaps because mv can't rename something else
+          # to itself, or perhaps because mv is so ancient that it does not
+          # support -f.
+
+          # Now remove or move aside any old file at destination location.
+          # We try this two ways since rm can't unlink itself on some
+          # systems and the destination file might be busy for other
+          # reasons.  In this case, the final cleanup might fail but the new
+          # file should still install successfully.
+          {
+            if test -f "$dstdir/$dstfile"; then
+              $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \
+              || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \
+              || {
+                echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2
+                (exit 1); exit
+              }
+            else
+              :
+            fi
+          } &&
+
+          # Now rename the file to the real destination.
+          $doit $mvcmd "$dsttmp" "$dstdir/$dstfile"
+        }
+    }
+  fi || { (exit 1); exit; }
+done
+
+# The final little trick to "correctly" pass the exit status to the exit trap.
+{
+  (exit 0); exit
+}
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/int64.h b/int64.h
new file mode 100644 (file)
index 0000000..64aa28d
--- /dev/null
+++ b/int64.h
@@ -0,0 +1,94 @@
+/*
+ * int64.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2004-6 Christian Franke
+ *
+ * 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 INT64_H_
+#define INT64_H_
+
+#define INT64_H_CVSID "$Id: int64.h,v 1.13 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+// 64 bit integer typedefs
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#else
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#else
+#ifdef HAVE_SYS_INTTYPES_H
+#include <sys/inttypes.h>
+#else
+#ifdef HAVE_SYS_INT_TYPES_H
+#include <sys/int_types.h>
+#else
+#if defined(_WIN32) && defined(_MSC_VER)
+// for MSVC 6.0
+typedef          __int64    int64_t;
+typedef unsigned __int64   uint64_t;
+#else
+// for systems with above includes missing (like ix86-pc-linux-gnulibc1),
+// default to GCC if types are undefined in types.h
+#include <sys/types.h>
+#ifndef HAVE_INT64_T
+typedef          long long  int64_t;
+#endif
+#ifndef HAVE_UINT64_T
+typedef unsigned long long uint64_t;
+#endif
+#endif // _WIN32 && _MSC_VER
+#endif // HAVE_SYS_INT_TYPES_H
+#endif // HAVE_SYS_INTTYPES_H
+#endif // HAVE_STDINT_H
+#endif // HAVE_INTTYPES_H
+
+// 64 bit integer format strings
+
+#if defined(_WIN32) && defined(_MSC_VER)
+// for MSVC 6.0
+#define PRId64 "I64d"
+#define PRIu64 "I64u"
+#define PRIx64 "I64x"
+#endif // _WIN32 && _MSC_VER
+
+// If macros not defined in inttypes.h, fix here.  Default is GCC
+// style
+#ifndef PRId64         
+#define PRId64 "lld"
+#endif // ndef PRId64
+
+#ifndef PRIu64
+#define PRIu64 "llu"
+#endif // ndef PRIu64
+
+#ifndef PRIx64
+#define PRIx64 "llx"
+#endif // ndef PRIx64
+
+
+#if defined(_WIN32) && defined(_MSC_VER)
+// for MSVC 6.0: "unsigned __int64 -> double" conversion not implemented (why?-)
+__inline double uint64_to_double(uint64_t ull) {
+  return ((int64_t)ull >= 0 ? (double)(int64_t)ull :
+    ((double)(int64_t)(ull - 9223372036854775808UI64)) + 9223372036854775808.0);
+}
+#else
+#define uint64_to_double(ull) ((double)(ull))
+#endif // _WIN32 && _MSC_VER
+
+
+#endif // INT64_H
diff --git a/knowndrives.c b/knowndrives.c
new file mode 100644 (file)
index 0000000..9494300
--- /dev/null
@@ -0,0 +1,1258 @@
+/*
+ * knowndrives.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ * Address of support mailing list: smartmontools-support@lists.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Philip Williams, Bruce Allen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include <stdio.h>
+#include "atacmds.h"
+#include "ataprint.h"
+#include "extern.h"
+#include "knowndrives.h"
+#include "utility.h" // includes <regex.h>
+
+const char *knowndrives_c_cvsid="$Id: knowndrives.c,v 1.139 2006/04/05 19:50:07 chrfranke Exp $"
+ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID UTILITY_H_CVSID;
+
+#define MODEL_STRING_LENGTH                         40
+#define FIRMWARE_STRING_LENGTH                       8
+#define TABLEPRINTWIDTH                             19
+
+// See vendorattributeargs[] array in atacmds.c for definitions.
+#define PRESET_9_MINUTES                   {   9,  1 }
+#define PRESET_9_TEMP                      {   9,  2 }
+#define PRESET_9_SECONDS                   {   9,  3 }
+#define PRESET_9_HALFMINUTES               {   9,  4 }
+#define PRESET_192_EMERGENCYRETRACTCYCLECT { 192,  1 }
+#define PRESET_193_LOADUNLOAD              { 193,  1 }
+#define PRESET_194_10XCELSIUS              { 194,  1 }
+#define PRESET_194_UNKNOWN                 { 194,  2 }
+#define PRESET_198_OFFLINESCANUNCSECTORCT  { 198,  1 }
+#define PRESET_200_WRITEERRORCOUNT         { 200,  1 }
+#define PRESET_201_DETECTEDTACOUNT         { 201,  1 }         
+#define PRESET_220_TEMP                    { 220,  1 }
+
+/* Arrays of preset vendor-specific attribute options for use in
+ * knowndrives[]. */
+
+extern int64_t bytes;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// These three are common to several models.
+const unsigned char vendoropts_9_minutes[][2] = {
+  PRESET_9_MINUTES,
+  {0,0}
+};
+const unsigned char vendoropts_9_halfminutes[][2] = {
+  PRESET_9_HALFMINUTES,
+  {0,0}
+};
+const unsigned char vendoropts_9_seconds[][2] = {
+  PRESET_9_SECONDS,
+  {0,0}
+};
+
+const unsigned char vendoropts_Maxtor_4D080H4[][2] = {
+  PRESET_9_MINUTES,
+  PRESET_194_UNKNOWN,
+  {0,0}
+};
+
+const unsigned char vendoropts_Fujitsu_MHS2020AT[][2] = {
+  PRESET_9_SECONDS,
+  PRESET_192_EMERGENCYRETRACTCYCLECT,
+  PRESET_198_OFFLINESCANUNCSECTORCT,
+  PRESET_200_WRITEERRORCOUNT,
+  PRESET_201_DETECTEDTACOUNT,
+  {0,0}
+};
+
+const unsigned char vendoropts_Fujitsu_MHR2040AT[][2] = {
+  PRESET_9_SECONDS,
+  PRESET_192_EMERGENCYRETRACTCYCLECT,
+  PRESET_198_OFFLINESCANUNCSECTORCT,
+  PRESET_200_WRITEERRORCOUNT,
+  {0,0}
+};
+
+const unsigned char vendoropts_Samsung_SV4012H[][2] = {
+  PRESET_9_HALFMINUTES,
+  {0,0}
+};
+
+const unsigned char vendoropts_Samsung_SV1204H[][2] = {
+  PRESET_9_HALFMINUTES,
+  PRESET_194_10XCELSIUS,
+  {0,0}
+};
+
+const unsigned char vendoropts_Hitachi_DK23XX[][2] = {
+  PRESET_9_MINUTES,
+  PRESET_193_LOADUNLOAD,
+  {0,0}
+};
+
+const char same_as_minus_F[]="Fixes byte order in some SMART data (same as -F samsung)";
+const char same_as_minus_F2[]="Fixes byte order in some SMART data (same as -F samsung2)";
+
+const char may_need_minus_F_disabled[] ="May need -F samsung disabled; see manual for details.";
+const char may_need_minus_F2_disabled[]="May need -F samsung2 disabled; see manual for details.";
+const char may_need_minus_F2_enabled[] ="May need -F samsung2 enabled; see manual for details.";
+const char may_need_minus_F_enabled[]  ="May need -F samsung or -F samsung2 enabled; see manual for details.";
+
+/* Special-purpose functions for use in knowndrives[]. */
+void specialpurpose_reverse_samsung(smartmonctrl *con)
+{
+  if (con->fixfirmwarebug==FIX_NOTSPECIFIED)
+    con->fixfirmwarebug = FIX_SAMSUNG;
+}
+void specialpurpose_reverse_samsung2(smartmonctrl *con)
+{
+  if (con->fixfirmwarebug==FIX_NOTSPECIFIED)
+    con->fixfirmwarebug = FIX_SAMSUNG2;
+}
+
+/* Table of settings for known drives terminated by an element containing all
+ * zeros.  The drivesettings structure is described in knowndrives.h.  Note
+ * that lookupdrive() will search knowndrives[] from the start to end or
+ * until it finds the first match, so the order in knowndrives[] is important
+ * for distinct entries that could match the same drive. */
+
+// Note that the table just below uses EXTENDED REGULAR EXPRESSIONS.
+// A good on-line reference for these is:
+// http://www.zeus.com/extra/docsystem/docroot/apps/web/docs/modules/access/regex.html
+
+const drivesettings knowndrives[] = {
+  { "IBM Deskstar 60GXP series",  // ER60A46A firmware
+    "(IBM-|Hitachi )?IC35L0[12346]0AVER07",
+    "^ER60A46A$",
+    NULL, NULL, NULL, NULL
+  },
+  { "IBM Deskstar 60GXP series",  // All other firmware
+    "(IBM-|Hitachi )?IC35L0[12346]0AVER07",
+    ".*",
+    "IBM Deskstar 60GXP drives may need upgraded SMART firmware.\n"
+    "Please see http://www.geocities.com/dtla_update/index.html#rel and\n"
+    "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n"
+    "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215",
+    NULL, NULL, NULL
+  },
+  { "IBM Deskstar 40GV & 75GXP series (A5AA/A6AA firmware)",
+    "(IBM-)?DTLA-30[57]0[123467][05]",
+    "^T[WX][123468AG][OF]A[56]AA$",
+    NULL, NULL, NULL, NULL
+  },
+  { "IBM Deskstar 40GV & 75GXP series (all other firmware)",
+    "(IBM-)?DTLA-30[57]0[123467][05]",
+    ".*",
+    "IBM Deskstar 40GV and 75GXP drives may need upgraded SMART firmware.\n"
+    "Please see http://www.geocities.com/dtla_update/ and\n"
+    "http://www-3.ibm.com/pc/support/site.wss/document.do?lndocid=MIGR-42215 or\n"
+    "http://www-1.ibm.com/support/docview.wss?uid=psg1MIGR-42215",
+    NULL, NULL, NULL
+  },
+  { NULL, // ExcelStor J240, J340, J360, J680, and J880
+    "^ExcelStor Technology J(24|34|36|68|88)0$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // Fujitsu M1623TAU
+    "^FUJITSU M1623TAU$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MHG and MHH series",
+    "^FUJITSU MH(G2102|H20(64|48|32))AT$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MHJ and MHK series",
+    "^FUJITSU MH[JK]....ATU?$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MPB series",
+    "^FUJITSU MPB....ATU?$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MPD and MPE series",
+    "^FUJITSU MP[DE]....A[HTE]$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MPF series",
+    "^FUJITSU MPF3(102A[HT]|153A[HT]|204A[HT])$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MPG series",
+    "^FUJITSU MPG3(102A(H|T  E)|153AH|204A(H|[HT]  E)|307A(H  E|T)|409A[HT]  E)$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MPC series",
+    "^FUJITSU MPC3(032AT|043AT|045AH|064A[HT]|084AT|096AT|102AT)$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { NULL, // Fujitsu MHN2300AT
+    "^FUJITSU MHN2300AT$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { NULL, // Fujitsu MHR2040AT
+    "^FUJITSU MHR2040AT$",
+    ".*",    // Tested on 40BA
+    NULL,
+    vendoropts_Fujitsu_MHR2040AT,
+    NULL, NULL
+  },
+  { NULL, // Fujitsu MHR2020AT
+    "^FUJITSU MHR2020AT$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MHSxxxxAT family",
+    "^FUJITSU MHS20[6432]0AT(  .)?$",
+    ".*",
+    NULL,
+    vendoropts_Fujitsu_MHS2020AT,
+    NULL, NULL
+  },
+  { NULL, // Fujitsu MHL2300AT, MHM2200AT, MHM2100AT, MHM2150AT
+    "^FUJITSU MH(L230|M2(20|10|15))0AT$",
+    ".*",
+    "This drive's firmware has a harmless Drive Identity Structure\n"
+      "checksum error bug.",
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MHT2xxxAT/MHU2100AT series",
+    "^FUJITSU MH(T20[23468]0AT( PL)?|U2100AT)$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { "Fujitsu MHTxxxxAH family",
+    "^FUJITSU MHT20[468]0AH$",
+    ".*",
+    NULL,
+    vendoropts_9_seconds,
+    NULL, NULL
+  },
+  { NULL, // Samsung SV4012H (known firmware)
+    "^SAMSUNG SV4012H$",
+    "^RM100-08$",
+    NULL,
+    vendoropts_Samsung_SV4012H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // Samsung SV4012H (all other firmware)
+    "^SAMSUNG SV4012H$",
+    ".*",
+    may_need_minus_F_disabled,
+    vendoropts_Samsung_SV4012H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // Samsung SV0412H (known firmware)
+    "^SAMSUNG SV0412H$",
+    "^SK100-01$",
+    NULL,
+    vendoropts_Samsung_SV1204H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // Samsung SV0412H (all other firmware)
+    "^SAMSUNG SV0412H$",
+    ".*",
+    may_need_minus_F_disabled,
+    vendoropts_Samsung_SV1204H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // Samsung SV1204H (known firmware)
+    "^SAMSUNG SV1204H$",
+    "^RK100-1[3-5]$",
+    NULL,
+    vendoropts_Samsung_SV1204H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // Samsung SV1204H (all other firmware)
+    "^SAMSUNG SV1204H$",
+    ".*",
+    may_need_minus_F_disabled,
+    vendoropts_Samsung_SV1204H,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { NULL, // SAMSUNG SV0322A tested with FW JK200-35
+    "^SAMSUNG SV0322A$",
+    ".*",
+    NULL,
+    NULL,
+    NULL,
+    NULL
+  },
+  { NULL, // SAMSUNG SP40A2H with RR100-07 firmware
+    "^SAMSUNG SP40A2H$",
+    "^RR100-07$",
+    NULL,
+    vendoropts_9_halfminutes,
+    specialpurpose_reverse_samsung,
+    same_as_minus_F
+  },
+  { 
+    NULL, // Any other Samsung disk with *-23 *-24 firmware
+    // SAMSUNG SP1213N (TL100-23 firmware)
+    // SAMSUNG SP0802N (TK100-23 firmware)
+    // Samsung SP1604N, tested with FW TM100-23 and TM100-24
+    "^SAMSUNG .*$",
+    ".*-2[34]$",
+    NULL,
+    vendoropts_Samsung_SV4012H,
+    specialpurpose_reverse_samsung2,
+    same_as_minus_F2
+  },
+  { NULL, // All Samsung drives with '.*-25' firmware
+    "^SAMSUNG.*",
+    ".*-25$",
+    may_need_minus_F2_disabled,
+    vendoropts_Samsung_SV4012H,
+    specialpurpose_reverse_samsung2,
+    same_as_minus_F2
+  },
+  { NULL, // All Samsung drives with '.*-26 or later (currently to -39)' firmware
+    "^SAMSUNG.*",
+    ".*-(2[6789]|3[0-9])$",
+    NULL,
+    vendoropts_Samsung_SV4012H,
+    NULL,
+    NULL
+  },
+  { NULL, // Samsung ALL OTHER DRIVES
+    "^SAMSUNG.*",
+    ".*",
+    may_need_minus_F_enabled,
+    NULL, NULL, NULL
+  },
+  { "Maxtor Fireball 541DX family",
+    "^Maxtor 2B0(0[468]|1[05]|20)H1$",
+    ".*",
+    NULL,
+    vendoropts_Maxtor_4D080H4,
+    NULL, NULL
+  },
+  { "Maxtor Fireball 3 family",
+    "^Maxtor 2F0[234]0[JL]0$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 2160 Ultra ATA family",
+    "^Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 2880 Ultra ATA family",
+    "^Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 3400 Ultra ATA family",
+    "^Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax D540X-4G family",
+    "^Maxtor 4G(120J6|160J[68])$",
+    ".*",
+    NULL,
+    vendoropts_Maxtor_4D080H4,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax D540X-4K family",
+    "^MAXTOR 4K(020H1|040H2|060H3|080H4)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus D740X family",
+    "^MAXTOR 6L0(20[JL]1|40[JL]2|60[JL]3|80[JL]4)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 5120 Ultra ATA 33 family",
+    "^Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 6800 Ultra ATA 66 family",
+    "^Maxtor 9(2732U8|2390U7|2049U6|1707U5|1366U4|1024U3|0845U3|0683U2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax D540X-4D",
+    "^Maxtor 4D0(20H1|40H2|60H3|80H4)$",
+    ".*",
+    NULL,
+    vendoropts_Maxtor_4D080H4,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 16 family",
+    "^Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 4320 family",
+    "^Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D4|90432D2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 17 VL family",
+    "^Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 20 VL family",
+    "^Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax VL 30 family",
+    "^Maxtor (33073U4|32049U3|31536U2|30768U1)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 36 family",
+    "^Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 40 ATA 66 series",
+    "^Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 40 series (Ultra ATA 66 and Ultra ATA 100)",
+    "^Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 40 VL Ultra ATA 100 series",
+    "^Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 45 Ulta ATA 100 family",
+    "^Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 60 family",
+    "^Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 80 family",
+    "^Maxtor (98196H8|96147H6)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 536DX family",
+    "^Maxtor 4W(100H6|080H6|060H4|040H3|030H2)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 8 family",
+    "^Maxtor 6E0[234]0L0$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax 10 family",
+    "^Maxtor 6(B(30|25|20|16|12|08)0[MPRS]|L(100P|120[MP]|160M|200[MPRS]|250[RS]|300[RS]))0$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor DiamondMax Plus 9 family",
+    "^Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor MaXLine Plus II",
+    "^Maxtor 7Y250[PM]0$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { "Maxtor MaXLine II family",
+    "^Maxtor [45]A(25|30|32)0[JN]0$",
+    ".*",
+    NULL,
+    vendoropts_9_minutes,
+    NULL, NULL
+  },
+  { NULL, // HITACHI_DK14FA-20B
+    "^HITACHI_DK14FA-20B$",
+    ".*",
+    NULL,
+    vendoropts_Hitachi_DK23XX,
+    NULL, NULL
+  },
+  { "HITACHI Travelstar DK23XX/DK23XXB series",
+    "^HITACHI_DK23..-..B?$",
+    ".*",
+    NULL,
+    vendoropts_Hitachi_DK23XX,
+    NULL, NULL
+  },
+  { "Hitachi Endurastar J4K20/N4K20 (formerly DK23FA-20J)",
+    "^(HITACHI_DK23FA-20J|HTA422020F9AT[JN]0)$",
+    ".*",
+    NULL,
+    vendoropts_Hitachi_DK23XX,
+    NULL, NULL
+  },
+  { "IBM Deskstar 14GXP and 16GP series",
+    "^IBM-DTTA-3(7101|7129|7144|5032|5043|5064|5084|5101|5129|5168)0$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM Deskstar 25GP and 22GXP family",
+    "^IBM-DJNA-3(5(101|152|203|250)|7(091|135|180|220))0$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "IBM Travelstar 25GS, 18GT, and 12GN family",
+    "^IBM-DARA-2(25|18|15|12|09|06)000$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM Travelstar 48GH, 30GN, and 15GN family",
+    "^(IBM-|Hitachi )?IC25(T048ATDA05|N0(30|20|15|12|10|07|06|05)ATDA04)-.$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM Travelstar 32GH, 30GT, and 20GN family",
+    "^IBM-DJSA-2(32|30|20|10|05)$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM Deskstar 37GP and 34GXP family",
+    "^IBM-DPTA-3(5(375|300|225|150)|7(342|273|205|136))0$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM/Hitachi Travelstar 60GH and 40GN family",
+    "^(IBM-|Hitachi )?IC25(T060ATC[SX]05|N0[4321]0ATC[SX]04)-.$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM/Hitachi Travelstar 40GNX family",
+    "^(IBM-|Hitachi )?IC25N0[42]0ATC[SX]05-.$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "Hitachi Travelstar 80GN family",
+    "^(Hitachi )?IC25N0[23468]0ATMR04-.$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Hitachi Travelstar 5K80 family",
+    "^HTS5480[8642]0M9AT00$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "Hitachi Travelstar 5K100 series",
+    "^HTS5410[1864]0G9(AT|SA)00$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "Hitachi Travelstar 7K60",
+    "^HTS726060M9AT00$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "Hitachi Travelstar E7K60 family",
+    "^HTE7260[46]0M9AT00$",
+    ".*",
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM/Hitachi Deskstar 120GXP family",
+    "^(IBM-)?IC35L((020|040|060|080|120)AVVA|0[24]0AVVN)07-[01]$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "IBM/Hitachi Deskstar GXP-180 family",
+    "^(IBM-)?IC35L(030|060|090|120|180)AVV207-[01]$",
+    ".*", 
+    NULL, NULL, NULL, NULL 
+  },
+  { "IBM Travelstar 14GS",
+    "^IBM-DCYA-214000$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "IBM Travelstar 4LP",
+    "^IBM-DTNA-2(180|216)0$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Hitachi Deskstar 7K80 series",
+    "^(Hitachi )?HDS7280([48]0PLAT20|(40)?PLA320)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Hitachi Deskstar 7K250 series",
+    "^(Hitachi )?HDS7225((40|80|12|16)VLAT20|(12|16|25)VLAT80|(80|12|16|25)VLSA80)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Hitachi Deskstar 7K400 series",
+    "^(Hitachi )?HDS724040KL(AT|SA)80$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK4025GAS
+    "^TOSHIBA MK4025GAS$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Toshiba 2.5\" HDD series", // TOSHIBA MK6021GAS [Bruce -- use for testing on laptop]
+    "^TOSHIBA MK6021GAS$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK6022GAX
+    "^TOSHIBA MK6022GAX$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK4019GAX/MK4019GAXB
+    "^TOSHIBA MK4019GAXB?$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK6409MAV
+    "^TOSHIBA MK6409MAV$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOS MK3019GAXB SUN30G
+    "^TOS MK3019GAXB SUN30G$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK2016GAP, MK2017GAP, MK2018GAP, MK2018GAS, MK2023GAS
+    "^TOSHIBA MK20(1[678]GAP|(18|23)GAS)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK4018GAS, MK4018GAP
+    "^TOSHIBA MK4018GA[SP]$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK3017GAP
+    "^TOSHIBA MK3017GAP$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // TOSHIBA MK8026GAX
+    "^TOSHIBA MK8026GAX$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Momentus family",
+    "^ST9(20|28|40|48)11A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Momentus 4200.2 Series",
+    "^ST9(100822|808210|60821|50212|402113|30219)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Momentus 5400.2 series",
+    "^ST9(100823|808211|60822|408114|308110)A$", 
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Medalist 8641 family",
+    "^ST3(2110|3221|4312|6531|8641)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U Series X family",
+    "^ST3(10014A(CE)?|20014A)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U Series 6 family",
+    "^ST3(8002|6002|4081|3061|2041)0A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U Series 5 family",
+    "^ST3(40823|30621|20413|15311|10211)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U4 family",
+    "^ST3(2112|4311|6421|8421)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U8 family",
+    "^ST3(8410|4313|17221|13021)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate U10 family",
+    "^ST3(20423|15323|10212)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda ATA II family",
+    "^ST3(3063|2042|1532|1021)0A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda ATA III family",
+    "^ST3(40824|30620|20414|15310|10215)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda ATA IV family",
+    "^ST3(20011|30011|40016|60021|80021)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda ATA V family",
+    "^ST3(12002(3A|4A|9A|3AS)|800(23A|15A|23AS)|60(015A|210A)|40017A)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda 5400.1",
+    "^ST340015A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda 7200.7 and 7200.7 Plus family",
+    "^ST3(200021A|200822AS?|16002[13]AS?|12002[26]AS?|1[26]0827AS|8001[13]AS?|80817AS|60014A|40014AS?)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Barracuda 7200.8 family",
+    "^ST3(400832|300831|250823|200826)AS?$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Medalist 17240, 13030, 10231, 8420, and 4310",
+    "^ST3(17240|13030|10231|8420|4310)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Seagate Medalist 17242, 13032, 10232, 8422, and 4312",
+    "^ST3(1724|1303|1023|842|431)2A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Protege",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD([2468]00E|1[26]00A)B-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar family",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD(2|3|4|6|8|10|12|16|18|20|25)00BB-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar WDxxxAB series",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD(3|4|6)00AB-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar WDxxxAA series",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD...?AA(-.*)?$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar WDxxxBA series",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD...BA$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // Western Digital Caviar AC12500, AC14300, AC23200, AC24300, AC25100,
+          // AC36400, AC38400
+    "^WDC AC(125|143|232|243|251|364|384)00.?",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar SE family",
+  /* Western Digital drives with this comment all appear to use Attribute 9 in
+   * a  non-standard manner.  These entries may need to be updated when it
+   * is understood exactly how Attribute 9 should be interpreted.
+   * UPDATE: this is probably explained by the WD firmware bug described in the
+   * smartmontools FAQ */
+    "^WDC WD((4|6|8|10|12|16|18|20|25|30|32)00JB|(12|20|25)00PB)-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar SE (Serial ATA) family",
+    "^WDC WD(4|8|12|16|20|25)00JD-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Caviar RE Serial ATA series",
+    "^WDC WD((12|16|25|32)00SD|4000YR)-.*$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Western Digital Raptor family",
+    "^WDC WD(360|740)GD",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL,  // QUANTUM BIGFOOT TS10.0A
+    "^QUANTUM BIGFOOT TS10.0A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALLlct15 20 and QUANTUM FIREBALLlct15 30
+    "^QUANTUM FIREBALLlct15 [23]0$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "QUANTUM FIREBALLlct20 series",
+    "^QUANTUM FIREBALLlct20 [234]0$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALL CX10.2A
+    "^QUANTUM FIREBALL CX10.2A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Quantum Fireball Plus LM series",
+    "^QUANTUM FIREBALLP LM(10.2|15|20.5|30)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { "Quantum Fireball CR series",
+    "^QUANTUM FIREBALL CR(4.3|8.4)A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALLP AS10.2, AS20.5, and AS40.0
+    "^QUANTUM FIREBALLP AS(10.2|20.5|40.0)$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALL EX6.4A
+    "^QUANTUM FIREBALL EX6.4A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALL ST3.2A
+    "^QUANTUM FIREBALL ST3.2A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALL EX3.2A
+    "^QUANTUM FIREBALL EX3.2A$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALLP KX27.3
+    "^QUANTUM FIREBALLP KX27.3$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  { NULL, // QUANTUM FIREBALLP KA10.1
+    "^QUANTUM FIREBALLP KA10.1$",
+    ".*",
+    NULL, NULL, NULL, NULL
+  },
+  /*------------------------------------------------------------
+   *  End of table.  Do not add entries below this marker.
+   *------------------------------------------------------------ */
+  {NULL, NULL, NULL, NULL, NULL, NULL, NULL}
+};
+
+// Searches knowndrives[] for a drive with the given model number and firmware
+// string.  If either the drive's model or firmware strings are not set by the
+// manufacturer then values of NULL may be used.  Returns the index of the
+// first match in knowndrives[] or -1 if no match if found.
+int lookupdrive(const char *model, const char *firmware)
+{
+  regex_t regex;
+  int i, index;
+  const char *empty = "";
+
+  model = model ? model : empty;
+  firmware = firmware ? firmware : empty;
+
+  for (i = 0, index = -1; index == -1 && knowndrives[i].modelregexp; i++) {
+    // Attempt to compile regular expression.
+    if (compileregex(&regex, knowndrives[i].modelregexp, REG_EXTENDED))
+      goto CONTINUE;
+
+    // Check whether model matches the regular expression in knowndrives[i].
+    if (!regexec(&regex, model, 0, NULL, 0)) {
+      // model matches, now check firmware.
+      if (!knowndrives[i].firmwareregexp)
+        // The firmware regular expression in knowndrives[i] is NULL, which is
+        // considered a match.
+        index = i;
+      else {
+        // Compare firmware against the regular expression in knowndrives[i].
+        regfree(&regex);  // Recycle regex.
+        if (compileregex(&regex, knowndrives[i].firmwareregexp, REG_EXTENDED))
+          goto CONTINUE;
+        if (!regexec(&regex, firmware, 0, NULL, 0))
+          index = i;
+      }
+    }
+  CONTINUE:
+    regfree(&regex);
+  }
+
+  return index;
+}
+
+
+// Shows all presets for drives in knowndrives[].
+void showonepreset(const drivesettings *drivetable){
+  
+  const unsigned char (* presets)[2] = drivetable->vendoropts;
+  int first_preset = 1;
+  
+  // Basic error check
+  if (!drivetable || !drivetable->modelregexp){
+    pout("Null known drive table pointer. Please report\n"
+         "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n");
+    return;
+  }
+  
+  // print model and firmware regular expressions
+  pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL REGEXP:", drivetable->modelregexp);
+  pout("%-*s %s\n", TABLEPRINTWIDTH, "FIRMWARE REGEXP:", drivetable->firmwareregexp ?
+       drivetable->firmwareregexp : "");
+  pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", drivetable->modelfamily ?
+       drivetable->modelfamily : "");
+  
+  // if there are any presets, then show them
+  if (presets && (*presets)[0]) while (1) {
+    char out[256];
+    const int attr = (*presets)[0], val  = (*presets)[1];
+    unsigned char fakearray[MAX_ATTRIBUTE_NUM];
+
+    // if we are at the end of the attribute list, break out
+    if (!attr)  
+      break;
+    
+    // This is a hack. ataPrintSmartAttribName() needs a pointer to an
+    // "array" to dereference, so we provide such a pointer.
+    fakearray[attr]=val;
+    ataPrintSmartAttribName(out, attr, fakearray);
+
+    // Use leading zeros instead of spaces so that everything lines up.
+    out[0] = (out[0] == ' ') ? '0' : out[0];
+    out[1] = (out[1] == ' ') ? '0' : out[1];
+    pout("%-*s %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "", out);
+    first_preset = 0;
+    presets++;
+  }
+  else
+    pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required.");
+
+  
+  // Is a special purpose function defined?  If so, describe it
+  if (drivetable->specialpurpose){
+    pout("%-*s ", TABLEPRINTWIDTH, "OTHER PRESETS:");
+    pout("%s\n", drivetable->functiondesc ?
+         drivetable->functiondesc : "A special purpose function "
+         "is defined for this drive"); 
+  }
+  
+  // Print any special warnings
+  if (drivetable->warningmsg){
+    pout("%-*s ", TABLEPRINTWIDTH, "WARNINGS:");
+    pout("%s\n", drivetable->warningmsg);
+  }
+  
+  return;
+}
+
+// Shows all presets for drives in knowndrives[].
+// Returns <0 on syntax error in regular expressions.
+int showallpresets(void){
+  int i;
+  int rc = 0;
+  regex_t regex;
+
+  // loop over all entries in the knowndrives[] table, printing them
+  // out in a nice format
+  for (i=0; knowndrives[i].modelregexp; i++){
+    showonepreset(&knowndrives[i]);
+    pout("\n");
+  }
+
+  // Check all regular expressions
+  for (i=0; knowndrives[i].modelregexp; i++){
+    if (compileregex(&regex, knowndrives[i].modelregexp, REG_EXTENDED))
+      rc = -1;
+    if (knowndrives[i].firmwareregexp) {
+      if (compileregex(&regex, knowndrives[i].firmwareregexp, REG_EXTENDED))
+        rc = -1;
+    }
+  }
+  pout("For information about adding a drive to the database see the FAQ on the\n");
+  pout("smartmontools home page: " PACKAGE_HOMEPAGE "\n");
+  return rc;
+}
+
+// Shows all matching presets for a drive in knowndrives[].
+// Returns # matching entries.
+int showmatchingpresets(const char *model, const char *firmware){
+  int i;
+  int cnt = 0;
+  const char * firmwaremsg = (firmware ? firmware : "(any)");
+  regex_t regex;
+
+  for (i=0; knowndrives[i].modelregexp; i++){
+    if (i > 0)
+      regfree(&regex);
+    if (compileregex(&regex, knowndrives[i].modelregexp, REG_EXTENDED))
+      continue;
+    if (regexec(&regex, model, 0, NULL, 0))
+      continue;
+    if (firmware && knowndrives[i].firmwareregexp) {
+      regfree(&regex);
+      if (compileregex(&regex, knowndrives[i].firmwareregexp, REG_EXTENDED))
+        continue;
+      if (regexec(&regex, firmware, 0, NULL, 0))
+        continue;
+    }
+    if (++cnt == 1)
+      pout("Drive found in smartmontools Database.  Drive identity strings:\n"
+           "%-*s %s\n"
+           "%-*s %s\n"
+           "match smartmontools Drive Database entry:\n",
+           TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmwaremsg);
+    else if (cnt == 2)
+      pout("and match these additional entries:\n");
+    showonepreset(&knowndrives[i]);
+    pout("\n");
+  }
+  regfree(&regex);
+  if (cnt == 0)
+    pout("No presets are defined for this drive.  Its identity strings:\n"
+         "MODEL:    %s\n"
+         "FIRMWARE: %s\n"
+         "do not match any of the known regular expressions.\n",
+         model, firmwaremsg);
+  return cnt;
+}
+
+// Shows the presets (if any) that are available for the given drive.
+void showpresets(const struct ata_identify_device *drive){
+  int i;
+  char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
+
+  // get the drive's model/firmware strings
+  formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH);
+  formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH);
+  
+  // and search to see if they match values in the table
+  if ((i = lookupdrive(model, firmware)) < 0) {
+    // no matches found
+    pout("No presets are defined for this drive.  Its identity strings:\n"
+         "MODEL:    %s\n"
+         "FIRMWARE: %s\n"
+         "do not match any of the known regular expressions.\n"
+         "Use -P showall to list all known regular expressions.\n",
+         model, firmware);
+    return;
+  }
+  
+  // We found a matching drive.  Print out all information about it.
+  pout("Drive found in smartmontools Database.  Drive identity strings:\n"
+       "%-*s %s\n"
+       "%-*s %s\n"
+       "match smartmontools Drive Database entry:\n",
+       TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmware);
+  showonepreset(&knowndrives[i]);
+  return;
+}
+
+// Sets preset vendor attribute options in opts by finding the entry
+// (if any) for the given drive in knowndrives[].  Values that have
+// already been set in opts will not be changed.  Returns <0 if drive
+// not recognized else index >=0 into drive database.
+int applypresets(const struct ata_identify_device *drive, unsigned char **optsptr,
+                smartmonctrl *con) {
+  int i;
+  unsigned char *opts;
+  char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
+  
+  if (*optsptr==NULL)
+    bytes+=MAX_ATTRIBUTE_NUM;
+  
+  if (*optsptr==NULL && !(*optsptr=(unsigned char *)calloc(MAX_ATTRIBUTE_NUM,1))){
+    pout("Unable to allocate memory in applypresets()");
+    bytes-=MAX_ATTRIBUTE_NUM;
+    EXIT(1);
+  }
+  
+  opts=*optsptr;
+  
+  // get the drive's model/firmware strings
+  formatdriveidstring(model, (char *)drive->model, MODEL_STRING_LENGTH);
+  formatdriveidstring(firmware, (char *)drive->fw_rev, FIRMWARE_STRING_LENGTH);
+  
+  // Look up the drive in knowndrives[].
+  if ((i = lookupdrive(model, firmware)) >= 0) {
+    
+    // if vendoropts is non-NULL then Attribute interpretation presets
+    if (knowndrives[i].vendoropts) {
+      const unsigned char (* presets)[2];
+      
+      // For each attribute in list of attribute/val pairs...
+      presets = knowndrives[i].vendoropts;
+      while (1) {
+       const int attr = (*presets)[0];
+       const int val  = (*presets)[1];
+       
+       if (!attr)  
+         break;
+       
+       // ... set attribute if user hasn't already done so.
+       if (!opts[attr])
+         opts[attr] = val;
+       presets++;
+      }
+    }
+    
+    // If a special-purpose function is defined for this drive then
+    // call it. Note that if command line arguments or Directives
+    // over-ride this choice, then the specialpurpose function that is
+    // called must deal with this.
+    if (knowndrives[i].specialpurpose)
+      (*knowndrives[i].specialpurpose)(con);
+  }
+  
+  // return <0 if drive wasn't recognized, or index>=0 into database
+  // if it was
+  return i;
+}
diff --git a/knowndrives.h b/knowndrives.h
new file mode 100644 (file)
index 0000000..1d1e751
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * knowndrives.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ * Address of support mailing list: smartmontools-support@lists.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Philip Williams, Bruce Allen
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef KNOWNDRIVES_H_
+#define KNOWNDRIVES_H_
+
+#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h,v 1.16 2006/04/05 19:50:07 chrfranke Exp $\n"
+
+/* 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)
+ *                  known.
+ *  modelregexp     POSIX regular expression to match the model of a device.
+ *                  This should never be NULL (except to terminate the
+ *                  knowndrives array).
+ *  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
+ *                  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.
+ */
+typedef struct drivesettings_s {
+  const char * const modelfamily;
+  const char * const modelregexp;
+  const char * const firmwareregexp;
+  const char * const warningmsg;
+  const unsigned char (* const vendoropts)[2];
+  void (* const specialpurpose)(smartmonctrl *);
+  const char * const functiondesc;
+} drivesettings;
+
+/* Table of settings for known drives.  Defined in knowndrives.c. */
+extern const drivesettings knowndrives[];
+
+// Searches knowndrives[] for a drive with the given model number and firmware
+// string.
+int lookupdrive(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);
+
+// Shows all presets for drives in knowndrives[].
+// Returns <0 on syntax error in regular expressions.
+int showallpresets(void);
+
+// Shows all matching presets for a drive in knowndrives[].
+// Returns # matching entries.
+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);
+
+#endif
diff --git a/missing b/missing
new file mode 100755 (executable)
index 0000000..64b5f90
--- /dev/null
+++ b/missing
@@ -0,0 +1,353 @@
+#! /bin/sh
+# Common stub for a few missing GNU programs while installing.
+
+scriptversion=2004-09-07.08
+
+# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004
+#   Free Software Foundation, Inc.
+# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that program.
+
+if test $# -eq 0; then
+  echo 1>&2 "Try \`$0 --help' for more information"
+  exit 1
+fi
+
+run=:
+
+# In the cases where this matters, `missing' is being run in the
+# srcdir already.
+if test -f configure.ac; then
+  configure_ac=configure.ac
+else
+  configure_ac=configure.in
+fi
+
+msg="missing on your system"
+
+case "$1" in
+--run)
+  # Try to run requested program, and just exit if it succeeds.
+  run=
+  shift
+  "$@" && exit 0
+  # Exit code 63 means version mismatch.  This often happens
+  # when the user try to use an ancient version of a tool on
+  # a file that requires a minimum version.  In this case we
+  # we should proceed has if the program had been absent, or
+  # if --run hadn't been passed.
+  if test $? = 63; then
+    run=:
+    msg="probably too old"
+  fi
+  ;;
+
+  -h|--h|--he|--hel|--help)
+    echo "\
+$0 [OPTION]... PROGRAM [ARGUMENT]...
+
+Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
+error status if there is no known handling for PROGRAM.
+
+Options:
+  -h, --help      display this help and exit
+  -v, --version   output version information and exit
+  --run           try to run the given command, and emulate it if it fails
+
+Supported PROGRAM values:
+  aclocal      touch file \`aclocal.m4'
+  autoconf     touch file \`configure'
+  autoheader   touch file \`config.h.in'
+  automake     touch all \`Makefile.in' files
+  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
+  flex         create \`lex.yy.c', if possible, from existing .c
+  help2man     touch the output file
+  lex          create \`lex.yy.c', if possible, from existing .c
+  makeinfo     touch the output file
+  tar          try tar, gnutar, gtar, then tar without non-portable flags
+  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]
+
+Send bug reports to <bug-automake@gnu.org>."
+    exit 0
+    ;;
+
+  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
+    echo "missing $scriptversion (GNU Automake)"
+    exit 0
+    ;;
+
+  -*)
+    echo 1>&2 "$0: Unknown \`$1' option"
+    echo 1>&2 "Try \`$0 --help' for more information"
+    exit 1
+    ;;
+
+esac
+
+# Now exit if we have it, but it failed.  Also exit now if we
+# don't have it and --version was passed (most likely to detect
+# the program).
+case "$1" in
+  lex|yacc)
+    # Not GNU programs, they don't have --version.
+    ;;
+
+  tar)
+    if test -n "$run"; then
+       echo 1>&2 "ERROR: \`tar' requires --run"
+       exit 1
+    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+       exit 1
+    fi
+    ;;
+
+  *)
+    if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
+       # We have it, but it failed.
+       exit 1
+    elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
+       # Could not run --version or --help.  This is probably someone
+       # running `$TOOL --version' or `$TOOL --help' to check whether
+       # $TOOL exists and not knowing $TOOL uses missing.
+       exit 1
+    fi
+    ;;
+esac
+
+# If it does not exist, or fails to run (possibly an outdated version),
+# try to emulate it.
+case "$1" in
+  aclocal*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
+         to install the \`Automake' and \`Perl' packages.  Grab them from
+         any GNU archive site."
+    touch aclocal.m4
+    ;;
+
+  autoconf)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`${configure_ac}'.  You might want to install the
+         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
+         archive site."
+    touch configure
+    ;;
+
+  autoheader)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
+         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
+         from any GNU archive site."
+    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
+    test -z "$files" && files="config.h"
+    touch_files=
+    for f in $files; do
+      case "$f" in
+      *:*) touch_files="$touch_files "`echo "$f" |
+                                      sed -e 's/^[^:]*://' -e 's/:.*//'`;;
+      *) touch_files="$touch_files $f.in";;
+      esac
+    done
+    touch $touch_files
+    ;;
+
+  automake*)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
+         You might want to install the \`Automake' and \`Perl' packages.
+         Grab them from any GNU archive site."
+    find . -type f -name Makefile.am -print |
+          sed 's/\.am$/.in/' |
+          while read f; do touch "$f"; done
+    ;;
+
+  autom4te)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, but is $msg.
+         You might have modified some files without having the
+         proper tools for further handling them.
+         You can get \`$1' as part of \`Autoconf' from any GNU
+         archive site."
+
+    file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'`
+    test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'`
+    if test -f "$file"; then
+       touch $file
+    else
+       test -z "$file" || exec >$file
+       echo "#! /bin/sh"
+       echo "# Created by GNU Automake missing as a replacement of"
+       echo "#  $ $@"
+       echo "exit 0"
+       chmod +x $file
+       exit 1
+    fi
+    ;;
+
+  bison|yacc)
+    echo 1>&2 "\
+WARNING: \`$1' $msg.  You should only need it if
+         you modified a \`.y' file.  You may need the \`Bison' package
+         in order for those modifications to take effect.  You can get
+         \`Bison' from any GNU archive site."
+    rm -f y.tab.c y.tab.h
+    if [ $# -ne 1 ]; then
+        eval LASTARG="\${$#}"
+       case "$LASTARG" in
+       *.y)
+           SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
+           if [ -f "$SRCFILE" ]; then
+                cp "$SRCFILE" y.tab.c
+           fi
+           SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
+           if [ -f "$SRCFILE" ]; then
+                cp "$SRCFILE" y.tab.h
+           fi
+         ;;
+       esac
+    fi
+    if [ ! -f y.tab.h ]; then
+       echo >y.tab.h
+    fi
+    if [ ! -f y.tab.c ]; then
+       echo 'main() { return 0; }' >y.tab.c
+    fi
+    ;;
+
+  lex|flex)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified a \`.l' file.  You may need the \`Flex' package
+         in order for those modifications to take effect.  You can get
+         \`Flex' from any GNU archive site."
+    rm -f lex.yy.c
+    if [ $# -ne 1 ]; then
+        eval LASTARG="\${$#}"
+       case "$LASTARG" in
+       *.l)
+           SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
+           if [ -f "$SRCFILE" ]; then
+                cp "$SRCFILE" lex.yy.c
+           fi
+         ;;
+       esac
+    fi
+    if [ ! -f lex.yy.c ]; then
+       echo 'main() { return 0; }' >lex.yy.c
+    fi
+    ;;
+
+  help2man)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+        you modified a dependency of a manual page.  You may need the
+        \`Help2man' package in order for those modifications to take
+        effect.  You can get \`Help2man' from any GNU archive site."
+
+    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+    if test -z "$file"; then
+       file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
+    fi
+    if [ -f "$file" ]; then
+       touch $file
+    else
+       test -z "$file" || exec >$file
+       echo ".ab help2man is required to generate this page"
+       exit 1
+    fi
+    ;;
+
+  makeinfo)
+    echo 1>&2 "\
+WARNING: \`$1' is $msg.  You should only need it if
+         you modified a \`.texi' or \`.texinfo' file, or any other file
+         indirectly affecting the aspect of the manual.  The spurious
+         call might also be the consequence of using a buggy \`make' (AIX,
+         DU, IRIX).  You might want to install the \`Texinfo' package or
+         the \`GNU make' package.  Grab either from any GNU archive site."
+    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
+    if test -z "$file"; then
+      file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
+      file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
+    fi
+    touch $file
+    ;;
+
+  tar)
+    shift
+
+    # We have already tried tar in the generic part.
+    # Look for gnutar/gtar before invocation to avoid ugly error
+    # messages.
+    if (gnutar --version > /dev/null 2>&1); then
+       gnutar "$@" && exit 0
+    fi
+    if (gtar --version > /dev/null 2>&1); then
+       gtar "$@" && exit 0
+    fi
+    firstarg="$1"
+    if shift; then
+       case "$firstarg" in
+       *o*)
+           firstarg=`echo "$firstarg" | sed s/o//`
+           tar "$firstarg" "$@" && exit 0
+           ;;
+       esac
+       case "$firstarg" in
+       *h*)
+           firstarg=`echo "$firstarg" | sed s/h//`
+           tar "$firstarg" "$@" && exit 0
+           ;;
+       esac
+    fi
+
+    echo 1>&2 "\
+WARNING: I can't seem to be able to run \`tar' with the given arguments.
+         You may want to install GNU tar or Free paxutils, or check the
+         command line arguments."
+    exit 1
+    ;;
+
+  *)
+    echo 1>&2 "\
+WARNING: \`$1' is needed, and is $msg.
+         You might have modified some files without having the
+         proper tools for further handling them.  Check the \`README' file,
+         it often tells you about the needed prerequisites for installing
+         this package.  You may also peek at any GNU archive site, in case
+         some other package would contain this missing \`$1' program."
+    exit 1
+    ;;
+esac
+
+exit 0
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
diff --git a/os_darwin.c b/os_darwin.c
new file mode 100644 (file)
index 0000000..d733290
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * os_darwin.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Geoffrey Keating <geoffk@geoffk.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdbool.h>
+#include <errno.h>
+#include <mach/mach.h>
+#include <mach/mach_error.h>
+#include <mach/mach_init.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOReturn.h>
+#include <IOKit/IOBSD.h>
+#include <IOKit/storage/ata/IOATAStorageDefines.h>
+#include <IOKit/storage/ata/ATASMARTLib.h>
+#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
+#include <IOKit/storage/IOMedia.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+  // No, I don't know why there isn't a header for this.
+#define kIOATABlockStorageDeviceClass   "IOATABlockStorageDevice"
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+#include "os_darwin.h"
+
+// Needed by '-V' option (CVS versioning) of smartd/smartctl
+const char *os_XXXX_c_cvsid="$Id: os_darwin.c,v 1.13 2006/04/12 14:54:28 ballen4705 Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+// Print examples for smartctl.
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+  printf(
+         "  smartctl -a disk0                            (Prints all SMART information)\n\n"
+         "  smartctl -t long /dev/disk0              (Executes extended disk self-test)\n\n"
+#ifdef HAVE_GETOPT_LONG
+         "  smartctl --smart=on --saveauto=on /dev/rdisk0 (Enables SMART on first disk)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/disk0\n"
+         "                                        (Prints Self-Test & Attribute errors)\n\n"
+#else
+         "  smartctl -s on -S on /dev/rdisk0              (Enables SMART on first disk)\n\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/disk0\n"
+         "                                        (Prints Self-Test & Attribute errors)\n\n"
+#endif
+         "  smartctl -a IOService:/MacRISC2PE/pci@f4000000/AppleMacRiscPCI/ata-6@D/AppleKauaiATA/ATADeviceNub@0/IOATABlockStorageDriver/IOATABlockStorageDevice\n"
+         "                                                 (You can use IOService: ...)\n\n"
+         "  smartctl -c IODeviceTree:/pci@f4000000/ata-6@D/@0:0\n"
+         "                                                       (... Or IODeviceTree:)\n"
+         );
+  return;
+}
+
+// tries to guess device type given the name (a path).  See utility.h
+// for return values.
+int guess_device_type (const char* dev_name) {
+  // Only ATA is supported right now, so that's what it'd better be.
+  dev_name = dev_name;  // suppress unused warning.
+  return CONTROLLER_ATA;
+}
+
+// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
+// smartd.  Returns number N of devices, or -1 if out of
+// memory. Allocates N+1 arrays: one of N pointers (devlist); the
+// other N arrays each contain null-terminated character strings.  In
+// the case N==0, no arrays are allocated because the array of 0
+// pointers has zero length, equivalent to calling malloc(0).
+int make_device_names (char*** devlist, const char* name) {
+  IOReturn err;
+  io_iterator_t i;
+  io_object_t device;
+  int result;
+  int index;
+  const char * cls;
+
+  if (strcmp (name, "ATA") == 0)
+    cls = kIOATABlockStorageDeviceClass;
+  else  // only ATA supported right now.
+    return 0;
+
+  err = IOServiceGetMatchingServices (kIOMasterPortDefault,
+                                     IOServiceMatching (cls),
+                                     &i);
+  if (err != kIOReturnSuccess)
+    return -1;
+
+  // Count the devices.
+  for (result = 0; (device = IOIteratorNext (i)) != MACH_PORT_NULL; result++)
+    IOObjectRelease (device);
+
+  // Create an array of service names.
+  IOIteratorReset (i);
+  *devlist = Calloc (result, sizeof (char *));
+  if (! *devlist)
+    goto error;
+  for (index = 0; (device = IOIteratorNext (i)) != MACH_PORT_NULL; index++)
+    {
+      io_string_t devName;
+      IORegistryEntryGetPath(device, kIOServicePlane, devName);
+      IOObjectRelease (device);
+
+      (*devlist)[index] = CustomStrDup (devName, true, __LINE__, __FILE__);
+      if (! (*devlist)[index])
+       goto error;
+    }
+  IOObjectRelease (i);
+
+  return result;
+
+ error:
+  IOObjectRelease (i);
+  if (*devlist)
+    {
+      for (index = 0; index < result; index++)
+       if ((*devlist)[index])
+         FreeNonZero ((*devlist)[index], 0, __LINE__, __FILE__);
+      FreeNonZero (*devlist, result * sizeof (char *), __LINE__, __FILE__);
+    }
+  return -1;
+}
+
+// Information that we keep about each device.
+
+static struct {
+  io_object_t ioob;
+  bool hassmart;
+  IOCFPlugInInterface **plugin;
+  IOATASMARTInterface **smartIf;
+} devices[20];
+
+// Like open().  Return non-negative integer handle, only used by the
+// functions below.  type=="ATA" or "SCSI".  The return value is
+// an index into the devices[] array.  If the device can't be opened,
+// sets errno and returns -1.
+// Acceptable device names are:
+// /dev/disk*
+// /dev/rdisk*
+// disk*
+// IOService:*
+// IODeviceTree:*
+int deviceopen(const char *pathname, char *type){
+  size_t devnum;
+  const char *devname;
+  io_object_t disk;
+  
+  if (strcmp (type, "ATA") != 0)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+  
+  // Find a free device number.
+  for (devnum = 0; devnum < sizeof (devices) / sizeof (devices[0]); devnum++)
+    if (! devices[devnum].ioob)
+      break;
+  if (devnum == sizeof (devices) / sizeof (devices[0]))
+    {
+      errno = EMFILE;
+      return -1;
+    }
+  
+  devname = NULL;
+  if (strncmp (pathname, "/dev/rdisk", 10) == 0)
+    devname = pathname + 6;
+  else if (strncmp (pathname, "/dev/disk", 9) == 0)
+    devname = pathname + 5;
+  else if (strncmp (pathname, "disk", 4) == 0)
+    // allow user to just say 'disk0'
+    devname = pathname;
+
+  // Find the device.
+  if (devname)
+    {
+      CFMutableDictionaryRef matcher;
+      matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname);
+      disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher);
+    }
+  else
+    {
+      disk = IORegistryEntryFromPath (kIOMasterPortDefault, pathname);
+    }
+
+  if (! disk)
+    {
+      errno = ENOENT;
+      return -1;
+    }
+  
+  // Find the ATA block storage driver that is the parent of this device
+  while (! IOObjectConformsTo (disk, kIOATABlockStorageDeviceClass))
+    {
+      IOReturn err;
+      io_object_t notdisk = disk;
+
+      err = IORegistryEntryGetParentEntry (notdisk, kIOServicePlane, &disk);
+      if (err != kIOReturnSuccess || ! disk)
+       {
+         errno = ENODEV;
+         IOObjectRelease (notdisk);
+         return -1;
+       }
+    }
+
+  devices[devnum].ioob = disk;
+  
+  {
+    CFDictionaryRef diskChars = NULL;
+    CFNumberRef diskFeatures = NULL;
+    UInt32 ataFeatures;
+
+    // Determine whether the drive actually supports SMART.
+    if ((diskChars = IORegistryEntryCreateCFProperty (disk, 
+                             CFSTR (kIOPropertyDeviceCharacteristicsKey),
+                                                     kCFAllocatorDefault,
+                                                     kNilOptions)) != NULL
+       && CFDictionaryGetValueIfPresent (diskChars, CFSTR ("ATA Features"),
+                                         (const void **)&diskFeatures)
+       && CFNumberGetValue (diskFeatures, kCFNumberLongType, &ataFeatures)
+       && (ataFeatures & kIOATAFeatureSMART))
+      devices[devnum].hassmart = true;
+    else
+      devices[devnum].hassmart = false;
+    if (diskChars)
+      CFRelease (diskChars);
+  }
+  
+  {
+    SInt32 dummy;
+  
+    devices[devnum].plugin = NULL;
+    devices[devnum].smartIf = NULL;
+
+    // Create an interface to the ATA SMART library.
+    if (devices[devnum].hassmart
+       && IOCreatePlugInInterfaceForService (disk,
+                                             kIOATASMARTUserClientTypeID,
+                                             kIOCFPlugInInterfaceID,
+                                             &devices[devnum].plugin,
+                                             &dummy) == kIOReturnSuccess)
+      (*devices[devnum].plugin)->QueryInterface
+       (devices[devnum].plugin,
+        CFUUIDGetUUIDBytes ( kIOATASMARTInterfaceID),
+        (LPVOID) &devices[devnum].smartIf);
+  }
+  
+  return devnum;
+}
+
+// Like close().  Acts only on integer handles returned by
+// deviceopen() above.
+int deviceclose(int fd){
+  if (devices[fd].smartIf)
+    (*devices[fd].smartIf)->Release (devices[fd].smartIf);
+  if (devices[fd].plugin)
+    IODestroyPlugInInterface (devices[fd].plugin);
+  IOObjectRelease (devices[fd].ioob);
+  devices[fd].ioob = MACH_PORT_NULL;
+  return 0;
+}
+
+// Interface to ATA devices.  See os_linux.c for the cannonical example.
+// DETAILED DESCRIPTION OF ARGUMENTS
+//   device: is the integer handle provided by deviceopen()
+//   command: defines the different operations, see atacmds.h
+//   select: additional input data IF NEEDED (which log, which type of
+//           self-test).
+//   data:   location to write output data, IF NEEDED (1 or 512 bytes).
+//   Note: not all commands use all arguments.
+// RETURN VALUES (for all commands BUT command==STATUS_CHECK)
+//  -1 if the command failed
+//   0 if the command succeeded,
+// RETURN VALUES if command==STATUS_CHECK
+//  -1 if the command failed OR the disk SMART status can't be determined
+//   0 if the command succeeded and disk SMART status is "OK"
+//   1 if the command succeeded and disk SMART status is "FAILING"
+
+// Things that aren't available in the Darwin interfaces:
+// - Tests other than short and extended (in particular, can't run
+//   an immediate offline test)
+// - Captive-mode tests, aborting tests
+// - ability to switch automatic offline testing on or off
+
+// Note that some versions of Darwin, at least 7H63 and earlier,
+// have a buggy library that treats the boolean value in
+// SMARTEnableDisableOperations, SMARTEnableDisableAutosave, and
+// SMARTExecuteOffLineImmediate as always being true.
+int marvell_command_interface(int fd, smart_command_set command,
+                     int select, char *data)
+{ return -1; }
+int
+ata_command_interface(int fd, smart_command_set command,
+                     int select, char *data)
+{
+  IOATASMARTInterface **ifp = devices[fd].smartIf;
+  IOATASMARTInterface *smartIf;
+  IOReturn err;
+  
+  if (! ifp)
+    return -1;
+  smartIf = *ifp;
+
+  switch (command)
+    {
+    case STATUS:
+      return 0;
+    case STATUS_CHECK:
+      {
+       Boolean is_failing;
+       err = smartIf->SMARTReturnStatus (ifp, &is_failing);
+       if (err == kIOReturnSuccess && is_failing)
+         return 1;
+       break;
+      }
+    case ENABLE:
+    case DISABLE:
+      err = smartIf->SMARTEnableDisableOperations (ifp, command == ENABLE);
+      break;
+    case AUTOSAVE:
+      err = smartIf->SMARTEnableDisableAutosave (ifp, select != 0);
+      break;
+    case IMMEDIATE_OFFLINE:
+      if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST)
+       {
+         errno = EINVAL;
+         return -1;
+       }
+      err = smartIf->SMARTExecuteOffLineImmediate (ifp, 
+                                                  select == EXTEND_SELF_TEST);
+      break;
+    case READ_VALUES:
+      err = smartIf->SMARTReadData (ifp, (ATASMARTData *)data);
+      break;
+    case READ_THRESHOLDS:
+      err = smartIf->SMARTReadDataThresholds (ifp, 
+                                             (ATASMARTDataThresholds *)data);
+      break;
+    case READ_LOG:
+      err = smartIf->SMARTReadLogAtAddress (ifp, select, data, 512);
+      break;
+    case WRITE_LOG:
+      err = smartIf->SMARTWriteLogAtAddress (ifp, select, data, 512);
+      break;
+    case IDENTIFY:
+      {
+       UInt32 dummy;
+       err = smartIf->GetATAIdentifyData (ifp, data, 512, &dummy);
+       if (err == kIOReturnSuccess && isbigendian())
+         {
+           int i;
+           /* The system has already byte-swapped, undo it.  */
+           for (i = 0; i < 256; i+=2)
+             swap2 (data + i);
+         }
+      }
+      break;
+    case CHECK_POWER_MODE:
+      // The information is right there in the device registry, but how
+      // to get to it portably?
+    default:
+      errno = ENOTSUP;
+      return -1;
+    }
+  if (err == kIOReturnExclusiveAccess)
+    errno = EBUSY;
+  return err == kIOReturnSuccess ? 0 : -1;
+}
+
+// There's no special handling needed for hidden devices, the kernel
+// must deal with them.
+int escalade_command_interface(int fd, int escalade_port, int escalade_type,
+                              smart_command_set command, int select,
+                              char *data)
+{
+  fd = fd;
+  escalade_port = escalade_port;
+  escalade_type = escalade_type;
+  command = command;
+  select = select;
+  data = data;
+  return -1;
+}
+
+// Interface to SCSI devices.  See os_linux.c
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) {
+  return -ENOSYS;
+}
diff --git a/os_darwin.h b/os_darwin.h
new file mode 100644 (file)
index 0000000..115d704
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * os_generic.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Geoff Keating <geoffk@geoffk.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 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_DARWIN_H_
+#define OS_DARWIN_H_
+
+#define OS_DARWIN_H_CVSID "$Id: os_darwin.h,v 1.5 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+// There isn't actually any content here yet.
+
+#endif /* OS_DARWIN_H_ */
diff --git a/os_darwin/English_Localizable.strings b/os_darwin/English_Localizable.strings
new file mode 100644 (file)
index 0000000..4f5d3d6
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+        <key>SMART disk monitoring</key>
+        <string>SMART disk monitoring</string>
+        <key>Starting SMART disk monitoring</key>
+        <string>Starting SMART disk monitoring</string>
+        <key>Stopping SMART disk monitoring</key>
+        <string>Stopping SMART disk monitoring</string>
+</dict>
+</plist>
diff --git a/os_darwin/SMART.in b/os_darwin/SMART.in
new file mode 100644 (file)
index 0000000..c37b5b2
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+# Darwin init file for smartd
+#
+# Home page of code is: http://smartmontools.sourceforge.net
+#
+# Copyright (C) 2004-6 Geoffrey Keating <geoffk@geoffk.org>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the Free
+# Software Foundation; either version 2, or (at your option) any later
+# version.
+#
+# You should have received a copy of the GNU General Public License (for
+# example COPYING); if not, write to the Free Software Foundation, Inc., 675
+# Mass Ave, Cambridge, MA 02139, USA.
+
+# $Id: SMART.in,v 1.4 2006/04/12 14:54:28 ballen4705 Exp $
+
+##
+# SMART monitoring
+##
+
+. /etc/rc.common
+
+StartService ()
+{
+    if [ "${SMARTd:=-YES-}" = "-YES-" ] &&
+       ! GetPID smartd > /dev/null; then
+
+        ConsoleMessage "Starting SMART disk monitoring"
+
+        /usr/sbin/smartd -p /var/run/smartd.pid
+    fi
+}
+
+StopService ()
+{
+    if pid=$(GetPID smartd); then
+        ConsoleMessage "Stopping SMART disk monitoring"
+        kill -TERM "${pid}"
+    else
+        echo "smartd is not running."
+    fi
+}
+
+RestartService ()
+{
+    if pid=$(GetPID smartd); then
+        kill -HUP "${pid}"
+    else
+        StartService
+    fi
+}
+
+RunService "$1"
diff --git a/os_darwin/StartupParameters.plist b/os_darwin/StartupParameters.plist
new file mode 100644 (file)
index 0000000..174d831
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  Description     = "SMART disk monitoring";
+  Provides        = ("SMART");
+  Requires        = ("System Log");
+}
diff --git a/os_freebsd.c b/os_freebsd.c
new file mode 100644 (file)
index 0000000..19bbef1
--- /dev/null
@@ -0,0 +1,1069 @@
+/*
+ * os_freebsd.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Eduard Martinescu <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <err.h>
+#include <camlib.h>
+#include <cam/scsi/scsi_message.h>
+#include <sys/ata.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <fcntl.h>
+#include <stddef.h>
+
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+#include "os_freebsd.h"
+
+static const char *filenameandversion="$Id: os_freebsd.c,v 1.49 2006/04/12 14:54:28 ballen4705 Exp $";
+
+const char *os_XXXX_c_cvsid="$Id: os_freebsd.c,v 1.49 2006/04/12 14:54:28 ballen4705 Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// Private table of open devices: guaranteed zero on startup since
+// part of static data.
+struct freebsd_dev_channel *devicetable[FREEBSD_MAXDEV];
+
+// forward declaration
+static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *ch);
+
+// print examples for smartctl
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+         "  smartctl -a /dev/ad0                       (Prints all SMART information)\n\n"
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/ad0\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl -t long /dev/ad0              (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/ad0\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "                                      (Prints Self-Test & Attribute errors)\n\n"
+         "  smartctl -a --device=3ware,2 /dev/twa0\n"
+         "  smartctl -a --device=3ware,2 /dev/twe0\n"
+         "                              (Prints all SMART information for ATA disk on\n"
+         "                                 third port of first 3ware RAID controller)\n"
+         );
+#else
+  printf(
+         "  smartctl -a /dev/ad0                       (Prints all SMART information)\n"
+         "  smartctl -s on -o on -S on /dev/ad0         (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/ad0              (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/ad0\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl -a -d 3ware,2 /dev/twa0\n"
+         "  smartctl -a -d 3ware,2 /dev/twe0\n"
+         );
+#endif
+  return;
+}
+
+// Like open().  Return positive integer handle, used by functions below only.  mode=="ATA" or "SCSI".
+int deviceopen (const char* dev, char* mode __unused) {
+  struct freebsd_dev_channel *fdchan;
+  int parse_ok, i;
+
+  // Search table for a free entry
+  for (i=0; i<FREEBSD_MAXDEV; i++)
+    if (!devicetable[i])
+      break;
+  
+  // If no free entry found, return error.  We have max allowed number
+  // of "file descriptors" already allocated.
+  if (i==FREEBSD_MAXDEV) {
+    errno=EMFILE;
+    return -1;
+  }
+
+  fdchan = calloc(1,sizeof(struct freebsd_dev_channel));
+  if (fdchan == NULL) {
+    // errno already set by call to malloc()
+    return -1;
+  }
+
+  parse_ok = parse_ata_chan_dev (dev,fdchan);
+  if (parse_ok == CONTROLLER_UNKNOWN) {
+    free(fdchan);
+    errno = ENOTTY;
+    return -1; // can't handle what we don't know
+  }
+
+  if (parse_ok == CONTROLLER_ATA) {
+#ifdef IOCATAREQUEST
+    if ((fdchan->device = open(dev,O_RDONLY))<0) {
+#else
+    if ((fdchan->atacommand = open("/dev/ata",O_RDWR))<0) {
+#endif
+      int myerror = errno;      //preserve across free call
+      free (fdchan);
+      errno = myerror;
+      return -1;
+    }
+  }
+
+  if (parse_ok == CONTROLLER_3WARE_678K_CHAR) {
+    char buf[512];
+    sprintf(buf,"/dev/twe%d",fdchan->device);
+#ifdef IOCATAREQUEST
+    if ((fdchan->device = open(buf,O_RDWR))<0) {
+#else
+    if ((fdchan->atacommand = open(buf,O_RDWR))<0) {
+#endif
+      int myerror = errno; // preserver across free call
+      free(fdchan);
+      errno=myerror;
+      return -1;
+    }
+  }
+
+  if (parse_ok == CONTROLLER_3WARE_9000_CHAR) {
+    char buf[512];
+    sprintf(buf,"/dev/twa%d",fdchan->device);
+#ifdef IOCATAREQUEST
+    if ((fdchan->device = open(buf,O_RDWR))<0) {
+#else
+    if ((fdchan->atacommand = open(buf,O_RDWR))<0) {
+#endif
+      int myerror = errno; // preserver across free call
+      free(fdchan);
+      errno=myerror;
+      return -1;
+    }
+  }
+
+  if (parse_ok == CONTROLLER_SCSI) {
+    // this is really a NO-OP, as the parse takes care
+    // of filling in correct details
+  }
+  
+  // return pointer to "file descriptor" table entry, properly offset.
+  devicetable[i]=fdchan;
+  return i+FREEBSD_FDOFFSET;
+}
+
+// Returns 1 if device not available/open/found else 0.  Also shifts fd into valid range.
+static int isnotopen(int *fd, struct freebsd_dev_channel** fdchan) {
+  // put valid "file descriptor" into range 0...FREEBSD_MAXDEV-1
+  *fd -= FREEBSD_FDOFFSET;
+  
+  // check for validity of "file descriptor".
+  if (*fd<0 || *fd>=FREEBSD_MAXDEV || !((*fdchan)=devicetable[*fd])) {
+    errno = ENODEV;
+    return 1;
+  }
+  
+  return 0;
+}
+
+// Like close().  Acts on handles returned by above function.
+int deviceclose (int fd) {
+  struct freebsd_dev_channel *fdchan;
+  int failed = 0;
+
+  // check for valid file descriptor
+  if (isnotopen(&fd, &fdchan))
+    return -1;
+  
+
+  // did we allocate a SCSI device name?
+  if (fdchan->devname)
+    free(fdchan->devname);
+  
+  // close device, if open
+#ifdef IOCATAREQUEST
+  if (fdchan->device)
+    failed=close(fdchan->device);
+#else
+  if (fdchan->atacommand)
+    failed=close(fdchan->atacommand);
+#endif
+
+  if (fdchan->scsicontrol)
+    failed=close(fdchan->scsicontrol);
+  
+  // if close succeeded, then remove from device list
+  // Eduard, should we also remove it from list if close() fails?  I'm
+  // not sure. Here I only remove it from list if close() worked.
+  if (!failed) {
+    free(fdchan);
+    devicetable[fd]=NULL;
+  }
+  
+  return failed;
+}
+
+#define NO_RETURN 0
+#define BAD_SMART 1
+#define NO_DISK_3WARE 2
+#define BAD_KERNEL 3
+#define MAX_MSG 3
+
+// Utility function for printing warnings
+void printwarning(int msgNo, const char* extra) {
+  static int printed[] = {0,0,0,0};
+  static const char* message[]={
+    "The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n can not be retrieved with this version of ATAng, please do not rely on this value\nYou should update to at least 5.2\n",
+    
+    "Error SMART Status command failed\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
+    
+    "You must specify a DISK # for 3ware drives with -d 3ware,<n> where <n> begins with 1 for first disk drive\n",
+    
+    "ATA support is not provided for this kernel version. Please ugrade to a recent 5-CURRENT kernel (post 09/01/2003 or so)\n"
+  };
+
+  if (msgNo >= 0 && msgNo <= MAX_MSG) {
+    if (!printed[msgNo]) {
+      printed[msgNo] = 1;
+      pout("%s", message[msgNo]);
+      if (extra)
+        pout("%s",extra);
+    }
+  }
+  return;
+}
+
+
+// Interface to ATA devices.  See os_linux.c
+int marvell_command_interface(int fd __unused, smart_command_set command __unused, int select __unused, char *data __unused) {
+       return -1;
+}
+
+int ata_command_interface(int fd, smart_command_set command, int select, char *data) {
+#if !defined(ATAREQUEST) && !defined(IOCATAREQUEST)
+  // sorry, but without ATAng, we can't do anything here
+  printwarning(BAD_KERNEL,NULL);
+  errno = ENOSYS;
+  return -1;
+#else
+  struct freebsd_dev_channel* con;
+  int retval, copydata=0;
+#ifdef IOCATAREQUEST
+  struct ata_ioc_request request;
+#else
+  struct ata_cmd iocmd;
+#endif
+  unsigned char buff[512];
+
+  // check that "file descriptor" is valid
+  if (isnotopen(&fd,&con))
+      return -1;
+
+  bzero(buff,512);
+
+#ifdef IOCATAREQUEST
+  bzero(&request,sizeof(struct ata_ioc_request));
+#else
+  bzero(&iocmd,sizeof(struct ata_cmd));
+#endif
+  bzero(buff,512);
+
+#ifndef IOCATAREQUEST
+  iocmd.cmd=ATAREQUEST;
+  iocmd.channel=con->channel;
+  iocmd.device=con->device;
+#define request iocmd.u.request
+#endif
+
+  request.u.ata.command=ATA_SMART_CMD;
+  request.timeout=600;
+  switch (command){
+  case READ_VALUES:
+    request.u.ata.feature=ATA_SMART_READ_VALUES;
+    request.u.ata.lba=0xc24f<<8;
+    request.flags=ATA_CMD_READ;
+    request.data=buff;
+    request.count=512;
+    copydata=1;
+    break;
+  case READ_THRESHOLDS:
+    request.u.ata.feature=ATA_SMART_READ_THRESHOLDS;
+    request.u.ata.count=1;
+    request.u.ata.lba=1|(0xc24f<<8);
+    request.flags=ATA_CMD_READ;
+    request.data=buff;
+    request.count=512;
+    copydata=1;
+    break;
+  case READ_LOG:
+    request.u.ata.feature=ATA_SMART_READ_LOG_SECTOR;
+    request.u.ata.lba=select|(0xc24f<<8);
+    request.u.ata.count=1;
+    request.flags=ATA_CMD_READ;
+    request.data=buff;
+    request.count=512;
+    copydata=1;
+    break;
+  case IDENTIFY:
+    request.u.ata.command=ATA_IDENTIFY_DEVICE;
+    request.flags=ATA_CMD_READ;
+    request.data=buff;
+    request.count=512;
+    copydata=1;
+    break;
+  case PIDENTIFY:
+    request.u.ata.command=ATA_IDENTIFY_PACKET_DEVICE;
+    request.flags=ATA_CMD_READ;
+    request.data=buff;
+    request.count=512;
+    copydata=1;
+    break;
+  case ENABLE:
+    request.u.ata.feature=ATA_SMART_ENABLE;
+    request.u.ata.lba=0xc24f<<8;
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  case DISABLE:
+    request.u.ata.feature=ATA_SMART_DISABLE;
+    request.u.ata.lba=0xc24f<<8;
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  case AUTO_OFFLINE:
+    // NOTE: According to ATAPI 4 and UP, this command is obsolete
+    request.u.ata.feature=ATA_SMART_AUTO_OFFLINE;
+    request.u.ata.lba=select|(0xc24f<<8);
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  case AUTOSAVE:
+    request.u.ata.feature=ATA_SMART_AUTOSAVE;
+    request.u.ata.count=0xf1;  // to enable autosave
+    request.u.ata.lba=0xc24f<<8;
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  case IMMEDIATE_OFFLINE:
+    request.u.ata.feature=ATA_SMART_IMMEDIATE_OFFLINE;
+    request.u.ata.lba = select|(0xc24f<<8); // put test in sector
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  case STATUS_CHECK: // same command, no HDIO in FreeBSD
+  case STATUS:
+    // this command only says if SMART is working.  It could be
+    // replaced with STATUS_CHECK below.
+    request.u.ata.feature=ATA_SMART_STATUS;
+    request.u.ata.lba=0xc24f<<8;
+    request.flags=ATA_CMD_CONTROL;
+    break;
+  default:
+    pout("Unrecognized command %d in ata_command_interface()\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", command);
+    errno=ENOSYS;
+    return -1;
+  }
+  
+  if (command==STATUS_CHECK){
+    unsigned const char normal_lo=0x4f, normal_hi=0xc2;
+    unsigned const char failed_lo=0xf4, failed_hi=0x2c;
+    unsigned char low,high;
+    
+#ifdef IOCATAREQUEST
+    if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error)
+#else
+    if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error)
+#endif
+      return -1;
+
+#if __FreeBSD_version < 502000
+    printwarning(NO_RETURN,NULL);
+#endif
+
+    high = (request.u.ata.lba >> 16) & 0xff;
+    low = (request.u.ata.lba >> 8) & 0xff;
+    
+    // Cyl low and Cyl high unchanged means "Good SMART status"
+    if (low==normal_lo && high==normal_hi)
+      return 0;
+    
+    // These values mean "Bad SMART status"
+    if (low==failed_lo && high==failed_hi)
+      return 1;
+    
+    // We haven't gotten output that makes sense; print out some debugging info
+    char buf[512];
+    sprintf(buf,"CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
+            (int)request.u.ata.command,
+            (int)request.u.ata.feature,
+            (int)request.u.ata.count,
+            (int)((request.u.ata.lba) & 0xff),
+            (int)((request.u.ata.lba>>8) & 0xff),
+            (int)((request.u.ata.lba>>16) & 0xff),
+            (int)request.error);
+    printwarning(BAD_SMART,buf);
+    return 0;   
+  }
+
+#ifdef IOCATAREQUEST
+  if ((retval=ioctl(con->device, IOCATAREQUEST, &request)) || request.error)
+#else
+  if ((retval=ioctl(con->atacommand, IOCATA, &iocmd)) || request.error)
+#endif
+  {
+    return -1;
+  }
+  // 
+  if (copydata)
+    memcpy(data, buff, 512);
+  
+  return 0;
+#endif
+}
+
+
+// Interface to SCSI devices.  See os_linux.c
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
+{
+  struct freebsd_dev_channel* con = NULL;
+  struct cam_device* cam_dev = NULL;
+  union ccb *ccb;
+  
+  
+    if (report > 0) {
+        unsigned int k;
+        const unsigned char * ucp = iop->cmnd;
+        const char * np;
+
+        np = scsi_get_opcode_name(ucp[0]);
+        pout(" [%s: ", np ? np : "<unknown opcode>");
+        for (k = 0; k < iop->cmnd_len; ++k)
+            pout("%02x ", ucp[k]);
+        if ((report > 1) && 
+            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+            pout("]\n  Outgoing data, len=%d%s:\n", (int)iop->dxfer_len,
+                 (trunc ? " [only first 256 bytes shown]" : ""));
+            dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+        }
+        else
+            pout("]");
+    }
+
+  // check that "file descriptor" is valid
+  if (isnotopen(&fd,&con))
+      return -ENOTTY;
+
+
+  if (!(cam_dev = cam_open_spec_device(con->devname,con->unitnum,O_RDWR,NULL))) {
+    warnx("%s",cam_errbuf);
+    return -1;
+  }
+
+  if (!(ccb = cam_getccb(cam_dev))) {
+    warnx("error allocating ccb");
+    return -ENOMEM;
+  }
+
+  // clear out structure, except for header that was filled in for us
+  bzero(&(&ccb->ccb_h)[1],
+        sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr));
+
+  cam_fill_csio(&ccb->csio,
+                /*retrires*/ 1,
+                /*cbfcnp*/ NULL,
+                /* flags */ (iop->dxfer_dir == DXFER_NONE ? CAM_DIR_NONE :(iop->dxfer_dir == DXFER_FROM_DEVICE ? CAM_DIR_IN : CAM_DIR_OUT)),
+                /* tagaction */ MSG_SIMPLE_Q_TAG,
+                /* dataptr */ iop->dxferp,
+                /* datalen */ iop->dxfer_len,
+                /* senselen */ iop->max_sense_len,
+                /* cdblen */ iop->cmnd_len,
+                /* timout (converted to seconds) */ iop->timeout*1000);
+  memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len);
+
+  if (cam_send_ccb(cam_dev,ccb) < 0) {
+    warn("error sending SCSI ccb");
+ #if __FreeBSD_version > 500000
+    cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr);
+ #endif
+    cam_freeccb(ccb);
+    return -1;
+  }
+
+  if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+ #if __FreeBSD_version > 500000
+    cam_error_print(cam_dev,ccb,CAM_ESF_ALL,CAM_EPF_ALL,stderr);
+ #endif
+    cam_freeccb(ccb);
+    return -1;
+  }
+
+  if (iop->sensep) {
+    memcpy(iop->sensep,&(ccb->csio.sense_data),sizeof(struct scsi_sense_data));
+    iop->resp_sense_len = sizeof(struct scsi_sense_data);
+  }
+
+  iop->scsi_status = ccb->csio.scsi_status;
+
+  cam_freeccb(ccb);
+  
+  if (cam_dev)
+    cam_close_device(cam_dev);
+
+  if (report > 0) {
+    int trunc;
+
+    pout("  status=0\n");
+    trunc = (iop->dxfer_len > 256) ? 1 : 0;
+    
+    pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+         (trunc ? " [only first 256 bytes shown]" : ""));
+    dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+  }
+  return 0;
+}
+
+// Interface to ATA devices behind 3ware escalade RAID controller cards.  See os_linux.c
+
+#define BUFFER_LEN_678K_CHAR ( sizeof(struct twe_usercommand) ) // 520
+#define BUFFER_LEN_9000_CHAR ( sizeof(TW_OSLI_IOCTL_NO_DATA_BUF) + sizeof(TWE_Command) ) // 2048
+#define TW_IOCTL_BUFFER_SIZE ( MAX(BUFFER_LEN_678K_CHAR, BUFFER_LEN_9000_CHAR) )
+
+int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data) {
+  // to hold true file descriptor
+  struct freebsd_dev_channel* con;
+
+  // return value and buffer for ioctl()
+  int  ioctlreturn, readdata=0;
+  struct twe_usercommand* cmd_twe = NULL;
+  TW_OSLI_IOCTL_NO_DATA_BUF* cmd_twa = NULL;
+  TWE_Command_ATA* ata = NULL;
+
+  // Used by both the SCSI and char interfaces
+  char ioctl_buffer[TW_IOCTL_BUFFER_SIZE];
+
+  if (disknum < 0) {
+    printwarning(NO_DISK_3WARE,NULL);
+    return -1;
+  }
+
+  // check that "file descriptor" is valid
+  if (isnotopen(&fd,&con))
+      return -1;
+
+  memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE);
+
+  if (escalade_type==CONTROLLER_3WARE_9000_CHAR) {
+    cmd_twa = (TW_OSLI_IOCTL_NO_DATA_BUF*)ioctl_buffer;
+    cmd_twa->pdata = ((TW_OSLI_IOCTL_WITH_PAYLOAD*)cmd_twa)->payload.data_buf;
+    cmd_twa->driver_pkt.buffer_length = 512;
+    ata = (TWE_Command_ATA*)&cmd_twa->cmd_pkt.command.cmd_pkt_7k;
+  } else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) {
+    cmd_twe = (struct twe_usercommand*)ioctl_buffer;
+    ata = &cmd_twe->tu_command.ata;
+  } else {
+    pout("Unrecognized escalade_type %d in freebsd_3ware_command_interface(disk %d)\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", escalade_type, disknum);
+    errno=ENOSYS;
+    return -1;
+  }
+
+  ata->opcode = TWE_OP_ATA_PASSTHROUGH;
+
+  // Same for (almost) all commands - but some reset below
+  ata->request_id    = 0xFF;
+  ata->unit   = disknum;
+  ata->host_id = 0;
+  ata->status        = 0;           
+  ata->flags         = 0x1;
+  ata->drive_head    = 0x0;
+  ata->sector_num    = 0;
+
+  // All SMART commands use this CL/CH signature.  These are magic
+  // values from the ATA specifications.
+  ata->cylinder_lo   = 0x4F;
+  ata->cylinder_hi   = 0xC2;
+  
+  // SMART ATA COMMAND REGISTER value
+  ata->command       = ATA_SMART_CMD;
+  
+  // Is this a command that reads or returns 512 bytes?
+  // passthru->param values are:
+  // 0x0 - non data command without TFR write check,
+  // 0x8 - non data command with TFR write check,
+  // 0xD - data command that returns data to host from device
+  // 0xF - data command that writes data from host to device
+  // passthru->size values are 0x5 for non-data and 0x07 for data
+  if (command == READ_VALUES     ||
+      command == READ_THRESHOLDS ||
+      command == READ_LOG        ||
+      command == IDENTIFY        ||
+      command == WRITE_LOG ) {
+    readdata=1;
+    if (escalade_type==CONTROLLER_3WARE_678K_CHAR) {
+      cmd_twe->tu_data = data;
+      cmd_twe->tu_size = 512;
+    }
+    ata->sgl_offset = 0x5;
+    ata->size         = 0x5;
+    ata->param        = 0xD;
+    ata->sector_count = 0x1;
+    // For 64-bit to work correctly, up the size of the command packet
+    // in dwords by 1 to account for the 64-bit single sgl 'address'
+    // field. Note that this doesn't agree with the typedefs but it's
+    // right (agree with kernel driver behavior/typedefs).
+    //if (sizeof(long)==8)
+    //  ata->size++;
+  }
+  else {
+    // Non data command -- but doesn't use large sector 
+    // count register values.  
+    ata->sgl_offset = 0x0;
+    ata->size         = 0x5;
+    ata->param        = 0x8;
+    ata->sector_count = 0x0;
+  }
+  
+  // Now set ATA registers depending upon command
+  switch (command){
+  case CHECK_POWER_MODE:
+    ata->command     = ATA_CHECK_POWER_MODE;
+    ata->features    = 0;
+    ata->cylinder_lo = 0;
+    ata->cylinder_hi = 0;
+    break;
+  case READ_VALUES:
+    ata->features = ATA_SMART_READ_VALUES;
+    break;
+  case READ_THRESHOLDS:
+    ata->features = ATA_SMART_READ_THRESHOLDS;
+    break;
+  case READ_LOG:
+    ata->features = ATA_SMART_READ_LOG_SECTOR;
+    // log number to return
+    ata->sector_num  = select;
+    break;
+  case WRITE_LOG:
+    readdata=0;
+    ata->features     = ATA_SMART_WRITE_LOG_SECTOR;
+    ata->sector_count = 1;
+    ata->sector_num   = select;
+    ata->param        = 0xF;  // PIO data write
+    break;
+  case IDENTIFY:
+    // ATA IDENTIFY DEVICE
+    ata->command     = ATA_IDENTIFY_DEVICE;
+    ata->features    = 0;
+    ata->cylinder_lo = 0;
+    ata->cylinder_hi = 0;
+    break;
+  case PIDENTIFY:
+    // 3WARE controller can NOT have packet device internally
+    pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", disknum);
+    errno=ENODEV;
+    return -1;
+  case ENABLE:
+    ata->features = ATA_SMART_ENABLE;
+    break;
+  case DISABLE:
+    ata->features = ATA_SMART_DISABLE;
+    break;
+  case AUTO_OFFLINE:
+    ata->features     = ATA_SMART_AUTO_OFFLINE;
+    // Enable or disable?
+    ata->sector_count = select;
+    break;
+  case AUTOSAVE:
+    ata->features     = ATA_SMART_AUTOSAVE;
+    // Enable or disable?
+    ata->sector_count = select;
+    break;
+  case IMMEDIATE_OFFLINE:
+    ata->features    = ATA_SMART_IMMEDIATE_OFFLINE;
+    // What test type to run?
+    ata->sector_num  = select;
+    break;
+  case STATUS_CHECK:
+    ata->features = ATA_SMART_STATUS;
+    break;
+  case STATUS:
+    // This is JUST to see if SMART is enabled, by giving SMART status
+    // command. But it doesn't say if status was good, or failing.
+    // See below for the difference.
+    ata->features = ATA_SMART_STATUS;
+    break;
+  default:
+    pout("Unrecognized command %d in freebsd_3ware_command_interface(disk %d)\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", command, disknum);
+    errno=ENOSYS;
+    return -1;
+  }
+
+  // Now send the command down through an ioctl()
+  if (escalade_type==CONTROLLER_3WARE_9000_CHAR) {
+#ifdef IOCATAREQUEST
+    ioctlreturn=ioctl(con->device,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa);
+#else
+    ioctlreturn=ioctl(con->atacommand,TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH,cmd_twa);
+#endif
+  } else {
+#ifdef IOCATAREQUEST
+    ioctlreturn=ioctl(con->device,TWEIO_COMMAND,cmd_twe);
+#else
+    ioctlreturn=ioctl(con->atacommand,TWEIO_COMMAND,cmd_twe);
+#endif
+  }
+
+  // Deal with the different error cases
+  if (ioctlreturn) {
+    if (!errno)
+      errno=EIO;
+    return -1;
+  }
+  
+  // See if the ATA command failed.  Now that we have returned from
+  // the ioctl() call, if passthru is valid, then:
+  // - ata->status contains the 3ware controller STATUS
+  // - ata->command contains the ATA STATUS register
+  // - ata->features contains the ATA ERROR register
+  //
+  // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS
+  // If bit 0 (error bit) is set, then ATA ERROR register is valid.
+  // While we *might* decode the ATA ERROR register, at the moment it
+  // doesn't make much sense: we don't care in detail why the error
+  // happened.
+  
+  if (ata->status || (ata->command & 0x21)) {
+    pout("Command failed, ata.status=(0x%2.2x), ata.command=(0x%2.2x), ata.flags=(0x%2.2x)\n",ata->status,ata->command,ata->flags);
+    errno=EIO;
+    return -1;
+  }
+  
+  // If this is a read data command, copy data to output buffer
+  if (readdata) {
+    if (escalade_type==CONTROLLER_3WARE_9000_CHAR)
+      memcpy(data, cmd_twa->pdata, 512);
+  }
+
+  // For STATUS_CHECK, we need to check register values
+  if (command==STATUS_CHECK) {
+    
+    // To find out if the SMART RETURN STATUS is good or failing, we
+    // need to examine the values of the Cylinder Low and Cylinder
+    // High Registers.
+    
+    unsigned short cyl_lo=ata->cylinder_lo;
+    unsigned short cyl_hi=ata->cylinder_hi;
+    
+    // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good.
+    if (cyl_lo==0x4F && cyl_hi==0xC2)
+      return 0;
+    
+    // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL
+    if (cyl_lo==0xF4 && cyl_hi==0x2C)
+      return 1;
+    
+      errno=EIO;
+      return -1;
+  }
+  
+  // copy sector count register (one byte!) to return data
+  if (command==CHECK_POWER_MODE)
+    *data=*(char *)&(ata->sector_count);
+  
+  // look for nonexistent devices/ports
+  if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) {
+    errno=ENODEV;
+    return -1;
+  }
+  
+  return 0;
+}
+
+static int get_tw_channel_unit (const char* name, int* unit, int* dev) {
+  const char *p;
+
+  /* device node sanity check */
+  for (p = name + 3; *p; p++)
+    if (*p < '0' || *p > '9')
+      return -1;
+  if (strlen(name) > 4 && *(name + 3) == '0')
+    return -1;
+
+  if (dev != NULL)
+    *dev=atoi(name + 3);
+
+  /* no need for unit number */
+  if (unit != NULL)
+    *unit=0;
+  return 0;
+}
+
+
+#ifndef IOCATAREQUEST
+static int get_ata_channel_unit ( const char* name, int* unit, int* dev) {
+#ifndef ATAREQUEST
+  *dev=0;
+  *unit=0;
+return 0;
+#else
+  // there is no direct correlation between name 'ad0, ad1, ...' and
+  // channel/unit number.  So we need to iterate through the possible
+  // channels and check each unit to see if we match names
+  struct ata_cmd iocmd;
+  int fd,maxunit;
+  
+  bzero(&iocmd, sizeof(struct ata_cmd));
+
+  if ((fd = open("/dev/ata", O_RDWR)) < 0)
+    return -errno;
+  
+  iocmd.cmd = ATAGMAXCHANNEL;
+  if (ioctl(fd, IOCATA, &iocmd) < 0) {
+    return -errno;
+    close(fd);
+  }
+  maxunit = iocmd.u.maxchan;
+  for (*unit = 0; *unit < maxunit; (*unit)++) {
+    iocmd.channel = *unit;
+    iocmd.device = -1;
+    iocmd.cmd = ATAGPARM;
+    if (ioctl(fd, IOCATA, &iocmd) < 0) {
+      close(fd);
+      return -errno;
+    }
+    if (iocmd.u.param.type[0] && !strcmp(name,iocmd.u.param.name[0])) {
+      *dev = 0;
+      break;
+    }
+    if (iocmd.u.param.type[1] && !strcmp(name,iocmd.u.param.name[1])) {
+      *dev = 1;
+      break;
+    }
+  }
+  close(fd);
+  if (*unit == maxunit)
+    return -1;
+  else
+    return 0;
+#endif
+}
+#endif
+
+// Guess device type (ata or scsi) based on device name (FreeBSD
+// specific) SCSI device name in FreeBSD can be sd, sr, scd, st, nst,
+// osst, nosst and sg.
+static const char * fbsd_dev_prefix = "/dev/";
+static const char * fbsd_dev_ata_disk_prefix = "ad";
+static const char * fbsd_dev_scsi_disk_plus = "da";
+static const char * fbsd_dev_scsi_tape1 = "sa";
+static const char * fbsd_dev_scsi_tape2 = "nsa";
+static const char * fbsd_dev_scsi_tape3 = "esa";
+static const char * fbsd_dev_twe_ctrl = "twe";
+static const char * fbsd_dev_twa_ctrl = "twa";
+
+static int parse_ata_chan_dev(const char * dev_name, struct freebsd_dev_channel *chan) {
+  int len;
+  int dev_prefix_len = strlen(fbsd_dev_prefix);
+  
+  // if dev_name null, or string length zero
+  if (!dev_name || !(len = strlen(dev_name)))
+    return CONTROLLER_UNKNOWN;
+  
+  // Remove the leading /dev/... if it's there
+  if (!strncmp(fbsd_dev_prefix, dev_name, dev_prefix_len)) {
+    if (len <= dev_prefix_len) 
+      // if nothing else in the string, unrecognized
+      return CONTROLLER_UNKNOWN;
+    // else advance pointer to following characters
+    dev_name += dev_prefix_len;
+  }
+  // form /dev/ad* or ad*
+  if (!strncmp(fbsd_dev_ata_disk_prefix, dev_name,
+               strlen(fbsd_dev_ata_disk_prefix))) {
+#ifndef IOCATAREQUEST
+    if (chan != NULL) {
+      if (get_ata_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) {
+        return CONTROLLER_UNKNOWN;
+      }
+    }
+#endif
+    return CONTROLLER_ATA;
+  }
+  
+  // form /dev/da* or da*
+  if (!strncmp(fbsd_dev_scsi_disk_plus, dev_name,
+               strlen(fbsd_dev_scsi_disk_plus)))
+    goto handlescsi;
+
+  // form /dev/sa* or sa*
+  if (!strncmp(fbsd_dev_scsi_tape1, dev_name,
+              strlen(fbsd_dev_scsi_tape1)))
+    goto handlescsi;
+
+  // form /dev/nsa* or nsa*
+  if (!strncmp(fbsd_dev_scsi_tape2, dev_name,
+              strlen(fbsd_dev_scsi_tape2)))
+    goto handlescsi;
+
+  // form /dev/esa* or esa*
+  if (!strncmp(fbsd_dev_scsi_tape3, dev_name,
+              strlen(fbsd_dev_scsi_tape3)))
+    goto handlescsi;
+  
+  if (!strncmp(fbsd_dev_twa_ctrl,dev_name,
+              strlen(fbsd_dev_twa_ctrl))) {
+    if (chan != NULL) {
+      if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) {
+       return CONTROLLER_UNKNOWN;
+      }
+    }
+    else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) {
+       return CONTROLLER_UNKNOWN;
+    }
+    return CONTROLLER_3WARE_9000_CHAR;
+  }
+
+  if (!strncmp(fbsd_dev_twe_ctrl,dev_name,
+              strlen(fbsd_dev_twe_ctrl))) {
+    if (chan != NULL) {
+      if (get_tw_channel_unit(dev_name,&(chan->channel),&(chan->device))<0) {
+       return CONTROLLER_UNKNOWN;
+      }
+    }
+    else if (get_tw_channel_unit(dev_name,NULL,NULL)<0) {
+       return CONTROLLER_UNKNOWN;
+    }
+    return CONTROLLER_3WARE_678K_CHAR;
+  }
+
+  // we failed to recognize any of the forms
+  return CONTROLLER_UNKNOWN;
+
+ handlescsi:
+  if (chan != NULL) {
+    if (!(chan->devname = calloc(1,DEV_IDLEN+1)))
+      return CONTROLLER_UNKNOWN;
+    
+    if (cam_get_device(dev_name,chan->devname,DEV_IDLEN,&(chan->unitnum)) == -1)
+      return CONTROLLER_UNKNOWN;
+  }
+  return CONTROLLER_SCSI;
+  
+}
+
+int guess_device_type (const char* dev_name) {
+  return parse_ata_chan_dev(dev_name,NULL);
+}
+
+// global variable holding byte count of allocated memory
+extern long long bytes;
+
+// we are going to take advantage of the fact that FreeBSD's devfs will only
+// have device entries for devices that exist.  So if we get the equivilent of
+// ls /dev/ad?, we have all the ATA devices on the system
+//
+// If any errors occur, leave errno set as it was returned by the
+// system call, and return <0.
+
+// Return values:
+// -1 out of memory
+// -2 to -5 errors in glob
+
+int get_dev_names(char*** names, const char* prefix) {
+  int n = 0;
+  char** mp;
+  int retglob,lim;
+  glob_t globbuf;
+  int i;
+  char pattern1[128],pattern2[128];
+
+  bzero(&globbuf,sizeof(globbuf));
+  // in case of non-clean exit
+  *names=NULL;
+
+  // handle 0-99 possible devices, will still be limited by MAX_NUM_DEV
+  sprintf(pattern1,"/dev/%s[0-9]",prefix);
+  sprintf(pattern2,"/dev/%s[0-9][0-9]",prefix);
+  
+  // Use glob to look for any directory entries matching the patterns
+  // first call inits with first pattern match, second call appends
+  // to first list. Turn on NOCHECK for second call. This results in no
+  // error if no more matches found, however it does append the actual
+  // pattern to the list of paths....
+  if ((retglob=glob(pattern1, GLOB_ERR, NULL, &globbuf)) ||
+      (retglob=glob(pattern2, GLOB_ERR|GLOB_APPEND|GLOB_NOCHECK,NULL,&globbuf))) {
+     int retval = -1;
+    // glob failed
+    if (retglob==GLOB_NOSPACE)
+      pout("glob(3) ran out of memory matching patterns (%s),(%s)\n",
+           pattern1, pattern2);
+    else if (retglob==GLOB_ABORTED)
+      pout("glob(3) aborted matching patterns (%s),(%s)\n",
+           pattern1, pattern2);
+    else if (retglob==GLOB_NOMATCH) {
+      pout("glob(3) found no matches for patterns (%s),(%s)\n",
+           pattern1, pattern2);
+      retval = 0;
+    }
+    else if (retglob)
+      pout("Unexplained error in glob(3) of patterns (%s),(%s)\n",
+           pattern1, pattern2);
+    
+    //  Free memory and return
+    globfree(&globbuf);
+
+    return retval;
+  }
+
+  // did we find too many paths?
+  // did we find too many paths?
+  lim = globbuf.gl_pathc < MAX_NUM_DEV ? globbuf.gl_pathc : MAX_NUM_DEV;
+  if (lim < globbuf.gl_pathc)
+    pout("glob(3) found %d > MAX=%d devices matching patterns (%s),(%s): ignoring %d paths\n", 
+         globbuf.gl_pathc, MAX_NUM_DEV, pattern1,pattern2,
+         globbuf.gl_pathc-MAX_NUM_DEV);
+  
+  // allocate space for up to lim number of ATA devices
+  if (!(mp =  (char **)calloc(lim, sizeof(char*)))){
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+
+  // now step through the list returned by glob.  No link checking needed
+  // in FreeBSD
+  for (i=0; i<globbuf.gl_pathc; i++){
+    // becuase of the NO_CHECK on second call to glob,
+    // the pattern itself will be added to path list..
+    // so ignore any paths that have the ']' from pattern
+    if (strchr(globbuf.gl_pathv[i],']') == NULL)
+      mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion);
+  }
+
+  globfree(&globbuf);
+  mp = realloc(mp,n*(sizeof(char*))); // shrink to correct size
+  bytes += (n)*(sizeof(char*)); // and set allocated byte count
+  *names=mp;
+  return n;
+}
+
+int make_device_names (char*** devlist, const char* name) {
+  if (!strcmp(name,"SCSI"))
+    return get_dev_names(devlist,"da");
+  else if (!strcmp(name,"ATA"))
+    return get_dev_names(devlist,"ad");
+  else
+    return 0;
+}
diff --git a/os_freebsd.h b/os_freebsd.h
new file mode 100644 (file)
index 0000000..da50231
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * os_freebsd.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Eduard Martinescu <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 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/
+ *
+ */
+
+/*-
+ * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2003 Paul Saab
+ * Copyright (c) 2003 Vinod Kashyap
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/*
+ * Copyright (c) 2004-05 Applied Micro Circuits Corporation.
+ * Copyright (c) 2004-05 Vinod Kashyap
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef OS_FREEBSD_H_
+#define OS_FREEBSD_H_
+
+#define OS_FREEBSD_H_CVSID "$Id: os_freebsd.h,v 1.20 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+struct freebsd_dev_channel {
+  int   channel;                // the ATA channel to work with
+  int   device;                 // the device on the channel
+  int   atacommand;             // the ATA Command file descriptor (/dev/ata)
+  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 MAX_NUM_DEV 26
+
+#ifdef  HAVE_SYS_TWEREG_H
+#include <sys/twereg.h>
+#else
+/**
+ *  The following cut out of twereg.h
+ *
+ */
+#if __FreeBSD_version < 500040
+#define __packed __attribute__((__packed__))
+#endif
+
+#define TWE_MAX_SGL_LENGTH             62
+#define TWE_MAX_ATA_SGL_LENGTH         60
+#define TWE_OP_ATA_PASSTHROUGH         0x11
+
+/* scatter/gather list entry */
+typedef struct
+{
+    u_int32_t  address;
+    u_int32_t  length;
+} __packed TWE_SG_Entry;
+
+typedef struct {
+    u_int8_t   opcode:5;               /* TWE_OP_INITCONNECTION */
+    u_int8_t   res1:3;         
+    u_int8_t   size;
+    u_int8_t   request_id;
+    u_int8_t   res2:4;
+    u_int8_t   host_id:4;
+    u_int8_t   status;
+    u_int8_t   flags;
+    u_int16_t  message_credits;
+    u_int32_t  response_queue_pointer;
+} __packed TWE_Command_INITCONNECTION;
+
+typedef struct
+{
+    u_int8_t   opcode:5;               /* TWE_OP_READ/TWE_OP_WRITE */
+    u_int8_t   res1:3;
+    u_int8_t   size;
+    u_int8_t   request_id;
+    u_int8_t   unit:4;
+    u_int8_t   host_id:4;
+    u_int8_t   status;
+    u_int8_t   flags;
+    u_int16_t  block_count;
+    u_int32_t  lba;
+    TWE_SG_Entry sgl[TWE_MAX_SGL_LENGTH];
+} __packed TWE_Command_IO;
+
+typedef struct
+{
+    u_int8_t   opcode:5;               /* TWE_OP_HOTSWAP */
+    u_int8_t   res1:3;
+    u_int8_t   size;
+    u_int8_t   request_id;
+    u_int8_t   unit:4;
+    u_int8_t   host_id:4;
+    u_int8_t   status;
+    u_int8_t   flags;
+    u_int8_t   action;
+#define TWE_OP_HOTSWAP_REMOVE          0x00    /* remove assumed-degraded unit */
+#define TWE_OP_HOTSWAP_ADD_CBOD                0x01    /* add CBOD to empty port */
+#define TWE_OP_HOTSWAP_ADD_SPARE       0x02    /* add spare to empty port */
+    u_int8_t   aport;
+} __packed TWE_Command_HOTSWAP;
+
+typedef struct
+{
+    u_int8_t   opcode:5;               /* TWE_OP_SETATAFEATURE */
+    u_int8_t   res1:3;
+    u_int8_t   size;
+    u_int8_t   request_id;
+    u_int8_t   unit:4;
+    u_int8_t   host_id:4;
+    u_int8_t   status;
+    u_int8_t   flags;
+    u_int8_t   feature;
+#define TWE_OP_SETATAFEATURE_WCE       0x02
+#define TWE_OP_SETATAFEATURE_DIS_WCE   0x82
+    u_int8_t   feature_mode;
+    u_int16_t  all_units;
+    u_int16_t  persistence;
+} __packed TWE_Command_SETATAFEATURE;
+
+typedef struct
+{
+    u_int8_t   opcode:5;               /* TWE_OP_CHECKSTATUS */
+    u_int8_t   res1:3;
+    u_int8_t   size;
+    u_int8_t   request_id;
+    u_int8_t   unit:4;
+    u_int8_t   res2:4;
+    u_int8_t   status;
+    u_int8_t   flags;
+    u_int16_t  target_status;          /* set low byte to target request's ID */
+} __packed TWE_Command_CHECKSTATUS;
+
+typedef struct
+{
+    u_int8_t   opcode:5;               /* TWE_OP_GETPARAM, TWE_OP_SETPARAM */
+    u_int8_t   res1:3;
+    u_int8_t   size;
+    u_int8_t   request_id;
+    u_int8_t   unit:4;
+    u_int8_t   host_id:4;
+    u_int8_t   status;
+    u_int8_t   flags;
+    u_int16_t  param_count;
+    TWE_SG_Entry sgl[TWE_MAX_SGL_LENGTH];
+} __packed TWE_Command_PARAM;
+
+typedef struct
+{
+    u_int8_t   opcode:5;               /* TWE_OP_REBUILDUNIT */
+    u_int8_t   res1:3;
+    u_int8_t   size;
+    u_int8_t   request_id;
+    u_int8_t   src_unit:4;
+    u_int8_t   host_id:4;
+    u_int8_t   status;
+    u_int8_t   flags;
+    u_int8_t   action:7;
+#define TWE_OP_REBUILDUNIT_NOP         0
+#define TWE_OP_REBUILDUNIT_STOP                2       /* stop all rebuilds */
+#define TWE_OP_REBUILDUNIT_START       4       /* start rebuild with lowest unit */
+#define TWE_OP_REBUILDUNIT_STARTUNIT   5       /* rebuild src_unit (not supported) */
+    u_int8_t   cs:1;                           /* request state change on src_unit */
+    u_int8_t   logical_subunit;                /* for RAID10 rebuild of logical subunit */
+} __packed TWE_Command_REBUILDUNIT;
+
+typedef struct
+{
+    u_int8_t   opcode:5;
+    u_int8_t   sgl_offset:3;
+    u_int8_t   size;
+    u_int8_t   request_id;
+    u_int8_t   unit:4;
+    u_int8_t   host_id:4;
+    u_int8_t   status;
+    u_int8_t   flags;
+    u_int16_t  param;
+    u_int16_t  features;
+    u_int16_t  sector_count;
+    u_int16_t  sector_num;
+    u_int16_t  cylinder_lo;
+    u_int16_t  cylinder_hi;
+    u_int8_t   drive_head;
+    u_int8_t   command;
+    TWE_SG_Entry sgl[TWE_MAX_ATA_SGL_LENGTH];
+} __packed TWE_Command_ATA;
+
+typedef struct
+{
+    u_int8_t   opcode:5;
+    u_int8_t   sgl_offset:3;
+    u_int8_t   size;
+    u_int8_t   request_id;
+    u_int8_t   unit:4;
+    u_int8_t   host_id:4;
+    u_int8_t   status;
+    u_int8_t   flags;
+#define TWE_FLAGS_SUCCESS      0x00
+#define TWE_FLAGS_INFORMATIONAL        0x01
+#define TWE_FLAGS_WARNING      0x02
+#define TWE_FLAGS_FATAL                0x03
+#define TWE_FLAGS_PERCENTAGE   (1<<8)  /* bits 0-6 indicate completion percentage */
+    u_int16_t  count;                  /* block count, parameter count, message credits */
+} __packed TWE_Command_Generic;
+
+/* command packet - must be TWE_ALIGNMENT aligned */
+typedef union
+{
+    TWE_Command_INITCONNECTION initconnection;
+    TWE_Command_IO             io;
+    TWE_Command_PARAM          param;
+    TWE_Command_CHECKSTATUS    checkstatus;
+    TWE_Command_REBUILDUNIT    rebuildunit;
+    TWE_Command_SETATAFEATURE  setatafeature;
+    TWE_Command_ATA            ata;
+    TWE_Command_Generic                generic;
+    u_int8_t                   pad[512];
+} TWE_Command;
+
+/* response queue entry */
+typedef union
+{
+    struct 
+    {
+       u_int32_t       undefined_1:4;
+       u_int32_t       response_id:8;
+       u_int32_t       undefined_2:20;
+    } u;
+    u_int32_t  value;
+} TWE_Response_Queue;
+
+#endif
+
+#ifdef  HAVE_SYS_TWEIO_H
+#include <sys/tweio.h>
+#else
+/*
+ * Following cut out of tweio.h
+ *
+ */
+/*
+ * User-space command
+ *
+ * Note that the command's scatter/gather list will be computed by the
+ * driver, and cannot be filled in by the consumer.
+ */
+struct twe_usercommand {
+    TWE_Command        tu_command;     /* command ready for the controller */
+    void       *tu_data;       /* pointer to data in userspace */
+    size_t     tu_size;        /* userspace data length */
+};
+
+#define TWEIO_COMMAND          _IOWR('T', 100, struct twe_usercommand)
+
+#endif
+
+#ifdef  HAVE_SYS_TW_OSL_IOCTL_H
+#include <sys/tw_osl_ioctl.h>
+#else
+/*
+ * Following cut out of tw_osl_types.h
+ *
+ */
+
+typedef void                   TW_VOID;
+typedef char                   TW_INT8;
+typedef unsigned char          TW_UINT8;
+typedef short                  TW_INT16;
+typedef unsigned short         TW_UINT16;
+typedef int                    TW_INT32;
+typedef unsigned int           TW_UINT32;
+typedef long long              TW_INT64;
+typedef unsigned long long     TW_UINT64;
+
+/*
+ * Following cut out of tw_cl_share.h
+ *
+ */
+
+#pragma pack(1)
+
+struct tw_cl_event_packet {
+       TW_UINT32       sequence_id;
+       TW_UINT32       time_stamp_sec;
+       TW_UINT16       aen_code;
+       TW_UINT8        severity;
+       TW_UINT8        retrieved;
+       TW_UINT8        repeat_count;
+       TW_UINT8        parameter_len;
+       TW_UINT8        parameter_data[98];
+       TW_UINT32       event_src;
+       TW_UINT8        severity_str[20];
+};
+
+#pragma pack()
+
+/*
+ * Following cut out of tw_cl_fwif.h
+ *
+ */
+
+#define TWA_FW_CMD_ATA_PASSTHROUGH             0x11
+
+#define TWA_SENSE_DATA_LENGTH          18
+
+#pragma pack(1)
+/* 7000 structures. */
+struct tw_cl_command_init_connect {
+       TW_UINT8        res1__opcode;   /* 3:5 */
+       TW_UINT8        size;
+       TW_UINT8        request_id;
+       TW_UINT8        res2;
+       TW_UINT8        status;
+       TW_UINT8        flags;
+       TW_UINT16       message_credits;
+       TW_UINT32       features;
+       TW_UINT16       fw_srl;
+       TW_UINT16       fw_arch_id;
+       TW_UINT16       fw_branch;
+       TW_UINT16       fw_build;
+       TW_UINT32       result;
+};
+
+
+/* Structure for downloading firmware onto the controller. */
+struct tw_cl_command_download_firmware {
+       TW_UINT8        sgl_off__opcode;/* 3:5 */
+       TW_UINT8        size;
+       TW_UINT8        request_id;
+       TW_UINT8        unit;
+       TW_UINT8        status;
+       TW_UINT8        flags;
+       TW_UINT16       param;
+       TW_UINT8        sgl[1];
+};
+
+
+/* Structure for hard resetting the controller. */
+struct tw_cl_command_reset_firmware {
+       TW_UINT8        res1__opcode;   /* 3:5 */
+       TW_UINT8        size;
+       TW_UINT8        request_id;
+       TW_UINT8        unit;
+       TW_UINT8        status;
+       TW_UINT8        flags;
+       TW_UINT8        res2;
+       TW_UINT8        param;
+};
+
+
+/* Structure for sending get/set param commands. */
+struct tw_cl_command_param {
+       TW_UINT8        sgl_off__opcode;/* 3:5 */
+       TW_UINT8        size;
+       TW_UINT8        request_id;
+       TW_UINT8        host_id__unit;  /* 4:4 */
+       TW_UINT8        status;
+       TW_UINT8        flags;
+       TW_UINT16       param_count;
+       TW_UINT8        sgl[1];
+};
+
+
+/* Generic command packet. */
+struct tw_cl_command_generic {
+       TW_UINT8        sgl_off__opcode;/* 3:5 */
+       TW_UINT8        size;
+       TW_UINT8        request_id;
+       TW_UINT8        host_id__unit;  /* 4:4 */
+       TW_UINT8        status;
+       TW_UINT8        flags;
+       TW_UINT16       count;  /* block cnt, parameter cnt, message credits */
+};
+
+
+/* Command packet header. */
+struct tw_cl_command_header {
+       TW_UINT8        sense_data[TWA_SENSE_DATA_LENGTH];
+       struct {
+               TW_INT8         reserved[4];
+               TW_UINT16       error;
+               TW_UINT8        padding;
+               TW_UINT8        res__severity;  /* 5:3 */
+       } status_block;
+       TW_UINT8        err_specific_desc[98];
+       struct {
+               TW_UINT8        size_header;
+               TW_UINT16       reserved;
+               TW_UINT8        size_sense;
+       } header_desc;
+};
+
+
+/* 7000 Command packet. */
+union tw_cl_command_7k {
+       struct tw_cl_command_init_connect       init_connect;
+       struct tw_cl_command_download_firmware  download_fw;
+       struct tw_cl_command_reset_firmware     reset_fw;
+       struct tw_cl_command_param              param;
+       struct tw_cl_command_generic            generic;
+       TW_UINT8        padding[1024 - sizeof(struct tw_cl_command_header)];
+};
+
+
+/* 9000 Command Packet. */
+struct tw_cl_command_9k {
+       TW_UINT8        res__opcode;    /* 3:5 */
+       TW_UINT8        unit;
+       TW_UINT16       lun_l4__req_id; /* 4:12 */
+       TW_UINT8        status;
+       TW_UINT8        sgl_offset; /* offset (in bytes) to sg_list, from the
+                                       end of sgl_entries */
+       TW_UINT16       lun_h4__sgl_entries;
+       TW_UINT8        cdb[16];
+       TW_UINT8        sg_list[872];/* total struct size =
+                                       1024-sizeof(cmd_hdr) */
+};
+
+
+/* Full command packet. */
+struct tw_cl_command_packet {
+       struct tw_cl_command_header     cmd_hdr;
+       union {
+               union tw_cl_command_7k  cmd_pkt_7k;
+               struct tw_cl_command_9k cmd_pkt_9k;
+       } command;
+};
+
+#pragma pack()
+
+/*
+ * Following cut out of tw_cl_ioctl.h
+ *
+ */
+
+#pragma pack(1)
+
+/* Structure used to handle GET/RELEASE LOCK ioctls. */
+struct tw_cl_lock_packet {
+       TW_UINT32       timeout_msec;
+       TW_UINT32       time_remaining_msec;
+       TW_UINT32       force_flag;
+};
+
+
+/* Structure used to handle GET COMPATIBILITY INFO ioctl. */
+struct tw_cl_compatibility_packet {
+       TW_UINT8        driver_version[32];/* driver version */
+       TW_UINT16       working_srl;    /* driver & firmware negotiated srl */
+       TW_UINT16       working_branch; /* branch # of the firmware that the
+                                       driver is compatible with */
+       TW_UINT16       working_build;  /* build # of the firmware that the
+                                       driver is compatible with */
+};
+
+
+/* Driver understandable part of the ioctl packet built by the API. */
+struct tw_cl_driver_packet {
+       TW_UINT32       control_code;
+       TW_UINT32       status;
+       TW_UINT32       unique_id;
+       TW_UINT32       sequence_id;
+       TW_UINT32       os_status;
+       TW_UINT32       buffer_length;
+};
+
+#pragma pack()
+
+/*
+ * Following cut out of tw_osl_ioctl.h
+ *
+ */
+
+#pragma pack(1)
+/*
+ * We need the structure below to ensure that the first byte of
+ * data_buf is not overwritten by the kernel, after we return
+ * from the ioctl call.  Note that cmd_pkt has been reduced
+ * to an array of 1024 bytes even though it's actually 2048 bytes
+ * in size.  This is because, we don't expect requests from user
+ * land requiring 2048 (273 sg elements) byte cmd pkts.
+ */
+typedef struct tw_osli_ioctl_no_data_buf {
+       struct tw_cl_driver_packet      driver_pkt;
+       TW_VOID                         *pdata; /* points to data_buf */
+       TW_INT8                         padding[488 - sizeof(TW_VOID *)];
+       struct tw_cl_command_packet     cmd_pkt;
+} TW_OSLI_IOCTL_NO_DATA_BUF;
+
+#pragma pack()
+
+#define TW_OSL_IOCTL_FIRMWARE_PASS_THROUGH             \
+       _IOWR('T', 202, TW_OSLI_IOCTL_NO_DATA_BUF)
+
+#pragma pack(1)
+
+typedef struct tw_osli_ioctl_with_payload {
+       struct tw_cl_driver_packet      driver_pkt;
+       TW_INT8                         padding[488];
+       struct tw_cl_command_packet     cmd_pkt;
+       union {
+               struct tw_cl_event_packet               event_pkt;
+               struct tw_cl_lock_packet                lock_pkt;
+               struct tw_cl_compatibility_packet       compat_pkt;
+               TW_INT8                                 data_buf[1];
+       } payload;
+} TW_OSLI_IOCTL_WITH_PAYLOAD;
+
+#pragma pack()
+
+#endif
+
+
+#ifndef __unused
+#define __unused __attribute__ ((__unused__))
+#endif
+
+#endif /* OS_FREEBSD_H_ */
diff --git a/os_generic.c b/os_generic.c
new file mode 100644 (file)
index 0000000..3c5b5c4
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * os_generic.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) YEAR YOUR_NAME <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*  PORTING NOTES AND COMMENTS
+
+    To port smartmontools to the OS of your choice, please:
+
+ [0] Contact smartmontools-support@lists.sourceforge.net to check
+     that it's not already been done.
+
+ [1] Make copies of os_generic.[hc] called os_myOS.[hc].
+
+ [2] Modify configure.in so that case "${host}" includes myOS.
+
+ [3] Verify that ./autogen.sh && ./configure && make compiles the
+     code.  If not, fix any compilation problems.  If your OS lacks
+     some function that is used elsewhere in the code, then add a
+     AC_CHECK_FUNCS([missingfunction]) line to configure.in, and
+     surround uses of the function with:
+     #ifdef HAVE_MISSINGFUNCTION
+     ... 
+     #endif
+     where the macro HAVE_MISSINGFUNCTION is (or is not) defined in
+     config.h.
+
+ [4] Provide the functions defined in this file by fleshing out the
+     skeletons below.  You can entirely eliminate the function
+     'unsupported()'.
+
+ [5] Contact smartmontools-support@lists.sourceforge.net to see
+     about checking your code into the smartmontools CVS archive.
+*/
+
+/*
+ Developer's note: for testing this file, use an unsupported system,
+ for example: ./configure --build=rs6000-ibm-aix && make
+*/
+
+
+// This is needed for the various HAVE_* macros and PROJECT_* macros.
+#include "config.h"
+
+// These are needed to define prototypes and structures for the
+// functions defined below
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+// This is to include whatever structures and prototypes you define in
+// os_generic.h
+#include "os_generic.h"
+
+// Needed by '-V' option (CVS versioning) of smartd/smartctl.  You
+// should have one *_H_CVSID macro appearing below for each file
+// appearing with #include "*.h" above.  Please list these (below) in
+// alphabetic/dictionary order.
+const char *os_XXXX_c_cvsid="$Id: os_generic.c,v 1.21 2006/04/12 14:54:28 ballen4705 Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_GENERIC_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+
+// This is here to prevent compiler warnings for unused arguments of
+// functions.
+#define ARGUSED(x) ((void)(x))
+
+// Please eliminate the following block: both the #include and
+// the 'unsupported()' function.  They are only here to warn
+// unsuspecting users that their Operating System is not supported! If
+// you wish, you can use a similar warning mechanism for any of the
+// functions in this file that you can not (or choose not to)
+// implement.
+
+
+#ifdef HAVE_UNAME
+#include <sys/utsname.h>
+#endif
+
+static void unsupported(){
+  static int warninggiven;
+
+  if (!warninggiven) {
+    char *osname;
+    extern unsigned char debugmode;
+    unsigned char savedebugmode=debugmode;
+    
+#ifdef HAVE_UNAME
+    struct utsname ostype;
+    uname(&ostype);
+    osname=ostype.sysname;
+#else
+    osname="host's";
+#endif
+
+    debugmode=1;
+    pout("\n"
+         "############################################################################\n"
+         "WARNING: smartmontools has not been ported to the %s Operating System.\n"
+         "Please see the files os_generic.c and os_generic.h for porting instructions.\n"
+         "############################################################################\n\n",
+         osname);
+    debugmode=savedebugmode;
+    warninggiven=1;
+  }
+  
+  return;
+}
+// End of the 'unsupported()' block that you should eliminate.
+
+
+// print examples for smartctl.  You should modify this function so
+// that the device paths are sensible for your OS, and to eliminate
+// unsupported commands (eg, 3ware controllers).
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+         "  smartctl -a /dev/hda                       (Prints all SMART information)\n\n"
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl -a --device=3ware,2 /dev/sda\n"
+         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
+         );
+#else
+  printf(
+         "  smartctl -a /dev/hda                       (Prints all SMART information)\n"
+         "  smartctl -s on -o on -S on /dev/hda         (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl -a -d 3ware,2 /dev/sda\n"
+         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
+         );
+#endif
+  return;
+}
+
+// tries to guess device type given the name (a path).  See utility.h
+// for return values.
+int guess_device_type (const char* dev_name) {
+  ARGUSED(dev_name);
+  unsupported();
+  return CONTROLLER_UNKNOWN;
+}
+
+// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
+// smartd.  Returns number N of devices, or -1 if out of
+// memory. Allocates N+1 arrays: one of N pointers (devlist); the
+// other N arrays each contain null-terminated character strings.  In
+// the case N==0, no arrays are allocated because the array of 0
+// pointers has zero length, equivalent to calling malloc(0).
+int make_device_names (char*** devlist, const char* name) {
+  ARGUSED(devlist);
+  ARGUSED(name);
+  unsupported();
+  return 0;
+}
+
+// Like open().  Return non-negative integer handle, only used by the
+// functions below.  type=="ATA" or "SCSI".  If you need to store
+// extra information about your devices, create a private internal
+// array within this file (see os_freebsd.c for an example).  If you
+// can not open the device (permission denied, does not exist, etc)
+// set errno as open() does and return <0.
+int deviceopen(const char *pathname, char *type){
+  ARGUSED(pathname);
+  ARGUSED(type);
+  unsupported();
+  return -1;
+}
+
+// Like close().  Acts only on integer handles returned by
+// deviceopen() above.
+int deviceclose(int fd){
+  ARGUSED(fd);
+  unsupported();
+  return 0;
+}
+
+// Interface to ATA devices.  See os_linux.c for the cannonical example.
+// DETAILED DESCRIPTION OF ARGUMENTS
+//   device: is the integer handle provided by deviceopen()
+//   command: defines the different operations, see atacmds.h
+//   select: additional input data IF NEEDED (which log, which type of
+//           self-test).
+//   data:   location to write output data, IF NEEDED (1 or 512 bytes).
+//   Note: not all commands use all arguments.
+// RETURN VALUES (for all commands BUT command==STATUS_CHECK)
+//  -1 if the command failed
+//   0 if the command succeeded,
+// RETURN VALUES if command==STATUS_CHECK
+//  -1 if the command failed OR the disk SMART status can't be determined
+//   0 if the command succeeded and disk SMART status is "OK"
+//   1 if the command succeeded and disk SMART status is "FAILING"
+int ata_command_interface(int fd, smart_command_set command, int select, char *data){
+  ARGUSED(fd);
+  ARGUSED(command);
+  ARGUSED(select);
+  ARGUSED(data);
+  unsupported();
+  return -1;
+}
+
+int marvell_command_interface(int fd, smart_command_set command, int select, char *data){
+  ARGUSED(fd);
+  ARGUSED(command);
+  ARGUSED(select);
+  ARGUSED(data);
+  unsupported();
+  return -1;
+}
+
+// Interface to ATA devices behind 3ware escalade/apache RAID
+// controller cards.  Same description as ata_command_interface()
+// above except that 0 <= disknum <= 15 specifies the ATA disk
+// attached to the controller, and controller_type specifies the
+// precise type of 3ware controller.  See os_linux.c
+int escalade_command_interface(int fd, int disknum, int controller_type, smart_command_set command, int select, char *data){
+  ARGUSED(fd);
+  ARGUSED(disknum);
+  ARGUSED(controller_type);
+  ARGUSED(command);
+  ARGUSED(select);
+  ARGUSED(data);
+
+  unsupported();
+  return -1;
+}
+
+#include <errno.h>
+// Interface to SCSI devices.  See os_linux.c
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) {
+  ARGUSED(fd);
+  ARGUSED(iop);
+  ARGUSED(report);
+  unsupported();
+  return -ENOSYS;
+}
diff --git a/os_generic.h b/os_generic.h
new file mode 100644 (file)
index 0000000..fae5209
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * os_generic.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) YEAR YOUR_NAME <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+// In the three following lines, change 'GENERIC' to your OS name
+#ifndef OS_GENERIC_H_
+#define OS_GENERIC_H_
+#define OS_GENERIC_H_CVSID "$Id: os_generic.h,v 1.6 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+// Additional material should start here.  Note: to keep the '-V' CVS
+// reporting option working as intended, you should only #include
+// system include files <something.h>.  Local #include files
+// <"something.h"> should be #included in os_generic.c
+
+#endif /* OS_GENERIC_H_ */
diff --git a/os_linux.c b/os_linux.c
new file mode 100644 (file)
index 0000000..26be586
--- /dev/null
@@ -0,0 +1,1527 @@
+/* 
+ *  os_linux.c
+ * 
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-6 Doug Gilbert <dougg@torque.net>
+ *
+ *  Parts of this file are derived from code that was
+ *
+ *  Written By: Adam Radford <linux@3ware.com>
+ *  Modifications By: Joel Jacobson <linux@3ware.com>
+ *                   Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *                    Brad Strand <linux@3ware.com>
+ *
+ *  Copyright (C) 1999-2003 3ware Inc.
+ *
+ *  Kernel compatablity By:     Andre Hedrick <andre@suse.com>
+ *  Non-Copyright (C) 2000      Andre Hedrick <andre@suse.com>
+ *
+ * Other ars of this file are derived from code that was
+ * 
+ * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
+ * Copyright (C) 2000 Andre Hedrick <andre@linux-ide.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ * 
+ */
+
+// This file contains the linux-specific IOCTL parts of
+// smartmontools. It includes one interface routine for ATA devices,
+// one for SCSI devices, and one for ATA devices behind escalade
+// controllers.
+
+#include <errno.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/sg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#ifndef makedev // old versions of types.h do not include sysmacros.h
+#include <sys/sysmacros.h>
+#endif
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "os_linux.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+#ifndef ENOTSUP
+#define ENOTSUP ENOSYS
+#endif
+typedef unsigned long long u8;
+
+#define ARGUSED(x) ((void)(x))
+
+static const char *filenameandversion="$Id: os_linux.c,v 1.82 2006/04/12 16:28:56 ballen4705 Exp $";
+
+const char *os_XXXX_c_cvsid="$Id: os_linux.c,v 1.82 2006/04/12 16:28:56 ballen4705 Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_LINUX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// global variable holding byte count of allocated memory
+extern long long bytes;
+
+
+
+/* This function will setup and fix device nodes for a 3ware controller. */
+#define MAJOR_STRING_LENGTH 3
+#define DEVICE_STRING_LENGTH 32
+#define NODE_STRING_LENGTH 16
+int setup_3ware_nodes(char *nodename, char *driver_name) {
+  int              tw_major      = 0;
+  int              index         = 0;
+  char             majorstring[MAJOR_STRING_LENGTH+1];
+  char             device_name[DEVICE_STRING_LENGTH+1];
+  char             nodestring[NODE_STRING_LENGTH];
+  struct stat      stat_buf;
+  FILE             *file;
+  
+  /* First try to open up /proc/devices */
+  if (!(file = fopen("/proc/devices", "r"))) {
+    pout("Error opening /proc/devices to check/create 3ware device nodes\n");
+    syserror("fopen");
+    return 0;  // don't fail here: user might not have /proc !
+  }
+  
+  /* Attempt to get device major number */
+  while (EOF != fscanf(file, "%3s %32s", majorstring, device_name)) {
+    majorstring[MAJOR_STRING_LENGTH]='\0';
+    device_name[DEVICE_STRING_LENGTH]='\0';
+    if (!strncmp(device_name, nodename, DEVICE_STRING_LENGTH)) {
+      tw_major = atoi(majorstring);
+      break;
+    }
+  }
+  fclose(file);
+  
+  /* See if we found a major device number */
+  if (!tw_major) {
+    pout("No major number for /dev/%s listed in /proc/devices. Is the %s driver loaded?\n", nodename, driver_name);
+    return 2;
+  }
+  
+  /* Now check if nodes are correct */
+  for (index=0; index<16; index++) {
+    sprintf(nodestring, "/dev/%s%d", nodename, index);
+         
+    /* Try to stat the node */
+    if ((stat(nodestring, &stat_buf))) {
+      /* Create a new node if it doesn't exist */
+      if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) {
+       pout("problem creating 3ware device nodes %s", nodestring);
+       syserror("mknod");
+       return 3;
+      }
+    }
+    
+    /* See if nodes major and minor numbers are correct */
+    if ((tw_major != (int)(major(stat_buf.st_rdev))) ||
+       (index    != (int)(minor(stat_buf.st_rdev))) ||
+       (!S_ISCHR(stat_buf.st_mode))) {
+      
+      /* Delete the old node */
+      if (unlink(nodestring)) {
+       pout("problem unlinking stale 3ware device node %s", nodestring);
+       syserror("unlink");
+       return 4;
+      }
+      
+      /* Make a new node */
+      if (mknod(nodestring, S_IFCHR|0600, makedev(tw_major, index))) {
+       pout("problem creating 3ware device nodes %s", nodestring);
+       syserror("mknod");
+       return 5;
+      }
+    }
+  }
+  return 0;
+}
+
+// equivalent to open(path, flags)
+int deviceopen(const char *pathname, char *type){
+  if (!strcmp(type,"SCSI")) {
+    int fd = open(pathname, O_RDWR | O_NONBLOCK);
+    if (fd < 0 && errno == EROFS)
+      fd = open(pathname, O_RDONLY | O_NONBLOCK);
+    return fd;
+  }
+  else if (!strcmp(type,"ATA")) 
+    return open(pathname, O_RDONLY | O_NONBLOCK);
+  else if (!strcmp(type,"ATA_3WARE_9000")) {
+    // the device nodes for this controller are dynamically assigned,
+    // so we need to check that they exist with the correct major
+    // numbers and if not, create them
+    if (setup_3ware_nodes("twa", "3w-9xxx")) {
+      if (!errno)
+       errno=ENXIO;
+      return -1;
+    }
+    return open(pathname, O_RDONLY | O_NONBLOCK);
+  }
+  else if (!strcmp(type,"ATA_3WARE_678K")) {
+    // the device nodes for this controller are dynamically assigned,
+    // so we need to check that they exist with the correct major
+    // numbers and if not, create them
+    if (setup_3ware_nodes("twe", "3w-xxxx")) {
+      if (!errno)
+       errno=ENXIO;
+      return -1;
+    }
+    return open(pathname, O_RDONLY | O_NONBLOCK);
+  }
+  else
+    return -1;
+}
+
+// equivalent to close(file descriptor)
+int deviceclose(int fd){
+  return close(fd);
+}
+
+// print examples for smartctl
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+         "  smartctl --all /dev/hda                    (Prints all SMART information)\n\n"
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl --test=long /dev/hda          (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl --all --device=3ware,2 /dev/sda\n"
+         "  smartctl --all --device=3ware,2 /dev/twe0\n"
+         "  smartctl --all --device=3ware,2 /dev/twa0\n"
+         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
+         );
+#else
+  printf(
+         "  smartctl -a /dev/hda                       (Prints all SMART information)\n"
+         "  smartctl -s on -o on -S on /dev/hda         (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         "  smartctl -a -d 3ware,2 /dev/sda\n"
+         "  smartctl -a -d 3ware,2 /dev/twa0\n"
+         "  smartctl -a -d 3ware,2 /dev/twe0\n"
+         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
+         );
+#endif
+  return;
+}
+
+
+// we are going to take advantage of the fact that Linux's devfs will only
+// have device entries for devices that exist.  So if we get the equivalent of
+// ls /dev/hd[a-t], we have all the ATA devices on the system
+//
+// If any errors occur, leave errno set as it was returned by the
+// system call, and return <0.
+int get_dev_names(char*** names, const char* pattern, const char* name, int max) {
+  int n = 0, retglob, i, lim;
+  char** mp;
+  glob_t globbuf;
+  
+  memset(&globbuf, 0, sizeof(globbuf));
+
+  // in case of non-clean exit
+  *names=NULL;
+  
+  // Use glob to look for any directory entries matching the pattern
+  if ((retglob=glob(pattern, GLOB_ERR, NULL, &globbuf))) {
+    
+    //  glob failed: free memory and return
+    globfree(&globbuf);
+    
+    if (retglob==GLOB_NOMATCH){
+      pout("glob(3) found no matches for pattern %s\n", pattern);
+      return 0;
+    }
+    
+    if (retglob==GLOB_NOSPACE)
+      pout("glob(3) ran out of memory matching pattern %s\n", pattern);
+#ifdef GLOB_ABORTED // missing in old versions of glob.h
+    else if (retglob==GLOB_ABORTED)
+      pout("glob(3) aborted matching pattern %s\n", pattern);
+#endif
+    else
+      pout("Unexplained error in glob(3) of pattern %s\n", pattern);
+    
+    return -1;
+  }
+
+  // did we find too many paths?
+  lim = ((int)globbuf.gl_pathc < max) ? (int)globbuf.gl_pathc : max;
+  if (lim < (int)globbuf.gl_pathc)
+    pout("glob(3) found %d > MAX=%d devices matching pattern %s: ignoring %d paths\n", 
+         (int)globbuf.gl_pathc, max, pattern, (int)(globbuf.gl_pathc-max));
+  
+  // allocate space for up to lim number of ATA devices
+  if (!(mp =  (char **)calloc(lim, sizeof(char*)))){
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+  
+  // now step through the list returned by glob.  If not a link, copy
+  // to list.  If it is a link, evaluate it and see if the path ends
+  // in "disc".
+  for (i=0; i<lim; i++){
+    int retlink;
+    
+    // prepare a buffer for storing the link
+    char linkbuf[1024];
+    
+    // see if path is a link
+    retlink=readlink(globbuf.gl_pathv[i], linkbuf, 1023);
+    
+    // if not a link (or a strange link), keep it
+    if (retlink<=0 || retlink>1023)
+      mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion);
+    else {
+      // or if it's a link that points to a disc, follow it
+      char *p;
+      linkbuf[retlink]='\0';
+      if ((p=strrchr(linkbuf,'/')) && !strcmp(p+1, "disc"))
+        // This is the branch of the code that gets followed if we are
+        // using devfs WITH traditional compatibility links. In this
+        // case, we add the traditional device name to the list that
+        // is returned.
+        mp[n++] = CustomStrDup(globbuf.gl_pathv[i], 1, __LINE__, filenameandversion);
+      else {
+        // This is the branch of the code that gets followed if we are
+        // using devfs WITHOUT traditional compatibility links.  In
+        // this case, we check that the link to the directory is of
+        // the correct type, and then append "disc" to it.
+        char tmpname[1024]={0};
+        char *type=strcmp(name,"ATA")?"scsi":"ide";
+        if (strstr(linkbuf, type)){
+          snprintf(tmpname, 1024, "%s/disc", globbuf.gl_pathv[i]);
+          mp[n++] = CustomStrDup(tmpname, 1, __LINE__, filenameandversion);
+        }
+      }
+    }
+  }
+  
+  // free memory, track memory usage
+  globfree(&globbuf);
+  mp = realloc(mp,n*(sizeof(char*)));
+  bytes += n*(sizeof(char*));
+  
+  // and set up return values
+  *names=mp;
+  return n;
+}
+
+// makes a list of device names to scan, for either ATA or SCSI
+// devices.  Return -1 if no memory remaining, else the number of
+// devices on the list, which can be >=0.
+int make_device_names (char*** devlist, const char* name) {
+  int retval, maxdev;
+  
+#if 0
+  // for testing case where no device names are found
+  return 0;
+#endif
+  
+  if (!strcmp(name,"SCSI"))
+    retval=get_dev_names(devlist,"/dev/sd[a-z]", name, maxdev=26);
+  else if (!strcmp(name,"ATA"))
+    retval=get_dev_names(devlist,"/dev/hd[a-t]", name, maxdev=20);
+  else
+    // don't recognize disk type!
+    return 0;
+
+  // if we found traditional links, we are done
+  if (retval>0)
+    return retval;
+  
+  // else look for devfs entries without traditional links
+  return get_dev_names(devlist,"/dev/discs/disc*", name, maxdev);
+}
+
+
+// PURPOSE
+//   This is an interface routine meant to isolate the OS dependent
+//   parts of the code, and to provide a debugging interface.  Each
+//   different port and OS needs to provide it's own interface.  This
+//   is the linux one.
+// DETAILED DESCRIPTION OF ARGUMENTS
+//   device: is the file descriptor provided by open()
+//   command: defines the different operations.
+//   select: additional input data if needed (which log, which type of
+//           self-test).
+//   data:   location to write output data, if needed (512 bytes).
+//   Note: not all commands use all arguments.
+// RETURN VALUES
+//  -1 if the command failed
+//   0 if the command succeeded,
+//   STATUS_CHECK routine: 
+//  -1 if the command failed
+//   0 if the command succeeded and disk SMART status is "OK"
+//   1 if the command succeeded and disk SMART status is "FAILING"
+
+
+// huge value of buffer size needed because HDIO_DRIVE_CMD assumes
+// that buff[3] is the data size.  Since the ATA_SMART_AUTOSAVE and
+// ATA_SMART_AUTO_OFFLINE use values of 0xf1 and 0xf8 we need the space.
+// Otherwise a 4+512 byte buffer would be enough.
+#define STRANGE_BUFFER_LENGTH (4+512*0xf8)
+
+int ata_command_interface(int device, smart_command_set command, int select, char *data){
+  unsigned char buff[STRANGE_BUFFER_LENGTH];
+  // positive: bytes to write to caller.  negative: bytes to READ from
+  // caller. zero: non-data command
+  int copydata=0;
+
+  const int HDIO_DRIVE_CMD_OFFSET = 4;
+
+  // See struct hd_drive_cmd_hdr in hdreg.h.  Before calling ioctl()
+  // buff[0]: ATA COMMAND CODE REGISTER
+  // buff[1]: ATA SECTOR NUMBER REGISTER == LBA LOW REGISTER
+  // buff[2]: ATA FEATURES REGISTER
+  // buff[3]: ATA SECTOR COUNT REGISTER
+
+  // Note that on return:
+  // buff[2] contains the ATA SECTOR COUNT REGISTER
+  
+  // clear out buff.  Large enough for HDIO_DRIVE_CMD (4+512 bytes)
+  memset(buff, 0, STRANGE_BUFFER_LENGTH);
+
+  buff[0]=ATA_SMART_CMD;
+  switch (command){
+  case CHECK_POWER_MODE:
+    buff[0]=ATA_CHECK_POWER_MODE;
+    copydata=1;
+    break;
+  case READ_VALUES:
+    buff[2]=ATA_SMART_READ_VALUES;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case READ_THRESHOLDS:
+    buff[2]=ATA_SMART_READ_THRESHOLDS;
+    buff[1]=buff[3]=1;
+    copydata=512;
+    break;
+  case READ_LOG:
+    buff[2]=ATA_SMART_READ_LOG_SECTOR;
+    buff[1]=select;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case WRITE_LOG:
+    break;
+  case IDENTIFY:
+    buff[0]=ATA_IDENTIFY_DEVICE;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case PIDENTIFY:
+    buff[0]=ATA_IDENTIFY_PACKET_DEVICE;
+    buff[3]=1;
+    copydata=512;
+    break;
+  case ENABLE:
+    buff[2]=ATA_SMART_ENABLE;
+    buff[1]=1;
+    break;
+  case DISABLE:
+    buff[2]=ATA_SMART_DISABLE;
+    buff[1]=1;
+    break;
+  case STATUS:
+    // this command only says if SMART is working.  It could be
+    // replaced with STATUS_CHECK below.
+    buff[2]=ATA_SMART_STATUS;
+    break;
+  case AUTO_OFFLINE:
+    buff[2]=ATA_SMART_AUTO_OFFLINE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case AUTOSAVE:
+    buff[2]=ATA_SMART_AUTOSAVE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case IMMEDIATE_OFFLINE:
+    buff[2]=ATA_SMART_IMMEDIATE_OFFLINE;
+    buff[1]=select;
+    break;
+  case STATUS_CHECK:
+    // This command uses HDIO_DRIVE_TASK and has different syntax than
+    // the other commands.
+    buff[1]=ATA_SMART_STATUS;
+    break;
+  default:
+    pout("Unrecognized command %d in linux_ata_command_interface()\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", command);
+    errno=ENOSYS;
+    return -1;
+  }
+  
+  // This command uses the HDIO_DRIVE_TASKFILE ioctl(). This is the
+  // only ioctl() that can be used to WRITE data to the disk.
+  if (command==WRITE_LOG) {    
+    unsigned char task[sizeof(ide_task_request_t)+512];
+    ide_task_request_t *reqtask=(ide_task_request_t *) task;
+    task_struct_t      *taskfile=(task_struct_t *) reqtask->io_ports;
+    int retval;
+
+    memset(task,      0, sizeof(task));
+    
+    taskfile->data           = 0;
+    taskfile->feature        = ATA_SMART_WRITE_LOG_SECTOR;
+    taskfile->sector_count   = 1;
+    taskfile->sector_number  = select;
+    taskfile->low_cylinder   = 0x4f;
+    taskfile->high_cylinder  = 0xc2;
+    taskfile->device_head    = 0;
+    taskfile->command        = ATA_SMART_CMD;
+    
+    reqtask->data_phase      = TASKFILE_OUT;
+    reqtask->req_cmd         = IDE_DRIVE_TASK_OUT;
+    reqtask->out_size        = 512;
+    reqtask->in_size         = 0;
+    
+    // copy user data into the task request structure
+    memcpy(task+sizeof(ide_task_request_t), data, 512);
+      
+    if ((retval=ioctl(device, HDIO_DRIVE_TASKFILE, task))) {
+      if (retval==-EINVAL)
+       pout("Kernel lacks HDIO_DRIVE_TASKFILE support; compile kernel with CONFIG_IDE_TASKFILE_IO set\n");
+      return -1;
+    }
+    return 0;
+  }
+    
+  // There are two different types of ioctls().  The HDIO_DRIVE_TASK
+  // one is this:
+  if (command==STATUS_CHECK){
+    int retval;
+
+    // NOT DOCUMENTED in /usr/src/linux/include/linux/hdreg.h. You
+    // have to read the IDE driver source code.  Sigh.
+    // buff[0]: ATA COMMAND CODE REGISTER
+    // buff[1]: ATA FEATURES REGISTER
+    // buff[2]: ATA SECTOR_COUNT
+    // buff[3]: ATA SECTOR NUMBER
+    // buff[4]: ATA CYL LO REGISTER
+    // buff[5]: ATA CYL HI REGISTER
+    // buff[6]: ATA DEVICE HEAD
+
+    unsigned const char normal_lo=0x4f, normal_hi=0xc2;
+    unsigned const char failed_lo=0xf4, failed_hi=0x2c;
+    buff[4]=normal_lo;
+    buff[5]=normal_hi;
+    
+    if ((retval=ioctl(device, HDIO_DRIVE_TASK, buff))) {
+      if (retval==-EINVAL) {
+       pout("Error SMART Status command via HDIO_DRIVE_TASK failed");
+       pout("Rebuild older linux 2.2 kernels with HDIO_DRIVE_TASK support added\n");
+      }
+      else
+       syserror("Error SMART Status command failed");
+      return -1;
+    }
+    
+    // Cyl low and Cyl high unchanged means "Good SMART status"
+    if (buff[4]==normal_lo && buff[5]==normal_hi)
+      return 0;
+    
+    // These values mean "Bad SMART status"
+    if (buff[4]==failed_lo && buff[5]==failed_hi)
+      return 1;
+    
+    // We haven't gotten output that makes sense; print out some debugging info
+    syserror("Error SMART Status command failed");
+    pout("Please get assistance from " PACKAGE_HOMEPAGE "\n");
+    pout("Register values returned from SMART Status command are:\n");
+    pout("CMD=0x%02x\n",(int)buff[0]);
+    pout("FR =0x%02x\n",(int)buff[1]);
+    pout("NS =0x%02x\n",(int)buff[2]);
+    pout("SC =0x%02x\n",(int)buff[3]);
+    pout("CL =0x%02x\n",(int)buff[4]);
+    pout("CH =0x%02x\n",(int)buff[5]);
+    pout("SEL=0x%02x\n",(int)buff[6]);
+    return -1;   
+  }
+  
+#if 1
+  // Note to people doing ports to other OSes -- don't worry about
+  // this block -- you can safely ignore it.  I have put it here
+  // because under linux when you do IDENTIFY DEVICE to a packet
+  // device, it generates an ugly kernel syslog error message.  This
+  // is harmless but frightens users.  So this block detects packet
+  // devices and make IDENTIFY DEVICE fail "nicely" without a syslog
+  // error message.
+  //
+  // If you read only the ATA specs, it appears as if a packet device
+  // *might* respond to the IDENTIFY DEVICE command.  This is
+  // misleading - it's because around the time that SFF-8020 was
+  // incorporated into the ATA-3/4 standard, the ATA authors were
+  // sloppy. See SFF-8020 and you will see that ATAPI devices have
+  // *always* had IDENTIFY PACKET DEVICE as a mandatory part of their
+  // command set, and return 'Command Aborted' to IDENTIFY DEVICE.
+  if (command==IDENTIFY || command==PIDENTIFY){
+    unsigned short deviceid[256];
+    // check the device identity, as seen when the system was booted
+    // or the device was FIRST registered.  This will not be current
+    // if the user has subsequently changed some of the parameters. If
+    // device is a packet device, swap the command interpretations.
+    if (!ioctl(device, HDIO_GET_IDENTITY, deviceid) && (deviceid[0] & 0x8000))
+      buff[0]=(command==IDENTIFY)?ATA_IDENTIFY_PACKET_DEVICE:ATA_IDENTIFY_DEVICE;
+  }
+#endif
+  
+  // We are now doing the HDIO_DRIVE_CMD type ioctl.
+  if ((ioctl(device, HDIO_DRIVE_CMD, buff)))
+    return -1;
+
+  // CHECK POWER MODE command returns information in the Sector Count
+  // register (buff[3]).  Copy to return data buffer.
+  if (command==CHECK_POWER_MODE)
+    buff[HDIO_DRIVE_CMD_OFFSET]=buff[2];
+
+  // if the command returns data then copy it back
+  if (copydata)
+    memcpy(data, buff+HDIO_DRIVE_CMD_OFFSET, copydata);
+  
+  return 0; 
+}
+
+// >>>>>> Start of general SCSI specific linux code
+
+/* Linux specific code.
+ * Historically smartmontools (and smartsuite before it) used the
+ * SCSI_IOCTL_SEND_COMMAND ioctl which is available to all linux device
+ * nodes that use the SCSI subsystem. A better interface has been available
+ * via the SCSI generic (sg) driver but this involves the extra step of
+ * mapping disk devices (e.g. /dev/sda) to the corresponding sg device
+ * (e.g. /dev/sg2). In the linux kernel 2.6 series most of the facilities of
+ * the sg driver have become available via the SG_IO ioctl which is available
+ * on all SCSI devices (on SCSI tape devices from lk 2.6.6).
+ * So the strategy below is to find out if the SG_IO ioctl is available and
+ * if so use it; failing that use the older SCSI_IOCTL_SEND_COMMAND ioctl.
+ * Should work in 2.0, 2.2, 2.4 and 2.6 series linux kernels. */
+
+#define MAX_DXFER_LEN 1024      /* can be increased if necessary */
+#define SEND_IOCTL_RESP_SENSE_LEN 16    /* ioctl limitation */
+#define SG_IO_RESP_SENSE_LEN 64 /* large enough see buffer */
+#define LSCSI_DRIVER_MASK  0xf /* mask out "suggestions" */
+#define LSCSI_DRIVER_SENSE  0x8 /* alternate CHECK CONDITION indication */
+#define LSCSI_DRIVER_TIMEOUT  0x6
+#define LSCSI_DID_TIME_OUT  0x3
+#define LSCSI_DID_BUS_BUSY  0x2
+#define LSCSI_DID_NO_CONNECT  0x1
+
+#ifndef SCSI_IOCTL_SEND_COMMAND
+#define SCSI_IOCTL_SEND_COMMAND 1
+#endif
+
+#define SG_IO_PRESENT_UNKNOWN 0
+#define SG_IO_PRESENT_YES 1
+#define SG_IO_PRESENT_NO 2
+
+static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
+                        int unknown);
+static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);
+
+static int sg_io_state = SG_IO_PRESENT_UNKNOWN;
+
+/* Preferred implementation for issuing SCSI commands in linux. This
+ * function uses the SG_IO ioctl. Return 0 if command issued successfully
+ * (various status values should still be checked). If the SCSI command
+ * cannot be issued then a negative errno value is returned. */
+static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
+                        int unknown)
+{
+#ifndef SG_IO
+    ARGUSED(dev_fd); ARGUSED(iop); ARGUSED(report);
+    return -ENOTTY;
+#else
+    struct sg_io_hdr io_hdr;
+
+    if (report > 0) {
+        int k, j;
+        const unsigned char * ucp = iop->cmnd;
+        const char * np;
+        char buff[256];
+        const int sz = (int)sizeof(buff);
+
+        np = scsi_get_opcode_name(ucp[0]);
+        j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+        for (k = 0; k < (int)iop->cmnd_len; ++k)
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+        if ((report > 1) && 
+            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+                          "data, len=%d%s:\n", (int)iop->dxfer_len,
+                          (trunc ? " [only first 256 bytes shown]" : ""));
+            dStrHex((const char *)iop->dxferp, 
+                   (trunc ? 256 : iop->dxfer_len) , 1);
+        }
+        else
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+        pout(buff);
+    }
+    memset(&io_hdr, 0, sizeof(struct sg_io_hdr));
+    io_hdr.interface_id = 'S';
+    io_hdr.cmd_len = iop->cmnd_len;
+    io_hdr.mx_sb_len = iop->max_sense_len;
+    io_hdr.dxfer_len = iop->dxfer_len;
+    io_hdr.dxferp = iop->dxferp;
+    io_hdr.cmdp = iop->cmnd;
+    io_hdr.sbp = iop->sensep;
+    /* sg_io_hdr interface timeout has millisecond units. Timeout of 0
+       defaults to 60 seconds. */
+    io_hdr.timeout = ((0 == iop->timeout) ? 60 : iop->timeout) * 1000;
+    switch (iop->dxfer_dir) {
+        case DXFER_NONE:
+            io_hdr.dxfer_direction = SG_DXFER_NONE;
+            break;
+        case DXFER_FROM_DEVICE:
+            io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
+            break;
+        case DXFER_TO_DEVICE:
+            io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
+            break;
+        default:
+            pout("do_scsi_cmnd_io: bad dxfer_dir\n");
+            return -EINVAL;
+    }
+    iop->resp_sense_len = 0;
+    iop->scsi_status = 0;
+    iop->resid = 0;
+    if (ioctl(dev_fd, SG_IO, &io_hdr) < 0) {
+        if (report && (! unknown))
+            pout("  SG_IO ioctl failed, errno=%d [%s]\n", errno,
+                strerror(errno));
+        return -errno;
+    }
+    if (report > 0) {
+        pout("  scsi_status=0x%x, host_status=0x%x, driver_status=0x%x\n"
+            "  info=0x%x  duration=%d milliseconds\n", io_hdr.status, 
+            io_hdr.host_status, io_hdr.driver_status, io_hdr.info,
+            io_hdr.duration);
+        if (report > 1) {
+            if (DXFER_FROM_DEVICE == iop->dxfer_dir) {
+                int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+                pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+                     (trunc ? " [only first 256 bytes shown]" : ""));
+                dStrHex((const char*)iop->dxferp, 
+                       (trunc ? 256 : iop->dxfer_len) , 1);
+            }
+        }
+    }
+    iop->resid = io_hdr.resid;
+    iop->scsi_status = io_hdr.status;
+
+    if (io_hdr.info | SG_INFO_CHECK) { /* error or warning */
+       int masked_driver_status = (LSCSI_DRIVER_MASK & io_hdr.driver_status);
+
+       if (0 != io_hdr.host_status) {
+            if ((LSCSI_DID_NO_CONNECT == io_hdr.host_status) ||
+                (LSCSI_DID_BUS_BUSY == io_hdr.host_status) ||
+                (LSCSI_DID_TIME_OUT == io_hdr.host_status))
+                return -ETIMEDOUT;
+           else
+               return -EIO;    /* catch all */
+        }
+        if (0 != masked_driver_status) {
+            if (LSCSI_DRIVER_TIMEOUT == masked_driver_status)
+                return -ETIMEDOUT;
+           else if (LSCSI_DRIVER_SENSE != masked_driver_status)
+               return -EIO;
+       }
+        if (LSCSI_DRIVER_SENSE == masked_driver_status)
+            iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
+        iop->resp_sense_len = io_hdr.sb_len_wr;
+        if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && 
+            iop->sensep && (iop->resp_sense_len > 0)) {
+            if (report > 1) {
+                pout("  >>> Sense buffer, len=%d:\n",
+                    (int)iop->resp_sense_len);
+                dStrHex((const char *)iop->sensep, iop->resp_sense_len , 1);
+            }
+        }
+        if (report) {
+            if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) {
+                pout("  status=%x: sense_key=%x asc=%x ascq=%x\n",
+                    iop->scsi_status, iop->sensep[2] & 0xf, iop->sensep[12],
+                    iop->sensep[13]);
+            }
+            else
+                pout("  status=0x%x\n", iop->scsi_status);
+        }
+    }
+    return 0;
+#endif
+}
+
+struct linux_ioctl_send_command
+{
+    int inbufsize;
+    int outbufsize;
+    UINT8 buff[MAX_DXFER_LEN + 16];
+};
+
+/* The Linux SCSI_IOCTL_SEND_COMMAND ioctl is primitive and it doesn't 
+ * support: CDB length (guesses it from opcode), resid and timeout.
+ * Patches in Linux 2.4.21 and 2.5.70 to extend SEND DIAGNOSTIC timeout
+ * to 2 hours in order to allow long foreground extended self tests. */
+static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
+{
+    struct linux_ioctl_send_command wrk;
+    int status, buff_offset;
+    size_t len;
+
+    memcpy(wrk.buff, iop->cmnd, iop->cmnd_len);
+    buff_offset = iop->cmnd_len;
+    if (report > 0) {
+        int k, j;
+        const unsigned char * ucp = iop->cmnd;
+        const char * np;
+        char buff[256];
+        const int sz = (int)sizeof(buff);
+
+        np = scsi_get_opcode_name(ucp[0]);
+        j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+        for (k = 0; k < (int)iop->cmnd_len; ++k)
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+        if ((report > 1) && 
+            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+                          "data, len=%d%s:\n", (int)iop->dxfer_len,
+                          (trunc ? " [only first 256 bytes shown]" : ""));
+            dStrHex((const char *)iop->dxferp, 
+                   (trunc ? 256 : iop->dxfer_len) , 1);
+        }
+        else
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+        pout(buff);
+    }
+    switch (iop->dxfer_dir) {
+        case DXFER_NONE:
+            wrk.inbufsize = 0;
+            wrk.outbufsize = 0;
+            break;
+        case DXFER_FROM_DEVICE:
+            wrk.inbufsize = 0;
+            if (iop->dxfer_len > MAX_DXFER_LEN)
+                return -EINVAL;
+            wrk.outbufsize = iop->dxfer_len;
+            break;
+        case DXFER_TO_DEVICE:
+            if (iop->dxfer_len > MAX_DXFER_LEN)
+                return -EINVAL;
+            memcpy(wrk.buff + buff_offset, iop->dxferp, iop->dxfer_len);
+            wrk.inbufsize = iop->dxfer_len;
+            wrk.outbufsize = 0;
+            break;
+        default:
+            pout("do_scsi_cmnd_io: bad dxfer_dir\n");
+            return -EINVAL;
+    }
+    iop->resp_sense_len = 0;
+    iop->scsi_status = 0;
+    iop->resid = 0;
+    status = ioctl(dev_fd, SCSI_IOCTL_SEND_COMMAND, &wrk);
+    if (-1 == status) {
+        if (report)
+            pout("  SCSI_IOCTL_SEND_COMMAND ioctl failed, errno=%d [%s]\n",
+                 errno, strerror(errno));
+        return -errno;
+    }
+    if (0 == status) {
+        if (report > 0)
+            pout("  status=0\n");
+        if (DXFER_FROM_DEVICE == iop->dxfer_dir) {
+            memcpy(iop->dxferp, wrk.buff, iop->dxfer_len);
+            if (report > 1) {
+                int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+                pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+                     (trunc ? " [only first 256 bytes shown]" : ""));
+                dStrHex((const char*)iop->dxferp, 
+                       (trunc ? 256 : iop->dxfer_len) , 1);
+            }
+        }
+        return 0;
+    }
+    iop->scsi_status = status & 0x7e; /* bits 0 and 7 used to be for vendors */
+    if (LSCSI_DRIVER_SENSE == ((status >> 24) & 0xf))
+        iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
+    len = (SEND_IOCTL_RESP_SENSE_LEN < iop->max_sense_len) ?
+                SEND_IOCTL_RESP_SENSE_LEN : iop->max_sense_len;
+    if ((SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) && 
+        iop->sensep && (len > 0)) {
+        memcpy(iop->sensep, wrk.buff, len);
+        iop->resp_sense_len = len;
+        if (report > 1) {
+            pout("  >>> Sense buffer, len=%d:\n", (int)len);
+            dStrHex((const char *)wrk.buff, len , 1);
+        }
+    }
+    if (report) {
+        if (SCSI_STATUS_CHECK_CONDITION == iop->scsi_status) {
+            pout("  status=%x: sense_key=%x asc=%x ascq=%x\n", status & 0xff,
+                 wrk.buff[2] & 0xf, wrk.buff[12], wrk.buff[13]);
+        }
+        else
+            pout("  status=0x%x\n", status);
+    }
+    if (iop->scsi_status > 0)
+        return 0;
+    else {
+        if (report > 0)
+            pout("  ioctl status=0x%x but scsi status=0, fail with EIO\n", 
+                 status);
+        return -EIO;      /* give up, assume no device there */
+    }
+}
+
+/* SCSI command transmission interface function, linux version.
+ * Returns 0 if SCSI command successfully launched and response
+ * received. Even when 0 is returned the caller should check 
+ * scsi_cmnd_io::scsi_status for SCSI defined errors and warnings
+ * (e.g. CHECK CONDITION). If the SCSI command could not be issued
+ * (e.g. device not present or timeout) or some other problem
+ * (e.g. timeout) then returns a negative errno value */
+int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
+{
+    int res;
+
+    /* implementation relies on static sg_io_state variable. If not
+     * previously set tries the SG_IO ioctl. If that succeeds assume
+     * that SG_IO ioctl functional. If it fails with an errno value
+     * other than ENODEV (no device) or permission then assume 
+     * SCSI_IOCTL_SEND_COMMAND is the only option. */
+    switch (sg_io_state) {
+    case SG_IO_PRESENT_UNKNOWN:
+        /* ignore report argument */
+       if (0 == (res = sg_io_cmnd_io(dev_fd, iop, report, 1))) {
+           sg_io_state = SG_IO_PRESENT_YES;
+           return 0;
+       } else if ((-ENODEV == res) || (-EACCES == res) || (-EPERM == res))
+           return res;         /* wait until we see a device */
+        sg_io_state = SG_IO_PRESENT_NO;
+       /* drop through by design */
+    case SG_IO_PRESENT_NO:
+       return sisc_cmnd_io(dev_fd, iop, report);
+    case SG_IO_PRESENT_YES:
+       return sg_io_cmnd_io(dev_fd, iop, report, 0);
+    default:
+       pout(">>>> do_scsi_cmnd_io: bad sg_io_state=%d\n", sg_io_state); 
+       sg_io_state = SG_IO_PRESENT_UNKNOWN;
+        return -EIO;   /* report error and reset state */
+    }
+}
+
+// >>>>>> End of general SCSI specific linux code
+
+
+// prototype
+void printwarning(smart_command_set command);
+
+// PURPOSE
+//   This is an interface routine meant to isolate the OS dependent
+//   parts of the code, and to provide a debugging interface.  Each
+//   different port and OS needs to provide it's own interface.  This
+//   is the linux interface to the 3ware 3w-xxxx driver.  It allows ATA
+//   commands to be passed through the SCSI driver.
+// DETAILED DESCRIPTION OF ARGUMENTS
+//   fd: is the file descriptor provided by open()
+//   disknum is the disk number (0 to 15) in the RAID array
+//   escalade_type indicates the type of controller type, and if scsi or char interface is used
+//   command: defines the different operations.
+//   select: additional input data if needed (which log, which type of
+//           self-test).
+//   data:   location to write output data, if needed (512 bytes).
+//   Note: not all commands use all arguments.
+// RETURN VALUES
+//  -1 if the command failed
+//   0 if the command succeeded,
+//   STATUS_CHECK routine: 
+//  -1 if the command failed
+//   0 if the command succeeded and disk SMART status is "OK"
+//   1 if the command succeeded and disk SMART status is "FAILING"
+
+
+/* 512 is the max payload size: increase if needed */
+#define BUFFER_LEN_678K      ( sizeof(TW_Ioctl)                  ) // 1044 unpacked, 1041 packed
+#define BUFFER_LEN_678K_CHAR ( sizeof(TW_New_Ioctl)+512-1        ) // 1539 unpacked, 1536 packed
+#define BUFFER_LEN_9000      ( sizeof(TW_Ioctl_Buf_Apache)+512-1 ) // 2051 unpacked, 2048 packed
+#define TW_IOCTL_BUFFER_SIZE ( MAX(MAX(BUFFER_LEN_678K, BUFFER_LEN_9000), BUFFER_LEN_678K_CHAR) )
+
+int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){
+
+  // return value and buffer for ioctl()
+  int  ioctlreturn, readdata=0;
+
+  // Used by both the SCSI and char interfaces
+  TW_Passthru *passthru=NULL;
+  char ioctl_buffer[TW_IOCTL_BUFFER_SIZE];
+
+  // only used for SCSI device interface
+  TW_Ioctl   *tw_ioctl=NULL;
+  TW_Output *tw_output=NULL;
+
+  // only used for 6000/7000/8000 char device interface
+  TW_New_Ioctl *tw_ioctl_char=NULL;
+
+  // only used for 9000 character device interface
+  TW_Ioctl_Buf_Apache *tw_ioctl_apache=NULL;
+  
+  memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE);
+
+  if (escalade_type==CONTROLLER_3WARE_9000_CHAR) {
+    tw_ioctl_apache                               = (TW_Ioctl_Buf_Apache *)ioctl_buffer;
+    tw_ioctl_apache->driver_command.control_code  = TW_IOCTL_FIRMWARE_PASS_THROUGH;
+    tw_ioctl_apache->driver_command.buffer_length = 512; /* payload size */
+    passthru                                      = (TW_Passthru *)&(tw_ioctl_apache->firmware_command.command.oldcommand);
+  }
+  else if (escalade_type==CONTROLLER_3WARE_678K_CHAR) {
+    tw_ioctl_char                                 = (TW_New_Ioctl *)ioctl_buffer;
+    tw_ioctl_char->data_buffer_length             = 512;
+    passthru                                      = (TW_Passthru *)&(tw_ioctl_char->firmware_command);
+  }
+  else if (escalade_type==CONTROLLER_3WARE_678K) {
+    tw_ioctl                                      = (TW_Ioctl *)ioctl_buffer;
+    tw_ioctl->cdb[0]                              = TW_IOCTL;
+    tw_ioctl->opcode                              = TW_ATA_PASSTHRU;
+    tw_ioctl->input_length                        = 512; // correct even for non-data commands
+    tw_ioctl->output_length                       = 512; // correct even for non-data commands
+    tw_output                                     = (TW_Output *)tw_ioctl;
+    passthru                                      = (TW_Passthru *)&(tw_ioctl->input_data);
+  }
+  else {
+    pout("Unrecognized escalade_type %d in linux_3ware_command_interface(disk %d)\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", escalade_type, disknum);
+    errno=ENOSYS;
+    return -1;
+  }
+
+  // Same for (almost) all commands - but some reset below
+  passthru->byte0.opcode  = TW_OP_ATA_PASSTHRU;
+  passthru->request_id    = 0xFF;
+  passthru->byte3.aport   = disknum;
+  passthru->byte3.host_id = 0;
+  passthru->status        = 0;           
+  passthru->flags         = 0x1;
+  passthru->drive_head    = 0x0;
+  passthru->sector_num    = 0;
+
+  // All SMART commands use this CL/CH signature.  These are magic
+  // values from the ATA specifications.
+  passthru->cylinder_lo   = 0x4F;
+  passthru->cylinder_hi   = 0xC2;
+  
+  // SMART ATA COMMAND REGISTER value
+  passthru->command       = ATA_SMART_CMD;
+  
+  // Is this a command that reads or returns 512 bytes?
+  // passthru->param values are:
+  // 0x0 - non data command without TFR write check,
+  // 0x8 - non data command with TFR write check,
+  // 0xD - data command that returns data to host from device
+  // 0xF - data command that writes data from host to device
+  // passthru->size values are 0x5 for non-data and 0x07 for data
+  if (command == READ_VALUES     ||
+      command == READ_THRESHOLDS ||
+      command == READ_LOG        ||
+      command == IDENTIFY        ||
+      command == WRITE_LOG ) {
+    readdata=1;
+    passthru->byte0.sgloff = 0x5;
+    passthru->size         = 0x7;
+    passthru->param        = 0xD;
+    passthru->sector_count = 0x1;
+    // For 64-bit to work correctly, up the size of the command packet
+    // in dwords by 1 to account for the 64-bit single sgl 'address'
+    // field. Note that this doesn't agree with the typedefs but it's
+    // right (agree with kernel driver behavior/typedefs).
+    if (escalade_type==CONTROLLER_3WARE_9000_CHAR && sizeof(long)==8)
+      passthru->size++;
+  }
+  else {
+    // Non data command -- but doesn't use large sector 
+    // count register values.  
+    passthru->byte0.sgloff = 0x0;
+    passthru->size         = 0x5;
+    passthru->param        = 0x8;
+    passthru->sector_count = 0x0;
+  }
+  
+  // Now set ATA registers depending upon command
+  switch (command){
+  case CHECK_POWER_MODE:
+    passthru->command     = ATA_CHECK_POWER_MODE;
+    passthru->features    = 0;
+    passthru->cylinder_lo = 0;
+    passthru->cylinder_hi = 0;
+    break;
+  case READ_VALUES:
+    passthru->features = ATA_SMART_READ_VALUES;
+    break;
+  case READ_THRESHOLDS:
+    passthru->features = ATA_SMART_READ_THRESHOLDS;
+    break;
+  case READ_LOG:
+    passthru->features = ATA_SMART_READ_LOG_SECTOR;
+    // log number to return
+    passthru->sector_num  = select;
+    break;
+  case WRITE_LOG:
+    if (escalade_type == CONTROLLER_3WARE_9000_CHAR)
+      memcpy((unsigned char *)tw_ioctl_apache->data_buffer, data, 512);
+    else if (escalade_type == CONTROLLER_3WARE_678K_CHAR)
+      memcpy((unsigned char *)tw_ioctl_char->data_buffer,   data, 512);
+    else {
+      // COMMAND NOT SUPPORTED VIA SCSI IOCTL INTERFACE
+      // memcpy(tw_output->output_data, data, 512);
+      printwarning(command);
+      errno=ENOTSUP;
+      return -1;
+    }
+    readdata=0;
+    passthru->features     = ATA_SMART_WRITE_LOG_SECTOR;
+    passthru->sector_count = 1;
+    passthru->sector_num   = select;
+    passthru->param        = 0xF;  // PIO data write
+    break;
+  case IDENTIFY:
+    // ATA IDENTIFY DEVICE
+    passthru->command     = ATA_IDENTIFY_DEVICE;
+    passthru->features    = 0;
+    passthru->cylinder_lo = 0;
+    passthru->cylinder_hi = 0;
+    break;
+  case PIDENTIFY:
+    // 3WARE controller can NOT have packet device internally
+    pout("WARNING - NO DEVICE FOUND ON 3WARE CONTROLLER (disk %d)\n", disknum);
+    pout("Note: /dev/sdX many need to be replaced with /dev/tweN or /dev/twaN\n");
+    errno=ENODEV;
+    return -1;
+  case ENABLE:
+    passthru->features = ATA_SMART_ENABLE;
+    break;
+  case DISABLE:
+    passthru->features = ATA_SMART_DISABLE;
+    break;
+  case AUTO_OFFLINE:
+    passthru->features     = ATA_SMART_AUTO_OFFLINE;
+    // Enable or disable?
+    passthru->sector_count = select;
+    break;
+  case AUTOSAVE:
+    passthru->features     = ATA_SMART_AUTOSAVE;
+    // Enable or disable?
+    passthru->sector_count = select;
+    break;
+  case IMMEDIATE_OFFLINE:
+    passthru->features    = ATA_SMART_IMMEDIATE_OFFLINE;
+    // What test type to run?
+    passthru->sector_num  = select;
+    break;
+  case STATUS_CHECK:
+    passthru->features = ATA_SMART_STATUS;
+    break;
+  case STATUS:
+    // This is JUST to see if SMART is enabled, by giving SMART status
+    // command. But it doesn't say if status was good, or failing.
+    // See below for the difference.
+    passthru->features = ATA_SMART_STATUS;
+    break;
+  default:
+    pout("Unrecognized command %d in linux_3ware_command_interface(disk %d)\n"
+         "Please contact " PACKAGE_BUGREPORT "\n", command, disknum);
+    errno=ENOSYS;
+    return -1;
+  }
+
+  // Now send the command down through an ioctl()
+  if (escalade_type==CONTROLLER_3WARE_9000_CHAR)
+    ioctlreturn=ioctl(fd, TW_IOCTL_FIRMWARE_PASS_THROUGH, tw_ioctl_apache);
+  else if (escalade_type==CONTROLLER_3WARE_678K_CHAR)
+    ioctlreturn=ioctl(fd, TW_CMD_PACKET_WITH_DATA, tw_ioctl_char);
+  else
+    ioctlreturn=ioctl(fd, SCSI_IOCTL_SEND_COMMAND, tw_ioctl);
+  
+  // Deal with the different error cases
+  if (ioctlreturn) {
+    if (CONTROLLER_3WARE_678K==escalade_type && ((command==AUTO_OFFLINE || command==AUTOSAVE) && select)){
+      // error here is probably a kernel driver whose version is too old
+      printwarning(command);
+      errno=ENOTSUP;
+    }
+    if (!errno)
+      errno=EIO;
+    return -1;
+  }
+  
+  // The passthru structure is valid after return from an ioctl if:
+  // - we are using the character interface OR 
+  // - we are using the SCSI interface and this is a NON-READ-DATA command
+  // For SCSI interface, note that we set passthru to a different
+  // value after ioctl().
+  if (CONTROLLER_3WARE_678K==escalade_type) {
+    if (readdata)
+      passthru=NULL;
+    else
+      passthru=(TW_Passthru *)&(tw_output->output_data);
+  }
+
+  // See if the ATA command failed.  Now that we have returned from
+  // the ioctl() call, if passthru is valid, then:
+  // - passthru->status contains the 3ware controller STATUS
+  // - passthru->command contains the ATA STATUS register
+  // - passthru->features contains the ATA ERROR register
+  //
+  // Check bits 0 (error bit) and 5 (device fault) of the ATA STATUS
+  // If bit 0 (error bit) is set, then ATA ERROR register is valid.
+  // While we *might* decode the ATA ERROR register, at the moment it
+  // doesn't make much sense: we don't care in detail why the error
+  // happened.
+  
+  if (passthru && (passthru->status || (passthru->command & 0x21))) {
+    errno=EIO;
+    return -1;
+  }
+  
+  // If this is a read data command, copy data to output buffer
+  if (readdata) {
+    if (escalade_type==CONTROLLER_3WARE_9000_CHAR)
+      memcpy(data, (unsigned char *)tw_ioctl_apache->data_buffer, 512);
+    else if (escalade_type==CONTROLLER_3WARE_678K_CHAR)
+      memcpy(data, (unsigned char *)tw_ioctl_char->data_buffer, 512);
+    else
+      memcpy(data, tw_output->output_data, 512);
+  }
+
+  // For STATUS_CHECK, we need to check register values
+  if (command==STATUS_CHECK) {
+    
+    // To find out if the SMART RETURN STATUS is good or failing, we
+    // need to examine the values of the Cylinder Low and Cylinder
+    // High Registers.
+    
+    unsigned short cyl_lo=passthru->cylinder_lo;
+    unsigned short cyl_hi=passthru->cylinder_hi;
+    
+    // If values in Cyl-LO and Cyl-HI are unchanged, SMART status is good.
+    if (cyl_lo==0x4F && cyl_hi==0xC2)
+      return 0;
+    
+    // If values in Cyl-LO and Cyl-HI are as follows, SMART status is FAIL
+    if (cyl_lo==0xF4 && cyl_hi==0x2C)
+      return 1;
+    
+    // Any other values mean that something has gone wrong with the command
+    if (CONTROLLER_3WARE_678K==escalade_type) {
+      printwarning(command);
+      errno=ENOSYS;
+      return 0;
+    }
+    else {
+      errno=EIO;
+      return -1;
+    }
+  }
+  
+  // copy sector count register (one byte!) to return data
+  if (command==CHECK_POWER_MODE)
+    *data=*(char *)&(passthru->sector_count);
+  
+  // look for nonexistent devices/ports
+  if (command==IDENTIFY && !nonempty((unsigned char *)data, 512)) {
+    errno=ENODEV;
+    return -1;
+  }
+  
+  return 0;
+}
+
+
+
+int marvell_command_interface(int device, 
+                              smart_command_set command, 
+                              int select, 
+                              char *data) {  
+  typedef struct {  
+    int  inlen;
+    int  outlen;
+    char cmd[540];
+  } mvsata_scsi_cmd;
+  
+  int copydata = 0;
+  mvsata_scsi_cmd  smart_command;
+  unsigned char *buff = (unsigned char *)&smart_command.cmd[6];
+  // See struct hd_drive_cmd_hdr in hdreg.h
+  // buff[0]: ATA COMMAND CODE REGISTER
+  // buff[1]: ATA SECTOR NUMBER REGISTER
+  // buff[2]: ATA FEATURES REGISTER
+  // buff[3]: ATA SECTOR COUNT REGISTER
+  
+  // clear out buff.  Large enough for HDIO_DRIVE_CMD (4+512 bytes)
+  memset(&smart_command, 0, sizeof(smart_command));
+  smart_command.inlen = 540;
+  smart_command.outlen = 540;
+  smart_command.cmd[0] = 0xC;  //Vendor-specific code
+  smart_command.cmd[4] = 6;     //command length
+  
+  buff[0] = ATA_SMART_CMD;
+  switch (command){
+  case CHECK_POWER_MODE:
+    buff[0]=ATA_CHECK_POWER_MODE;
+    break;
+  case READ_VALUES:
+    buff[2]=ATA_SMART_READ_VALUES;
+    copydata=buff[3]=1;
+    break;
+  case READ_THRESHOLDS:
+    buff[2]=ATA_SMART_READ_THRESHOLDS;
+    copydata=buff[1]=buff[3]=1;
+    break;
+  case READ_LOG:
+    buff[2]=ATA_SMART_READ_LOG_SECTOR;
+    buff[1]=select;
+    copydata=buff[3]=1;
+    break;
+  case IDENTIFY:
+    buff[0]=ATA_IDENTIFY_DEVICE;
+    copydata=buff[3]=1;
+    break;
+  case PIDENTIFY:
+    buff[0]=ATA_IDENTIFY_PACKET_DEVICE;
+    copydata=buff[3]=1;
+    break;
+  case ENABLE:
+    buff[2]=ATA_SMART_ENABLE;
+    buff[1]=1;
+    break;
+  case DISABLE:
+    buff[2]=ATA_SMART_DISABLE;
+    buff[1]=1;
+    break;
+  case STATUS:
+  case STATUS_CHECK:
+    // this command only says if SMART is working.  It could be
+    // replaced with STATUS_CHECK below.
+    buff[2] = ATA_SMART_STATUS;
+    break;
+  case AUTO_OFFLINE:
+    buff[2]=ATA_SMART_AUTO_OFFLINE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case AUTOSAVE:
+    buff[2]=ATA_SMART_AUTOSAVE;
+    buff[3]=select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+    break;
+  case IMMEDIATE_OFFLINE:
+    buff[2]=ATA_SMART_IMMEDIATE_OFFLINE;
+    buff[1]=select;
+    break;
+  default:
+    pout("Unrecognized command %d in mvsata_os_specific_handler()\n", command);
+    exit(1);
+    break;
+  }  
+  // There are two different types of ioctls().  The HDIO_DRIVE_TASK
+  // one is this:
+  // We are now doing the HDIO_DRIVE_CMD type ioctl.
+  if (ioctl(device, SCSI_IOCTL_SEND_COMMAND, (void *)&smart_command))
+      return -1;
+
+  if (command==CHECK_POWER_MODE) {
+    // LEON -- CHECK THIS PLEASE.  THIS SHOULD BE THE SECTOR COUNT
+    // REGISTER, AND IT MIGHT BE buff[2] NOT buff[3].  Bruce
+    data[0]=buff[3];
+    return 0;
+  }
+
+  // Always succeed on a SMART status, as a disk that failed returned  
+  // buff[4]=0xF4, buff[5]=0x2C, i.e. "Bad SMART status" (see below).
+  if (command == STATUS)
+    return 0;
+  //Data returned is starting from 0 offset  
+  if (command == STATUS_CHECK)
+  {
+    // Cyl low and Cyl high unchanged means "Good SMART status"
+    if (buff[4] == 0x4F && buff[5] == 0xC2)
+      return 0;    
+    // These values mean "Bad SMART status"
+    if (buff[4] == 0xF4 && buff[5] == 0x2C)
+      return 1;    
+    // We haven't gotten output that makes sense; print out some debugging info
+    syserror("Error SMART Status command failed");
+    pout("Please get assistance from %s\n",PACKAGE_BUGREPORT);
+    pout("Register values returned from SMART Status command are:\n");
+    pout("CMD =0x%02x\n",(int)buff[0]);
+    pout("FR =0x%02x\n",(int)buff[1]);
+    pout("NS =0x%02x\n",(int)buff[2]);
+    pout("SC =0x%02x\n",(int)buff[3]);
+    pout("CL =0x%02x\n",(int)buff[4]);
+    pout("CH =0x%02x\n",(int)buff[5]);
+    pout("SEL=0x%02x\n",(int)buff[6]);
+    return -1;   
+  }  
+
+  if (copydata)
+    memcpy(data, buff, 512);
+  return 0; 
+}
+
+
+// Utility function for printing warnings
+void printwarning(smart_command_set command){
+  static int printed[4]={0,0,0,0};
+  const char* message=
+    "can not be passed through the 3ware 3w-xxxx driver.  This can be fixed by\n"
+    "applying a simple 3w-xxxx driver patch that can be found here:\n"
+    PACKAGE_HOMEPAGE "\n"
+    "Alternatively, upgrade your 3w-xxxx driver to version 1.02.00.037 or greater.\n\n";
+
+  if (command==AUTO_OFFLINE && !printed[0]) {
+    printed[0]=1;
+    pout("The SMART AUTO-OFFLINE ENABLE command (smartmontools -o on option/Directive)\n%s", message);
+  } 
+  else if (command==AUTOSAVE && !printed[1]) {
+    printed[1]=1;
+    pout("The SMART AUTOSAVE ENABLE command (smartmontools -S on option/Directive)\n%s", message);
+  }
+  else if (command==STATUS_CHECK && !printed[2]) {
+    printed[2]=1;
+    pout("The SMART RETURN STATUS return value (smartmontools -H option/Directive)\n%s", message);
+  }
+  else if (command==WRITE_LOG && !printed[3])  {
+    printed[3]=1;
+    pout("The SMART WRITE LOG command (smartmontools -t selective) only supported via char /dev/tw[ae] interface\n");
+  }
+  
+  return;
+}
+
+// Guess device type (ata or scsi) based on device name (Linux
+// specific) SCSI device name in linux can be sd, sr, scd, st, nst,
+// osst, nosst and sg.
+static const char * lin_dev_prefix = "/dev/";
+static const char * lin_dev_ata_disk_plus = "h";
+static const char * lin_dev_ata_devfs_disk_plus = "ide/";
+static const char * lin_dev_scsi_devfs_disk_plus = "scsi/";
+static const char * lin_dev_scsi_disk_plus = "s";
+static const char * lin_dev_scsi_tape1 = "ns";
+static const char * lin_dev_scsi_tape2 = "os";
+static const char * lin_dev_scsi_tape3 = "nos";
+static const char * lin_dev_3ware_9000_char = "twa";
+static const char * lin_dev_3ware_678k_char = "twe";
+
+int guess_device_type(const char * dev_name) {
+  int len;
+  int dev_prefix_len = strlen(lin_dev_prefix);
+  
+  // if dev_name null, or string length zero
+  if (!dev_name || !(len = strlen(dev_name)))
+    return CONTROLLER_UNKNOWN;
+  
+  // Remove the leading /dev/... if it's there
+  if (!strncmp(lin_dev_prefix, dev_name, dev_prefix_len)) {
+    if (len <= dev_prefix_len)
+      // if nothing else in the string, unrecognized
+      return CONTROLLER_UNKNOWN;
+    // else advance pointer to following characters
+    dev_name += dev_prefix_len;
+  }
+  
+  // form /dev/h* or h*
+  if (!strncmp(lin_dev_ata_disk_plus, dev_name,
+               strlen(lin_dev_ata_disk_plus)))
+    return CONTROLLER_ATA;
+  
+  // form /dev/ide/* or ide/*
+  if (!strncmp(lin_dev_ata_devfs_disk_plus, dev_name,
+               strlen(lin_dev_ata_devfs_disk_plus)))
+    return CONTROLLER_ATA;
+
+  // form /dev/s* or s*
+  if (!strncmp(lin_dev_scsi_disk_plus, dev_name,
+               strlen(lin_dev_scsi_disk_plus)))
+    return CONTROLLER_SCSI;
+
+  // form /dev/scsi/* or scsi/*
+  if (!strncmp(lin_dev_scsi_devfs_disk_plus, dev_name,
+               strlen(lin_dev_scsi_devfs_disk_plus)))
+    return CONTROLLER_SCSI;
+  
+  // form /dev/ns* or ns*
+  if (!strncmp(lin_dev_scsi_tape1, dev_name,
+               strlen(lin_dev_scsi_tape1)))
+    return CONTROLLER_SCSI;
+  
+  // form /dev/os* or os*
+  if (!strncmp(lin_dev_scsi_tape2, dev_name,
+               strlen(lin_dev_scsi_tape2)))
+    return CONTROLLER_SCSI;
+  
+  // form /dev/nos* or nos*
+  if (!strncmp(lin_dev_scsi_tape3, dev_name,
+               strlen(lin_dev_scsi_tape3)))
+    return CONTROLLER_SCSI;
+
+  // form /dev/twa*
+  if (!strncmp(lin_dev_3ware_9000_char, dev_name,
+               strlen(lin_dev_3ware_9000_char)))
+    return CONTROLLER_3WARE_9000_CHAR;
+
+  // form /dev/twe*
+  if (!strncmp(lin_dev_3ware_678k_char, dev_name,
+               strlen(lin_dev_3ware_678k_char)))
+    return CONTROLLER_3WARE_678K_CHAR;
+
+  // we failed to recognize any of the forms
+  return CONTROLLER_UNKNOWN;
+}
+
+
+#if 0
+
+[ed@firestorm ed]$ ls -l  /dev/discs
+total 0
+lr-xr-xr-x    1 root     root           30 Dec 31  1969 disc0 -> ../ide/host2/bus0/target0/lun0/
+lr-xr-xr-x    1 root     root           30 Dec 31  1969 disc1 -> ../ide/host2/bus1/target0/lun0/
+[ed@firestorm ed]$ ls -l  dev/ide/host*/bus*/target*/lun*/disc
+ls: dev/ide/host*/bus*/target*/lun*/disc: No such file or directory
+[ed@firestorm ed]$ ls -l  /dev/ide/host*/bus*/target*/lun*/disc
+brw-------    1 root     root      33,   0 Dec 31  1969 /dev/ide/host2/bus0/target0/lun0/disc
+brw-------    1 root     root      34,   0 Dec 31  1969 /dev/ide/host2/bus1/target0/lun0/disc
+[ed@firestorm ed]$ ls -l  /dev/ide/c*b*t*u*
+ls: /dev/ide/c*b*t*u*: No such file or directory
+[ed@firestorm ed]$ 
+Script done on Fri Nov  7 13:46:28 2003
+
+#endif
diff --git a/os_linux.h b/os_linux.h
new file mode 100644 (file)
index 0000000..71a4102
--- /dev/null
@@ -0,0 +1,390 @@
+/* 
+ *  os_linux.h
+ * 
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ *
+ * Derived from code that was
+ *
+ *  Written By: Adam Radford <linux@3ware.com>
+ *  Modifications By: Joel Jacobson <linux@3ware.com>
+ *                   Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *                    Brad Strand <linux@3ware.com>
+ *
+ *  Copyright (C) 1999-2003 3ware Inc.
+ *
+ *  Kernel compatablity By:     Andre Hedrick <andre@suse.com>
+ *  Non-Copyright (C) 2000      Andre Hedrick <andre@suse.com>
+ *
+ *
+ * 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_LINUX_H_
+#define OS_LINUX_H_
+
+#define OS_LINUX_H_CVSID "$Id: os_linux.h,v 1.24 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+/* 
+   The following definitions/macros/prototypes are used for three
+   different interfaces, referred to as "the three cases" below.
+   CONTROLLER_3WARE_678K      -- 6000, 7000, and 8000 controllers via /dev/sd?
+   CONTROLLER_3WARE_678K_CHAR -- 6000, 7000, and 8000 controllers via /dev/twe?
+   CONTROLLER_3WARE_9000_CHAR -- 9000 controllers via /dev/twa?
+*/
+
+// USED FOR ALL THREE CASES
+
+#define u32 unsigned int
+#define TW_OP_ATA_PASSTHRU 0x11
+#define MAX(x,y) ( (x)>(y)?(x):(y) )
+
+#pragma pack(1)
+/* Scatter gather list entry */
+typedef struct TAG_TW_SG_Entry {
+  unsigned int address;
+  unsigned int length;
+} TW_SG_Entry;
+
+/* Command header for ATA pass-thru.  Note that for different
+   drivers/interfaces the length of sg_list (here TW_ATA_PASS_SGL_MAX)
+   is different.  But it can be taken as same for all three cases
+   because it's never used to define any other structures, and we
+   never use anything in the sg_list or beyond! */
+
+#define TW_ATA_PASS_SGL_MAX      60
+
+typedef struct TAG_TW_Passthru {
+  struct {
+    unsigned char opcode:5;
+    unsigned char sgloff:3;
+  } byte0;
+  unsigned char size;
+  unsigned char request_id;
+  struct { 
+    unsigned char aport:4;
+    unsigned char host_id:4;
+  } byte3;
+  unsigned char status;  // On return, contains 3ware STATUS register
+  unsigned char flags;
+  unsigned short param;
+  unsigned short features;  // On return, contains ATA ERROR register
+  unsigned short sector_count;
+  unsigned short sector_num;
+  unsigned short cylinder_lo;
+  unsigned short cylinder_hi;
+  unsigned char drive_head;
+  unsigned char command; // On return, contains ATA STATUS register
+  TW_SG_Entry sg_list[TW_ATA_PASS_SGL_MAX];
+  unsigned char padding[12];
+} TW_Passthru;
+
+// the following are for the SCSI interface only 
+
+// Ioctl buffer: Note that this defn has changed in kernel tree...
+// Total size is 1041 bytes -- this is really weird
+
+#define TW_IOCTL                 0x80
+#define TW_ATA_PASSTHRU          0x1e
+
+// Adam -- should this be #pramga packed? Otherwise table_id gets
+// moved for byte alignment.  Without packing, input passthru for SCSI
+// ioctl is 31 bytes in.  With packing it is 30 bytes in.
+typedef struct TAG_TW_Ioctl { 
+  int input_length;
+  int output_length;
+  unsigned char cdb[16];
+  unsigned char opcode;
+  // This one byte of padding is missing from the typedefs in the
+  // kernel code, but it is indeed present.  We put it explicitly
+  // here, so that the structure can be packed.  Adam agrees with
+  // this.
+  unsigned char packing;
+  unsigned short table_id;
+  unsigned char parameter_id;
+  unsigned char parameter_size_bytes;
+  unsigned char unit_index;
+  // Size up to here is 30 bytes + 1 padding!
+  unsigned char input_data[499];
+  // Reserve lots of extra space for commands that set Sector Count
+  // register to large values
+  unsigned char output_data[512]; // starts 530 bytes in!
+  // two more padding bytes here if structure NOT packed.
+} TW_Ioctl;
+
+/* Ioctl buffer output -- SCSI interface only! */
+typedef struct TAG_TW_Output {
+  int padding[2];
+  char output_data[512];
+} TW_Output; 
+
+// What follows is needed for 9000 char interface only
+
+#define TW_IOCTL_FIRMWARE_PASS_THROUGH 0x108
+#define TW_MAX_SGL_LENGTH_9000 61
+
+typedef struct TAG_TW_Ioctl_Driver_Command_9000 {
+  unsigned int control_code;
+  unsigned int status;
+  unsigned int unique_id;
+  unsigned int sequence_id;
+  unsigned int os_specific;
+  unsigned int buffer_length;
+} TW_Ioctl_Driver_Command_9000;
+
+/* Command Packet */
+typedef struct TW_Command_9000 {
+  /* First DWORD */
+  struct {
+    unsigned char opcode:5;
+    unsigned char sgl_offset:3;
+  } byte0;
+  unsigned char size;
+  unsigned char request_id;
+  struct {
+    unsigned char unit:4;
+    unsigned char host_id:4;
+  } byte3;
+  /* Second DWORD */
+  unsigned char status;
+  unsigned char flags;
+  union {
+    unsigned short block_count;
+    unsigned short parameter_count;
+    unsigned short message_credits;
+  } byte6;
+  union {
+    struct {
+      u32 lba;
+      TW_SG_Entry sgl[TW_MAX_SGL_LENGTH_9000];
+      u32 padding;
+    } io;
+    struct {
+      TW_SG_Entry sgl[TW_MAX_SGL_LENGTH_9000];
+      u32 padding[2];
+    } param;
+    struct {
+      u32 response_queue_pointer;
+      u32 padding[125]; /* pad entire structure to 512 bytes */
+    } init_connection;
+    struct {
+      char version[504];
+    } ioctl_miniport_version;
+  } byte8;
+} TW_Command_9000;
+
+/* Command Packet for 9000+ controllers */
+typedef struct TAG_TW_Command_Apache {
+  struct {
+    unsigned char opcode:5;
+    unsigned char reserved:3;
+  } command;
+  unsigned char   unit;
+  unsigned short  request_id;
+  unsigned char   sense_length;
+  unsigned char   sgl_offset;
+  unsigned short  sgl_entries;
+  unsigned char   cdb[16];
+  TW_SG_Entry     sg_list[TW_MAX_SGL_LENGTH_9000];
+} TW_Command_Apache;
+
+/* New command packet header */
+typedef struct TAG_TW_Command_Apache_Header {
+  unsigned char sense_data[18];
+  struct {
+    char reserved[4];
+    unsigned short error;
+    unsigned char status;
+    struct {
+      unsigned char severity:3;
+      unsigned char reserved:5;
+    } substatus_block;
+  } status_block;
+  unsigned char err_specific_desc[102];
+} TW_Command_Apache_Header;
+
+/* This struct is a union of the 2 command packets */
+typedef struct TAG_TW_Command_Full_9000 {
+  TW_Command_Apache_Header header;
+  union {
+    TW_Command_9000   oldcommand;
+    TW_Command_Apache newcommand;
+  } command;
+  unsigned char padding[384]; /* Pad to 1024 bytes */
+} TW_Command_Full_9000;
+
+typedef struct TAG_TW_Ioctl_Apache {
+  TW_Ioctl_Driver_Command_9000 driver_command;
+  char                         padding[488];
+  TW_Command_Full_9000         firmware_command;
+  char                         data_buffer[1];
+  // three bytes of padding here if structure not packed!
+} TW_Ioctl_Buf_Apache;
+
+
+
+// START OF DEFINITIONS FOR THE CHARACTER INTERFACE TO THE
+// 6000/7000/8000 drivers
+
+#define TW_MAX_SGL_LENGTH        62
+#define TW_CMD_PACKET_WITH_DATA 0x1f
+
+/* Command Packet */
+typedef struct TW_Command {
+  /* First DWORD */
+  struct {
+    unsigned char opcode:5;
+    unsigned char sgl_offset:3;
+  } byte0;
+  unsigned char size;
+  unsigned char request_id;
+  struct {
+    unsigned char unit:4;
+    unsigned char host_id:4;
+  } byte3;
+  /* Second DWORD */
+  unsigned char status;
+  unsigned char flags;
+  union {
+    unsigned short block_count;
+    unsigned short parameter_count;
+    unsigned short message_credits;
+  } byte6;
+  union {
+    struct {
+      u32 lba;
+      TW_SG_Entry sgl[TW_MAX_SGL_LENGTH];
+      u32 padding;     /* pad to 512 bytes */
+    } io;
+    struct {
+      TW_SG_Entry sgl[TW_MAX_SGL_LENGTH];
+      u32 padding[2];
+    } param;
+    struct {
+      u32 response_queue_pointer;
+      u32 padding[125];
+    } init_connection;
+    struct {
+      char version[504];
+    } ioctl_miniport_version;
+  } byte8;
+} TW_Command;
+
+typedef struct TAG_TW_New_Ioctl {
+  unsigned int  data_buffer_length;
+  unsigned char padding [508];
+  TW_Command    firmware_command;
+  char          data_buffer[1];
+  // three bytes of padding here
+} TW_New_Ioctl;
+#pragma pack()
+
+#if 0
+// Useful for checking/understanding packing of 3ware data structures
+// above.
+void my(int x, char *y){
+  printf("The size of %30s is: %5d\n",y, x);
+  return;
+}
+
+int main() {
+  TW_Ioctl tmp;
+  my(sizeof(TW_SG_Entry),"TW_SG_Entry");
+  my(sizeof(TW_Passthru),"TW_Passthru");
+  my(sizeof(TW_Ioctl),"TW_Ioctl");
+  my(sizeof(TW_Output),"TW_Output");
+  my(sizeof(TW_Ioctl_Driver_Command_9000),"TW_Ioctl_Driver_Command_9000");
+  my(sizeof(TW_Command_9000),"TW_Command_9000");
+  my(sizeof(TW_Command_Apache),"TW_Command_Apache");
+  my(sizeof(TW_Command_Apache_Header),"TW_Command_Apache_Header");
+  my(sizeof(TW_Command_Full_9000),"TW_Command_Full_9000");
+  my(sizeof(TW_Ioctl_Buf_Apache),"TW_Ioctl_Buf_Apache");
+  my(sizeof(TW_Command),"TW_Command");
+  my(sizeof(TW_New_Ioctl),"TW_New_Ioctl");                                                                
+  printf("TW_Ioctl.table_id - start = %d (irrelevant)\n",
+         (void *)&tmp.table_id - (void *)&tmp);
+  printf("TW_Ioctl.input_data - start = %d (input passthru location)\n",
+         (void *)&tmp.input_data - (void *)&tmp);
+  printf("TW_Ioctl.output_data - start = %d (irrelevant)\n",
+         (void *)&tmp.output_data - (void *)&tmp);
+  return 0;
+}
+#endif
+
+// The following definitions are from hdreg.h in the kernel source
+// tree.  They don't carry any Copyright statements, but I think they
+// are primarily from Mark Lord and Andre Hedrick.
+typedef unsigned char task_ioreg_t;
+
+typedef struct hd_drive_task_hdr {
+  task_ioreg_t data;
+  task_ioreg_t feature;
+  task_ioreg_t sector_count;
+  task_ioreg_t sector_number;
+  task_ioreg_t low_cylinder;
+  task_ioreg_t high_cylinder;
+  task_ioreg_t device_head;
+  task_ioreg_t command;
+} task_struct_t;
+
+typedef union ide_reg_valid_s {
+  unsigned all                 : 16;
+  struct {
+    unsigned data              : 1;
+    unsigned error_feature     : 1;
+    unsigned sector            : 1;
+    unsigned nsector           : 1;
+    unsigned lcyl              : 1;
+    unsigned hcyl              : 1;
+    unsigned select            : 1;
+    unsigned status_command    : 1;
+    unsigned data_hob          : 1;
+    unsigned error_feature_hob : 1;
+    unsigned sector_hob                : 1;
+    unsigned nsector_hob       : 1;
+    unsigned lcyl_hob          : 1;
+    unsigned hcyl_hob          : 1;
+    unsigned select_hob                : 1;
+    unsigned control_hob       : 1;
+  } b;
+} ide_reg_valid_t;
+
+typedef struct ide_task_request_s {
+  task_ioreg_t    io_ports[8];
+  task_ioreg_t    hob_ports[8];
+  ide_reg_valid_t  out_flags;
+  ide_reg_valid_t  in_flags;
+  int             data_phase;
+  int             req_cmd;
+  unsigned long           out_size;
+  unsigned long           in_size;
+} ide_task_request_t;
+
+#define TASKFILE_NO_DATA         0x0000
+#define TASKFILE_IN              0x0001
+#define TASKFILE_OUT             0x0004
+#define HDIO_DRIVE_TASK_HDR_SIZE  8*sizeof(task_ioreg_t)
+#define IDE_DRIVE_TASK_NO_DATA        0
+#define IDE_DRIVE_TASK_IN             2
+#define IDE_DRIVE_TASK_OUT            3
+#define HDIO_DRIVE_CMD            0x031f
+#define HDIO_DRIVE_TASK           0x031e
+#define HDIO_DRIVE_TASKFILE       0x031d
+#define HDIO_GET_IDENTITY         0x030d
+
+#endif /* OS_LINUX_H_ */
diff --git a/os_netbsd.c b/os_netbsd.c
new file mode 100644 (file)
index 0000000..4f6cee0
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * os_netbsd.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Sergey Svishchev <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+#include "os_netbsd.h"
+#include <unistd.h>
+
+const char *os_XXXX_c_cvsid = "$Id: os_netbsd.c,v 1.15 2006/04/12 14:54:28 ballen4705 Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_NETBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+/* global variable holding byte count of allocated memory */
+extern long long bytes;
+
+enum warnings {
+  BAD_SMART, NO_3WARE, MAX_MSG
+};
+
+/* Utility function for printing warnings */
+void
+printwarning(int msgNo, const char *extra)
+{
+  static int printed[] = {0, 0};
+  static const char *message[] = {
+    "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
+    PACKAGE_STRING " does not currentlly support twe(4) devices (3ware Escalade)\n",
+  };
+
+  if (msgNo >= 0 && msgNo <= MAX_MSG) {
+    if (!printed[msgNo]) {
+      printed[msgNo] = 1;
+      pout("%s", message[msgNo]);
+      if (extra)
+       pout("%s", extra);
+    }
+  }
+  return;
+}
+
+static const char *net_dev_prefix = "/dev/";
+static const char *net_dev_ata_disk = "wd";
+static const char *net_dev_scsi_disk = "sd";
+static const char *net_dev_scsi_tape = "enrst";
+
+/* Guess device type(ata or scsi) based on device name */
+int
+guess_device_type(const char *dev_name)
+{
+  int len;
+  int dev_prefix_len = strlen(net_dev_prefix);
+
+  if (!dev_name || !(len = strlen(dev_name)))
+    return CONTROLLER_UNKNOWN;
+
+  if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) {
+    if (len <= dev_prefix_len)
+      return CONTROLLER_UNKNOWN;
+    else
+      dev_name += dev_prefix_len;
+  }
+  if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk)))
+    return CONTROLLER_ATA;
+
+  if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk)))
+    return CONTROLLER_SCSI;
+
+  if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape)))
+    return CONTROLLER_SCSI;
+
+  return CONTROLLER_UNKNOWN;
+}
+
+int
+get_dev_names(char ***names, const char *prefix)
+{
+  char *disknames, *p, **mp;
+  int n = 0;
+  int sysctl_mib[2];
+  size_t sysctl_len;
+
+  *names = NULL;
+
+  sysctl_mib[0] = CTL_HW;
+  sysctl_mib[1] = HW_DISKNAMES;
+  if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
+    pout("Failed to get value of sysctl `hw.disknames'\n");
+    return -1;
+  }
+  if (!(disknames = malloc(sysctl_len))) {
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+  if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
+    pout("Failed to get value of sysctl `hw.disknames'\n");
+    return -1;
+  }
+  if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+  for (p = strtok(disknames, " "); p; p = strtok(NULL, " ")) {
+    if (strncmp(p, prefix, strlen(prefix))) {
+      continue;
+    }
+    mp[n] = malloc(strlen(net_dev_prefix) + strlen(p) + 2);
+    if (!mp[n]) {
+      pout("Out of memory constructing scan device list\n");
+      return -1;
+    }
+    sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
+    bytes += strlen(mp[n]) + 1;
+    n++;
+  }
+
+  mp = realloc(mp, n * (sizeof(char *)));
+  bytes += (n) * (sizeof(char *));
+  *names = mp;
+  return n;
+}
+
+int
+make_device_names(char ***devlist, const char *name)
+{
+  if (!strcmp(name, "SCSI"))
+    return get_dev_names(devlist, net_dev_scsi_disk);
+  else if (!strcmp(name, "ATA"))
+    return get_dev_names(devlist, net_dev_ata_disk);
+  else
+    return 0;
+}
+
+int
+deviceopen(const char *pathname, char *type)
+{
+  if (!strcmp(type, "SCSI")) {
+    int fd = open(pathname, O_RDWR | O_NONBLOCK);
+    if (fd < 0 && errno == EROFS)
+      fd = open(pathname, O_RDONLY | O_NONBLOCK);
+    return fd;
+  } else if (!strcmp(type, "ATA"))
+    return open(pathname, O_RDWR | O_NONBLOCK);
+  else
+    return -1;
+}
+
+int
+deviceclose(int fd)
+{
+  return close(fd);
+}
+
+int
+marvell_command_interface(int fd, smart_command_set command, int select, char *data)
+{ return -1; }
+
+int
+ata_command_interface(int fd, smart_command_set command, int select, char *data)
+{
+  struct atareq req;
+  unsigned char inbuf[DEV_BSIZE];
+  int retval, copydata = 0;
+
+  memset(&req, 0, sizeof(req));
+  memset(&inbuf, 0, sizeof(inbuf));
+
+  switch (command) {
+  case READ_VALUES:
+    req.flags = ATACMD_READ;
+    req.features = WDSM_RD_DATA;
+    req.command = WDCC_SMART;
+    req.databuf = inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case READ_THRESHOLDS:
+    req.flags = ATACMD_READ;
+    req.features = WDSM_RD_THRESHOLDS;
+    req.command = WDCC_SMART;
+    req.databuf = inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case READ_LOG:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_READ_LOG_SECTOR;  /* XXX missing from wdcreg.h */
+    req.command = WDCC_SMART;
+    req.databuf = inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case WRITE_LOG:
+    memcpy(inbuf, data, 512);
+    req.flags = ATACMD_WRITE;
+    req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */
+    req.command = WDCC_SMART;
+    req.databuf = inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case IDENTIFY:
+    req.flags = ATACMD_READ;
+    req.command = WDCC_IDENTIFY;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case PIDENTIFY:
+    req.flags = ATACMD_READ;
+    req.command = ATAPI_IDENTIFY_DEVICE;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case ENABLE:
+    req.flags = ATACMD_READ;
+    req.features = WDSM_ENABLE_OPS;
+    req.command = WDCC_SMART;
+    req.cylinder = WDSMART_CYL;
+    req.timeout = 1000;
+    break;
+  case DISABLE:
+    req.flags = ATACMD_READ;
+    req.features = WDSM_DISABLE_OPS;
+    req.command = WDCC_SMART;
+    req.cylinder = WDSMART_CYL;
+    req.timeout = 1000;
+    break;
+  case AUTO_OFFLINE:
+    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_AUTO_OFFLINE;     /* XXX missing from wdcreg.h */
+    req.command = WDCC_SMART;
+    req.databuf = inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case AUTOSAVE:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */
+    req.command = WDCC_SMART;
+    req.cylinder = WDSMART_CYL;
+    req.sec_count = 0xf1;
+    /* to enable autosave */
+    req.timeout = 1000;
+    break;
+  case IMMEDIATE_OFFLINE:
+    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_IMMEDIATE_OFFLINE;        /* XXX missing from wdcreg.h */
+    req.command = WDCC_SMART;
+    req.databuf = inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = WDSMART_CYL;
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case STATUS_CHECK:
+    /* same command, no HDIO in NetBSD */
+  case STATUS:
+    req.flags = ATACMD_READ;
+    req.features = WDSM_STATUS;
+    req.command = WDCC_SMART;
+    req.cylinder = WDSMART_CYL;
+    req.timeout = 1000;
+    break;
+  case CHECK_POWER_MODE:
+    req.flags = ATACMD_READREG;
+    req.command = WDCC_CHECK_PWR;
+    req.timeout = 1000;
+    break;
+  default:
+    pout("Unrecognized command %d in ata_command_interface()\n", command);
+    errno = ENOSYS;
+    return -1;
+  }
+
+  if (command == STATUS_CHECK) {
+    char buf[512];
+
+    unsigned const short normal = WDSMART_CYL, failed = 0x2cf4;
+
+    if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
+      perror("Failed command");
+      return -1;
+    }
+    /* Cyl low and Cyl high unchanged means "Good SMART status" */
+    if (req.cylinder == normal)
+      return 0;
+
+    /* These values mean "Bad SMART status" */
+    if (req.cylinder == failed)
+      return 1;
+
+    /* We haven't gotten output that makes sense; 
+     * print out some debugging info */
+    snprintf(buf, sizeof(buf),
+      "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
+      (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num,
+      (int) (le16toh(req.cylinder) & 0xff), (int) ((le16toh(req.cylinder) >> 8) & 0xff),
+      (int) req.error);
+    printwarning(BAD_SMART, buf);
+    return 0;
+  }
+  if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
+    perror("Failed command");
+    return -1;
+  }
+  if (command == CHECK_POWER_MODE)
+    data[0] = req.sec_count;
+
+  if (copydata)
+    memcpy(data, inbuf, 512);
+
+  return 0;
+}
+
+int
+escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data)
+{
+  printwarning(NO_3WARE, NULL);
+  return -1;
+}
+
+int
+do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
+{
+  struct scsireq sc;
+
+  if (report > 0) {
+    size_t k;
+
+    const unsigned char *ucp = iop->cmnd;
+    const char *np;
+
+    np = scsi_get_opcode_name(ucp[0]);
+    pout(" [%s: ", np ? np : "<unknown opcode>");
+    for (k = 0; k < iop->cmnd_len; ++k)
+      pout("%02x ", ucp[k]);
+    if ((report > 1) &&
+      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+      pout("]\n  Outgoing data, len=%d%s:\n", (int) iop->dxfer_len,
+       (trunc ? " [only first 256 bytes shown]" : ""));
+      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
+    } else
+      pout("]");
+  }
+  memset(&sc, 0, sizeof(sc));
+  memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
+  sc.cmdlen = iop->cmnd_len;
+  sc.databuf = iop->dxferp;
+  sc.datalen = iop->dxfer_len;
+  sc.senselen = iop->max_sense_len;
+  sc.timeout = iop->timeout == 0 ? 60000 : (1000 * iop->timeout);
+  sc.flags =
+    (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ :       /* XXX */
+    (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
+
+  if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
+    warn("error sending SCSI ccb");
+    return -1;
+  }
+  iop->resid = sc.datalen - sc.datalen_used;
+  iop->scsi_status = sc.status;
+  if (iop->sensep) {
+    memcpy(iop->sensep, sc.sense, sc.senselen_used);
+    iop->resp_sense_len = sc.senselen_used;
+  }
+  if (report > 0) {
+    int trunc;
+
+    pout("  status=0\n");
+    trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+    pout("  Incoming data, len=%d%s:\n", (int) iop->dxfer_len,
+      (trunc ? " [only first 256 bytes shown]" : ""));
+    dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
+  }
+  return 0;
+}
+
+/* print examples for smartctl */
+void 
+print_smartctl_examples()
+{
+  char p;
+
+  p = 'a' + getrawpartition();
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+    "  smartctl -a /dev/wd0%c                      (Prints all SMART information)\n\n"
+    "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
+    "                                              (Enables SMART on first disk)\n\n"
+    "  smartctl -t long /dev/wd0%c             (Executes extended disk self-test)\n\n"
+    "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
+    "                                      (Prints Self-Test & Attribute errors)\n",
+    p, p, p, p
+    );
+#else
+  printf(
+    "  smartctl -a /dev/wd0%c                     (Prints all SMART information)\n"
+    "  smartctl -s on -o on -S on /dev/wd0%c        (Enables SMART on first disk)\n"
+    "  smartctl -t long /dev/wd0%c            (Executes extended disk self-test)\n"
+    "  smartctl -A -l selftest -q errorsonly /dev/wd0%c"
+    "                                      (Prints Self-Test & Attribute errors)\n",
+    p, p, p, p
+    );
+#endif
+  return;
+}
diff --git a/os_netbsd.h b/os_netbsd.h
new file mode 100644 (file)
index 0000000..b5ed733
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * os_netbsd.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 Sergey Svishchev <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 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_NETBSD_H_
+#define OS_NETBSD_H_
+
+#define OS_NETBSD_H_CVSID "$Id: os_netbsd.h,v 1.9 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+#include <sys/device.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <sys/scsiio.h>
+#include <sys/ataio.h>
+
+#define ata_smart_selftestlog __netbsd_ata_smart_selftestlog
+#include <dev/ata/atareg.h>
+#if HAVE_DEV_ATA_ATAVAR_H
+#include <dev/ata/atavar.h>
+#endif
+#include <dev/ic/wdcreg.h>
+#undef ata_smart_selftestlog
+
+#include <err.h>
+#include <fcntl.h>
+#include <util.h>
+
+#ifndef        WDSM_RD_THRESHOLDS      /* pre-1.6.2 system */
+#define        WDSM_RD_THRESHOLDS      0xd1
+#endif
+#ifndef        WDSMART_CYL
+#define        WDSMART_CYL             0xc24f
+#endif
+
+#endif /* OS_NETBSD_H_ */
diff --git a/os_openbsd.c b/os_openbsd.c
new file mode 100644 (file)
index 0000000..33a7039
--- /dev/null
@@ -0,0 +1,448 @@
+/*
+ * os_openbsd.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 David Snyder <smartmontools-support@lists.sourceforge.net>
+ *
+ * Derived from os_netbsd.c by Sergey Svishchev <smartmontools-support@lists.sourceforge.net>, Copyright (C) 2003-6 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+#include "os_openbsd.h"
+
+const char *os_XXXX_c_cvsid = "$Id: os_openbsd.c,v 1.10 2006/04/12 14:54:28 ballen4705 Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_OPENBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+/* global variable holding byte count of allocated memory */
+extern long long bytes;
+
+enum warnings {
+  BAD_SMART, NO_3WARE, MAX_MSG
+};
+
+/* Utility function for printing warnings */
+void
+printwarning(int msgNo, const char *extra)
+{
+  static int printed[] = {0, 0};
+  static const char *message[] = {
+    "Error: SMART Status command failed.\nPlease get assistance from \n" PACKAGE_HOMEPAGE "\nRegister values returned from SMART Status command are:\n",
+    PACKAGE_STRING " does not currentlly support twe(4) devices (3ware Escalade)\n",
+  };
+
+  if (msgNo >= 0 && msgNo <= MAX_MSG) {
+    if (!printed[msgNo]) {
+      printed[msgNo] = 1;
+      pout("%s", message[msgNo]);
+      if (extra)
+       pout("%s", extra);
+    }
+  }
+  return;
+}
+
+static const char *net_dev_prefix = "/dev/";
+static const char *net_dev_ata_disk = "wd";
+static const char *net_dev_scsi_disk = "sd";
+static const char *net_dev_scsi_tape = "st";
+
+/* Guess device type(ata or scsi) based on device name */
+int
+guess_device_type(const char *dev_name)
+{
+  int len;
+  int dev_prefix_len = strlen(net_dev_prefix);
+
+  if (!dev_name || !(len = strlen(dev_name)))
+    return CONTROLLER_UNKNOWN;
+
+  if (!strncmp(net_dev_prefix, dev_name, dev_prefix_len)) {
+    if (len <= dev_prefix_len)
+      return CONTROLLER_UNKNOWN;
+    else
+      dev_name += dev_prefix_len;
+  }
+  if (!strncmp(net_dev_ata_disk, dev_name, strlen(net_dev_ata_disk)))
+    return CONTROLLER_ATA;
+
+  if (!strncmp(net_dev_scsi_disk, dev_name, strlen(net_dev_scsi_disk)))
+    return CONTROLLER_SCSI;
+
+  if (!strncmp(net_dev_scsi_tape, dev_name, strlen(net_dev_scsi_tape)))
+    return CONTROLLER_SCSI;
+
+  return CONTROLLER_UNKNOWN;
+}
+
+int
+get_dev_names(char ***names, const char *prefix)
+{
+  char *disknames, *p, **mp;
+  int n = 0;
+  int sysctl_mib[2];
+  size_t sysctl_len;
+
+  *names = NULL;
+
+  sysctl_mib[0] = CTL_HW;
+  sysctl_mib[1] = HW_DISKNAMES;
+  if (-1 == sysctl(sysctl_mib, 2, NULL, &sysctl_len, NULL, 0)) {
+    pout("Failed to get value of sysctl `hw.disknames'\n");
+    return -1;
+  }
+  if (!(disknames = malloc(sysctl_len))) {
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+  if (-1 == sysctl(sysctl_mib, 2, disknames, &sysctl_len, NULL, 0)) {
+    pout("Failed to get value of sysctl `hw.disknames'\n");
+    return -1;
+  }
+  if (!(mp = (char **) calloc(strlen(disknames) / 2, sizeof(char *)))) {
+    pout("Out of memory constructing scan device list\n");
+    return -1;
+  }
+  for (p = strtok(disknames, ","); p; p = strtok(NULL, ",")) {
+    if (strncmp(p, prefix, strlen(prefix))) {
+      continue;
+    }
+    mp[n] = malloc(strlen(net_dev_prefix) + strlen(p) + 2);
+    if (!mp[n]) {
+      pout("Out of memory constructing scan device list\n");
+      return -1;
+    }
+    sprintf(mp[n], "%s%s%c", net_dev_prefix, p, 'a' + getrawpartition());
+    bytes += strlen(mp[n]) + 1;
+    n++;
+  }
+
+  mp = realloc(mp, n * (sizeof(char *)));
+  bytes += (n) * (sizeof(char *));
+  *names = mp;
+  return n;
+}
+
+int
+make_device_names(char ***devlist, const char *name)
+{
+  if (!strcmp(name, "SCSI"))
+    return get_dev_names(devlist, net_dev_scsi_disk);
+  else if (!strcmp(name, "ATA"))
+    return get_dev_names(devlist, net_dev_ata_disk);
+  else
+    return 0;
+}
+
+int
+deviceopen(const char *pathname, char *type)
+{
+  if (!strcmp(type, "SCSI")) {
+    int fd = open(pathname, O_RDWR | O_NONBLOCK);
+    if (fd < 0 && errno == EROFS)
+      fd = open(pathname, O_RDONLY | O_NONBLOCK);
+    return fd;
+  } else if (!strcmp(type, "ATA"))
+    return open(pathname, O_RDWR | O_NONBLOCK);
+  else
+    return -1;
+}
+
+int
+deviceclose(int fd)
+{
+  return close(fd);
+}
+
+int
+marvell_command_interface(int fd, smart_command_set command, int select, char *data)
+{ return -1; }
+
+int
+ata_command_interface(int fd, smart_command_set command, int select, char *data)
+{
+  struct atareq req;
+  unsigned char inbuf[DEV_BSIZE];
+  int retval, copydata = 0;
+
+  memset(&req, 0, sizeof(req));
+  memset(&inbuf, 0, sizeof(inbuf));
+
+  switch (command) {
+  case READ_VALUES:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_READ_VALUES;
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case READ_THRESHOLDS:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_READ_THRESHOLDS;
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case READ_LOG:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_READ_LOG_SECTOR;  /* XXX missing from wdcreg.h */
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case WRITE_LOG:
+    memcpy(inbuf, data, 512);
+    req.flags = ATACMD_WRITE;
+    req.features = ATA_SMART_WRITE_LOG_SECTOR; /* XXX missing from wdcreg.h */
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case IDENTIFY:
+    req.flags = ATACMD_READ;
+    req.command = WDCC_IDENTIFY;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case PIDENTIFY:
+    req.flags = ATACMD_READ;
+    req.command = ATAPI_IDENTIFY_DEVICE;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.timeout = 1000;
+    copydata = 1;
+    break;
+  case ENABLE:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_ENABLE;
+    req.command = ATAPI_SMART;
+    req.cylinder = htole16(WDSMART_CYL);
+    req.timeout = 1000;
+    break;
+  case DISABLE:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_DISABLE;
+    req.command = ATAPI_SMART;
+    req.cylinder = htole16(WDSMART_CYL);
+    req.timeout = 1000;
+    break;
+  case AUTO_OFFLINE:
+    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_AUTO_OFFLINE;     /* XXX missing from wdcreg.h */
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case AUTOSAVE:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_AUTOSAVE; /* XXX missing from wdcreg.h */
+    req.command = ATAPI_SMART;
+    req.cylinder = htole16(WDSMART_CYL);
+    req.sec_count = 0xf1;
+    /* to enable autosave */
+    req.timeout = 1000;
+    break;
+  case IMMEDIATE_OFFLINE:
+    /* NOTE: According to ATAPI 4 and UP, this command is obsolete */
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_IMMEDIATE_OFFLINE;        /* XXX missing from wdcreg.h */
+    req.command = ATAPI_SMART;
+    req.databuf = (caddr_t) inbuf;
+    req.datalen = sizeof(inbuf);
+    req.cylinder = htole16(WDSMART_CYL);
+    req.sec_num = select;
+    req.sec_count = 1;
+    req.timeout = 1000;
+    break;
+  case STATUS_CHECK:
+    /* same command, no HDIO in NetBSD */
+  case STATUS:
+    req.flags = ATACMD_READ;
+    req.features = ATA_SMART_STATUS;
+    req.command = ATAPI_SMART;
+    req.cylinder = htole16(WDSMART_CYL);
+    req.timeout = 1000;
+    break;
+  case CHECK_POWER_MODE:
+    req.flags = ATACMD_READREG;
+    req.command = WDCC_CHECK_PWR;
+    req.timeout = 1000;
+    break;
+  default:
+    pout("Unrecognized command %d in ata_command_interface()\n", command);
+    errno = ENOSYS;
+    return -1;
+  }
+
+  if (command == STATUS_CHECK) {
+    char buf[512];
+
+    unsigned const short normal = WDSMART_CYL, failed = 0x2cf4;
+
+    if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
+      perror("Failed command");
+      return -1;
+    }
+    /* Cyl low and Cyl high unchanged means "Good SMART status" */
+    if (letoh16(req.cylinder) == normal)
+      return 0;
+
+    /* These values mean "Bad SMART status" */
+    if (letoh16(req.cylinder) == failed)
+      return 1;
+
+    /* We haven't gotten output that makes sense; 
+     * print out some debugging info */
+    snprintf(buf, sizeof(buf),
+      "CMD=0x%02x\nFR =0x%02x\nNS =0x%02x\nSC =0x%02x\nCL =0x%02x\nCH =0x%02x\nRETURN =0x%04x\n",
+      (int) req.command, (int) req.features, (int) req.sec_count, (int) req.sec_num,
+      (int) (letoh16(req.cylinder) & 0xff), (int) ((letoh16(req.cylinder) >> 8) & 0xff),
+      (int) req.error);
+    printwarning(BAD_SMART, buf);
+    return 0;
+  }
+  if ((retval = ioctl(fd, ATAIOCCOMMAND, &req))) {
+    perror("Failed command");
+    return -1;
+  }
+  if (command == CHECK_POWER_MODE)
+    data[0] = req.sec_count;
+
+  if (copydata)
+    memcpy(data, inbuf, 512);
+
+  return 0;
+}
+
+int
+escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data)
+{
+  printwarning(NO_3WARE, NULL);
+  return -1;
+}
+
+int
+do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
+{
+  struct scsireq sc;
+
+  if (report > 0) {
+    size_t k;
+
+    const unsigned char *ucp = iop->cmnd;
+    const char *np;
+
+    np = scsi_get_opcode_name(ucp[0]);
+    pout(" [%s: ", np ? np : "<unknown opcode>");
+    for (k = 0; k < iop->cmnd_len; ++k)
+      pout("%02x ", ucp[k]);
+    if ((report > 1) &&
+      (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+      int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+      pout("]\n  Outgoing data, len=%d%s:\n", (int) iop->dxfer_len,
+       (trunc ? " [only first 256 bytes shown]" : ""));
+      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
+    } else
+      pout("]");
+  }
+  memset(&sc, 0, sizeof(sc));
+  memcpy(sc.cmd, iop->cmnd, iop->cmnd_len);
+  sc.cmdlen = iop->cmnd_len;
+  sc.databuf = iop->dxferp;
+  sc.datalen = iop->dxfer_len;
+  sc.senselen = iop->max_sense_len;
+  sc.timeout = iop->timeout == 0 ? 60000 : iop->timeout;       /* XXX */
+  sc.flags =
+    (iop->dxfer_dir == DXFER_NONE ? SCCMD_READ :       /* XXX */
+    (iop->dxfer_dir == DXFER_FROM_DEVICE ? SCCMD_READ : SCCMD_WRITE));
+
+  if (ioctl(fd, SCIOCCOMMAND, &sc) < 0) {
+    warn("error sending SCSI ccb");
+    return -1;
+  }
+  iop->resid = sc.datalen - sc.datalen_used;
+  iop->scsi_status = sc.status;
+  if (iop->sensep) {
+    memcpy(iop->sensep, sc.sense, sc.senselen_used);
+    iop->resp_sense_len = sc.senselen_used;
+  }
+  if (report > 0) {
+    int trunc;
+
+    pout("  status=0\n");
+    trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+    pout("  Incoming data, len=%d%s:\n", (int) iop->dxfer_len,
+      (trunc ? " [only first 256 bytes shown]" : ""));
+    dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len), 1);
+  }
+  return 0;
+}
+
+/* print examples for smartctl */
+void 
+print_smartctl_examples()
+{
+  char p;
+
+  p = 'a' + getrawpartition();
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+    "  smartctl -a /dev/wd0%c                      (Prints all SMART information)\n\n"
+    "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/wd0%c\n"
+    "                                              (Enables SMART on first disk)\n\n"
+    "  smartctl -t long /dev/wd0%c             (Executes extended disk self-test)\n\n"
+    "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/wd0%c\n"
+    "                                      (Prints Self-Test & Attribute errors)\n",
+    p, p, p, p
+    );
+#else
+  printf(
+    "  smartctl -a /dev/wd0%c                     (Prints all SMART information)\n"
+    "  smartctl -s on -o on -S on /dev/wd0%c        (Enables SMART on first disk)\n"
+    "  smartctl -t long /dev/wd0%c            (Executes extended disk self-test)\n"
+    "  smartctl -A -l selftest -q errorsonly /dev/wd0%c"
+    "                                      (Prints Self-Test & Attribute errors)\n",
+    p, p, p, p
+    );
+#endif
+  return;
+}
diff --git a/os_openbsd.h b/os_openbsd.h
new file mode 100644 (file)
index 0000000..356343a
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * os_openbsd.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 David Snyder <smartmontools-support@lists.sourceforge.net>
+ *
+ * Derived from os_netbsd.c by Sergey Svishchev <smartmontools-support@lists.sourceforge.net>, Copyright (C) 2003-6 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 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_OPENBSD_H_
+#define OS_OPENBSD_H_
+
+#define OS_OPENBSD_H_CVSID "$Id: os_openbsd.h,v 1.4 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+/* from NetBSD: atareg.h,v 1.17, by Manuel Bouyer */
+/* Actually fits _perfectly_ into OBSDs wdcreg.h, but... */
+/* Subcommands for SMART (features register) */
+#define WDSMART_CYL             0xc24f
+
+#include <sys/device.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#include <sys/scsiio.h>
+#include <sys/ataio.h>
+
+#define ata_smart_selftestlog __openbsd_ata_smart_selftestlog
+#include <dev/ata/atareg.h>
+#if HAVE_DEV_ATA_ATAVAR_H
+#include <dev/ata/atavar.h>
+#endif
+#include <dev/ic/wdcreg.h>
+#undef ata_smart_selftestlog
+
+#include <err.h>
+#include <fcntl.h>
+#include <util.h>
+
+#endif /* OS_OPENBSD_H_ */
diff --git a/os_solaris.c b/os_solaris.c
new file mode 100644 (file)
index 0000000..d68197c
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * os_solaris.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 SAWADA Keiji <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-6 Casper Dik <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+// These are needed to define prototypes for the functions defined below
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+// This is to include whatever prototypes you define in os_solaris.h
+#include "os_solaris.h"
+
+#define ARGUSED(x) ((void)(x))
+
+extern long long bytes;
+
+static const char *filenameandversion="$Id: os_solaris.c,v 1.26 2006/04/12 14:54:28 ballen4705 Exp $";
+
+const char *os_XXXX_c_cvsid="$Id: os_solaris.c,v 1.26 2006/04/12 14:54:28 ballen4705 Exp $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_SOLARIS_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+// The printwarning() function warns about unimplemented functions
+int printedout[2];
+char *unimplemented[2]={
+  "ATA command routine ata_command_interface()",
+  "3ware Escalade Controller command routine escalade_command_interface()",
+};
+
+int printwarning(int which){
+  if (!unimplemented[which])
+    return 0;
+
+  if (printedout[which])
+    return 1;
+  
+  printedout[which]=1;
+  
+  pout("\n"
+       "#######################################################################\n"
+       "%s NOT IMPLEMENTED under Solaris.\n"
+       "Please contact " PACKAGE_BUGREPORT " if\n"
+       "you want to help in porting smartmontools to Solaris.\n"
+       "#######################################################################\n"
+       "\n",
+       unimplemented[which]);
+
+  return 1;
+}
+
+// print examples for smartctl
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+         "  smartctl -a /dev/rdsk/c0t0d0s0             (Prints all SMART information)\n\n"
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/rdsk/c0t0d0s0\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl -t long /dev/rdsk/c0t0d0s0 (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/rdsk/c0t0d0s0\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+         );
+#else
+  printf(
+         "  smartctl -a /dev/rdsk/c0t0d0s0               (Prints all SMART information)\n"
+         "  smartctl -s on -o on -S on /dev/rdsk/c0t0d0s0 (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/rdsk/c0t0d0s0      (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/rdsk/c0t0d0s0\n"
+         "                                        (Prints Self-Test & Attribute errors)\n"
+         );
+#endif
+  return;
+}
+
+static const char *uscsidrvrs[] = {
+        "sd",
+        "ssd",
+        "st"
+};
+
+static const char *atadrvrs[] = {
+        "cmdk",
+        "dad",
+};
+
+static int
+isdevtype(const char *dev_name, const char *table[], int tsize)
+{
+  char devpath[MAXPATHLEN];
+  int i;
+  char *basename;
+
+  if (realpath(dev_name, devpath) == NULL)
+    return 0;
+  if ((basename = strrchr(devpath, '/')) == NULL)
+    return 0;
+
+  basename++;
+
+  for (i = 0; i < tsize; i++) {
+    int l = strlen(table[i]);
+    if (strncmp(basename, table[i], l) == 0 && basename[l] == '@')
+      return 1;
+  }
+  return 0;
+}
+
+static int
+isscsidev(const char *path)
+{
+  return isdevtype(path, uscsidrvrs, sizeof (uscsidrvrs) / sizeof (char *));
+}
+
+static int
+isatadev(const char *path)
+{
+  return isdevtype(path, atadrvrs, sizeof (atadrvrs) / sizeof (char *));
+}
+
+// tries to guess device type given the name (a path)
+int guess_device_type (const char* dev_name) {
+  if (isscsidev(dev_name))
+    return CONTROLLER_SCSI;
+  else if (isatadev(dev_name))
+    return CONTROLLER_ATA;
+  else
+    return CONTROLLER_UNKNOWN;
+}
+
+struct pathlist {
+        char **names;
+        int  nnames;
+        int  maxnames;
+};
+
+static int
+addpath(const char *path, struct pathlist *res)
+{
+        if (++res->nnames > res->maxnames) {
+                res->maxnames += 16;
+                res->names = realloc(res->names, res->maxnames * sizeof (char *));
+                if (res->names == NULL)
+                        return -1;
+                bytes += 16*sizeof(char *);
+        }
+        if (!(res->names[res->nnames-1] = CustomStrDup((char *)path, 1, __LINE__, filenameandversion)))
+                return -1;
+        return 0;
+}
+
+static int 
+grokdir(const char *dir, struct pathlist *res, int testfun(const char *))
+{
+        char pathbuf[MAXPATHLEN];
+        size_t len;
+        DIR *dp;
+        struct dirent *de;
+        int isdisk = strstr(dir, "dsk") != NULL;
+        char *p;
+
+        len = snprintf(pathbuf, sizeof (pathbuf), "%s/", dir);
+        if (len >= sizeof (pathbuf))
+                return -1;
+
+        dp = opendir(dir);
+        if (dp == NULL)
+                return 0;
+
+        while ((de = readdir(dp)) != NULL) {
+                if (de->d_name[0] == '.')
+                        continue;
+
+                if (strlen(de->d_name) + len >= sizeof (pathbuf))
+                        continue;
+
+                if (isdisk) {
+                        /* Disk represented by slice 0 */
+                        p = strstr(de->d_name, "s0");
+                        /* String doesn't end in "s0\0" */
+                        if (p == NULL || p[2] != '\0')
+                                continue;
+                } else {
+                        /* Tape drive represented by the all-digit device */
+                        for (p = de->d_name; *p; p++)
+                                if (!isdigit((int)(*p)))
+                                        break;
+                        if (*p != '\0')
+                                continue;
+                }
+                strcpy(&pathbuf[len], de->d_name);
+                if (testfun(pathbuf)) {
+                        if (addpath(pathbuf, res) == -1) {
+                                closedir(dp);
+                                return -1;
+                        }
+                }
+        }
+        closedir(dp);
+
+        return 0;
+}
+
+// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
+// smartd.  Returns number of devices, or -1 if out of memory.
+int make_device_names (char*** devlist, const char* name) {
+        struct pathlist res;
+
+        res.nnames = res.maxnames = 0;
+        res.names = NULL;
+        if (strcmp(name, "SCSI") == 0) {
+                if (grokdir("/dev/rdsk", &res, isscsidev) == -1)
+                        return -1;
+                if (grokdir("/dev/rmt", &res, isscsidev) == -1)
+                        return -1;
+       } else if (strcmp(name, "ATA") == 0) {
+                if (grokdir("/dev/rdsk", &res, isatadev) == -1)
+                        return -1;
+       } else {
+                // non-SCSI and non-ATA case not implemented
+                *devlist=NULL;
+                return 0;
+       }
+
+       // shrink array to min possible size
+       res.names = realloc(res.names, res.nnames * sizeof (char *));
+       bytes -= sizeof(char *)*(res.maxnames-res.nnames);
+
+       // pass list back
+       *devlist = res.names;
+       return res.nnames;
+}
+
+// Like open().  Return integer handle, used by functions below only.
+// type="ATA" or "SCSI".
+int deviceopen(const char *pathname, char *type){
+  if (!strcmp(type,"SCSI")) 
+    return open(pathname, O_RDWR | O_NONBLOCK);
+  else if (!strcmp(type,"ATA")) 
+    return open(pathname, O_RDONLY | O_NONBLOCK);
+  else
+    return -1;
+}
+
+// Like close().  Acts on handles returned by above function.
+int deviceclose(int fd){
+    return close(fd);
+}
+
+static void swap_sector(void *p)
+{
+    int i;
+    unsigned char t, *cp = p;
+    for(i = 0; i < 256; i++) {
+        t = cp[0]; cp[0] = cp[1]; cp[1] = t;
+        cp += 2;
+    }
+}
+
+// Interface to ATA devices.  See os_linux.c
+int marvell_command_interface(int fd, smart_command_set command, int select, char *data){
+    ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
+    return -1;
+}
+
+int ata_command_interface(int fd, smart_command_set command, int select, char *data){
+#if defined(__sparc)
+    int err;
+    switch (command){
+    case CHECK_POWER_MODE:
+       /* currently not recognized */
+       return -1;
+    case READ_VALUES:
+       return smart_read_data(fd, data);
+    case READ_THRESHOLDS:
+       return smart_read_thresholds(fd, data);
+    case READ_LOG:
+       return smart_read_log(fd, select, 1, data);
+    case IDENTIFY:
+       err = ata_identify(fd, data);
+       if(err) return err;
+       swap_sector(data);
+       return 0;
+    case PIDENTIFY:
+       err = ata_pidentify(fd, data);
+       if(err) return err;
+       swap_sector(data);
+       return 0;
+    case ENABLE:
+       return smart_enable(fd);
+    case DISABLE:
+       return smart_disable(fd);
+    case STATUS:
+       return smart_status(fd);
+    case AUTO_OFFLINE:
+       return smart_auto_offline(fd, select);
+    case AUTOSAVE:
+       return smart_auto_save(fd, select);
+    case IMMEDIATE_OFFLINE:
+       return smart_immediate_offline(fd, select);
+    case STATUS_CHECK:
+       return smart_status_check(fd);
+    default:
+       pout("Unrecognized command %d in ata_command_interface() of os_solaris.c\n", command);
+       exit(1);
+       break;
+    }
+#else /* __sparc */
+    // avoid gcc warnings//
+    fd=command=select=0;
+    data=NULL;
+
+    /* Above smart_* routines uses undocumented ioctls of "dada"
+     * driver, which is specific to SPARC Solaris.  See
+     * os_solaris_ata.s for further details. x86 Solaris seems not to
+     * provide similar or alternative interface... */
+    if (printwarning(0))
+       return -1;
+#endif
+    return -1;
+}
+
+// Interface to ATA devices behind 3ware escalade RAID controller cards.  See os_linux.c
+int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data){
+  // avoid gcc warnings//
+  fd=disknum=escalade_type=command=select=0;
+  data=NULL;
+
+  if (printwarning(1))
+    return -1;
+  return -1;
+}
+
+#include <errno.h>
+#include <sys/scsi/generic/commands.h>
+#include <sys/scsi/generic/status.h>
+#include <sys/scsi/impl/types.h>
+#include <sys/scsi/impl/uscsi.h>
+
+// Interface to SCSI devices.  See os_linux.c
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report) {
+  struct uscsi_cmd uscsi;
+
+    if (report > 0) {
+        int k;
+        const unsigned char * ucp = iop->cmnd;
+        const char * np;
+
+        np = scsi_get_opcode_name(ucp[0]);
+        pout(" [%s: ", np ? np : "<unknown opcode>");
+        for (k = 0; k < (int)iop->cmnd_len; ++k)
+            pout("%02x ", ucp[k]);
+        if ((report > 1) && 
+            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+            pout("]\n  Outgoing data, len=%d%s:\n", (int)iop->dxfer_len,
+                 (trunc ? " [only first 256 bytes shown]" : ""));
+            dStrHex((char *)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+        }
+        else
+            pout("]");
+    }
+
+
+  memset(&uscsi, 0, sizeof (uscsi));
+
+  uscsi.uscsi_cdb = (void *)iop->cmnd;
+  uscsi.uscsi_cdblen = iop->cmnd_len;
+  if (iop->timeout == 0)
+    uscsi.uscsi_timeout = 60; /* XXX */
+  else
+    uscsi.uscsi_timeout = iop->timeout;
+  uscsi.uscsi_bufaddr = (void *)iop->dxferp;
+  uscsi.uscsi_buflen = iop->dxfer_len;
+  uscsi.uscsi_rqbuf = (void *)iop->sensep;
+  uscsi.uscsi_rqlen = iop->max_sense_len;
+
+  switch (iop->dxfer_dir) {
+  case DXFER_NONE:
+  case DXFER_FROM_DEVICE:
+    uscsi.uscsi_flags = USCSI_READ;
+    break;
+  case DXFER_TO_DEVICE:
+    uscsi.uscsi_flags = USCSI_WRITE;
+    break;
+  default:
+    return -EINVAL;
+  }
+  uscsi.uscsi_flags |= USCSI_ISOLATE;
+
+  if (ioctl(fd, USCSICMD, &uscsi))
+    return -errno;
+
+  iop->scsi_status = uscsi.uscsi_status;
+  iop->resid = uscsi.uscsi_resid;
+  iop->resp_sense_len = iop->max_sense_len - uscsi.uscsi_rqresid;
+
+  if (report > 0) {
+    int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+    pout("  status=0\n");
+    
+    pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+         (trunc ? " [only first 256 bytes shown]" : ""));
+    dStrHex((char *)iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+  }
+
+  return (0);
+}
diff --git a/os_solaris.h b/os_solaris.h
new file mode 100644 (file)
index 0000000..202dcac
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * os_solaris.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2003-6 SAWADA Keiji <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2003-6 Casper Dik <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * 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_SOLARIS_H_
+#define OS_SOLARIS_H_
+
+#define OS_SOLARIS_H_CVSID "$Id: os_solaris.h,v 1.12 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+// 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_solaris.c
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+// function prototypes for functions defined in os_solaris_ata.s
+int smart_read_data(int fd, void *data);
+int smart_read_thresholds(int fd, void *data);
+int smart_read_log(int fd, int s, int count, void *data);
+int ata_identify(int fd, void *data);
+int ata_pidentify(int fd, void *data);
+int smart_enable(int fd);
+int smart_disable(int fd);
+int smart_status(int fd);
+int smart_auto_offline(int fd, int s);
+int smart_auto_save(int fd, int s);
+int smart_immediate_offline(int fd, int s);
+int smart_status_check(int fd);
+
+// wrapper macros
+#define smart_enable_auto_save(fd)     smart_auto_save(fd, 0xf1)
+#define smart_disable_auto_save(fd)    smart_auto_save(fd, 0x00)
+
+#endif /* OS_SOLARIS_H_ */
diff --git a/os_solaris_ata.s b/os_solaris_ata.s
new file mode 100644 (file)
index 0000000..3199e17
--- /dev/null
@@ -0,0 +1,697 @@
+! 
+!   os_solaris_ata.s
+! 
+!   Home page of code is: http://smartmontools.sourceforge.net
+! 
+!   Copyright (C) 2003-6 SAWADA Keiji <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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+! 
+! 
+!        --------------------------------------------------------
+!        direct access routines to ATA device under Solaris/SPARC
+!        --------------------------------------------------------
+! 
+! Information
+! -----------
+! 
+! In Solaris, programmer can pass SCSI command to target device directly
+! by using USCSI ioctl or using "scg" generic SCSI driver.  But, such
+! method does not exist for ATA devices.
+! 
+! However, I can access Solaris kernel source because I am subscriber of
+! Source Foundation Program of Solaris.  So, I can find method of
+! accessing ATA device directly.  The method is to pack command in
+! undocumented structure and issue ioctl that appears only in kernel
+! source.  Yes, that is the same way in using USCSI interface.
+! 
+! But, I met difficulty in disclosing this technique.  I have signed NDA
+! with Sun that inhibits me not to violate their intellectual property.
+! 
+! Fortunately, Sun allows licensees to publish "Interfaces" if:
+! 
+! (1) he/she treats Solaris code as confidential
+! 
+! (2) and he/she doesn't incorporate Sun's code into his/her code
+! 
+! (3) and disclose enough information to use "Interface" to everyone.
+! 
+! So, I publish that technique in assembly code or object code because:
+! 
+! (1) I believe Sun's intellectural property is not invaded because I
+!     didn't reveal any struct member and ioctl to non-licensee.
+! 
+! (2) no piece of kernel source is included in this code.
+! 
+! (3) And finally, I publish enough information below in order to use
+!     this code.
+! 
+! For last reason, please don't remove "Calling Interface" section from
+! distribution.
+! 
+! 
+! Calling Interface
+! -----------------
+! 
+! Name of function/macro presents corresponding S.M.A.R.T. command.
+! 
+! Parameters are described below.
+! 
+! int fd
+! 
+!     File descriptor of ATA device.  Device would be
+!     /dev/rdsk/cXtXdXsX.
+! 
+!     Device should be raw device serviced by "dada" driver.  ATAPI
+!     CD-ROM/R/RW, DVD-ROM, and so on are not allowed because they are
+!     serviced by "sd" driver.  On x86 Solaris, "cmdk" driver services
+!     them, this routines doesn't work.
+! 
+! int s
+!     Select sector for service.  For example, this indicates log sector
+!     number for smart_read_log() function.  Probably you need to read
+!     ATA specification for this parameter.
+! 
+! void *data
+!     Data going to be read/written.  It don't have to be word aligned,
+!     But data shall points valid user memory space.
+! 
+! This is very tiny routines, but if you feel this insufficient, please
+! let me know.
+! 
+!                                      ksw / SAWADA Keiji
+!                                      <card_captor@users.sourceforge.net>
+       .file   "solaris-ata-in.c"
+       .section        ".rodata"
+       .align 8
+.LLC0:
+       .asciz  "$Id: os_solaris_ata.s,v 1.5 2006/04/12 14:54:28 ballen4705 Exp $"
+       .global os_solaris_ata_s_cvsid
+       .section        ".data"
+       .align 4
+       .type   os_solaris_ata_s_cvsid, #object
+       .size   os_solaris_ata_s_cvsid, 4
+os_solaris_ata_s_cvsid:
+       .long   .LLC0
+       .section        ".text"
+       .align 4
+       .type   ata_cmd, #function
+       .proc   04
+ata_cmd:
+       !#PROLOGUE# 0
+       save    %sp, -184, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %i1, [%fp+72]
+       st      %i2, [%fp+76]
+       st      %i3, [%fp+80]
+       st      %i4, [%fp+84]
+       st      %i5, [%fp+88]
+       ld      [%fp+92], %g1
+       st      %g1, [%fp-76]
+       ld      [%fp-76], %g1
+       and     %g1, 3, %g1
+       cmp     %g1, 0
+       be      .LL2
+       nop
+       mov     -2, %g1
+       st      %g1, [%fp-80]
+       b       .LL1
+        nop
+.LL2:
+       add     %fp, -56, %g1
+       mov     %g1, %o0
+       mov     0, %o1
+       mov     36, %o2
+       call    memset, 0
+        nop
+       add     %fp, -72, %g1
+       mov     %g1, %o0
+       mov     0, %o1
+       mov     16, %o2
+       call    memset, 0
+        nop
+       ld      [%fp+72], %g1
+       stb     %g1, [%fp-72]
+       mov     1, %g1
+       stb     %g1, [%fp-71]
+       mov     1, %g1
+       stb     %g1, [%fp-70]
+       ld      [%fp+76], %g1
+       stb     %g1, [%fp-69]
+       ld      [%fp+84], %g1
+       sll     %g1, 9, %g1
+       st      %g1, [%fp-68]
+       ld      [%fp+80], %g1
+       st      %g1, [%fp-60]
+       mov     10, %g1
+       sth     %g1, [%fp-52]
+       ld      [%fp+88], %g1
+       cmp     %g1, 0
+       be      .LL3
+       nop
+       mov     14, %g1
+       st      %g1, [%fp-84]
+       b       .LL4
+        nop
+.LL3:
+       mov     6, %g1
+       st      %g1, [%fp-84]
+.LL4:
+       ld      [%fp-84], %g1
+       st      %g1, [%fp-48]
+       ld      [%fp+88], %g1
+       sll     %g1, 9, %g1
+       st      %g1, [%fp-44]
+       ld      [%fp+88], %g1
+       sll     %g1, 9, %g1
+       st      %g1, [%fp-40]
+       ld      [%fp+88], %g1
+       cmp     %g1, 0
+       be      .LL5
+       nop
+       ld      [%fp+92], %g1
+       st      %g1, [%fp-88]
+       b       .LL6
+        nop
+.LL5:
+       st      %g0, [%fp-88]
+.LL6:
+       ld      [%fp-88], %g1
+       st      %g1, [%fp-36]
+       add     %fp, -72, %g1
+       st      %g1, [%fp-32]
+       add     %fp, -56, %g1
+       ld      [%fp+68], %o0
+       mov     1481, %o1
+       mov     %g1, %o2
+       call    ioctl, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-80]
+.LL1:
+       ld      [%fp-80], %i0
+       ret
+       restore
+       .size   ata_cmd, .-ata_cmd
+       .align 4
+       .global ata_identify
+       .type   ata_identify, #function
+       .proc   04
+ata_identify:
+       !#PROLOGUE# 0
+       save    %sp, -648, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %i1, [%fp+72]
+       add     %fp, -536, %g1
+       st      %g1, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     236, %o1
+       mov     0, %o2
+       mov     0, %o3
+       mov     1, %o4
+       mov     1, %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       add     %fp, -536, %g1
+       ld      [%fp+72], %o0
+       mov     %g1, %o1
+       mov     512, %o2
+       call    memcpy, 0
+        nop
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL8
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-540]
+       b       .LL9
+        nop
+.LL8:
+       st      %g0, [%fp-540]
+.LL9:
+       ld      [%fp-540], %g1
+       mov     %g1, %i0
+       ret
+       restore
+       .size   ata_identify, .-ata_identify
+       .align 4
+       .global ata_pidentify
+       .type   ata_pidentify, #function
+       .proc   04
+ata_pidentify:
+       !#PROLOGUE# 0
+       save    %sp, -648, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %i1, [%fp+72]
+       add     %fp, -536, %g1
+       st      %g1, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     161, %o1
+       mov     0, %o2
+       mov     0, %o3
+       mov     1, %o4
+       mov     1, %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       add     %fp, -536, %g1
+       ld      [%fp+72], %o0
+       mov     %g1, %o1
+       mov     512, %o2
+       call    memcpy, 0
+        nop
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL11
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-540]
+       b       .LL12
+        nop
+.LL11:
+       st      %g0, [%fp-540]
+.LL12:
+       ld      [%fp-540], %g1
+       mov     %g1, %i0
+       ret
+       restore
+       .size   ata_pidentify, .-ata_pidentify
+       .align 4
+       .global smart_read_data
+       .type   smart_read_data, #function
+       .proc   04
+smart_read_data:
+       !#PROLOGUE# 0
+       save    %sp, -648, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %i1, [%fp+72]
+       add     %fp, -536, %g1
+       st      %g1, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     176, %o1
+       mov     208, %o2
+       sethi   %hi(12733440), %g1
+       or      %g1, 768, %o3
+       mov     0, %o4
+       mov     1, %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       add     %fp, -536, %g1
+       ld      [%fp+72], %o0
+       mov     %g1, %o1
+       mov     512, %o2
+       call    memcpy, 0
+        nop
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL14
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-540]
+       b       .LL15
+        nop
+.LL14:
+       st      %g0, [%fp-540]
+.LL15:
+       ld      [%fp-540], %g1
+       mov     %g1, %i0
+       ret
+       restore
+       .size   smart_read_data, .-smart_read_data
+       .align 4
+       .global smart_read_thresholds
+       .type   smart_read_thresholds, #function
+       .proc   04
+smart_read_thresholds:
+       !#PROLOGUE# 0
+       save    %sp, -648, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %i1, [%fp+72]
+       add     %fp, -536, %g1
+       st      %g1, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     176, %o1
+       mov     209, %o2
+       sethi   %hi(12733440), %g1
+       or      %g1, 769, %o3
+       mov     1, %o4
+       mov     1, %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       add     %fp, -536, %g1
+       ld      [%fp+72], %o0
+       mov     %g1, %o1
+       mov     512, %o2
+       call    memcpy, 0
+        nop
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL17
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-540]
+       b       .LL18
+        nop
+.LL17:
+       st      %g0, [%fp-540]
+.LL18:
+       ld      [%fp-540], %g1
+       mov     %g1, %i0
+       ret
+       restore
+       .size   smart_read_thresholds, .-smart_read_thresholds
+       .align 4
+       .global smart_auto_save
+       .type   smart_auto_save, #function
+       .proc   04
+smart_auto_save:
+       !#PROLOGUE# 0
+       save    %sp, -128, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %i1, [%fp+72]
+       st      %g0, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     176, %o1
+       mov     210, %o2
+       sethi   %hi(12733440), %g1
+       or      %g1, 768, %o3
+       ld      [%fp+72], %o4
+       mov     0, %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL20
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-24]
+       b       .LL21
+        nop
+.LL20:
+       st      %g0, [%fp-24]
+.LL21:
+       ld      [%fp-24], %g1
+       mov     %g1, %i0
+       ret
+       restore
+       .size   smart_auto_save, .-smart_auto_save
+       .align 4
+       .global smart_immediate_offline
+       .type   smart_immediate_offline, #function
+       .proc   04
+smart_immediate_offline:
+       !#PROLOGUE# 0
+       save    %sp, -128, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %i1, [%fp+72]
+       ld      [%fp+72], %g1
+       and     %g1, 255, %o5
+       sethi   %hi(12733440), %g1
+       or      %g1, 768, %g1
+       or      %o5, %g1, %g1
+       st      %g0, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     176, %o1
+       mov     212, %o2
+       mov     %g1, %o3
+       mov     0, %o4
+       mov     0, %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL23
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-24]
+       b       .LL24
+        nop
+.LL23:
+       st      %g0, [%fp-24]
+.LL24:
+       ld      [%fp-24], %g1
+       mov     %g1, %i0
+       ret
+       restore
+       .size   smart_immediate_offline, .-smart_immediate_offline
+       .align 4
+       .global smart_read_log
+       .type   smart_read_log, #function
+       .proc   04
+smart_read_log:
+       !#PROLOGUE# 0
+       save    %sp, -128, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %i1, [%fp+72]
+       st      %i2, [%fp+76]
+       st      %i3, [%fp+80]
+       ld      [%fp+72], %g1
+       and     %g1, 255, %o5
+       sethi   %hi(12733440), %g1
+       or      %g1, 768, %g1
+       or      %o5, %g1, %o5
+       ld      [%fp+80], %g1
+       st      %g1, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     176, %o1
+       mov     213, %o2
+       mov     %o5, %o3
+       ld      [%fp+76], %o4
+       ld      [%fp+76], %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL26
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-24]
+       b       .LL27
+        nop
+.LL26:
+       st      %g0, [%fp-24]
+.LL27:
+       ld      [%fp-24], %g1
+       mov     %g1, %i0
+       ret
+       restore
+       .size   smart_read_log, .-smart_read_log
+       .align 4
+       .global smart_enable
+       .type   smart_enable, #function
+       .proc   04
+smart_enable:
+       !#PROLOGUE# 0
+       save    %sp, -128, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %g0, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     176, %o1
+       mov     216, %o2
+       sethi   %hi(12733440), %g1
+       or      %g1, 768, %o3
+       mov     0, %o4
+       mov     0, %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL29
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-24]
+       b       .LL30
+        nop
+.LL29:
+       st      %g0, [%fp-24]
+.LL30:
+       ld      [%fp-24], %g1
+       mov     %g1, %i0
+       ret
+       restore
+       .size   smart_enable, .-smart_enable
+       .align 4
+       .global smart_disable
+       .type   smart_disable, #function
+       .proc   04
+smart_disable:
+       !#PROLOGUE# 0
+       save    %sp, -128, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %g0, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     176, %o1
+       mov     217, %o2
+       sethi   %hi(12733440), %g1
+       or      %g1, 768, %o3
+       mov     0, %o4
+       mov     0, %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL32
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-24]
+       b       .LL33
+        nop
+.LL32:
+       st      %g0, [%fp-24]
+.LL33:
+       ld      [%fp-24], %g1
+       mov     %g1, %i0
+       ret
+       restore
+       .size   smart_disable, .-smart_disable
+       .align 4
+       .global smart_status
+       .type   smart_status, #function
+       .proc   04
+smart_status:
+       !#PROLOGUE# 0
+       save    %sp, -128, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %g0, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     176, %o1
+       mov     218, %o2
+       sethi   %hi(12733440), %g1
+       or      %g1, 768, %o3
+       mov     0, %o4
+       mov     0, %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL35
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-24]
+       b       .LL36
+        nop
+.LL35:
+       st      %g0, [%fp-24]
+.LL36:
+       ld      [%fp-24], %g1
+       mov     %g1, %i0
+       ret
+       restore
+       .size   smart_status, .-smart_status
+       .align 4
+       .global smart_status_check
+       .type   smart_status_check, #function
+       .proc   04
+smart_status_check:
+       !#PROLOGUE# 0
+       save    %sp, -128, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %g0, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     176, %o1
+       mov     218, %o2
+       sethi   %hi(12733440), %g1
+       or      %g1, 768, %o3
+       mov     0, %o4
+       mov     0, %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL38
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-24]
+       b       .LL37
+        nop
+.LL38:
+       st      %g0, [%fp-24]
+.LL37:
+       ld      [%fp-24], %i0
+       ret
+       restore
+       .size   smart_status_check, .-smart_status_check
+       .align 4
+       .global smart_auto_offline
+       .type   smart_auto_offline, #function
+       .proc   04
+smart_auto_offline:
+       !#PROLOGUE# 0
+       save    %sp, -128, %sp
+       !#PROLOGUE# 1
+       st      %i0, [%fp+68]
+       st      %i1, [%fp+72]
+       st      %g0, [%sp+92]
+       ld      [%fp+68], %o0
+       mov     176, %o1
+       mov     219, %o2
+       sethi   %hi(12733440), %g1
+       or      %g1, 768, %o3
+       ld      [%fp+72], %o4
+       mov     0, %o5
+       call    ata_cmd, 0
+        nop
+       mov     %o0, %g1
+       st      %g1, [%fp-20]
+       ld      [%fp-20], %g1
+       cmp     %g1, 0
+       be      .LL40
+       nop
+       mov     -1, %g1
+       st      %g1, [%fp-24]
+       b       .LL41
+        nop
+.LL40:
+       st      %g0, [%fp-24]
+.LL41:
+       ld      [%fp-24], %g1
+       mov     %g1, %i0
+       ret
+       restore
+       .size   smart_auto_offline, .-smart_auto_offline
+       .ident  "GCC: (GNU) 3.4.2"
diff --git a/os_win32.c b/os_win32.c
new file mode 100644 (file)
index 0000000..a210e39
--- /dev/null
@@ -0,0 +1,1619 @@
+/*
+ * os_win32.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "config.h"
+#include "int64.h"
+#include "atacmds.h"
+#include "extern.h"
+extern smartmonctrl * con; // con->permissive,reportataioctl
+#include "scsicmds.h"
+#include "utility.h"
+extern int64_t bytes; // malloc() byte count
+
+#include <errno.h>
+#ifdef _DEBUG
+#include <assert.h>
+#else
+#define assert(x) /**/
+#endif
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <stddef.h> // offsetof()
+#include <io.h> // access()
+
+#define ARGUSED(x) ((void)(x))
+
+// Needed by '-V' option (CVS versioning) of smartd/smartctl
+const char *os_XXXX_c_cvsid="$Id: os_win32.c,v 1.33 2006/04/07 15:02:19 chrfranke Exp $"
+ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+
+#ifndef HAVE_GET_OS_VERSION_STR
+#error define of HAVE_GET_OS_VERSION_STR missing in config.h
+#endif
+
+// Return build host and OS version as static string
+const char * get_os_version_str()
+{
+       static char vstr[sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1+sizeof("-2003r2-sp2.1")+13];
+       char * const vptr = vstr+sizeof(SMARTMONTOOLS_BUILD_HOST)-3-1;
+       const int vlen = sizeof(vstr)-(sizeof(SMARTMONTOOLS_BUILD_HOST)-3);
+
+       OSVERSIONINFOEXA vi;
+       const char * w;
+
+       // remove "-pc" to avoid long lines
+       assert(!strncmp(SMARTMONTOOLS_BUILD_HOST+5, "pc-", 3));
+       strcpy(vstr, "i686-"); strcpy(vstr+5, SMARTMONTOOLS_BUILD_HOST+5+3);
+       assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
+
+       memset(&vi, 0, sizeof(vi));
+       vi.dwOSVersionInfoSize = sizeof(vi);
+       if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
+               memset(&vi, 0, sizeof(vi));
+               vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+               if (!GetVersionExA((OSVERSIONINFOA *)&vi))
+                       return vstr;
+       }
+
+       if (vi.dwPlatformId > 0xff || vi.dwMajorVersion > 0xff || vi.dwMinorVersion > 0xff)
+               return vstr;
+
+       switch (vi.dwPlatformId << 16 | vi.dwMajorVersion << 8 | vi.dwMinorVersion) {
+         case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400| 0:
+               w = (vi.szCSDVersion[1] == 'B' ||
+                    vi.szCSDVersion[1] == 'C'     ? "95-osr2" : "95");    break;
+         case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|10:
+               w = (vi.szCSDVersion[1] == 'A'     ? "98se"    : "98");    break;
+         case VER_PLATFORM_WIN32_WINDOWS<<16|0x0400|90: w = "me";     break;
+       //case VER_PLATFORM_WIN32_NT     <<16|0x0300|51: w = "nt3.51"; break;
+         case VER_PLATFORM_WIN32_NT     <<16|0x0400| 0: w = "nt4";    break;
+         case VER_PLATFORM_WIN32_NT     <<16|0x0500| 0: w = "2000";   break;
+         case VER_PLATFORM_WIN32_NT     <<16|0x0500| 1:
+               w = (!GetSystemMetrics(87/*SM_MEDIACENTER*/) ?   "xp"
+                                                            :   "xp-mc"); break;
+         case VER_PLATFORM_WIN32_NT     <<16|0x0500| 2:
+               w = (!GetSystemMetrics(89/*SM_SERVERR2*/) ?      "2003"
+                                                            :   "2003r2"); break;
+         case VER_PLATFORM_WIN32_NT     <<16|0x0600| 0: w = "vista";  break;
+         default: w = 0; break;
+       }
+
+       if (!w)
+               snprintf(vptr, vlen, "-%s%lu.%lu",
+                       (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "9x"),
+                       vi.dwMajorVersion, vi.dwMinorVersion);
+       else if (vi.wServicePackMinor)
+               snprintf(vptr, vlen, "-%s-sp%u.%u", w, vi.wServicePackMajor, vi.wServicePackMinor);
+       else if (vi.wServicePackMajor)
+               snprintf(vptr, vlen, "-%s-sp%u", w, vi.wServicePackMajor);
+       else
+               snprintf(vptr, vlen, "-%s", w);
+       return vstr;
+}
+
+
+static int ata_open(int drive);
+static void ata_close(int fd);
+static int ata_scan(unsigned long * drives);
+
+static int aspi_open(unsigned adapter, unsigned id);
+static void aspi_close(int fd);
+static int aspi_scan(unsigned long * drives);
+
+
+static int is_permissive()
+{
+       if (con->permissive <= 0) {
+               pout("To continue, add one or more '-T permissive' options.\n");
+               return 0;
+       }
+       con->permissive--;
+       return 1;
+}
+
+static const char * skipdev(const char * s)
+{
+       return (!strncmp(s, "/dev/", 5) ? s + 5 : s);
+}
+
+
+// tries to guess device type given the name (a path).  See utility.h
+// for return values.
+int guess_device_type (const char * dev_name)
+{
+       dev_name = skipdev(dev_name);
+       if (!strncmp(dev_name, "hd", 2))
+               return CONTROLLER_ATA;
+       if (!strncmp(dev_name, "scsi", 4))
+               return CONTROLLER_SCSI;
+       return CONTROLLER_UNKNOWN;
+}
+
+
+// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
+// smartd.  Returns number N of devices, or -1 if out of
+// memory. Allocates N+1 arrays: one of N pointers (devlist), the
+// others each contain null-terminated character strings.
+int make_device_names (char*** devlist, const char* type)
+{
+       unsigned long drives[3];
+       int i, j, n, nmax, sz;
+       const char * path;
+
+       drives[0] = drives[1] = drives[2] = 0;
+       if (!strcmp(type, "ATA")) {
+               // bit i set => drive i present
+               n = ata_scan(drives);
+               path = "/dev/hda";
+               nmax = 10;
+       }
+       else if (!strcmp(type, "SCSI")) {
+               // bit i set => drive with ID (i & 0x7) on adapter (i >> 3) present
+               n = aspi_scan(drives);
+               path = "/dev/scsi00";
+               nmax = 10*8;
+       }
+       else
+               return -1;
+
+       if (n <= 0)
+               return 0;
+
+       // Alloc devlist
+       sz = n * sizeof(char **);
+       *devlist = (char **)malloc(sz); bytes += sz;
+
+       // Add devices
+       for (i = j = 0; i < n; i++) {
+               char * s;
+               sz = strlen(path)+1;
+               s = (char *)malloc(sz); bytes += sz;
+               strcpy(s, path);
+               while (j < nmax && !(drives[j >> 5] & (1L << (j & 0x1f))))
+                       j++;
+               assert(j < nmax);
+               if (nmax <= 10) {
+                       assert(j <= 9);
+                       s[sz-2] += j; // /dev/hd[a-j]
+               }
+               else {
+                       assert((j >> 3) <= 9);
+                       s[sz-3] += (j >> 3);  // /dev/scsi[0-9].....
+                       s[sz-2] += (j & 0x7); //          .....[0-7]
+               }
+               (*devlist)[i] = s;
+               j++;
+       }
+       return n;
+}
+
+
+// Like open().  Return positive integer handle, only used by
+// functions below.  type="ATA" or "SCSI".  If you need to store extra
+// information about your devices, create a private internal array
+// within this file (see os_freebsd.c for an example).
+int deviceopen(const char * pathname, char *type)
+{
+       int len;
+       pathname = skipdev(pathname);
+       len = strlen(pathname);
+
+       if (!strcmp(type, "ATA")) {
+               // hd[a-z] => ATA 0-9
+               if (!(len  == 3 && pathname[0] == 'h' && pathname[1] == 'd'
+                         && 'a' <= pathname[2] && pathname[2] <= 'j')) {
+                       errno = ENOENT;
+                       return -1;
+               }
+               return ata_open(pathname[2] - 'a');
+       }
+
+       if (!strcmp(type, "SCSI")) {
+               // scsi[0-9][0-f] => SCSI Adapter 0-9, ID 0-15, LUN 0
+               unsigned adapter = ~0, id = ~0; int n = -1;
+               if (!(sscanf(pathname,"scsi%1u%1x%n", &adapter, &id, &n) == 2 && n == len)) {
+                       errno = ENOENT;
+                       return -1;
+               }
+               return aspi_open(adapter, id);
+       }
+       errno = ENOENT;
+       return -1;
+}
+
+
+// Like close().  Acts only on handles returned by above function.
+// (Never called in smartctl!)
+int deviceclose(int fd)
+{
+       if (fd < 0x100) {
+               ata_close(fd);
+       }
+       else {
+               aspi_close(fd);
+       }
+       return 0;
+}
+
+
+// print examples for smartctl
+void print_smartctl_examples(){
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
+         "  smartctl -a /dev/hda                       (Prints all SMART information)\n\n"
+#ifdef HAVE_GETOPT_LONG
+         "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hda\n"
+         "                                              (Enables SMART on first disk)\n\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n\n"
+         "  smartctl --attributes --log=selftest --quietmode=errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+#else
+         "  smartctl -s on -o on -S on /dev/hda         (Enables SMART on first disk)\n"
+         "  smartctl -t long /dev/hda              (Executes extended disk self-test)\n"
+         "  smartctl -A -l selftest -q errorsonly /dev/hda\n"
+         "                                      (Prints Self-Test & Attribute errors)\n"
+#endif
+         "  smartctl -a /dev/scsi21\n"
+         "             (Prints all information for SCSI disk on ASPI adapter 2, ID 1)\n"
+  );
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// ATA Interface
+/////////////////////////////////////////////////////////////////////////////
+
+// SMART_* IOCTLs, also known as DFP_* (Disk Fault Protection)
+
+// Deklarations from:
+// http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntdddisk.h?rev=1.3
+
+#define FILE_READ_ACCESS       0x0001
+#define FILE_WRITE_ACCESS      0x0002
+#define METHOD_BUFFERED             0
+#define CTL_CODE(DeviceType, Function, Method, Access) (((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
+
+#define FILE_DEVICE_DISK          7
+#define IOCTL_DISK_BASE        FILE_DEVICE_DISK
+
+#define SMART_GET_VERSION \
+  CTL_CODE(IOCTL_DISK_BASE, 0x0020, METHOD_BUFFERED, FILE_READ_ACCESS)
+
+#define SMART_RCV_DRIVE_DATA \
+  CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define SMART_SEND_DRIVE_COMMAND \
+  CTL_CODE(IOCTL_DISK_BASE, 0x0021, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define SMART_CYL_LOW  0x4F
+#define SMART_CYL_HI   0xC2
+
+#pragma pack(1)
+
+typedef struct _GETVERSIONOUTPARAMS {
+       UCHAR  bVersion;
+       UCHAR  bRevision;
+       UCHAR  bReserved;
+       UCHAR  bIDEDeviceMap;
+       ULONG  fCapabilities;
+       ULONG  dwReserved[4];
+} GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS;
+
+typedef struct _IDEREGS {
+       UCHAR  bFeaturesReg;
+       UCHAR  bSectorCountReg;
+       UCHAR  bSectorNumberReg;
+       UCHAR  bCylLowReg;
+       UCHAR  bCylHighReg;
+       UCHAR  bDriveHeadReg;
+       UCHAR  bCommandReg;
+       UCHAR  bReserved;
+} IDEREGS, *PIDEREGS, *LPIDEREGS;
+
+typedef struct _SENDCMDINPARAMS {
+       ULONG  cBufferSize;
+       IDEREGS  irDriveRegs;
+       UCHAR  bDriveNumber;
+       UCHAR  bReserved[3];
+       ULONG  dwReserved[4];
+       UCHAR  bBuffer[1];
+} SENDCMDINPARAMS, *PSENDCMDINPARAMS, *LPSENDCMDINPARAMS;
+
+/* DRIVERSTATUS.bDriverError constants (just for info, not used)
+#define SMART_NO_ERROR                    0
+#define SMART_IDE_ERROR                   1
+#define SMART_INVALID_FLAG                2
+#define SMART_INVALID_COMMAND             3
+#define SMART_INVALID_BUFFER              4
+#define SMART_INVALID_DRIVE               5
+#define SMART_INVALID_IOCTL               6
+#define SMART_ERROR_NO_MEM                7
+#define SMART_INVALID_REGISTER            8
+#define SMART_NOT_SUPPORTED               9
+#define SMART_NO_IDE_DEVICE               10
+*/
+
+typedef struct _DRIVERSTATUS {
+       UCHAR  bDriverError;
+       UCHAR  bIDEError;
+       UCHAR  bReserved[2];
+       ULONG  dwReserved[2];
+} DRIVERSTATUS, *PDRIVERSTATUS, *LPDRIVERSTATUS;
+
+typedef struct _SENDCMDOUTPARAMS {
+       ULONG  cBufferSize;
+       DRIVERSTATUS  DriverStatus;
+       UCHAR  bBuffer[1];
+} SENDCMDOUTPARAMS, *PSENDCMDOUTPARAMS, *LPSENDCMDOUTPARAMS;
+
+#pragma pack()
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+static void print_ide_regs(const IDEREGS * r, int out)
+{
+       pout("%s=0x%02x,%s=0x%02x, SC=0x%02x, NS=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n",
+       (out?"STS":"CMD"), r->bCommandReg, (out?"ERR":" FR"), r->bFeaturesReg,
+       r->bSectorCountReg, r->bSectorNumberReg, r->bCylLowReg, r->bCylHighReg, r->bDriveHeadReg);
+}
+
+static void print_ide_regs_io(const IDEREGS * ri, const IDEREGS * ro)
+{
+       pout("    Input : "); print_ide_regs(ri, 0);
+       if (ro) {
+               pout("    Output: "); print_ide_regs(ro, 1);
+       }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+// call SMART_* ioctl
+
+static int smart_ioctl(HANDLE hdevice, int drive, IDEREGS * regs, char * data, unsigned datasize)
+{
+       SENDCMDINPARAMS inpar;
+       unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512];
+       const SENDCMDOUTPARAMS * outpar;
+       DWORD code, num_out;
+       unsigned int size_out;
+       const char * name;
+
+       assert(SMART_SEND_DRIVE_COMMAND == 0x07c084);
+       assert(SMART_RCV_DRIVE_DATA == 0x07c088);
+       assert(sizeof(SENDCMDINPARAMS)-1 == 32);
+       assert(sizeof(SENDCMDOUTPARAMS)-1 == 16);
+
+       memset(&inpar, 0, sizeof(inpar));
+       inpar.irDriveRegs = *regs;
+       // drive is set to 0-3 on Win9x only
+       inpar.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4);
+       inpar.bDriveNumber = drive;
+
+       assert(datasize == 0 || datasize == 512);
+       if (datasize) {
+               code = SMART_RCV_DRIVE_DATA; name = "SMART_RCV_DRIVE_DATA";
+               inpar.cBufferSize = size_out = 512;
+       }
+       else {
+               code = SMART_SEND_DRIVE_COMMAND; name = "SMART_SEND_DRIVE_COMMAND";
+               if (regs->bFeaturesReg == ATA_SMART_STATUS)
+                       size_out = sizeof(IDEREGS); // ioctl returns new IDEREGS as data
+                       // Note: cBufferSize must be 0 on Win9x
+               else
+                       size_out = 0;
+       }
+
+       memset(&outbuf, 0, sizeof(outbuf));
+
+       if (!DeviceIoControl(hdevice, code, &inpar, sizeof(SENDCMDINPARAMS)-1,
+               outbuf, sizeof(SENDCMDOUTPARAMS)-1 + size_out, &num_out, NULL)) {
+               // CAUTION: DO NOT change "regs" Parameter in this case, see ata_command_interface()
+               long err = GetLastError();
+               if (con->reportataioctl && (err != ERROR_INVALID_PARAMETER || con->reportataioctl > 1)) {
+                       pout("  %s failed, Error=%ld\n", name, err);
+                       print_ide_regs_io(regs, NULL);
+               }
+               errno = (   err == ERROR_INVALID_FUNCTION /*9x*/
+                        || err == ERROR_INVALID_PARAMETER/*NT/2K/XP*/ ? ENOSYS : EIO);
+               return -1;
+       }
+       // NOTE: On Win9x, inpar.irDriveRegs now contains the returned regs
+
+       outpar = (const SENDCMDOUTPARAMS *)outbuf;
+
+       if (outpar->DriverStatus.bDriverError) {
+               if (con->reportataioctl) {
+                       pout("  %s failed, DriverError=0x%02x, IDEError=0x%02x\n", name,
+                               outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError);
+                       print_ide_regs_io(regs, NULL);
+               }
+               errno = EIO;
+               return -1;
+       }
+
+       if (con->reportataioctl > 1) {
+               pout("  %s suceeded, bytes returned: %lu (buffer %lu)\n", name,
+                       num_out, outpar->cBufferSize);
+               print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
+                       (const IDEREGS *)(outpar->bBuffer) : NULL));
+       }
+
+       if (datasize)
+               memcpy(data, outpar->bBuffer, 512);
+       else if (regs->bFeaturesReg == ATA_SMART_STATUS)
+               *regs = *(const IDEREGS *)(outpar->bBuffer);
+
+       return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// IDE PASS THROUGH for W2K/XP (does not work on W9x/NT4)
+// Only used for SMART commands not supported by SMART_* IOCTLs
+//
+// Based on WinATA.cpp, 2002 c't/Matthias Withopf
+// ftp://ftp.heise.de/pub/ct/listings/0207-218.zip
+
+#define FILE_DEVICE_CONTROLLER  4
+#define IOCTL_SCSI_BASE         FILE_DEVICE_CONTROLLER
+
+#define IOCTL_IDE_PASS_THROUGH \
+  CTL_CODE(IOCTL_SCSI_BASE, 0x040A, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#pragma pack(1)
+
+typedef struct {
+       IDEREGS IdeReg;
+       ULONG   DataBufferSize;
+       UCHAR   DataBuffer[1];
+} ATA_PASS_THROUGH;
+
+#pragma pack()
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
+{ 
+       unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize;
+       ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
+       DWORD num_out;
+       const unsigned char magic = 0xcf;
+       assert(sizeof(ATA_PASS_THROUGH)-1 == 12);
+       assert(IOCTL_IDE_PASS_THROUGH == 0x04d028);
+
+       if (!buf) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       buf->IdeReg = *regs;
+       buf->DataBufferSize = datasize;
+       if (datasize)
+               buf->DataBuffer[0] = magic;
+
+       if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH,
+               buf, size, buf, size, &num_out, NULL)) {
+               long err = GetLastError();
+               if (con->reportataioctl)
+                       pout("  IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err);
+               VirtualFree(buf, 0, MEM_RELEASE);
+               errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+               return -1;
+       }
+
+       // Check ATA status
+       if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) {
+               if (con->reportataioctl) {
+                       pout("  IOCTL_IDE_PASS_THROUGH command failed:\n");
+                       print_ide_regs_io(regs, &buf->IdeReg);
+               }
+               VirtualFree(buf, 0, MEM_RELEASE);
+               errno = EIO;
+               return -1;
+       }
+
+       // Check and copy data
+       if (datasize) {
+               if (   num_out != size
+                   || (buf->DataBuffer[0] == magic && !nonempty(buf->DataBuffer+1, datasize-1))) {
+                       if (con->reportataioctl) {
+                               pout("  IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n",
+                                       num_out, buf->DataBufferSize);
+                               print_ide_regs_io(regs, &buf->IdeReg);
+                       }
+                       VirtualFree(buf, 0, MEM_RELEASE);
+                       errno = EIO;
+                       return -1;
+               }
+               memcpy(data, buf->DataBuffer, datasize);
+       }
+
+       if (con->reportataioctl > 1) {
+               pout("  IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %lu (buffer %lu)\n",
+                       num_out, buf->DataBufferSize);
+               print_ide_regs_io(regs, &buf->IdeReg);
+       }
+       *regs = buf->IdeReg;
+
+       // Caution: VirtualFree() fails if parameter "dwSize" is nonzero
+       VirtualFree(buf, 0, MEM_RELEASE);
+       return 0;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// ATA PASS THROUGH via SCSI PASS THROUGH for NT4 only
+// Only used for SMART commands not supported by SMART_* IOCTLs
+
+// Declarations from:
+// http://cvs.sourceforge.net/viewcvs.py/mingw/w32api/include/ddk/ntddscsi.h?rev=1.2
+
+#define IOCTL_SCSI_PASS_THROUGH \
+       CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define SCSI_IOCTL_DATA_OUT          0
+#define SCSI_IOCTL_DATA_IN           1
+#define SCSI_IOCTL_DATA_UNSPECIFIED  2
+// undocumented SCSI opcode to for ATA passthrough
+#define SCSIOP_ATA_PASSTHROUGH    0xCC
+
+typedef struct _SCSI_PASS_THROUGH {
+       USHORT  Length;
+       UCHAR  ScsiStatus;
+       UCHAR  PathId;
+       UCHAR  TargetId;
+       UCHAR  Lun;
+       UCHAR  CdbLength;
+       UCHAR  SenseInfoLength;
+       UCHAR  DataIn;
+       ULONG  DataTransferLength;
+       ULONG  TimeOutValue;
+       ULONG/*_PTR*/ DataBufferOffset;
+       ULONG  SenseInfoOffset;
+       UCHAR  Cdb[16];
+} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH;
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+static int ata_via_scsi_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned datasize)
+{
+       typedef struct {
+               SCSI_PASS_THROUGH spt;
+               ULONG Filler;
+               UCHAR ucSenseBuf[32];
+               UCHAR ucDataBuf[512];
+       } SCSI_PASS_THROUGH_WITH_BUFFERS;
+
+       SCSI_PASS_THROUGH_WITH_BUFFERS sb;
+       IDEREGS * cdbregs;
+       unsigned int size;
+       DWORD num_out;
+       const unsigned char magic = 0xcf;
+
+       assert(sizeof(SCSI_PASS_THROUGH) == 44);
+       assert(IOCTL_SCSI_PASS_THROUGH == 0x04d004);
+
+       memset(&sb, 0, sizeof(sb));
+       sb.spt.Length = sizeof(SCSI_PASS_THROUGH);
+       //sb.spt.PathId = 0;
+       sb.spt.TargetId = 1;
+       //sb.spt.Lun = 0;
+       sb.spt.CdbLength = 10; sb.spt.SenseInfoLength = 24;
+       sb.spt.TimeOutValue = 10;
+       sb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucSenseBuf);
+       size = offsetof(SCSI_PASS_THROUGH_WITH_BUFFERS, ucDataBuf);
+       sb.spt.DataBufferOffset = size;
+       if (datasize) {
+               if (datasize > sizeof(sb.ucDataBuf)) {
+                       errno = EINVAL;
+                       return -1;
+               }
+               sb.spt.DataIn = SCSI_IOCTL_DATA_IN;
+               sb.spt.DataTransferLength = datasize;
+               size += datasize;
+               sb.ucDataBuf[0] = magic;
+       }
+       else {
+               sb.spt.DataIn = SCSI_IOCTL_DATA_UNSPECIFIED;
+               //sb.spt.DataTransferLength = 0;
+       }
+
+       // Use pseudo SCSI command followed by registers
+       sb.spt.Cdb[0] = SCSIOP_ATA_PASSTHROUGH;
+       cdbregs = (IDEREGS *)(sb.spt.Cdb+2);
+       *cdbregs = *regs;
+
+       if (!DeviceIoControl(hdevice, IOCTL_SCSI_PASS_THROUGH,
+               &sb, size, &sb, size, &num_out, NULL)) {
+               long err = GetLastError();
+               if (con->reportataioctl)
+                       pout("  ATA via IOCTL_SCSI_PASS_THROUGH failed, Error=%ld\n", err);
+               errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO);
+               return -1;
+       }
+
+       // Cannot check ATA status, because command does not return IDEREGS
+
+       // Check and copy data
+       if (datasize) {
+               if (   num_out != size
+                   || (sb.ucDataBuf[0] == magic && !nonempty(sb.ucDataBuf+1, datasize-1))) {
+                       if (con->reportataioctl) {
+                               pout("  ATA via IOCTL_SCSI_PASS_THROUGH output data missing (%lu)\n", num_out);
+                               print_ide_regs_io(regs, NULL);
+                       }
+                       errno = EIO;
+                       return -1;
+               }
+               memcpy(data, sb.ucDataBuf, datasize);
+       }
+
+       if (con->reportataioctl > 1) {
+               pout("  ATA via IOCTL_SCSI_PASS_THROUGH suceeded, bytes returned: %lu\n", num_out);
+               print_ide_regs_io(regs, NULL);
+       }
+       return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+static HANDLE h_ata_ioctl = 0;
+
+
+// Print SMARTVSD error message, return errno
+
+static int smartvsd_error()
+{
+       char path[MAX_PATH];
+       unsigned len;
+       if (!(5 <= (len = GetSystemDirectoryA(path, MAX_PATH)) && len < MAX_PATH/2))
+               return ENOENT;
+       // SMARTVSD.VXD present?
+       strcpy(path+len, "\\IOSUBSYS\\SMARTVSD.VXD");
+       if (!access(path, 0)) {
+               // Yes, standard IDE driver used?
+               HANDLE h;
+               if (   (h = CreateFileA("\\\\.\\ESDI_506",
+                            GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                            NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE
+                   && GetLastError() == ERROR_FILE_NOT_FOUND                             ) {
+                       pout("Standard IDE driver ESDI_506.PDR not used, or no IDE/ATA drives present.\n");
+                       return ENOENT;
+               }
+               else {
+                       if (h != INVALID_HANDLE_VALUE) // should not happen
+                               CloseHandle(h);
+                       pout("SMART driver SMARTVSD.VXD is installed, but not loaded.\n");
+                       return ENOSYS;
+               }
+       }
+       else {
+               // Some Windows versions install SMARTVSD.VXD in SYSTEM directory
+               // http://support.microsoft.com/default.aspx?scid=kb;en-us;265854
+               strcpy(path+len, "\\SMARTVSD.VXD");
+               if (!access(path, 0)) {
+                       path[len] = 0;
+                       pout("SMART driver is not properly installed,\n"
+                                " move SMARTVSD.VXD from \"%s\" to \"%s\\IOSUBSYS\"\n"
+                                " and reboot Windows.\n", path, path);
+               }
+               else {
+                       path[len] = 0;
+                       pout("SMARTVSD.VXD is missing in folder \"%s\\IOSUBSYS\".\n", path);
+               }
+               return ENOSYS;
+       }
+}
+
+
+static int ata_open(int drive)
+{
+       int win9x;
+       char devpath[30];
+       GETVERSIONOUTPARAMS vers;
+       DWORD num_out;
+
+       assert(SMART_GET_VERSION == 0x074080);
+       assert(sizeof(GETVERSIONOUTPARAMS) == 24);
+
+       // TODO: This version does not allow to open more than 1 ATA devices
+       if (h_ata_ioctl) {
+               errno = ENFILE;
+               return -1;
+       }
+
+       win9x = ((GetVersion() & 0x80000000) != 0);
+
+       if (!(0 <= drive && drive <= (win9x ? 3 : 9))) {
+               errno = ENOENT;
+               return -1;
+       }
+       // path depends on Windows Version
+       if (win9x)
+               strcpy(devpath, "\\\\.\\SMARTVSD");
+       else
+               snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", drive);
+
+       // Open device
+       if ((h_ata_ioctl = CreateFileA(devpath,
+               GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+               NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
+               long err = GetLastError();      
+               pout("Cannot open device %s, Error=%ld\n", devpath, err);
+               if (err == ERROR_FILE_NOT_FOUND)
+                       errno = (win9x ? smartvsd_error() : ENOENT);
+               else if (err == ERROR_ACCESS_DENIED) {
+                       if (!win9x)
+                               pout("Administrator rights are necessary to access physical drives.\n");
+                       errno = EACCES;
+               }
+               else
+                       errno = EIO;
+               h_ata_ioctl = 0;
+               return -1;
+       }
+
+       // Get drive map
+       memset(&vers, 0, sizeof(vers));
+       if (!DeviceIoControl(h_ata_ioctl, SMART_GET_VERSION,
+               NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
+               pout("%s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError());
+               if (!win9x)
+                       pout("If this is a SCSI disk, try \"scsi<adapter><id>\".\n");
+               if (!is_permissive()) {
+                       CloseHandle(h_ata_ioctl); h_ata_ioctl = 0;
+                       errno = ENOSYS;
+                       return -1;
+               }
+       }
+
+       if (con->reportataioctl > 1)
+               pout("%s: SMART_GET_VERSION (%ld bytes):\n"
+                    " Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
+                       devpath, num_out, vers.bVersion, vers.bRevision,
+                       vers.fCapabilities, vers.bIDEDeviceMap);
+
+       // TODO: Check vers.fCapabilities here?
+
+       if (!win9x)
+               // NT4/2K/XP: Drive exists, Drive number not necessary for ioctl
+               return 0;
+
+       // Win9x/ME: Check device presence & type
+       if (((vers.bIDEDeviceMap >> drive) & 0x11) != 0x01) {
+               unsigned char atapi = (vers.bIDEDeviceMap >> drive) & 0x10;
+               pout((  atapi
+                     ? "Drive %d is an ATAPI device (IDEDeviceMap=0x%02x).\n"
+                         : "Drive %d does not exist (IDEDeviceMap=0x%02x).\n"),
+                       drive, vers.bIDEDeviceMap);
+               // Win9x Drive existence check may not work as expected
+               // The atapi.sys driver incorrectly fills in the bIDEDeviceMap with 0x01
+               // http://support.microsoft.com/support/kb/articles/Q196/1/20.ASP
+               if (!is_permissive()) {
+                       CloseHandle(h_ata_ioctl); h_ata_ioctl = 0;
+                       errno = (atapi ? ENOSYS : ENOENT);
+                       return -1;
+               }
+       }
+       // Use drive number as fd for ioctl
+       return drive;
+}
+
+
+static void ata_close(int fd)
+{
+       ARGUSED(fd);
+       CloseHandle(h_ata_ioctl);
+       h_ata_ioctl = 0;
+}
+
+
+// Scan for ATA drives, fill bitmask of drives present, return #drives
+
+static int ata_scan(unsigned long * drives)
+{
+       int win9x = ((GetVersion() & 0x80000000) != 0);
+       int cnt = 0, i;
+
+       for (i = 0; i <= 9; i++) {
+               char devpath[30];
+               GETVERSIONOUTPARAMS vers;
+               DWORD num_out;
+               HANDLE h;
+               if (win9x)
+                       strcpy(devpath, "\\\\.\\SMARTVSD");
+               else
+                       snprintf(devpath, sizeof(devpath)-1, "\\\\.\\PhysicalDrive%d", i);
+
+               // Open device
+               if ((h = CreateFileA(devpath,
+                       GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                       NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) {
+                       if (con->reportataioctl > 1)
+                               pout(" %s: Open failed, Error=%ld\n", devpath, GetLastError());
+                       if (win9x)
+                               break; // SMARTVSD.VXD missing or no ATA devices
+                       continue; // Disk not found or access denied (break;?)
+               }
+
+               // Get drive map
+               memset(&vers, 0, sizeof(vers));
+               if (!DeviceIoControl(h, SMART_GET_VERSION,
+                       NULL, 0, &vers, sizeof(vers), &num_out, NULL)) {
+                       if (con->reportataioctl)
+                               pout(" %s: SMART_GET_VERSION failed, Error=%ld\n", devpath, GetLastError());
+                       CloseHandle(h);
+                       if (win9x)
+                               break; // Should not happen
+                       continue; // Non ATA disk or no SMART ioctl support (possibly SCSI disk)
+               }
+               CloseHandle(h);
+
+               if (con->reportataioctl)
+                       pout(" %s: SMART_GET_VERSION (%ld bytes):\n"
+                            "  Vers = %d.%d, Caps = 0x%lx, DeviceMap = 0x%02x\n",
+                               devpath, num_out, vers.bVersion, vers.bRevision,
+                               vers.fCapabilities, vers.bIDEDeviceMap);
+
+               if (win9x) {
+                       // Check ATA device presence, remove ATAPI devices
+                       drives[0] = (vers.bIDEDeviceMap & 0xf) & ~((vers.bIDEDeviceMap >> 4) & 0xf);
+                       cnt = (drives[0]&1) + ((drives[0]>>1)&1) + ((drives[0]>>2)&1) + ((drives[0]>>3)&1);
+                       break;
+               }
+
+               // ATA drive exists and driver supports SMART ioctl
+               drives[0] |= (1L << i);
+               cnt++;
+       }
+
+       return cnt;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Interface to ATA devices.  See os_linux.c
+int ata_command_interface(int fd, smart_command_set command, int select, char * data)
+{
+       IDEREGS regs;
+       int copydata, try_ioctl;
+
+       if (!(0 <= fd && fd <= 3)) {
+               errno = EBADF;
+               return -1;
+       }
+
+       // CMD,CYL default to SMART, changed by P?IDENTIFY and CHECK_POWER_MODE
+       memset(&regs, 0, sizeof(regs));
+       regs.bCommandReg = ATA_SMART_CMD;
+       regs.bCylHighReg = SMART_CYL_HI; regs.bCylLowReg = SMART_CYL_LOW;
+       copydata = 0;
+       try_ioctl = 0x01; // 0x01=SMART_*, 0x02=IDE_PASS_THROUGH [, 0x04=ATA_PASS_THROUGH]
+
+       switch (command) {
+         case WRITE_LOG:
+               // TODO. Not supported by SMART IOCTL (no data out ioctl available),
+               //  also not supported by IOCTL_IDE_PASS_THROUGH (data out not working)
+               errno = ENOSYS;
+               return -1;
+         case CHECK_POWER_MODE:
+               regs.bCommandReg = ATA_CHECK_POWER_MODE;
+               regs.bCylLowReg = regs.bCylHighReg = 0;
+               try_ioctl = 0x02; // IOCTL_IDE_PASS_THROUGH
+               // Note: returns SectorCountReg in data[0]
+               break;
+         case READ_VALUES:
+               regs.bFeaturesReg = ATA_SMART_READ_VALUES;
+               regs.bSectorNumberReg = regs.bSectorCountReg = 1;
+               copydata = 1;
+               break;
+         case READ_THRESHOLDS:
+               regs.bFeaturesReg = ATA_SMART_READ_THRESHOLDS;
+               regs.bSectorNumberReg = regs.bSectorCountReg = 1;
+               copydata = 1;
+               break;
+         case READ_LOG:
+               regs.bFeaturesReg = ATA_SMART_READ_LOG_SECTOR;
+               regs.bSectorNumberReg = select;
+               regs.bSectorCountReg = 1;
+               // Read log only supported on Win9x, retry with pass through command
+               try_ioctl = 0x03; // SMART_RCV_DRIVE_DATA, then IOCTL_IDE_PASS_THROUGH
+               copydata = 1;
+               break;
+         case IDENTIFY:
+               // Note: WinNT4/2000/XP return identify data cached during boot
+               // (true for SMART_RCV_DRIVE_DATA and IOCTL_IDE_PASS_THROUGH)
+               regs.bCommandReg = ATA_IDENTIFY_DEVICE;
+               regs.bCylLowReg = regs.bCylHighReg = 0;
+               regs.bSectorCountReg = 1;
+               copydata = 1;
+               break;
+         case PIDENTIFY:
+               regs.bCommandReg = ATA_IDENTIFY_PACKET_DEVICE;
+               regs.bCylLowReg = regs.bCylHighReg = 0;
+               regs.bSectorCountReg = 1;
+               copydata = 1;
+               break;
+         case ENABLE:
+               regs.bFeaturesReg = ATA_SMART_ENABLE;
+               regs.bSectorNumberReg = 1;
+               break;
+         case DISABLE:
+               regs.bFeaturesReg = ATA_SMART_DISABLE;
+               regs.bSectorNumberReg = 1;
+               break;
+         case STATUS:
+         case STATUS_CHECK:
+               regs.bFeaturesReg = ATA_SMART_STATUS;
+               break;
+         case AUTO_OFFLINE:
+               regs.bFeaturesReg = ATA_SMART_AUTO_OFFLINE;
+               regs.bSectorCountReg = select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+               break;
+         case AUTOSAVE:
+               regs.bFeaturesReg = ATA_SMART_AUTOSAVE;
+               regs.bSectorCountReg = select;   // YET NOTE - THIS IS A NON-DATA COMMAND!!
+               break;
+         case IMMEDIATE_OFFLINE:
+               regs.bFeaturesReg = ATA_SMART_IMMEDIATE_OFFLINE;
+               regs.bSectorNumberReg = select;
+               if (select == ABORT_SELF_TEST) // Abort only supported on Win9x, try
+                       try_ioctl = 0x03; // SMART_SEND_DRIVE_COMMAND, then IOCTL_IDE_PASS_THROUGH
+               break;
+         default:
+               pout("Unrecognized command %d in win32_ata_command_interface()\n"
+                "Please contact " PACKAGE_BUGREPORT "\n", command);
+               errno = ENOSYS;
+               return -1;
+       }
+
+       if (try_ioctl & 0x01) {
+               if (smart_ioctl(h_ata_ioctl, fd, &regs, data, (copydata?512:0))) {
+                       if (!(try_ioctl & 0x02) || errno != ENOSYS)
+                               return -1;
+                       // CAUTION: smart_ioctl() MUST NOT change "regs" Parameter in this case
+               }
+               else
+                       try_ioctl = 0;
+       }
+
+       if (try_ioctl & 0x02) {
+               errno = 0;
+               if ((GetVersion() & 0x8000ffff) == 0x00000004) {
+                       // Special case WinNT4
+                       if (command == CHECK_POWER_MODE) { // SCSI_PASS_THROUGH does not return regs!
+                               errno = ENOSYS;
+                               return -1;
+                       }
+                       if (ata_via_scsi_pass_through_ioctl(h_ata_ioctl, &regs, data, (copydata?512:0)))
+                               return -1;
+               }
+               else {
+                       if (ide_pass_through_ioctl(h_ata_ioctl, &regs, data, (copydata?512:0)))
+                               return -1;
+               }
+               try_ioctl = 0;
+       }
+       assert(!try_ioctl);
+
+       switch (command) {
+         case CHECK_POWER_MODE:
+               // Return power mode from SectorCountReg in data[0]
+               data[0] = regs.bSectorCountReg;
+               return 0;
+
+         case STATUS_CHECK:
+               // Cyl low and Cyl high unchanged means "Good SMART status"
+               if (regs.bCylHighReg == SMART_CYL_HI && regs.bCylLowReg == SMART_CYL_LOW)
+                 return 0;
+
+               // These values mean "Bad SMART status"
+               if (regs.bCylHighReg == 0x2c && regs.bCylLowReg == 0xf4)
+                 return 1;
+
+               // We haven't gotten output that makes sense; print out some debugging info
+               syserror("Error SMART Status command failed");
+               pout("Please get assistance from %s\n", PACKAGE_HOMEPAGE);
+               print_ide_regs(&regs, 1);
+               errno = EIO;
+               return -1;
+
+         default:
+               return 0;
+       }
+       /*NOTREACHED*/
+}
+
+
+#ifndef HAVE_ATA_IDENTIFY_IS_CACHED
+#error define of HAVE_ATA_IDENTIFY_IS_CACHED missing in config.h
+#endif
+
+// Return true if OS caches the ATA identify sector
+int ata_identify_is_cached(int fd)
+{
+       ARGUSED(fd);
+       // WinNT4/2000/XP => true, Win9x/ME => false
+       return ((GetVersion() & 0x80000000) == 0);
+}
+
+
+// Print not implemeted warning once
+static void pr_not_impl(const char * what, int * warned)
+{
+       if (*warned)
+               return;
+       pout(
+               "#######################################################################\n"
+               "%s\n"
+               "NOT IMPLEMENTED under Win32.\n"
+               "Please contact " PACKAGE_BUGREPORT " if\n"
+               "you want to help in porting smartmontools to Win32.\n"
+               "#######################################################################\n"
+               "\n", what
+       );
+       *warned = 1;
+}
+
+// Interface to ATA devices behind 3ware escalade RAID controller cards.  See os_linux.c
+int escalade_command_interface(int fd, int disknum, int escalade_type, smart_command_set command, int select, char *data)
+{
+       static int warned = 0;
+       ARGUSED(fd); ARGUSED(disknum); ARGUSED(escalade_type); ARGUSED(command); ARGUSED(select); ARGUSED(data);
+       pr_not_impl("3ware Escalade Controller command routine escalade_command_interface()", &warned);
+       errno = ENOSYS;
+       return -1;
+}
+
+// Interface to ATA devices behind Marvell chip-set based controllers.  See os_linux.c
+int marvell_command_interface(int fd, smart_command_set command, int select, char * data)
+{
+       static int warned = 0;
+       ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
+       pr_not_impl("Marvell chip-set command routine marvell_command_interface()", &warned);
+       errno = ENOSYS;
+       return -1;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// ASPI Interface
+/////////////////////////////////////////////////////////////////////////////
+
+#pragma pack(1)
+
+#define ASPI_SENSE_SIZE 18
+
+// ASPI SCSI Request block header
+
+typedef struct {
+       unsigned char cmd;             // 00: Command code
+       unsigned char status;          // 01: ASPI status
+       unsigned char adapter;         // 02: Host adapter number
+       unsigned char flags;           // 03: Request flags
+       unsigned char reserved[4];     // 04: 0
+} ASPI_SRB_HEAD;
+
+// SRB for host adapter inquiry
+
+typedef struct {
+       ASPI_SRB_HEAD h;               // 00: Header
+       unsigned char adapters;        // 08: Number of adapters
+       unsigned char target_id;       // 09: Target ID ?
+       char manager_id[16];           // 10: SCSI manager ID
+       char adapter_id[16];           // 26: Host adapter ID
+       unsigned char parameters[16];  // 42: Host adapter unique parmameters
+} ASPI_SRB_INQUIRY;
+
+// SRB for get device type
+
+typedef struct {
+       ASPI_SRB_HEAD h;               // 00: Header
+       unsigned char target_id;       // 08: Target ID
+       unsigned char lun;             // 09: LUN
+       unsigned char devtype;         // 10: Device type
+       unsigned char reserved;        // 11: Reserved
+} ASPI_SRB_DEVTYPE;
+
+// SRB for SCSI I/O
+
+typedef struct {
+       ASPI_SRB_HEAD h;               // 00: Header
+       unsigned char target_id;       // 08: Target ID
+       unsigned char lun;             // 09: LUN
+       unsigned char reserved[2];     // 10: Reserved
+       unsigned long data_size;       // 12: Data alloc. lenght
+       void * data_addr;              // 16: Data buffer pointer
+       unsigned char sense_size;      // 20: Sense alloc. length
+       unsigned char cdb_size;        // 21: CDB length
+       unsigned char host_status;     // 22: Host status
+       unsigned char target_status;   // 23: Target status
+       void * event_handle;           // 24: Event handle
+       unsigned char workspace[20];   // 28: ASPI workspace
+       unsigned char cdb[16+ASPI_SENSE_SIZE];
+} ASPI_SRB_IO;
+
+// Macro to retrieve start of sense information
+#define ASPI_SRB_SENSE(srb,cdbsz) ((srb)->cdb + 16)
+
+// SRB union
+
+typedef union {
+       ASPI_SRB_HEAD h;       // Common header
+       ASPI_SRB_INQUIRY q;    // Inquiry
+       ASPI_SRB_DEVTYPE t;    // Device type
+       ASPI_SRB_IO i;         // I/O
+} ASPI_SRB;
+
+#pragma pack()
+
+// ASPI commands
+#define ASPI_CMD_ADAPTER_INQUIRE        0x00
+#define ASPI_CMD_GET_DEVICE_TYPE        0x01
+#define ASPI_CMD_EXECUTE_IO             0x02
+#define ASPI_CMD_ABORT_IO               0x03
+
+// Request flags
+#define ASPI_REQFLAG_DIR_TO_HOST        0x08
+#define ASPI_REQFLAG_DIR_TO_TARGET      0x10
+#define ASPI_REQFLAG_DIR_NO_XFER        0x18
+#define ASPI_REQFLAG_EVENT_NOTIFY       0x40
+
+// ASPI status
+#define ASPI_STATUS_IN_PROGRESS         0x00
+#define ASPI_STATUS_NO_ERROR            0x01
+#define ASPI_STATUS_ABORTED             0x02
+#define ASPI_STATUS_ABORT_ERR           0x03
+#define ASPI_STATUS_ERROR               0x04
+#define ASPI_STATUS_INVALID_COMMAND     0x80
+#define ASPI_STATUS_INVALID_ADAPTER     0x81
+#define ASPI_STATUS_INVALID_TARGET      0x82
+#define ASPI_STATUS_NO_ADAPTERS         0xE8
+
+// Adapter (host) status
+#define ASPI_HSTATUS_NO_ERROR           0x00
+#define ASPI_HSTATUS_SELECTION_TIMEOUT  0x11
+#define ASPI_HSTATUS_DATA_OVERRUN       0x12
+#define ASPI_HSTATUS_BUS_FREE           0x13
+#define ASPI_HSTATUS_BUS_PHASE_ERROR    0x14
+#define ASPI_HSTATUS_BAD_SGLIST         0x1A
+
+// Target status
+#define ASPI_TSTATUS_NO_ERROR           0x00
+#define ASPI_TSTATUS_CHECK_CONDITION    0x02
+#define ASPI_TSTATUS_BUSY               0x08
+#define ASPI_TSTATUS_RESERV_CONFLICT    0x18
+
+
+static HINSTANCE h_aspi_dll; // DLL handle
+static UINT (* aspi_entry)(ASPI_SRB * srb); // ASPI entrypoint
+static unsigned num_aspi_adapters;
+
+#ifdef __CYGWIN__
+// h_aspi_dll+aspi_entry is not inherited by Cygwin's fork()
+static DWORD aspi_dll_pid; // PID of DLL owner to detect fork()
+#define aspi_entry_valid() (aspi_entry && (aspi_dll_pid == GetCurrentProcessId()))
+#else
+#define aspi_entry_valid() (!!aspi_entry)
+#endif
+
+
+static int aspi_call(ASPI_SRB * srb)
+{
+       int i;
+       aspi_entry(srb);
+       i = 0;
+       while (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
+               if (++i > 100/*10sek*/) {
+                       pout("ASPI Adapter %u: Timed out\n", srb->h.adapter);
+                       aspi_entry = 0;
+                       h_aspi_dll = INVALID_HANDLE_VALUE;
+                       errno = EIO;
+                       return -1;
+               }
+               if (con->reportscsiioctl > 1)
+                       pout("ASPI Adapter %u: Waiting (%d) ...\n", srb->h.adapter, i);
+               Sleep(100);
+       }
+       return 0;
+}
+
+
+// Get ASPI entrypoint from wnaspi32.dll
+
+static FARPROC aspi_get_address(const char * name, int verbose)
+{
+       FARPROC addr;
+       assert(h_aspi_dll && h_aspi_dll != INVALID_HANDLE_VALUE);
+
+       if (!(addr = GetProcAddress(h_aspi_dll, name))) {
+               if (verbose)
+                       pout("Missing %s() in WNASPI32.DLL\n", name);
+               aspi_entry = 0;
+               FreeLibrary(h_aspi_dll);
+               h_aspi_dll = INVALID_HANDLE_VALUE;
+               errno = ENOSYS;
+               return 0;
+       }
+       return addr;
+}
+
+
+static int aspi_open_dll(int verbose)
+{
+       UINT (*aspi_info)(void);
+       UINT info, rc;
+
+       assert(!aspi_entry_valid());
+
+       // Check structure layout
+       assert(sizeof(ASPI_SRB_HEAD) == 8);
+       assert(sizeof(ASPI_SRB_INQUIRY) == 58);
+       assert(sizeof(ASPI_SRB_DEVTYPE) == 12);
+       assert(sizeof(ASPI_SRB_IO) == 64+ASPI_SENSE_SIZE);
+       assert(offsetof(ASPI_SRB,h.cmd) == 0);
+       assert(offsetof(ASPI_SRB,h.flags) == 3);
+       assert(offsetof(ASPI_SRB_IO,lun) == 9);
+       assert(offsetof(ASPI_SRB_IO,data_addr) == 16);
+       assert(offsetof(ASPI_SRB_IO,workspace) == 28);
+       assert(offsetof(ASPI_SRB_IO,cdb) == 48);
+
+       if (h_aspi_dll == INVALID_HANDLE_VALUE) {
+               // do not retry
+               errno = ENOENT;
+               return -1;
+       }
+
+       // Load ASPI DLL
+       if (!(h_aspi_dll = LoadLibraryA("WNASPI32.DLL"))) {
+               if (verbose)
+                       pout("Cannot load WNASPI32.DLL, Error=%ld\n", GetLastError());
+               h_aspi_dll = INVALID_HANDLE_VALUE;
+               errno = ENOENT;
+               return -1;
+       }
+       if (con->reportscsiioctl > 1) {
+               // Print full path of WNASPI32.DLL
+               char path[MAX_PATH];
+               if (!GetModuleFileName(h_aspi_dll, path, sizeof(path)))
+                       strcpy(path, "*unknown*");
+               pout("Using ASPI interface \"%s\"\n", path);
+       }
+
+       // Get ASPI entrypoints
+       if (!(aspi_info = (UINT (*)(void))aspi_get_address("GetASPI32SupportInfo", verbose)))
+               return -1;
+       if (!(aspi_entry = (UINT (*)(ASPI_SRB *))aspi_get_address("SendASPI32Command", verbose)))
+               return -1;
+
+       // Init ASPI manager and get number of adapters
+       info = (aspi_info)();
+       if (con->reportscsiioctl > 1)
+               pout("GetASPI32SupportInfo() returns 0x%04x\n", info);
+       rc = (info >> 8) & 0xff;
+       if (rc == ASPI_STATUS_NO_ADAPTERS) {
+               num_aspi_adapters = 0;
+       }
+       else if (rc == ASPI_STATUS_NO_ERROR) {
+               num_aspi_adapters = info & 0xff;
+       }
+       else {
+               if (verbose)
+                       pout("Got strange 0x%04x from GetASPI32SupportInfo()\n", info);
+               aspi_entry = 0;
+               FreeLibrary(h_aspi_dll);
+               h_aspi_dll = INVALID_HANDLE_VALUE;
+               errno = ENOENT;
+               return -1;
+       }
+
+       if (con->reportscsiioctl)
+               pout("%u ASPI Adapter%s detected\n",num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
+
+#ifdef __CYGWIN__
+       // save PID to detect fork() in aspi_entry_valid()
+       aspi_dll_pid = GetCurrentProcessId();
+#endif
+       assert(aspi_entry_valid());
+       return 0;
+}
+
+
+static int aspi_io_call(ASPI_SRB * srb, unsigned timeout)
+{
+       HANDLE event;
+       // Create event
+       if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL))) {
+               pout("CreateEvent(): Error=%ld\n", GetLastError()); return -EIO;
+       }
+       srb->i.event_handle = event;
+       srb->h.flags |= ASPI_REQFLAG_EVENT_NOTIFY;
+       // Start ASPI request
+       aspi_entry(srb);
+       if (((volatile ASPI_SRB *)srb)->h.status == ASPI_STATUS_IN_PROGRESS) {
+               // Wait for event
+               DWORD rc = WaitForSingleObject(event, timeout*1000L);
+               if (rc != WAIT_OBJECT_0) {
+                       if (rc == WAIT_TIMEOUT) {
+                               pout("ASPI Adapter %u, ID %u: Timed out after %u seconds\n",
+                                       srb->h.adapter, srb->i.target_id, timeout);
+                       }
+                       else {
+                               pout("WaitForSingleObject(%lx) = 0x%lx,%ld, Error=%ld\n",
+                                       (unsigned long)event, rc, rc, GetLastError());
+                       }
+                       // TODO: ASPI_ABORT_IO command
+                       aspi_entry = 0;
+                       h_aspi_dll = INVALID_HANDLE_VALUE;
+                       return -EIO;
+               }
+       }
+       CloseHandle(event);
+       return 0;
+}
+
+
+static int aspi_open(unsigned adapter, unsigned id)
+{
+       ASPI_SRB srb;
+       if (!(adapter <= 9 && id < 16)) {
+               errno = ENOENT;
+               return -1;
+       }
+
+       if (!aspi_entry_valid()) {
+               if (aspi_open_dll(1/*verbose*/))
+                       return -1;
+       }
+
+       // Adapter OK?
+       if (adapter >= num_aspi_adapters) {
+               pout("ASPI Adapter %u does not exist (%u Adapter%s detected).\n",
+                       adapter, num_aspi_adapters, (num_aspi_adapters!=1?"s":""));
+               if (!is_permissive()) {
+                       errno = ENOENT;
+                       return -1;
+               }
+       }
+
+       // Device present ?
+       memset(&srb, 0, sizeof(srb));
+       srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
+       srb.h.adapter = adapter; srb.i.target_id = id;
+       if (aspi_call(&srb)) {
+               errno = EIO;
+               return -1;
+       }
+       if (srb.h.status != ASPI_STATUS_NO_ERROR) {
+               pout("ASPI Adapter %u, ID %u: No such device (Status=0x%02x)\n", adapter, id, srb.h.status);
+               if (!is_permissive()) {
+                       errno = (srb.h.status == ASPI_STATUS_INVALID_TARGET ? ENOENT : EIO);
+                       return -1;
+               }
+       }
+       else if (con->reportscsiioctl)
+               pout("ASPI Adapter %u, ID %u: Device Type=0x%02x\n", adapter, id, srb.t.devtype);
+
+       return (0x0100 | ((adapter & 0xf)<<4) | (id & 0xf));
+}
+
+
+static void aspi_close(int fd)
+{
+       // No FreeLibrary(h_aspi_dll) to prevent problems with ASPI threads
+       ARGUSED(fd);
+}
+
+
+// Scan for SCSI drives, fill bitmask [adapter:0-9][id:0-7] of drives present,
+// return #drives
+
+static int aspi_scan(unsigned long * drives)
+{
+       int cnt = 0;
+       unsigned ad;
+
+       if (!aspi_entry_valid()) {
+               if (aspi_open_dll(con->reportscsiioctl/*default is quiet*/))
+                       return 0;
+       }
+
+       for (ad = 0; ad < num_aspi_adapters; ad++) {
+               ASPI_SRB srb; unsigned id;
+
+               if (ad > 9) {
+                       if (con->reportscsiioctl)
+                               pout(" ASPI Adapter %u: Ignored\n", ad);
+                       continue;
+               }
+
+               // Get adapter name
+               memset(&srb, 0, sizeof(srb));
+               srb.h.cmd = ASPI_CMD_ADAPTER_INQUIRE;
+               srb.h.adapter = ad;
+               if (aspi_call(&srb))
+                       return 0;
+
+               if (srb.h.status != ASPI_STATUS_NO_ERROR) {
+                       if (con->reportscsiioctl)
+                               pout(" ASPI Adapter %u: Status=0x%02x\n", ad, srb.h.status);
+                       continue;
+               }
+
+               if (con->reportscsiioctl) {
+                       int i;
+                       for (i = 1; i < 16 && srb.q.adapter_id[i]; i++)
+                               if (!(' ' <= srb.q.adapter_id[i] && srb.q.adapter_id[i] <= '~'))
+                                       srb.q.adapter_id[i] = '?';
+                       pout(" ASPI Adapter %u (\"%.16s\"):\n", ad, srb.q.adapter_id);
+               }
+
+               for (id = 0; id <= 7; id++) {
+                       // Get device type
+                       memset(&srb, 0, sizeof(srb));
+                       srb.h.cmd = ASPI_CMD_GET_DEVICE_TYPE;
+                       srb.h.adapter = ad; srb.i.target_id = id;
+                       if (aspi_call(&srb))
+                               return 0;
+                       if (srb.h.status != ASPI_STATUS_NO_ERROR) {
+                               if (con->reportscsiioctl > 1)
+                                       pout("  ID %u: No such device (Status=0x%02x)\n", id, srb.h.status);
+                               continue;
+                       }
+                       if (con->reportscsiioctl)
+                               pout("  ID %u: Device Type=0x%02x\n", id, srb.t.devtype);
+                       if (srb.t.devtype == 0x00/*HDD*/) {
+                               drives[ad >> 2] |= (1L << (((ad & 0x3) << 3) + id));
+                               cnt++;
+                       }
+               }
+       }
+       return cnt;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Interface to SCSI devices.  See os_linux.c
+int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
+{
+       ASPI_SRB srb;
+
+       if (!aspi_entry_valid())
+               return -EBADF;
+       if (!((fd & ~0xff) == 0x100))
+               return -EBADF;
+
+       if (!(iop->cmnd_len == 6 || iop->cmnd_len == 10 || iop->cmnd_len == 12)) {
+               pout("do_scsi_cmnd_io: bad CDB length\n");
+               return -EINVAL;
+       }
+
+       if (report > 0) {
+               // From os_linux.c
+               int k, j;
+               const unsigned char * ucp = iop->cmnd;
+               const char * np;
+               char buff[256];
+               const int sz = (int)sizeof(buff);
+
+               np = scsi_get_opcode_name(ucp[0]);
+               j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+               for (k = 0; k < (int)iop->cmnd_len; ++k)
+                       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+               if ((report > 1) && 
+                       (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+                       int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+                       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+                                                 "data, len=%d%s:\n", (int)iop->dxfer_len,
+                                                 (trunc ? " [only first 256 bytes shown]" : ""));
+                       dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+               }
+               else
+                       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+               pout(buff);
+       }
+
+       memset(&srb, 0, sizeof(srb));
+       srb.h.cmd = ASPI_CMD_EXECUTE_IO;
+       srb.h.adapter = ((fd >> 4) & 0xf);
+       srb.i.target_id = (fd & 0xf);
+       //srb.i.lun = 0;
+       srb.i.sense_size = ASPI_SENSE_SIZE;
+       srb.i.cdb_size = iop->cmnd_len;
+       memcpy(srb.i.cdb, iop->cmnd, iop->cmnd_len);
+
+       switch (iop->dxfer_dir) {
+               case DXFER_NONE:
+                       srb.h.flags = ASPI_REQFLAG_DIR_NO_XFER;
+                       break;
+               case DXFER_FROM_DEVICE:
+                       srb.h.flags = ASPI_REQFLAG_DIR_TO_HOST;
+                       srb.i.data_size = iop->dxfer_len;
+                       srb.i.data_addr = iop->dxferp;
+                       break;
+               case DXFER_TO_DEVICE:
+                       srb.h.flags = ASPI_REQFLAG_DIR_TO_TARGET;
+                       srb.i.data_size = iop->dxfer_len;
+                       srb.i.data_addr = iop->dxferp;
+                       break;
+               default:
+                       pout("do_scsi_cmnd_io: bad dxfer_dir\n");
+                       return -EINVAL;
+       }
+
+       iop->resp_sense_len = 0;
+       iop->scsi_status = 0;
+       iop->resid = 0;
+
+       if (aspi_io_call(&srb, (iop->timeout ? iop->timeout : 60))) {
+               // Timeout
+               return -EIO;
+       }
+
+       if (srb.h.status != ASPI_STATUS_NO_ERROR) {
+               if (   srb.h.status        == ASPI_STATUS_ERROR
+                   && srb.i.host_status   == ASPI_HSTATUS_NO_ERROR
+                   && srb.i.target_status == ASPI_TSTATUS_CHECK_CONDITION) {
+                       // Sense valid
+                       const unsigned char * sense = ASPI_SRB_SENSE(&srb.i, iop->cmnd_len);
+                       int len = (ASPI_SENSE_SIZE < iop->max_sense_len ? ASPI_SENSE_SIZE : iop->max_sense_len);
+                       iop->scsi_status = SCSI_STATUS_CHECK_CONDITION;
+                       if (len > 0 && iop->sensep) {
+                               memcpy(iop->sensep, sense, len);
+                               iop->resp_sense_len = len;
+                               if (report > 1) {
+                                       pout("  >>> Sense buffer, len=%d:\n", (int)len);
+                                       dStrHex(iop->sensep, len , 1);
+                               }
+                       }
+                       if (report) {
+                               pout("  sense_key=%x asc=%x ascq=%x\n",
+                                sense[2] & 0xf, sense[12], sense[13]);
+                       }
+                       return 0;
+               }
+               else {
+                       if (report)
+                               pout("  ASPI call failed, (0x%02x,0x%02x,0x%02x)\n", srb.h.status, srb.i.host_status, srb.i.target_status);
+                       return -EIO;
+               }
+       }
+
+       if (report > 0)
+               pout("  OK\n");
+
+       if (iop->dxfer_dir == DXFER_FROM_DEVICE && report > 1) {
+                int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+                pout("  Incoming data, len=%d%s:\n", (int)iop->dxfer_len,
+                         (trunc ? " [only first 256 bytes shown]" : ""));
+                               dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+       }
+
+       return 0;
+}
diff --git a/os_win32/daemon_win32.c b/os_win32/daemon_win32.c
new file mode 100644 (file)
index 0000000..2cac47f
--- /dev/null
@@ -0,0 +1,1266 @@
+/*
+ * os_win32/daemon_win32.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <io.h>
+
+#define WIN32_LEAN_AND_MEAN
+// Need MB_SERVICE_NOTIFICATION (NT4/2000/XP), IsDebuggerPresent() (Win98/ME/NT4/2000/XP)
+#define _WIN32_WINNT 0x0400 
+#include <windows.h>
+#ifdef _DEBUG
+#include <crtdbg.h>
+#endif
+
+#include "daemon_win32.h"
+
+const char *daemon_win32_c_cvsid = "$Id: daemon_win32.c,v 1.10 2006/04/12 14:54:28 ballen4705 Exp $"
+DAEMON_WIN32_H_CVSID;
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define ARGUSED(x) ((void)(x))
+
+// Prevent spawning of child process if debugging
+#ifdef _DEBUG
+#define debugging() IsDebuggerPresent()
+#else
+#define debugging() FALSE
+#endif
+
+
+#define EVT_NAME_LEN 260
+
+// Internal events (must be > SIGUSRn)
+#define EVT_RUNNING   100 // Exists when running, signaled on creation
+#define EVT_DETACHED  101 // Signaled when child detaches from console
+#define EVT_RESTART   102 // Signaled when child should restart
+
+static void make_name(char * name, int sig)
+{
+       int i;
+       if (!GetModuleFileNameA(NULL, name, EVT_NAME_LEN-10))
+               strcpy(name, "DaemonEvent");
+       for (i = 0; name[i]; i++) {
+               char c = name[i];
+               if (!(   ('0' <= c && c <= '9')
+                     || ('A' <= c && c <= 'Z')
+                     || ('a' <= c && c <= 'z')))
+                         name[i] = '_';
+       }
+       sprintf(name+strlen(name), "-%d", sig);
+}
+
+
+static HANDLE create_event(int sig, BOOL initial, BOOL errmsg, BOOL * exists)
+{
+       char name[EVT_NAME_LEN];
+       HANDLE h;
+       if (sig >= 0)
+               make_name(name, sig);
+       else
+               name[0] = 0;
+       if (exists)
+               *exists = FALSE;
+       if (!(h = CreateEventA(NULL, FALSE, initial, (name[0] ? name : NULL)))) {
+               if (errmsg)
+                       fprintf(stderr, "CreateEvent(.,\"%s\"): Error=%ld\n", name, GetLastError());
+               return 0;
+       }
+
+       if (GetLastError() == ERROR_ALREADY_EXISTS) {
+               if (!exists) {
+                       if (errmsg)
+                               fprintf(stderr, "CreateEvent(.,\"%s\"): Exists\n", name);
+                       CloseHandle(h);
+                       return 0;
+               }
+               *exists = TRUE;
+       }
+       return h;
+}
+
+
+static HANDLE open_event(int sig)
+{
+       char name[EVT_NAME_LEN];
+       make_name(name, sig);
+       return OpenEventA(EVENT_MODIFY_STATE, FALSE, name);
+}
+
+
+static int event_exists(int sig)
+{
+       char name[EVT_NAME_LEN];
+       HANDLE h;
+       make_name(name, sig);
+       if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name)))
+               return 0;
+       CloseHandle(h);
+       return 1;
+}
+
+
+static int sig_event(int sig)
+{
+       char name[EVT_NAME_LEN];
+       HANDLE h;
+       make_name(name, sig);
+       if (!(h = OpenEventA(EVENT_MODIFY_STATE, FALSE, name))) {
+               make_name(name, EVT_RUNNING);
+               if (!(h = OpenEvent(EVENT_MODIFY_STATE, FALSE, name)))
+                       return -1;
+               CloseHandle(h);
+               return 0;
+       }
+       SetEvent(h);
+       CloseHandle(h);
+       return 1;
+}
+
+
+static void daemon_help(FILE * f, const char * ident, const char * message)
+{
+       fprintf(f,
+               "%s: %s.\n"
+               "Use \"%s status|stop|reload|restart|sigusr1|sigusr2\" to control daemon.\n",
+               ident, message, ident);
+       fflush(f);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Parent Process
+
+
+static BOOL WINAPI parent_console_handler(DWORD event)
+{
+       switch (event) {
+               case CTRL_C_EVENT:
+               case CTRL_BREAK_EVENT:
+                       return TRUE; // Ignore
+       }
+       return FALSE; // continue with next handler ...
+}
+
+
+static int parent_main(HANDLE rev)
+{
+       HANDLE dev;
+       HANDLE ht[2];
+       char * cmdline;
+       STARTUPINFO si;
+       PROCESS_INFORMATION pi;
+       DWORD rc, exitcode;
+
+       // Ignore ^C, ^BREAK in parent
+       SetConsoleCtrlHandler(parent_console_handler, TRUE/*add*/);
+
+       // Create event used by child to signal daemon_detach()
+       if (!(dev = create_event(EVT_DETACHED, FALSE/*not signaled*/, TRUE, NULL/*must not exist*/))) {
+               CloseHandle(rev);
+               return 101;
+       }
+
+       // Restart process with same args
+       cmdline = GetCommandLineA();
+       memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
+       
+       if (!CreateProcessA(
+               NULL, cmdline,
+               NULL, NULL, TRUE/*inherit*/,
+               0, NULL, NULL, &si, &pi)) {
+               fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError());
+               CloseHandle(rev); CloseHandle(dev);
+               return 101;
+       }
+       CloseHandle(pi.hThread);
+
+       // Wait for daemon_detach() or exit()
+       ht[0] = dev; ht[1] = pi.hProcess;
+       rc = WaitForMultipleObjects(2, ht, FALSE/*or*/, INFINITE);
+       if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+2)) {
+               fprintf(stderr, "WaitForMultipleObjects returns %lX\n", rc);
+               TerminateProcess(pi.hProcess, 200);
+       }
+       CloseHandle(rev); CloseHandle(dev);
+
+       // Get exit code
+       if (!GetExitCodeProcess(pi.hProcess, &exitcode))
+               exitcode = 201;
+       else if (exitcode == STILL_ACTIVE) // detach()ed, assume OK
+               exitcode = 0;
+
+       CloseHandle(pi.hProcess);
+       return exitcode;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Child Process
+
+
+static int svc_mode;   // Running as service?
+static int svc_paused; // Service paused?
+
+static void service_report_status(int state, int waithint);
+
+
+// Tables of signal handler and corresponding events
+typedef void (*sigfunc_t)(int);
+
+#define MAX_SIG_HANDLERS 8
+
+static int num_sig_handlers = 0;
+static sigfunc_t sig_handlers[MAX_SIG_HANDLERS];
+static int sig_numbers[MAX_SIG_HANDLERS];
+static HANDLE sig_events[MAX_SIG_HANDLERS];
+
+static HANDLE sighup_handle, sigint_handle, sigbreak_handle;
+static HANDLE sigterm_handle, sigusr1_handle;
+
+static HANDLE running_event;
+
+static int reopen_stdin, reopen_stdout, reopen_stderr;
+
+
+// Handler for windows console events
+
+static BOOL WINAPI child_console_handler(DWORD event)
+{
+       // Caution: runs in a new thread
+       // TODO: Guard with a mutex
+       HANDLE h = 0;
+       switch (event) {
+               case CTRL_C_EVENT: // <CONTROL-C> (SIGINT)
+                       h = sigint_handle; break;
+               case CTRL_BREAK_EVENT: // <CONTROL-Break> (SIGBREAK/SIGQUIT)
+               case CTRL_CLOSE_EVENT: // User closed console or abort via task manager
+                       h = sigbreak_handle; break;
+               case CTRL_LOGOFF_EVENT: // Logout/Shutdown (SIGTERM)
+               case CTRL_SHUTDOWN_EVENT:
+                       h = sigterm_handle; break;
+       }
+       if (!h)
+               return FALSE; // continue with next handler
+       // Signal event
+       if (!SetEvent(h))
+               return FALSE;
+       return TRUE;
+}
+
+
+static void child_exit(void)
+{
+       int i;
+       char * cmdline;
+       HANDLE rst;
+       STARTUPINFO si;
+       PROCESS_INFORMATION pi;
+
+       for (i = 0; i < num_sig_handlers; i++)
+               CloseHandle(sig_events[i]);
+       num_sig_handlers = 0;
+       CloseHandle(running_event); running_event = 0;
+
+       // Restart?
+       if (!(rst = open_event(EVT_RESTART)))
+               return; // No => normal exit
+
+       // Yes => Signal exit and restart process
+       Sleep(500);
+       SetEvent(rst);
+       CloseHandle(rst);
+       Sleep(500);
+
+       cmdline = GetCommandLineA();
+       memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
+       si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE;
+
+       if (!CreateProcessA(
+               NULL, cmdline,
+               NULL, NULL, TRUE/*inherit*/,
+               0, NULL, NULL, &si, &pi)) {
+               fprintf(stderr, "CreateProcess(.,\"%s\",.) failed, Error=%ld\n", cmdline, GetLastError());
+       }
+       CloseHandle(pi.hThread); CloseHandle(pi.hProcess);
+}
+
+static int child_main(HANDLE hev,int (*main_func)(int, char **), int argc, char **argv)
+{
+       // Keep EVT_RUNNING open until exit
+       running_event = hev;
+
+       // Install console handler
+       SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
+
+       // Install restart handler
+       atexit(child_exit);
+
+       // Continue in main_func() to do the real work
+       return main_func(argc, argv);
+}
+
+
+// Simulate signal()
+
+sigfunc_t daemon_signal(int sig, sigfunc_t func)
+{
+       int i;
+       HANDLE h;
+       if (func == SIG_DFL || func == SIG_IGN)
+               return func; // TODO
+       for (i = 0; i < num_sig_handlers; i++) {
+               if (sig_numbers[i] == sig) {
+                       sigfunc_t old = sig_handlers[i];
+                       sig_handlers[i] = func;
+                       return old;
+               }
+       }
+       if (num_sig_handlers >= MAX_SIG_HANDLERS)
+               return SIG_ERR;
+       if (!(h = create_event((!svc_mode ? sig : -1), FALSE, TRUE, NULL)))
+               return SIG_ERR;
+       sig_events[num_sig_handlers]   = h;
+       sig_numbers[num_sig_handlers]  = sig;
+       sig_handlers[num_sig_handlers] = func;
+       switch (sig) {
+               case SIGHUP:   sighup_handle   = h; break;
+               case SIGINT:   sigint_handle   = h; break;
+               case SIGTERM:  sigterm_handle  = h; break;
+               case SIGBREAK: sigbreak_handle = h; break;
+               case SIGUSR1:  sigusr1_handle  = h; break;
+       }
+       num_sig_handlers++;
+       return SIG_DFL;
+}
+
+
+// strsignal()
+
+const char * daemon_strsignal(int sig)
+{
+       switch (sig) {
+               case SIGHUP:  return "SIGHUP";
+               case SIGINT:  return "SIGINT";
+               case SIGTERM: return "SIGTERM";
+               case SIGBREAK:return "SIGBREAK";
+               case SIGUSR1: return "SIGUSR1";
+               case SIGUSR2: return "SIGUSR2";
+               default:      return "*UNKNOWN*";
+       }
+}
+
+
+// Simulate sleep()
+
+void daemon_sleep(int seconds)
+{
+       do {
+               if (num_sig_handlers <= 0) {
+                       Sleep(seconds*1000L);
+               }
+               else {
+                       // Wait for any signal or timeout
+                       DWORD rc = WaitForMultipleObjects(num_sig_handlers, sig_events,
+                               FALSE/*OR*/, seconds*1000L);
+                       if (rc != WAIT_TIMEOUT) {
+                               if (!(/*WAIT_OBJECT_0(0) <= rc && */ rc < WAIT_OBJECT_0+(unsigned)num_sig_handlers)) {
+                                       fprintf(stderr,"WaitForMultipleObjects returns %lu\n", rc);
+                                       Sleep(seconds*1000L);
+                                       return;
+                               }
+                               // Call Handler
+                               sig_handlers[rc-WAIT_OBJECT_0](sig_numbers[rc-WAIT_OBJECT_0]);
+                               break;
+                       }
+               }
+       } while (svc_paused);
+}
+
+
+// Disable/Enable console
+
+void daemon_disable_console()
+{
+       SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/);
+       reopen_stdin = reopen_stdout = reopen_stderr = 0;
+       if (isatty(fileno(stdin))) {
+               fclose(stdin); reopen_stdin = 1;
+       }
+       if (isatty(fileno(stdout))) {
+               fclose(stdout); reopen_stdout = 1;
+       }
+       if (isatty(fileno(stderr))) {
+               fclose(stderr); reopen_stderr = 1;
+       }
+       FreeConsole();
+       SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
+}
+
+int daemon_enable_console(const char * title)
+{
+       BOOL ok;
+       SetConsoleCtrlHandler(child_console_handler, FALSE/*remove*/);
+       ok = AllocConsole();
+       SetConsoleCtrlHandler(child_console_handler, TRUE/*add*/);
+       if (!ok)
+               return -1;
+       if (title)
+               SetConsoleTitleA(title);
+       if (reopen_stdin)
+               freopen("conin$",  "r", stdin);
+       if (reopen_stdout)
+               freopen("conout$", "w", stdout);
+       if (reopen_stderr)
+               freopen("conout$", "w", stderr);
+       reopen_stdin = reopen_stdout = reopen_stderr = 0;
+       return 0;
+}
+
+
+// Detach daemon from console & parent
+
+int daemon_detach(const char * ident)
+{
+       if (!svc_mode) {
+               if (ident) {
+                       // Print help
+                       FILE * f = ( isatty(fileno(stdout)) ? stdout
+                                          : isatty(fileno(stderr)) ? stderr : NULL);
+                       if (f)
+                               daemon_help(f, ident, "now detaches from console into background mode");
+               }
+               // Signal detach to parent
+               if (sig_event(EVT_DETACHED) != 1) {
+                       if (!debugging())
+                               return -1;
+               }
+               daemon_disable_console();
+       }
+       else {
+               // Signal end of initialization to service control manager
+               service_report_status(SERVICE_RUNNING, 0);
+               reopen_stdin = reopen_stdout = reopen_stderr = 1;
+       }
+
+       return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// MessageBox
+
+#ifndef _MT
+//MT runtime not necessary, because mbox_thread uses no unsafe lib functions
+//#error Program must be linked with multithreaded runtime library
+#endif
+
+static LONG mbox_count; // # mbox_thread()s
+static HANDLE mbox_mutex; // Show 1 box at a time (not necessary for service)
+
+typedef struct mbox_args_s {
+       HANDLE taken; const char * title, * text; int mode;
+} mbox_args;
+
+
+// Thread to display one message box
+
+static ULONG WINAPI mbox_thread(LPVOID arg)
+{
+       // Take args
+       mbox_args * mb = (mbox_args *)arg;
+       char title[100]; char text[1000]; int mode;
+       strncpy(title, mb->title, sizeof(title)-1); title[sizeof(title)-1] = 0;
+       strncpy(text , mb->text , sizeof(text )-1); text [sizeof(text )-1] = 0;
+       mode = mb->mode;
+       SetEvent(mb->taken);
+
+       // Show only one box at a time
+       WaitForSingleObject(mbox_mutex, INFINITE);
+       MessageBoxA(NULL, text, title, mode);
+       ReleaseMutex(mbox_mutex);
+
+       InterlockedDecrement(&mbox_count);
+       return 0;
+}
+
+
+// Display a message box
+int daemon_messagebox(int system, const char * title, const char * text)
+{
+       mbox_args mb;
+       HANDLE ht; DWORD tid;
+
+       // Create mutex during first call
+       if (!mbox_mutex)
+               mbox_mutex = CreateMutex(NULL/*!inherit*/, FALSE/*!owned*/, NULL/*unnamed*/);
+
+       // Allow at most 10 threads
+       if (InterlockedIncrement(&mbox_count) > 10) {
+               InterlockedDecrement(&mbox_count);
+               return -1;
+       }
+
+       // Create thread
+       mb.taken = CreateEvent(NULL/*!inherit*/, FALSE, FALSE/*!signaled*/, NULL/*unnamed*/);
+       mb.mode = MB_OK|MB_ICONWARNING
+                |(svc_mode?MB_SERVICE_NOTIFICATION:0)
+                |(system?MB_SYSTEMMODAL:MB_APPLMODAL);
+       mb.title = title; mb.text = text;
+       mb.text = text;
+       if (!(ht = CreateThread(NULL, 0, mbox_thread, &mb, 0, &tid)))
+               return -1;
+
+       // Wait for args taken
+       if (WaitForSingleObject(mb.taken, 10000) != WAIT_OBJECT_0)
+               TerminateThread(ht, 0);
+       CloseHandle(mb.taken);
+       CloseHandle(ht);
+       return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+// Spawn a command and redirect <inpbuf >outbuf
+// return command's exitcode or -1 on error
+
+int daemon_spawn(const char * cmd,
+                 const char * inpbuf, int inpsize,
+                 char *       outbuf, int outsize )
+{
+       HANDLE pipe_inp_r, pipe_inp_w, pipe_out_r, pipe_out_w, pipe_err_w, h;
+       char temp_path[MAX_PATH];
+       DWORD flags, num_io, exitcode;
+       int use_file, state, i;
+       SECURITY_ATTRIBUTES sa;
+       STARTUPINFO si; PROCESS_INFORMATION pi;
+       HANDLE self = GetCurrentProcess();
+
+       if (GetVersion() & 0x80000000L) {
+               // Win9x/ME: A calling process never receives EOF if output of COMMAND.COM or
+               // any other DOS program is redirected via a pipe. Using a temp file instead.
+               use_file = 1; flags = DETACHED_PROCESS;
+       }
+       else {
+               // NT4/2000/XP: If DETACHED_PROCESS is used, CMD.EXE opens a new console window
+               // for each external command in a redirected .BAT file.
+               // Even (DETACHED_PROCESS|CREATE_NO_WINDOW) does not work.
+               use_file = 0; flags = CREATE_NO_WINDOW;
+       }
+
+       // Create stdin pipe with inheritable read side
+       memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa);
+       sa.bInheritHandle = TRUE;
+       if (!CreatePipe(&pipe_inp_r, &h, &sa/*inherit*/, inpsize*2+13))
+               return -1;
+       if (!DuplicateHandle(self, h, self, &pipe_inp_w,
+               0, FALSE/*!inherit*/, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) {
+               CloseHandle(pipe_inp_r);
+               return -1;
+       }
+
+       memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa);
+       sa.bInheritHandle = TRUE;
+       if (!use_file) {
+               // Create stdout pipe with inheritable write side
+               if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize)) {
+                       CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+                       return -1;
+               }
+       }
+       else {
+               // Create temp file with inheritable write handle
+               char temp_dir[MAX_PATH];
+               if (!GetTempPathA(sizeof(temp_dir), temp_dir))
+                       strcpy(temp_dir, ".");
+               if (!GetTempFileNameA(temp_dir, "out"/*prefix*/, 0/*create unique*/, temp_path)) {
+                       CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+                       return -1;
+               }
+               if ((h = CreateFileA(temp_path, GENERIC_READ|GENERIC_WRITE,
+                       0/*no sharing*/, &sa/*inherit*/, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) {
+                       CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+                       return -1;
+               }
+               if (!DuplicateHandle(self, h, self, &pipe_out_w,
+                       GENERIC_WRITE, TRUE/*inherit*/, 0)) {
+                       CloseHandle(h); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+                       return -1;
+               }
+       }
+
+       if (!DuplicateHandle(self, h, self, &pipe_out_r,
+               GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
+               CloseHandle(pipe_out_w); CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+               return -1;
+       }
+
+       // Create stderr handle as dup of stdout write side
+       if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
+               0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
+               CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
+               CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+               return -1;
+       }
+
+       // Create process with pipes/file as stdio
+       memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
+       si.hStdInput  = pipe_inp_r;
+       si.hStdOutput = pipe_out_w;
+       si.hStdError  = pipe_err_w;
+       si.dwFlags = STARTF_USESTDHANDLES;
+       if (!CreateProcessA(
+               NULL, (char*)cmd,
+               NULL, NULL, TRUE/*inherit*/,
+               flags/*DETACHED_PROCESS or CREATE_NO_WINDOW*/,
+               NULL, NULL, &si, &pi)) {
+               CloseHandle(pipe_err_w);
+               CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
+               CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
+               return -1;
+       }
+       CloseHandle(pi.hThread);
+       // Close inherited handles
+       CloseHandle(pipe_inp_r);
+       CloseHandle(pipe_out_w);
+       CloseHandle(pipe_err_w);
+
+       // Copy inpbuf to stdin
+       // convert \n => \r\n 
+       for (i = 0; i < inpsize; ) {
+               int len = 0;
+               while (i+len < inpsize && inpbuf[i+len] != '\n')
+                       len++;
+               if (len > 0)
+                       WriteFile(pipe_inp_w, inpbuf+i, len, &num_io, NULL);
+               i += len;
+               if (i < inpsize) {
+                       WriteFile(pipe_inp_w, "\r\n", 2, &num_io, NULL);
+                       i++;
+               }
+       }
+       CloseHandle(pipe_inp_w);
+
+       exitcode = 42;
+       for (state = 0; state < 2; state++) {
+               // stdout pipe: read pipe first
+               // stdout file: wait for process first
+               if (state == use_file) {
+                       // Copy stdout to output buffer until full, rest to /dev/null
+                       // convert \r\n => \n
+                       if (use_file)
+                               SetFilePointer(pipe_out_r, 0, NULL, FILE_BEGIN);
+                       for (i = 0; ; ) {
+                               char buf[256];
+                               int j;
+                               if (!ReadFile(pipe_out_r, buf, sizeof(buf), &num_io, NULL) || num_io == 0)
+                                       break;
+                               for (j = 0; i < outsize-1 && j < (int)num_io; j++) {
+                                       if (buf[j] != '\r')
+                                               outbuf[i++] = buf[j];
+                               }
+                       }
+                       outbuf[i] = 0;
+                       CloseHandle(pipe_out_r);
+                       if (use_file)
+                               DeleteFileA(temp_path);
+               }
+               else {
+                       // Wait for process exitcode
+                       WaitForSingleObject(pi.hProcess, INFINITE);
+                       GetExitCodeProcess(pi.hProcess, &exitcode);
+                       CloseHandle(pi.hProcess);
+               }
+       }
+       return exitcode;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Initd Functions
+
+static int wait_signaled(HANDLE h, int seconds)
+{
+       int i;
+       for (i = 0; ; ) {
+               if (WaitForSingleObject(h, 1000L) == WAIT_OBJECT_0)
+                       return 0;
+               if (++i >= seconds)
+                       return -1;
+               fputchar('.'); fflush(stdout);
+       }
+}
+
+
+static int wait_evt_running(int seconds, int exists)
+{
+       int i;
+       if (event_exists(EVT_RUNNING) == exists)
+               return 0;
+       for (i = 0; ; ) {
+               Sleep(1000);
+               if (event_exists(EVT_RUNNING) == exists)
+                       return 0;
+               if (++i >= seconds)
+                       return -1;
+               fputchar('.'); fflush(stdout);
+       }
+}
+
+
+static int is_initd_command(char * s)
+{
+       if (!strcmp(s, "status"))
+               return EVT_RUNNING;
+       if (!strcmp(s, "stop"))
+               return SIGTERM;
+       if (!strcmp(s, "reload"))
+               return SIGHUP;
+       if (!strcmp(s, "sigusr1"))
+               return SIGUSR1;
+       if (!strcmp(s, "sigusr2"))
+               return SIGUSR2;
+       if (!strcmp(s, "restart"))
+               return EVT_RESTART;
+       return -1;
+}
+
+
+static int initd_main(const char * ident, int argc, char **argv)
+{
+       int rc;
+       if (argc < 2)
+               return -1;
+       if ((rc = is_initd_command(argv[1])) < 0)
+               return -1;
+       if (argc != 2) {
+               printf("%s: no arguments allowed for command %s\n", ident, argv[1]);
+               return 1;
+       }
+
+       switch (rc) {
+               default:
+               case EVT_RUNNING:
+                       printf("Checking for %s:", ident); fflush(stdout);
+                       rc = event_exists(EVT_RUNNING);
+                       puts(rc ? " running" : " not running");
+                       return (rc ? 0 : 1);
+
+               case SIGTERM:
+                       printf("Stopping %s:", ident); fflush(stdout);
+                       rc = sig_event(SIGTERM);
+                       if (rc <= 0) {
+                               puts(rc < 0 ? " not running" : " error");
+                               return (rc < 0 ? 0 : 1);
+                       }
+                       rc = wait_evt_running(10, 0);
+                       puts(!rc ? " done" : " timeout");
+                       return (!rc ? 0 : 1);
+
+               case SIGHUP:
+                       printf("Reloading %s:", ident); fflush(stdout);
+                       rc = sig_event(SIGHUP);
+                       puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running");
+                       return (rc > 0 ? 0 : 1);
+
+               case SIGUSR1:
+               case SIGUSR2:
+                       printf("Sending SIGUSR%d to %s:", (rc-SIGUSR1+1), ident); fflush(stdout);
+                       rc = sig_event(rc);
+                       puts(rc > 0 ? " done" : rc == 0 ? " error" : " not running");
+                       return (rc > 0 ? 0 : 1);
+
+               case EVT_RESTART:
+                       {
+                               HANDLE rst;
+                               printf("Stopping %s:", ident); fflush(stdout);
+                               if (event_exists(EVT_DETACHED)) {
+                                       puts(" not detached, cannot restart");
+                                       return 1;
+                               }
+                               if (!(rst = create_event(EVT_RESTART, FALSE, FALSE, NULL))) {
+                                       puts(" error");
+                                       return 1;
+                               }
+                               rc = sig_event(SIGTERM);
+                               if (rc <= 0) {
+                                       puts(rc < 0 ? " not running" : " error");
+                                       CloseHandle(rst);
+                                       return 1;
+                               }
+                               rc = wait_signaled(rst, 10);
+                               CloseHandle(rst);
+                               if (rc) {
+                                       puts(" timeout");
+                                       return 1;
+                               }
+                               puts(" done");
+                               Sleep(100);
+
+                               printf("Starting %s:", ident); fflush(stdout);
+                               rc = wait_evt_running(10, 1);
+                               puts(!rc ? " done" : " error");
+                               return (!rc ? 0 : 1);
+                       }
+       }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Windows Service Functions
+
+int daemon_winsvc_exitcode; // Set by app to exit(code)
+
+static SERVICE_STATUS_HANDLE svc_handle;
+static SERVICE_STATUS svc_status;
+
+
+// Report status to SCM
+
+static void service_report_status(int state, int seconds)
+{
+       // TODO: Avoid race
+       static DWORD checkpoint = 1;
+       static DWORD accept_more = SERVICE_ACCEPT_PARAMCHANGE; // Win2000/XP
+       svc_status.dwCurrentState = state;
+       svc_status.dwWaitHint = seconds*1000;
+       switch (state) {
+               default:
+                       svc_status.dwCheckPoint = checkpoint++;
+                       break;
+               case SERVICE_RUNNING:
+               case SERVICE_STOPPED:
+                       svc_status.dwCheckPoint = 0;
+       }
+       switch (state) {
+               case SERVICE_START_PENDING:
+               case SERVICE_STOP_PENDING:
+                       svc_status.dwControlsAccepted = 0;
+                       break;
+               default:
+                       svc_status.dwControlsAccepted =
+                               SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|
+                               SERVICE_ACCEPT_PAUSE_CONTINUE|accept_more;
+                       break;
+       }
+       if (!SetServiceStatus(svc_handle, &svc_status)) {
+               if (svc_status.dwControlsAccepted & accept_more) {
+                       // Retry without SERVICE_ACCEPT_PARAMCHANGE (WinNT4)
+                       svc_status.dwControlsAccepted &= ~accept_more;
+                       accept_more = 0;
+                       SetServiceStatus(svc_handle, &svc_status);
+               }
+       }
+}
+
+
+// Control the service, called by SCM
+
+static void WINAPI service_control(DWORD ctrlcode)
+{
+       switch (ctrlcode) {
+               case SERVICE_CONTROL_STOP:
+               case SERVICE_CONTROL_SHUTDOWN:
+                       service_report_status(SERVICE_STOP_PENDING, 30);
+                       svc_paused = 0;
+                       SetEvent(sigterm_handle);
+                       break;
+               case SERVICE_CONTROL_PARAMCHANGE: // Win2000/XP
+                       service_report_status(svc_status.dwCurrentState, 0);
+                       svc_paused = 0;
+                       SetEvent(sighup_handle); // reload
+                       break;
+               case SERVICE_CONTROL_PAUSE:
+                       service_report_status(SERVICE_PAUSED, 0);
+                       svc_paused = 1;
+                       break;
+               case SERVICE_CONTROL_CONTINUE:
+                       service_report_status(SERVICE_RUNNING, 0);
+                       {
+                               int was_paused = svc_paused;
+                               svc_paused = 0;
+                               SetEvent(was_paused ? sighup_handle : sigusr1_handle); // reload:recheck
+                       }
+                       break;
+               case SERVICE_CONTROL_INTERROGATE:
+               default: // unknown
+                       service_report_status(svc_status.dwCurrentState, 0);
+                       break;
+       }
+}
+
+
+// Exit handler for service
+
+static void service_exit(void)
+{
+       // Close signal events
+       int i;
+       for (i = 0; i < num_sig_handlers; i++)
+               CloseHandle(sig_events[i]);
+       num_sig_handlers = 0;
+
+       // Set exitcode
+       if (daemon_winsvc_exitcode) {
+               svc_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
+               svc_status.dwServiceSpecificExitCode = daemon_winsvc_exitcode;
+       }
+       // Report stopped
+       service_report_status(SERVICE_STOPPED, 0);
+}
+
+
+// Variables for passing main(argc, argv) from daemon_main to service_main()
+static int (*svc_main_func)(int, char **);
+static int svc_main_argc;
+static char ** svc_main_argv;
+
+// Main function for service, called by service dispatcher
+
+static void WINAPI service_main(DWORD argc, LPSTR * argv)
+{
+       char path[MAX_PATH], *p;
+       ARGUSED(argc);
+
+       // Register control handler
+       svc_handle = RegisterServiceCtrlHandler(argv[0], service_control);
+
+       // Init service status
+       svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
+       service_report_status(SERVICE_START_PENDING, 10);
+
+       // Service started in \windows\system32, change to .exe directory
+       if (GetModuleFileNameA(NULL, path, sizeof(path)) && (p = strrchr(path, '\\'))) {
+               *p = 0; SetCurrentDirectoryA(path);
+       }
+       
+       // Install exit handler
+       atexit(service_exit);
+
+       // Do the real work, service status later updated by daemon_detach()
+       daemon_winsvc_exitcode = svc_main_func(svc_main_argc, svc_main_argv);
+
+       exit(daemon_winsvc_exitcode);
+       // ... continued in service_exit()
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Windows Service Admin Functions
+
+// Set Service description (Win2000/XP)
+
+static int svcadm_setdesc(SC_HANDLE hs, const char * desc)
+{
+       HANDLE hdll;
+       BOOL (WINAPI * ChangeServiceConfig2A_p)(SC_HANDLE, DWORD, LPVOID);
+       BOOL ret;
+       if (!(hdll = LoadLibraryA("ADVAPI32.DLL")))
+               return FALSE;
+       if (!((ChangeServiceConfig2A_p = (BOOL (WINAPI *)(SC_HANDLE, DWORD, LPVOID))GetProcAddress(hdll, "ChangeServiceConfig2A"))))
+               ret = FALSE;
+       else {
+               SERVICE_DESCRIPTIONA sd = { (char *)desc };
+               ret = ChangeServiceConfig2A_p(hs, SERVICE_CONFIG_DESCRIPTION, &sd);
+       }
+       FreeLibrary(hdll);
+       return ret;
+}
+
+
+// Service install/remove commands
+
+static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opts,
+                       int argc, char **argv                                      )
+{
+       int remove; long err;
+       SC_HANDLE hm, hs;
+
+       if (argc < 2)
+               return -1;
+       if (!strcmp(argv[1], "install"))
+               remove = 0;
+       else if (!strcmp(argv[1], "remove")) {
+               if (argc != 2) {
+                       printf("%s: no arguments allowed for command remove\n", ident);
+                       return 1;
+               }
+               remove = 1;
+       }
+       else
+               return -1;
+
+       printf("%s service %s:", (!remove?"Installing":"Removing"), ident); fflush(stdout);
+
+       // Open SCM
+       if (!(hm = OpenSCManager(NULL/*local*/, NULL/*default*/, SC_MANAGER_ALL_ACCESS))) {
+               if ((err = GetLastError()) == ERROR_ACCESS_DENIED)
+                       puts(" access to SCManager denied");
+               else if (err == ERROR_CALL_NOT_IMPLEMENTED)
+                       puts(" services not implemented on this version of Windows");
+               else
+                       printf(" cannot open SCManager, Error=%ld\n", err);
+               return 1;
+       }
+
+       if (!remove) {
+               char path[MAX_PATH+100];
+               int i;
+               // Get program path
+               if (!GetModuleFileNameA(NULL, path, MAX_PATH)) {
+                       printf(" unknown program path, Error=%ld\n", GetLastError());
+                       CloseServiceHandle(hm);
+                       return 1;
+               }
+               // Append options
+               strcat(path, " "); strcat(path, svc_opts->cmd_opt);
+               for (i = 2; i < argc; i++) {
+                       const char * s = argv[i];
+                       if (strlen(path)+strlen(s)+1 >= sizeof(path))
+                               break;
+                       strcat(path, " "); strcat(path, s);
+               }
+               // Create
+               if (!(hs = CreateService(hm,
+                       svc_opts->svcname, svc_opts->dispname,
+                       SERVICE_ALL_ACCESS,
+                       SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,
+                       SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
+                       NULL/*no load ordering*/, NULL/*no tag id*/,
+                       ""/*no depedencies*/, NULL/*local system account*/, NULL/*no pw*/))) {
+                       if ((err = GetLastError()) == ERROR_SERVICE_EXISTS)
+                               puts(" the service is already installed");
+                       else if (err == ERROR_SERVICE_MARKED_FOR_DELETE)
+                               puts(" service is still running and marked for deletion\n"
+                                    "Stop the service and retry install");
+                       else
+                               printf(" failed, Error=%ld\n", err);
+                       CloseServiceHandle(hm);
+                       return 1;
+               }
+               // Set optional description
+               if (svc_opts->descript)
+                       svcadm_setdesc(hs, svc_opts->descript);
+       }
+       else {
+               // Open
+               if (!(hs = OpenService(hm, svc_opts->svcname, SERVICE_ALL_ACCESS))) {
+                       puts(" not found");
+                       CloseServiceHandle(hm);
+                       return 1;
+               }
+               // TODO: Stop service if running
+               // Remove
+               if (!DeleteService(hs)) {
+                       if ((err = GetLastError()) == ERROR_SERVICE_MARKED_FOR_DELETE)
+                               puts(" service is still running and marked for deletion\n"
+                                    "Stop the service to remove it");
+                       else
+                               printf(" failed, Error=%ld\n", err);
+                       CloseServiceHandle(hs); CloseServiceHandle(hm);
+                       return 1;
+               }
+       }
+       puts(" done");
+       CloseServiceHandle(hs); CloseServiceHandle(hm);
+       return 0;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Main Function
+
+// This function must be called from main()
+// main_func is the function doing the real work
+
+int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts,
+                int (*main_func)(int, char **), int argc, char **argv      )
+{
+       int rc;
+#ifdef _DEBUG
+       // Enable Debug heap checks
+       _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG)
+               |_CRTDBG_ALLOC_MEM_DF|_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_LEAK_CHECK_DF);
+#endif
+
+       // Check for [status|stop|reload|restart|sigusr1|sigusr2] parameters
+       if ((rc = initd_main(ident, argc, argv)) >= 0)
+               return rc;
+       // Check for [install|remove] parameters
+       if (svc_opts && (rc = svcadm_main(ident, svc_opts, argc, argv)) >= 0)
+               return rc;
+
+       // Run as service if svc_opts.cmd_opt is given as first(!) argument
+       svc_mode = (svc_opts && argc >= 2 && !strcmp(argv[1], svc_opts->cmd_opt));
+
+       if (!svc_mode) {
+               // Daemon: Try to simulate a Unix-like daemon
+               HANDLE rev;
+               BOOL exists;
+
+               // Create main event to detect process type:
+               // 1. new: parent process => start child and wait for detach() or exit() of child.
+               // 2. exists && signaled: child process => do the real work, signal detach() to parent
+               // 3. exists && !signaled: already running => exit()
+               if (!(rev = create_event(EVT_RUNNING, TRUE/*signaled*/, TRUE, &exists)))
+                       return 100;
+
+               if (!exists && !debugging()) {
+                       // Event new => parent process
+                       return parent_main(rev);
+               }
+
+               if (WaitForSingleObject(rev, 0) == WAIT_OBJECT_0) {
+                       // Event was signaled => In child process
+                       return child_main(rev, main_func, argc, argv);
+               }
+
+               // Event no longer signaled => Already running!
+               daemon_help(stdout, ident, "already running");
+               CloseHandle(rev);
+               return 1;
+       }
+       else {
+               // Service: Start service_main() via SCM
+               SERVICE_TABLE_ENTRY service_table[] = {
+                       { (char*)svc_opts->svcname, service_main }, { NULL, NULL }
+               };
+
+               svc_main_func = main_func;
+               svc_main_argc = argc;
+               svc_main_argv = argv;
+               if (!StartServiceCtrlDispatcher(service_table)) {
+                       printf("%s: cannot dispatch service, Error=%ld\n"
+                               "Option \"%s\" cannot be used to start %s as a service from console.\n"
+                               "Use \"%s install ...\" to install the service\n"
+                               "and \"net start %s\" to start it.\n",
+                               ident, GetLastError(), svc_opts->cmd_opt, ident, ident, ident);
+
+#ifdef _DEBUG
+                       if (debugging())
+                               service_main(argc, argv);
+#endif
+                       return 100;
+               }
+               Sleep(1000);
+               ExitThread(0); // Do not redo exit() processing
+               /*NOTREACHED*/
+               return 0;
+       }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Test Program
+
+#ifdef TEST
+
+static volatile sig_atomic_t caughtsig = 0;
+
+static void sig_handler(int sig)
+{
+       caughtsig = sig;
+}
+
+static void test_exit(void)
+{
+       printf("Main exit\n");
+}
+
+int test_main(int argc, char **argv)
+{
+       int i;
+       int debug = 0;
+       char * cmd = 0;
+
+       printf("PID=%ld\n", GetCurrentProcessId());
+       for (i = 0; i < argc; i++) {
+               printf("%d: \"%s\"\n", i, argv[i]);
+               if (!strcmp(argv[i],"-d"))
+                       debug = 1;
+       }
+       if (argc > 1 && argv[argc-1][0] != '-')
+               cmd = argv[argc-1];
+
+       daemon_signal(SIGINT, sig_handler);
+       daemon_signal(SIGBREAK, sig_handler);
+       daemon_signal(SIGTERM, sig_handler);
+       daemon_signal(SIGHUP, sig_handler);
+       daemon_signal(SIGUSR1, sig_handler);
+       daemon_signal(SIGUSR2, sig_handler);
+
+       atexit(test_exit);
+
+       if (!debug) {
+               printf("Preparing to detach...\n");
+               Sleep(2000);
+               daemon_detach("test");
+               printf("Detached!\n");
+       }
+
+       for (;;) {
+               daemon_sleep(1);
+               printf("."); fflush(stdout);
+               if (caughtsig) {
+                       if (caughtsig == SIGUSR2) {
+                               debug ^= 1;
+                               if (debug)
+                                       daemon_enable_console("Daemon[Debug]");
+                               else
+                                       daemon_disable_console();
+                       }
+                       else if (caughtsig == SIGUSR1 && cmd) {
+                               char inpbuf[200], outbuf[1000]; int rc;
+                               strcpy(inpbuf, "Hello\nWorld!\n");
+                               rc = daemon_spawn(cmd, inpbuf, strlen(inpbuf), outbuf, sizeof(outbuf));
+                               if (!debug)
+                                       daemon_enable_console("Command output");
+                               printf("\"%s\" returns %d\n", cmd, rc);
+                               if (rc >= 0)
+                                       printf("output:\n%s.\n", outbuf);
+                               fflush(stdout);
+                               if (!debug) {
+                                       Sleep(10000); daemon_disable_console();
+                               }
+                       }
+                       printf("[PID=%ld: Signal=%d]", GetCurrentProcessId(), caughtsig); fflush(stdout);
+                       if (caughtsig == SIGTERM || caughtsig == SIGBREAK)
+                               break;
+                       caughtsig = 0;
+               }
+       }
+       printf("\nExiting on signal %d\n", caughtsig);
+       return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+       static const daemon_winsvc_options svc_opts = {
+       "-s", "test", "Test Service", "Service to test daemon_win32.c Module"
+       };
+
+       return daemon_main("testd", &svc_opts, test_main, argc, argv);
+}
+
+#endif
diff --git a/os_win32/daemon_win32.h b/os_win32/daemon_win32.h
new file mode 100644 (file)
index 0000000..60b72a3
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * os_win32/daemon_win32.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef DAEMON_WIN32_H
+#define DAEMON_WIN32_H
+
+#define DAEMON_WIN32_H_CVSID "$Id: daemon_win32.h,v 1.6 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+#include <signal.h>
+
+// Additional non-ANSI signals
+#define SIGHUP  (NSIG+1)
+#define SIGUSR1 (NSIG+2)
+#define SIGUSR2 (NSIG+3)
+
+
+// Options for Windows service
+typedef struct daemon_winsvc_options_s {
+       const char * cmd_opt;  // argv[1] option for services
+       // For service "install" command only:
+       const char * svcname;  // Service name
+       const char * dispname; // Service display name
+       const char * descript; // Service description
+} daemon_winsvc_options;
+
+
+// This function must be called from main()
+int daemon_main(const char * ident, const daemon_winsvc_options * svc_opts,
+                int (*main_func)(int, char **), int argc, char **argv      );
+
+// exit(code) returned by a service
+extern int daemon_winsvc_exitcode;
+
+// Simulate signal()
+void (*daemon_signal(int sig, void (*func)(int)))(int);
+const char * daemon_strsignal(int sig);
+
+// Simulate sleep()
+void daemon_sleep(int seconds);
+
+// Disable/Enable console
+void daemon_disable_console(void);
+int daemon_enable_console(const char * title);
+
+// Detach from console
+int daemon_detach(const char * ident);
+
+// Display a message box
+int daemon_messagebox(int system, const char * title, const char * text);
+
+// Spawn a process and redirect stdio
+int daemon_spawn(const char * cmd,
+                 const char * inpbuf, int inpsize,
+                 char *       outbuf, int outsize );
+
+#endif // DAEMON_WIN32_H
diff --git a/os_win32/hostname_win32.c b/os_win32/hostname_win32.c
new file mode 100644 (file)
index 0000000..6202eff
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * os_win32/hostname_win32.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "hostname_win32.h"
+
+const char * hostname_win32_c_cvsid = "$Id: hostname_win32.c,v 1.4 2006/04/12 14:54:28 ballen4705 Exp $" HOSTNAME_WIN32_H_CVSID;
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <string.h>
+
+#ifndef MAX_HOSTNAME_LEN
+
+// From IPHlpApi.dll:
+
+#define MAX_HOSTNAME_LEN    132
+#define MAX_DOMAIN_NAME_LEN 132
+#define MAX_SCOPE_ID_LEN    260
+
+typedef struct {
+  char String[4 * 4];
+} IP_ADDRESS_STRING, 
+*PIP_ADDRESS_STRING, IP_MASK_STRING, *PIP_MASK_STRING;
+
+typedef struct _IP_ADDR_STRING {
+  struct _IP_ADDR_STRING* Next;
+  IP_ADDRESS_STRING IpAddress;
+  IP_MASK_STRING IpMask;
+  DWORD Context;
+} IP_ADDR_STRING, 
+*PIP_ADDR_STRING;
+
+typedef struct {
+  char HostName[MAX_HOSTNAME_LEN];
+  char DomainName[MAX_DOMAIN_NAME_LEN];
+  PIP_ADDR_STRING CurrentDnsServer;
+  IP_ADDR_STRING DnsServerList;
+  UINT NodeType;
+  char ScopeId[MAX_SCOPE_ID_LEN];
+  UINT EnableRouting;
+  UINT EnableProxy;
+  UINT EnableDns;
+} FIXED_INFO,
+*PFIXED_INFO;
+
+DWORD WINAPI GetNetworkParams(PFIXED_INFO info, PULONG size);
+
+#endif // MAX_HOSTNAME_LEN
+
+
+// Call GetComputerNameEx() if available (Win2000/XP)
+
+static BOOL CallGetComputerNameExA(int type, LPSTR name, LPDWORD size)
+{
+       HANDLE hdll;
+       BOOL (WINAPI * GetComputerNameExA_p)(int/*enum COMPUTER_NAME_FORMAT*/, LPSTR, LPDWORD);
+       BOOL ret;
+       if (!(hdll = LoadLibraryA("KERNEL32.DLL")))
+               return FALSE;
+       if (!(GetComputerNameExA_p = (BOOL (WINAPI *)(int, LPSTR, LPDWORD))GetProcAddress(hdll, "GetComputerNameExA")))
+               ret = FALSE;
+       else
+               ret = GetComputerNameExA_p(type, name, size);
+       FreeLibrary(hdll);
+       return ret;
+}
+
+
+// Call GetNetworkParams() if available (Win98/ME/2000/XP)
+
+static DWORD CallGetNetworkParams(PFIXED_INFO info, PULONG size)
+{
+       HANDLE hdll;
+       DWORD (WINAPI * GetNetworkParams_p)(PFIXED_INFO, PULONG);
+       DWORD ret;
+       if (!(hdll = LoadLibraryA("IPHlpApi.dll")))
+               return ERROR_NOT_SUPPORTED;
+       if (!(GetNetworkParams_p = (DWORD (WINAPI *)(PFIXED_INFO, PULONG))GetProcAddress(hdll, "GetNetworkParams")))
+               ret = ERROR_NOT_SUPPORTED;
+       else
+               ret = GetNetworkParams_p(info, size);
+       FreeLibrary(hdll);
+       return ret;
+}
+
+
+// Get host/domainname from registry (Win98/ME/NT4/2000/XP)
+
+static DWORD GetNamesFromRegistry(BOOL domain, char * name, int len)
+{
+       HKEY hk; DWORD size, type;
+       if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,
+           (GetVersion() & 0x80000000
+            ? "System\\CurrentControlSet\\Services\\VxD\\MSTCP" //Win9x/ME
+            : "System\\CurrentControlSet\\Services\\Tcpip\\Parameters"),
+           0, KEY_READ, &hk) != ERROR_SUCCESS)
+               return 0;
+       size = len-1;
+       if (!(RegQueryValueExA(hk, (!domain?"HostName":"Domain"), 0, &type, name, &size) == ERROR_SUCCESS && type == REG_SZ))
+               size = 0;
+       if (size == 0 && domain) {
+               size = len-1;
+               if (!(RegQueryValueExA(hk, "DhcpDomain", 0, &type, name, &size) == ERROR_SUCCESS && type == REG_SZ))
+                       size = 0;
+       }
+       RegCloseKey(hk);
+       return size;
+}
+
+
+static int gethostdomname(int domain, char * name, int len)
+{
+       DWORD size; FIXED_INFO info;
+
+       // try KERNEL32.dll::GetComputerNameEx()
+       size = len - 1;
+       if (CallGetComputerNameExA((!domain ? 1:2/*ComputerNameDnsHost:Domain*/), name, &size))
+               return 0;
+
+       // try IPHlpApi.dll::GetNetworkParams() 
+       size = sizeof(info);
+       if (CallGetNetworkParams(&info, &size) == ERROR_SUCCESS) {
+               strncpy(name, (!domain?info.HostName:info.DomainName), len-1); name[len-1] = 0;
+               return 0;
+       }
+
+       // try registry
+       if (GetNamesFromRegistry(domain, name, len))
+               return 0;
+
+       if (domain)
+               return -1;
+
+       // last resort: get NETBIOS name
+       size = len - 1;
+       if (GetComputerNameA(name, &size))
+               return 0;
+
+       return -1;
+}
+
+
+int gethostname(char * name, int len)
+{
+       return gethostdomname(0, name, len);
+}
+
+
+int getdomainname(char * name, int len)
+{
+       return gethostdomname(1, name, len);
+}
+
+
+#ifdef TEST
+
+#include <stdio.h>
+
+main()
+{
+       char name[256];
+       if (gethostname(name, sizeof(name)))
+               strcpy(name, "Error");
+       printf("hostname=\"%s\"\n", name);
+       if (getdomainname(name, sizeof(name)))
+               strcpy(name, "Error");
+       printf("domainname=\"%s\"\n", name);
+       return 0;
+}
+
+#endif
diff --git a/os_win32/hostname_win32.h b/os_win32/hostname_win32.h
new file mode 100644 (file)
index 0000000..259735f
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * os_win32/hostname_win32.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef HOSTNAME_WIN32_H
+#define HOSTNAME_WIN32_H
+
+#define HOSTNAME_WIN32_H_CVSID "$Id: hostname_win32.h,v 1.3 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+int gethostname(char * name, int len);
+int getdomainname(char * name, int len);
+
+#endif // HOSTNAME_WIN32_H
diff --git a/os_win32/syslog.h b/os_win32/syslog.h
new file mode 100644 (file)
index 0000000..7ef23f0
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * os_win32/syslog.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef SYSLOG_H
+#define SYSLOG_H
+
+#define SYSLOG_H_CVSID "$Id: syslog.h,v 1.4 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+#include <stdarg.h>
+
+/* EVENTLOG_ERROR_TYPE: */
+#define LOG_EMERG       0
+#define LOG_ALERT       1
+#define LOG_CRIT        2
+#define LOG_ERR         3
+/* EVENTLOG_WARNING_TYPE: */
+#define LOG_WARNING     4
+/* EVENTLOG_INFORMATION_TYPE: */
+#define LOG_NOTICE      5
+#define LOG_INFO        6
+#define LOG_DEBUG       7
+
+/* event log: */
+#define LOG_DAEMON      ( 3<<3)
+/* ident.log: */
+#define LOG_LOCAL0      (16<<3)
+/* ident1-7.log: */
+#define LOG_LOCAL1      (17<<3)
+#define LOG_LOCAL2      (18<<3)
+#define LOG_LOCAL3      (19<<3)
+#define LOG_LOCAL4      (20<<3)
+#define LOG_LOCAL5      (21<<3)
+#define LOG_LOCAL6      (22<<3)
+#define LOG_LOCAL7      (23<<3)
+
+#define LOG_FACMASK     0x03f8
+#define LOG_FAC(f)      (((f) & LOG_FACMASK) >> 3)
+#define LOG_PID         0x01
+
+void openlog(const char * ident, int option, int facility);
+
+void closelog(void);
+
+void vsyslog(int priority, const char * message, va_list args);
+
+#endif /* SYSLOG_H */
diff --git a/os_win32/syslog_win32.c b/os_win32/syslog_win32.c
new file mode 100644 (file)
index 0000000..7ed039f
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * os_win32/syslog_win32.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2004-6 Christian Franke <smartmontools-support@lists.sourceforge.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+// Win32 Emulation of syslog() for smartd
+// Writes to windows event log on NT4/2000/XP
+// (Register syslogevt.exe as event message file)
+// Writes to file "<ident>.log" on 9x/ME.
+// If facility is set to LOG_LOCAL[0-7], log is written to
+// file "<ident>.log", stdout, stderr, "<ident>[1-5].log".
+
+
+#include "syslog.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <process.h> // getpid()
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h> // RegisterEventSourceA(), ReportEventA(), ...
+
+const char *syslog_win32_c_cvsid = "$Id: syslog_win32.c,v 1.6 2006/04/12 14:54:29 ballen4705 Exp $"
+SYSLOG_H_CVSID;
+
+#ifdef _MSC_VER
+// MSVC
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+
+#define ARGUSED(x) ((void)(x))
+
+
+#ifndef _MT
+//MT runtime not necessary, because thread uses no unsafe lib functions
+//#error Program must be linked with multithreaded runtime library
+#endif
+
+
+#ifdef TESTEVT
+// Redirect event log to stdout for testing
+
+static BOOL Test_ReportEventA(HANDLE h, WORD type, WORD cat, DWORD id, PSID usid,
+                                               WORD nstrings, WORD datasize, LPCTSTR * strings, LPVOID data)
+{
+       int i;
+       printf("%u %lu:%s", type, id, nstrings != 1?"\n":"");
+       for (i = 0; i < nstrings; i++)
+               printf(" \"%s\"\n", strings[i]);
+       fflush(stdout);
+       return TRUE;
+}
+
+HANDLE Test_RegisterEventSourceA(LPCTSTR server, LPCTSTR source)
+{
+       return (HANDLE)42;
+}
+
+#define ReportEventA Test_ReportEventA
+#define RegisterEventSourceA Test_RegisterEventSourceA
+#endif // TESTEVT
+
+
+// Event message ids,
+// should be identical to MSG_SYSLOG* in "syslogevt.h"
+// (generated by "mc" from "syslogevt.mc")
+#define MSG_SYSLOG                       0x00000000L
+#define MSG_SYSLOG_01                    0x00000001L
+// ...
+#define MSG_SYSLOG_10                    0x0000000AL
+
+static char sl_ident[100];
+static char sl_logpath[sizeof(sl_ident) + sizeof("0.log")-1];
+static FILE * sl_logfile;
+static char sl_pidstr[16];
+static HANDLE sl_hevtsrc;
+
+
+// Ring buffer for event log output via thread
+#define MAXLINES 10
+#define LINELEN 200
+
+static HANDLE evt_hthread;
+static char evt_lines[MAXLINES][LINELEN+1];
+static int evt_priorities[MAXLINES];
+static volatile int evt_timeout;
+static int evt_index_in, evt_index_out;
+static HANDLE evt_wait_in, evt_wait_out;
+
+
+// Map syslog priority to event type
+
+static WORD pri2evtype(int priority)
+{
+       switch (priority) {
+               default:
+               case LOG_EMERG: case LOG_ALERT:
+               case LOG_CRIT:  case LOG_ERR:
+                       return EVENTLOG_ERROR_TYPE;
+               case LOG_WARNING:
+                       return EVENTLOG_WARNING_TYPE;
+               case LOG_NOTICE: case LOG_INFO:
+               case LOG_DEBUG:
+                       return EVENTLOG_INFORMATION_TYPE;
+       }
+}
+
+
+// Map syslog priority to string
+
+static const char * pri2text(int priority)
+{
+       switch (priority) {
+               case LOG_EMERG:   return "EMERG";
+               case LOG_ALERT:   return "ALERT";
+               case LOG_CRIT:    return "CRIT";
+               default:
+               case LOG_ERR:     return "ERROR";
+               case LOG_WARNING: return "Warn";
+               case LOG_NOTICE:  return "Note";
+               case LOG_INFO:    return "Info";
+               case LOG_DEBUG:   return "Debug";
+       }
+}
+
+
+// Output cnt events from ring buffer
+
+static void report_events(int cnt)
+{
+       const char * msgs[3+MAXLINES];
+
+       int i, pri;
+       if (cnt <= 0)
+               return;
+       if (cnt > MAXLINES)
+               cnt = MAXLINES;
+
+       pri = evt_priorities[evt_index_out];
+
+       msgs[0] = sl_ident;
+       msgs[1] = sl_pidstr;
+       msgs[2] = pri2text(pri);
+       for (i = 0; i < cnt; i++) {
+               //assert(evt_priorities[evt_index_out] == pri);
+               msgs[3+i] = evt_lines[evt_index_out];
+               if (++evt_index_out >= MAXLINES)
+                       evt_index_out = 0;
+       }
+       ReportEventA(sl_hevtsrc,
+               pri2evtype(pri), // type
+               0, MSG_SYSLOG+cnt,    // category, message id
+               NULL,                 // no security id
+               (WORD)(3+cnt), 0,     // 3+cnt strings, ...
+               msgs, NULL);          // ...          , no data
+}
+
+
+// Thread to combine several syslog lines into one event log entry
+
+static ULONG WINAPI event_logger_thread(LPVOID arg)
+{
+       int cnt;
+       ARGUSED(arg);
+
+       cnt = 0;
+       for (;;) {
+               // Wait for first line ...
+               int prior, i, rest;
+               if (cnt == 0) {
+                       if (WaitForSingleObject(evt_wait_out, (evt_timeout? INFINITE : 0)) != WAIT_OBJECT_0)
+                               break;
+                       cnt = 1;
+               }
+
+               // ... wait some time for more lines with same prior
+               i = evt_index_out;
+               prior = evt_priorities[i];
+               rest = 0;
+               while (cnt < MAXLINES) {
+                       long timeout =
+                               evt_timeout * ((1000L * (MAXLINES-cnt+1))/MAXLINES);
+                       if (WaitForSingleObject(evt_wait_out, timeout) != WAIT_OBJECT_0)
+                               break;
+                       if (++i >= MAXLINES)
+                               i = 0;
+                       if (evt_priorities[i] != prior) {
+                               rest = 1;
+                               break;
+                       }
+                       cnt++;
+               }
+
+               // Output all in one event log entry
+               report_events(cnt);
+
+               // Signal space
+               if (!ReleaseSemaphore(evt_wait_in, cnt, NULL))
+                       break;
+               cnt = rest;
+       }
+       return 0;
+}
+
+
+static void on_exit_event_logger(void)
+{
+       // Output lines immediate if exiting
+       evt_timeout = 0; 
+       // Wait for thread to finish
+       WaitForSingleObject(evt_hthread, 1000L);
+       CloseHandle(evt_hthread);
+#if 0
+       if (sl_hevtsrc) {
+               DeregisterEventSource(sl_hevtsrc); sl_hevtsrc = 0;
+       }
+#else
+       // Leave event message source open to prevent losing messages during shutdown
+#endif
+}
+
+
+static int start_event_logger()
+{
+       DWORD tid;
+       evt_timeout = 1;
+       if (   !(evt_wait_in  = CreateSemaphore(NULL,  MAXLINES, MAXLINES, NULL))
+               || !(evt_wait_out = CreateSemaphore(NULL,         0, MAXLINES, NULL))) {
+               fprintf(stderr,"CreateSemaphore failed, Error=%ld\n", GetLastError());
+               return -1;
+       }
+       if (!(evt_hthread = CreateThread(NULL, 0, event_logger_thread, NULL, 0, &tid))) {
+               fprintf(stderr,"CreateThread failed, Error=%ld\n", GetLastError());
+               return -1;
+       }
+       atexit(on_exit_event_logger);
+       return 0;
+}
+
+
+// Write lines to event log ring buffer
+
+static void write_event_log(int priority, const char * lines)
+{
+       int cnt = 0;
+       int i;
+       for (i = 0; lines[i]; i++) {
+               int len = 0;
+               while (lines[i+len] && lines[i+len] != '\n')
+                       len++;
+                       ;
+               if (len > 0) {
+                       // Wait for space
+                       if (WaitForSingleObject(evt_wait_in, INFINITE) != WAIT_OBJECT_0)
+                               return;
+                       // Copy line
+                       evt_priorities[evt_index_in] = priority;
+                       memcpy(evt_lines[evt_index_in], lines+i, (len < LINELEN ? len : LINELEN));
+                       if (len < LINELEN)
+                               evt_lines[evt_index_in][len] = 0;
+                       if (++evt_index_in >= MAXLINES)
+                               evt_index_in = 0;
+                       // Signal avail if ring buffer full
+                       if (++cnt >= MAXLINES) {
+                               ReleaseSemaphore(evt_wait_out, cnt, NULL);
+                               cnt = 0;
+                       }
+                       i += len;
+               }
+               if (!lines[i])
+                       break;
+       }
+
+       // Signal avail
+       if (cnt > 0)
+               ReleaseSemaphore(evt_wait_out, cnt, NULL);
+       Sleep(1);
+}
+
+
+// Write lines to logfile
+
+static void write_logfile(FILE * f, int priority, const char * lines)
+{
+       time_t now; char stamp[sizeof("2004-04-04 10:00:00")+13];
+       int i;
+
+       now = time((time_t*)0);
+       if (!strftime(stamp, sizeof(stamp)-1, "%Y-%m-%d %H:%M:%S", localtime(&now)))
+               strcpy(stamp,"?");
+
+       for (i = 0; lines[i]; i++) {
+               int len = 0;
+               while (lines[i+len] && lines[i+len] != '\n')
+                       len++;
+               if (len > 0) {
+                       fprintf(f, "%s %s[%s]: %-5s: ",
+                               stamp, sl_ident, sl_pidstr, pri2text(priority));
+                       fwrite(lines+i, len, 1, f);
+                       fputc('\n', f);
+                       i += len;
+               }
+               if (!lines[i])
+                       break;
+       }
+}
+
+
+void openlog(const char *ident, int logopt, int facility)
+{
+       int pid;
+       if (sl_logpath[0] || sl_logfile || sl_hevtsrc)
+               return; // Already open
+
+       strncpy(sl_ident, ident, sizeof(sl_ident)-1);
+       // logopt==LOG_PID assumed
+       ARGUSED(logopt);
+       pid = getpid();
+       if (snprintf(sl_pidstr, sizeof(sl_pidstr)-1, (pid >= 0 ? "%d" : "0x%X"), pid) < 0)
+               strcpy(sl_pidstr,"?");
+
+       if (facility == LOG_LOCAL0) // "ident.log"
+               strcat(strcpy(sl_logpath, sl_ident), ".log");
+       else if (facility == LOG_LOCAL1) // stdout
+               sl_logfile = stdout;
+       else if (facility == LOG_LOCAL2) // stderr
+               sl_logfile = stderr;
+       else if (LOG_LOCAL2 < facility && facility <= LOG_LOCAL7) { // "ident[1-5].log"
+               snprintf(sl_logpath, sizeof(sl_logpath)-1, "%s%d.log",
+                       sl_ident, LOG_FAC(facility)-LOG_FAC(LOG_LOCAL2));
+       }
+       else // Assume LOG_DAEMON, use event log if possible, else "ident.log"
+       if (!(sl_hevtsrc = RegisterEventSourceA(NULL/*localhost*/, sl_ident))) {
+               // Cannot open => Use logfile
+               long err = GetLastError();
+               strcat(strcpy(sl_logpath, sl_ident), ".log");
+               if (GetVersion() & 0x80000000)
+                       fprintf(stderr, "%s: No event log on Win9x/ME, writing to %s\n",
+                               sl_ident, sl_logpath);
+               else
+                       fprintf(stderr, "%s: Cannot register event source (Error=%ld), writing to %s\n",
+                               sl_ident, err, sl_logpath);
+       }
+       else {
+               // Start event log thread
+               start_event_logger();
+       }
+       //assert(sl_logpath[0] || sl_logfile || sl_hevtsrc);
+
+}
+
+
+void closelog()
+{
+}
+
+
+void vsyslog(int priority, const char * message, va_list args)
+{
+       char buffer[1000];
+
+       // Translation of %m to error text not supported yet
+       if (strstr(message, "%m"))
+               message = "Internal error: \"%%m\" in log message";
+
+       // Format message
+       if (vsnprintf(buffer, sizeof(buffer)-1, message, args) < 0)
+               strcpy(buffer, "Internal Error: buffer overflow");
+
+       if (sl_hevtsrc) {
+               // Write to event log
+               write_event_log(priority, buffer);
+       }
+       else if (sl_logfile) {
+               // Write to stdout/err
+               write_logfile(sl_logfile, priority, buffer);
+               fflush(sl_logfile);
+       }
+       else if (sl_logpath[0]) {
+               // Append to logfile
+               FILE * f;
+               if (!(f = fopen(sl_logpath, "a")))
+                       return;
+               write_logfile(f, priority, buffer);
+               fclose(f);
+       }
+}
+
+
+#ifdef TEST
+// Test program
+
+void syslog(int priority, const char *message, ...)
+{
+       va_list args;
+       va_start(args, message);
+       vsyslog(priority, message, args);
+       va_end(args);
+}
+
+int main(int argc, char* argv[])
+{
+       int i;
+       openlog(argc < 2 ? "test" : argv[1], LOG_PID, (argc < 3 ? LOG_DAEMON : LOG_LOCAL1));
+       syslog(LOG_INFO,    "Info\n");
+       syslog(LOG_WARNING, "Warning %d\n\n", 42);
+       syslog(LOG_ERR,     "Error %s", "Fatal");
+       for (i = 0; i < 100; i++) {
+               char buf[LINELEN];
+               if (i % 13 == 0)
+                       Sleep(1000L);
+               sprintf(buf, "Log Line %d\n", i);
+               syslog(i % 17 ? LOG_INFO : LOG_ERR, buf);
+       }
+       closelog();
+       return 0;
+}
+
+#endif
diff --git a/posix/regcomp.c b/posix/regcomp.c
new file mode 100644 (file)
index 0000000..f25ecae
--- /dev/null
@@ -0,0 +1,3544 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   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.  */
+
+static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
+                                         int length, reg_syntax_t syntax);
+static void re_compile_fastmap_iter (regex_t *bufp,
+                                    const re_dfastate_t *init_state,
+                                    char *fastmap);
+static reg_errcode_t init_dfa (re_dfa_t *dfa, int pat_len);
+static reg_errcode_t init_word_char (re_dfa_t *dfa);
+#ifdef RE_ENABLE_I18N
+static void free_charset (re_charset_t *cset);
+#endif /* RE_ENABLE_I18N */
+static void free_workarea_compile (regex_t *preg);
+static reg_errcode_t create_initial_state (re_dfa_t *dfa);
+static reg_errcode_t analyze (re_dfa_t *dfa);
+static reg_errcode_t analyze_tree (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_first (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_next (re_dfa_t *dfa, bin_tree_t *node);
+static void calc_epsdest (re_dfa_t *dfa, bin_tree_t *node);
+static reg_errcode_t duplicate_node_closure (re_dfa_t *dfa, int top_org_node,
+                                            int top_clone_node, int root_node,
+                                            unsigned int constraint);
+static reg_errcode_t duplicate_node (int *new_idx, re_dfa_t *dfa, int org_idx,
+                                    unsigned int constraint);
+static int search_duplicated_node (re_dfa_t *dfa, int org_node,
+                                  unsigned int constraint);
+static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
+static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
+                                        int node, int root);
+static void calc_inveclosure (re_dfa_t *dfa);
+static int fetch_number (re_string_t *input, re_token_t *token,
+                        reg_syntax_t syntax);
+static re_token_t fetch_token (re_string_t *input, reg_syntax_t syntax);
+static int peek_token (re_token_t *token, re_string_t *input,
+                       reg_syntax_t syntax);
+static int peek_token_bracket (re_token_t *token, re_string_t *input,
+                              reg_syntax_t syntax);
+static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
+                         reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg,
+                                 re_token_t *token, reg_syntax_t syntax,
+                                 int nest, reg_errcode_t *err);
+static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg,
+                                re_token_t *token, reg_syntax_t syntax,
+                                int nest, reg_errcode_t *err);
+static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg,
+                                    re_token_t *token, reg_syntax_t syntax,
+                                    int nest, reg_errcode_t *err);
+static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg,
+                                 re_token_t *token, reg_syntax_t syntax,
+                                 int nest, reg_errcode_t *err);
+static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp,
+                                re_dfa_t *dfa, re_token_t *token,
+                                reg_syntax_t syntax, reg_errcode_t *err);
+static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa,
+                                     re_token_t *token, reg_syntax_t syntax,
+                                     reg_errcode_t *err);
+static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
+                                           re_string_t *regexp,
+                                           re_token_t *token, int token_len,
+                                           re_dfa_t *dfa,
+                                           reg_syntax_t syntax);
+static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
+                                         re_string_t *regexp,
+                                         re_token_t *token);
+#ifndef _LIBC
+# ifdef RE_ENABLE_I18N
+static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset,
+                                     re_charset_t *mbcset, int *range_alloc,
+                                     bracket_elem_t *start_elem,
+                                     bracket_elem_t *end_elem);
+static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset,
+                                            re_charset_t *mbcset,
+                                            int *coll_sym_alloc,
+                                            const unsigned char *name);
+# else /* not RE_ENABLE_I18N */
+static reg_errcode_t build_range_exp (re_bitset_ptr_t sbcset,
+                                     bracket_elem_t *start_elem,
+                                     bracket_elem_t *end_elem);
+static reg_errcode_t build_collating_symbol (re_bitset_ptr_t sbcset,
+                                            const unsigned char *name);
+# endif /* not RE_ENABLE_I18N */
+#endif /* not _LIBC */
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset,
+                                       re_charset_t *mbcset,
+                                       int *equiv_class_alloc,
+                                       const unsigned char *name);
+static reg_errcode_t build_charclass (re_bitset_ptr_t sbcset,
+                                     re_charset_t *mbcset,
+                                     int *char_class_alloc,
+                                     const unsigned char *class_name,
+                                     reg_syntax_t syntax);
+#else  /* not RE_ENABLE_I18N */
+static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset,
+                                       const unsigned char *name);
+static reg_errcode_t build_charclass (re_bitset_ptr_t sbcset,
+                                     const unsigned char *class_name,
+                                     reg_syntax_t syntax);
+#endif /* not RE_ENABLE_I18N */
+static bin_tree_t *build_word_op (re_dfa_t *dfa, int not, reg_errcode_t *err);
+static void free_bin_tree (bin_tree_t *tree);
+static bin_tree_t *create_tree (bin_tree_t *left, bin_tree_t *right,
+                               re_token_type_t type, int index);
+static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
+\f
+/* This table gives an error message for each of the error codes listed
+   in regex.h.  Obviously the order here has to be same as there.
+   POSIX doesn't require that we do anything for REG_NOERROR,
+   but why not be nice?  */
+
+const char __re_error_msgid[] attribute_hidden =
+  {
+#define REG_NOERROR_IDX        0
+    gettext_noop ("Success")   /* REG_NOERROR */
+    "\0"
+#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
+    gettext_noop ("No match")  /* REG_NOMATCH */
+    "\0"
+#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
+    gettext_noop ("Invalid regular expression") /* REG_BADPAT */
+    "\0"
+#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
+    gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
+    "\0"
+#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
+    gettext_noop ("Invalid character class name") /* REG_ECTYPE */
+    "\0"
+#define REG_EESCAPE_IDX        (REG_ECTYPE_IDX + sizeof "Invalid character class name")
+    gettext_noop ("Trailing backslash") /* REG_EESCAPE */
+    "\0"
+#define REG_ESUBREG_IDX        (REG_EESCAPE_IDX + sizeof "Trailing backslash")
+    gettext_noop ("Invalid back reference") /* REG_ESUBREG */
+    "\0"
+#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
+    gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */
+    "\0"
+#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+    gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
+    "\0"
+#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
+    gettext_noop ("Unmatched \\{") /* REG_EBRACE */
+    "\0"
+#define REG_BADBR_IDX  (REG_EBRACE_IDX + sizeof "Unmatched \\{")
+    gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
+    "\0"
+#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
+    gettext_noop ("Invalid range end") /* REG_ERANGE */
+    "\0"
+#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
+    gettext_noop ("Memory exhausted") /* REG_ESPACE */
+    "\0"
+#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
+    gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
+    "\0"
+#define REG_EEND_IDX   (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
+    gettext_noop ("Premature end of regular expression") /* REG_EEND */
+    "\0"
+#define REG_ESIZE_IDX  (REG_EEND_IDX + sizeof "Premature end of regular expression")
+    gettext_noop ("Regular expression too big") /* REG_ESIZE */
+    "\0"
+#define REG_ERPAREN_IDX        (REG_ESIZE_IDX + sizeof "Regular expression too big")
+    gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
+  };
+
+const size_t __re_error_msgid_idx[] attribute_hidden =
+  {
+    REG_NOERROR_IDX,
+    REG_NOMATCH_IDX,
+    REG_BADPAT_IDX,
+    REG_ECOLLATE_IDX,
+    REG_ECTYPE_IDX,
+    REG_EESCAPE_IDX,
+    REG_ESUBREG_IDX,
+    REG_EBRACK_IDX,
+    REG_EPAREN_IDX,
+    REG_EBRACE_IDX,
+    REG_BADBR_IDX,
+    REG_ERANGE_IDX,
+    REG_ESPACE_IDX,
+    REG_BADRPT_IDX,
+    REG_EEND_IDX,
+    REG_ESIZE_IDX,
+    REG_ERPAREN_IDX
+  };
+\f
+/* Entry points for GNU code.  */
+
+/* re_compile_pattern is the GNU regular expression compiler: it
+   compiles PATTERN (of length LENGTH) and puts the result in BUFP.
+   Returns 0 if the pattern was valid, otherwise an error string.
+
+   Assumes the `allocated' (and perhaps `buffer') and `translate' fields
+   are set in BUFP on entry.  */
+
+const char *
+re_compile_pattern (pattern, length, bufp)
+    const char *pattern;
+    size_t length;
+    struct re_pattern_buffer *bufp;
+{
+  reg_errcode_t ret;
+
+  /* And GNU code determines whether or not to get register information
+     by passing null for the REGS argument to re_match, etc., not by
+     setting no_sub.  */
+  bufp->no_sub = 0;
+
+  /* Match anchors at newline.  */
+  bufp->newline_anchor = 1;
+
+  ret = re_compile_internal (bufp, pattern, length, re_syntax_options);
+
+  if (!ret)
+    return NULL;
+  return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+#ifdef _LIBC
+weak_alias (__re_compile_pattern, re_compile_pattern)
+#endif
+
+/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
+   also be assigned to arbitrarily: each pattern buffer stores its own
+   syntax, so it can be changed between regex compilations.  */
+/* This has no initializer because initialized variables in Emacs
+   become read-only after dumping.  */
+reg_syntax_t re_syntax_options;
+
+
+/* Specify the precise syntax of regexps for compilation.  This provides
+   for compatibility for various utilities which historically have
+   different, incompatible syntaxes.
+
+   The argument SYNTAX is a bit mask comprised of the various bits
+   defined in regex.h.  We return the old syntax.  */
+
+reg_syntax_t
+re_set_syntax (syntax)
+    reg_syntax_t syntax;
+{
+  reg_syntax_t ret = re_syntax_options;
+
+  re_syntax_options = syntax;
+  return ret;
+}
+#ifdef _LIBC
+weak_alias (__re_set_syntax, re_set_syntax)
+#endif
+
+int
+re_compile_fastmap (bufp)
+    struct re_pattern_buffer *bufp;
+{
+  re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+  char *fastmap = bufp->fastmap;
+
+  memset (fastmap, '\0', sizeof (char) * SBC_MAX);
+  re_compile_fastmap_iter (bufp, dfa->init_state, fastmap);
+  if (dfa->init_state != dfa->init_state_word)
+    re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap);
+  if (dfa->init_state != dfa->init_state_nl)
+    re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap);
+  if (dfa->init_state != dfa->init_state_begbuf)
+    re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap);
+  bufp->fastmap_accurate = 1;
+  return 0;
+}
+#ifdef _LIBC
+weak_alias (__re_compile_fastmap, re_compile_fastmap)
+#endif
+
+static inline void
+re_set_fastmap (char *fastmap, int icase, int ch)
+{
+  fastmap[ch] = 1;
+  if (icase)
+    fastmap[tolower (ch)] = 1;
+}
+
+/* Helper function for re_compile_fastmap.
+   Compile fastmap for the initial_state INIT_STATE.  */
+
+static void
+re_compile_fastmap_iter (bufp, init_state, fastmap)
+     regex_t *bufp;
+     const re_dfastate_t *init_state;
+     char *fastmap;
+{
+  re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+  int node_cnt;
+  int icase = (MB_CUR_MAX == 1 && (bufp->syntax & RE_ICASE));
+  for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt)
+    {
+      int node = init_state->nodes.elems[node_cnt];
+      re_token_type_t type = dfa->nodes[node].type;
+
+      if (type == CHARACTER)
+       re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c);
+      else if (type == SIMPLE_BRACKET)
+       {
+         int i, j, ch;
+         for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+           for (j = 0; j < UINT_BITS; ++j, ++ch)
+             if (dfa->nodes[node].opr.sbcset[i] & (1 << j))
+               re_set_fastmap (fastmap, icase, ch);
+       }
+#ifdef RE_ENABLE_I18N
+      else if (type == COMPLEX_BRACKET)
+       {
+         int i;
+         re_charset_t *cset = dfa->nodes[node].opr.mbcset;
+         if (cset->non_match || cset->ncoll_syms || cset->nequiv_classes
+             || cset->nranges || cset->nchar_classes)
+           {
+# ifdef _LIBC
+             if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0)
+               {
+                 /* In this case we want to catch the bytes which are
+                    the first byte of any collation elements.
+                    e.g. In da_DK, we want to catch 'a' since "aa"
+                         is a valid collation element, and don't catch
+                         'b' since 'b' is the only collation element
+                         which starts from 'b'.  */
+                 int j, ch;
+                 const int32_t *table = (const int32_t *)
+                   _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+                 for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+                   for (j = 0; j < UINT_BITS; ++j, ++ch)
+                     if (table[ch] < 0)
+                       re_set_fastmap (fastmap, icase, ch);
+               }
+# else
+             if (MB_CUR_MAX > 1)
+               for (i = 0; i < SBC_MAX; ++i)
+                 if (__btowc (i) == WEOF)
+                   re_set_fastmap (fastmap, icase, i);
+# endif /* not _LIBC */
+           }
+         for (i = 0; i < cset->nmbchars; ++i)
+           {
+             char buf[256];
+             mbstate_t state;
+             memset (&state, '\0', sizeof (state));
+             __wcrtomb (buf, cset->mbchars[i], &state);
+             re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
+           }
+       }
+#endif /* RE_ENABLE_I18N */
+      else if (type == END_OF_RE || type == OP_PERIOD)
+       {
+         memset (fastmap, '\1', sizeof (char) * SBC_MAX);
+         if (type == END_OF_RE)
+           bufp->can_be_null = 1;
+         return;
+       }
+    }
+}
+\f
+/* Entry point for POSIX code.  */
+/* regcomp takes a regular expression as a string and compiles it.
+
+   PREG is a regex_t *.  We do not expect any fields to be initialized,
+   since POSIX says we shouldn't.  Thus, we set
+
+     `buffer' to the compiled pattern;
+     `used' to the length of the compiled pattern;
+     `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
+       REG_EXTENDED bit in CFLAGS is set; otherwise, to
+       RE_SYNTAX_POSIX_BASIC;
+     `newline_anchor' to REG_NEWLINE being set in CFLAGS;
+     `fastmap' to an allocated space for the fastmap;
+     `fastmap_accurate' to zero;
+     `re_nsub' to the number of subexpressions in PATTERN.
+
+   PATTERN is the address of the pattern string.
+
+   CFLAGS is a series of bits which affect compilation.
+
+     If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
+     use POSIX basic syntax.
+
+     If REG_NEWLINE is set, then . and [^...] don't match newline.
+     Also, regexec will try a match beginning after every newline.
+
+     If REG_ICASE is set, then we considers upper- and lowercase
+     versions of letters to be equivalent when matching.
+
+     If REG_NOSUB is set, then when PREG is passed to regexec, that
+     routine will report only success or failure, and nothing about the
+     registers.
+
+   It returns 0 if it succeeds, nonzero if it doesn't.  (See regex.h for
+   the return codes and their meanings.)  */
+
+int
+regcomp (preg, pattern, cflags)
+    regex_t *__restrict preg;
+    const char *__restrict pattern;
+    int cflags;
+{
+  reg_errcode_t ret;
+  reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED
+                        : RE_SYNTAX_POSIX_BASIC);
+
+  preg->buffer = NULL;
+  preg->allocated = 0;
+  preg->used = 0;
+
+  /* Try to allocate space for the fastmap.  */
+  preg->fastmap = re_malloc (char, SBC_MAX);
+  if (BE (preg->fastmap == NULL, 0))
+    return REG_ESPACE;
+
+  syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0;
+
+  /* If REG_NEWLINE is set, newlines are treated differently.  */
+  if (cflags & REG_NEWLINE)
+    { /* REG_NEWLINE implies neither . nor [^...] match newline.  */
+      syntax &= ~RE_DOT_NEWLINE;
+      syntax |= RE_HAT_LISTS_NOT_NEWLINE;
+      /* It also changes the matching behavior.  */
+      preg->newline_anchor = 1;
+    }
+  else
+    preg->newline_anchor = 0;
+  preg->no_sub = !!(cflags & REG_NOSUB);
+  preg->translate = NULL;
+
+  ret = re_compile_internal (preg, pattern, strlen (pattern), syntax);
+
+  /* POSIX doesn't distinguish between an unmatched open-group and an
+     unmatched close-group: both are REG_EPAREN.  */
+  if (ret == REG_ERPAREN)
+    ret = REG_EPAREN;
+
+  /* We have already checked preg->fastmap != NULL.  */
+  if (BE (ret == REG_NOERROR, 1))
+    /* Compute the fastmap now, since regexec cannot modify the pattern
+       buffer.  This function nevers fails in this implementation.  */
+    (void) re_compile_fastmap (preg);
+  else
+    {
+      /* Some error occurred while compiling the expression.  */
+      re_free (preg->fastmap);
+      preg->fastmap = NULL;
+    }
+
+  return (int) ret;
+}
+#ifdef _LIBC
+weak_alias (__regcomp, regcomp)
+#endif
+
+/* Returns a message corresponding to an error code, ERRCODE, returned
+   from either regcomp or regexec.   We don't use PREG here.  */
+
+size_t
+regerror (errcode, preg, errbuf, errbuf_size)
+    int errcode;
+    const regex_t *preg;
+    char *errbuf;
+    size_t errbuf_size;
+{
+  const char *msg;
+  size_t msg_size;
+
+  if (BE (errcode < 0
+         || errcode >= (int) (sizeof (__re_error_msgid_idx)
+                              / sizeof (__re_error_msgid_idx[0])), 0))
+    /* Only error codes returned by the rest of the code should be passed
+       to this routine.  If we are given anything else, or if other regex
+       code generates an invalid error code, then the program has a bug.
+       Dump core so we can fix it.  */
+    abort ();
+
+  msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]);
+
+  msg_size = strlen (msg) + 1; /* Includes the null.  */
+
+  if (BE (errbuf_size != 0, 1))
+    {
+      if (BE (msg_size > errbuf_size, 0))
+       {
+#if defined HAVE_MEMPCPY || defined _LIBC
+         *((char *) __mempcpy (errbuf, msg, errbuf_size - 1)) = '\0';
+#else
+         memcpy (errbuf, msg, errbuf_size - 1);
+         errbuf[errbuf_size - 1] = 0;
+#endif
+       }
+      else
+       memcpy (errbuf, msg, msg_size);
+    }
+
+  return msg_size;
+}
+#ifdef _LIBC
+weak_alias (__regerror, regerror)
+#endif
+
+
+static void
+free_dfa_content (re_dfa_t *dfa)
+{
+  int i, j;
+
+  re_free (dfa->subexps);
+
+  for (i = 0; i < dfa->nodes_len; ++i)
+    {
+      re_token_t *node = dfa->nodes + i;
+#ifdef RE_ENABLE_I18N
+      if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
+       free_charset (node->opr.mbcset);
+      else
+#endif /* RE_ENABLE_I18N */
+       if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
+         re_free (node->opr.sbcset);
+    }
+  re_free (dfa->nexts);
+  for (i = 0; i < dfa->nodes_len; ++i)
+    {
+      if (dfa->eclosures != NULL)
+       re_node_set_free (dfa->eclosures + i);
+      if (dfa->inveclosures != NULL)
+       re_node_set_free (dfa->inveclosures + i);
+      if (dfa->edests != NULL)
+       re_node_set_free (dfa->edests + i);
+    }
+  re_free (dfa->edests);
+  re_free (dfa->eclosures);
+  re_free (dfa->inveclosures);
+  re_free (dfa->nodes);
+
+  for (i = 0; i <= dfa->state_hash_mask; ++i)
+    {
+      struct re_state_table_entry *entry = dfa->state_table + i;
+      for (j = 0; j < entry->num; ++j)
+       {
+         re_dfastate_t *state = entry->array[j];
+         free_state (state);
+       }
+      re_free (entry->array);
+    }
+  re_free (dfa->state_table);
+
+  if (dfa->word_char != NULL)
+    re_free (dfa->word_char);
+#ifdef DEBUG
+  re_free (dfa->re_str);
+#endif
+
+  re_free (dfa);
+}
+
+
+/* Free dynamically allocated space used by PREG.  */
+
+void
+regfree (preg)
+    regex_t *preg;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  if (BE (dfa != NULL, 1))
+    free_dfa_content (dfa);
+
+  re_free (preg->fastmap);
+}
+#ifdef _LIBC
+weak_alias (__regfree, regfree)
+#endif
+\f
+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them unless specifically requested.  */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+
+/* BSD has one and only one pattern buffer.  */
+static struct re_pattern_buffer re_comp_buf;
+
+char *
+# ifdef _LIBC
+/* Make these definitions weak in libc, so POSIX programs can redefine
+   these names if they don't use our functions, and still use
+   regcomp/regexec above without link errors.  */
+weak_function
+# endif
+re_comp (s)
+     const char *s;
+{
+  reg_errcode_t ret;
+  char *fastmap;
+
+  if (!s)
+    {
+      if (!re_comp_buf.buffer)
+       return gettext ("No previous regular expression");
+      return 0;
+    }
+
+  if (re_comp_buf.buffer)
+    {
+      fastmap = re_comp_buf.fastmap;
+      re_comp_buf.fastmap = NULL;
+      __regfree (&re_comp_buf);
+      memset (&re_comp_buf, '\0', sizeof (re_comp_buf));
+      re_comp_buf.fastmap = fastmap;
+    }
+
+  if (re_comp_buf.fastmap == NULL)
+    {
+      re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
+      if (re_comp_buf.fastmap == NULL)
+       return (char *) gettext (__re_error_msgid
+                                + __re_error_msgid_idx[(int) REG_ESPACE]);
+    }
+
+  /* Since `re_exec' always passes NULL for the `regs' argument, we
+     don't need to initialize the pattern buffer fields which affect it.  */
+
+  /* Match anchors at newlines.  */
+  re_comp_buf.newline_anchor = 1;
+
+  ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options);
+
+  if (!ret)
+    return NULL;
+
+  /* Yes, we're discarding `const' here if !HAVE_LIBINTL.  */
+  return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
+}
+
+#ifdef _LIBC
+libc_freeres_fn (free_mem)
+{
+  __regfree (&re_comp_buf);
+}
+#endif
+
+#endif /* _REGEX_RE_COMP */
+\f
+/* Internal entry point.
+   Compile the regular expression PATTERN, whose length is LENGTH.
+   SYNTAX indicate regular expression's syntax.  */
+
+static reg_errcode_t
+re_compile_internal (preg, pattern, length, syntax)
+     regex_t *preg;
+     const char * pattern;
+     int length;
+     reg_syntax_t syntax;
+{
+  reg_errcode_t err = REG_NOERROR;
+  re_dfa_t *dfa;
+  re_string_t regexp;
+
+  /* Initialize the pattern buffer.  */
+  preg->fastmap_accurate = 0;
+  preg->syntax = syntax;
+  preg->not_bol = preg->not_eol = 0;
+  preg->used = 0;
+  preg->re_nsub = 0;
+  preg->can_be_null = 0;
+  preg->regs_allocated = REGS_UNALLOCATED;
+
+  /* Initialize the dfa.  */
+  dfa = (re_dfa_t *) preg->buffer;
+  if (preg->allocated < sizeof (re_dfa_t))
+    {
+      /* If zero allocated, but buffer is non-null, try to realloc
+        enough space.  This loses if buffer's address is bogus, but
+        that is the user's responsibility.  If ->buffer is NULL this
+        is a simple allocation.  */
+      dfa = re_realloc (preg->buffer, re_dfa_t, 1);
+      if (dfa == NULL)
+       return REG_ESPACE;
+      preg->allocated = sizeof (re_dfa_t);
+    }
+  preg->buffer = (unsigned char *) dfa;
+  preg->used = sizeof (re_dfa_t);
+
+  err = init_dfa (dfa, length);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      re_free (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+      return err;
+    }
+#ifdef DEBUG
+  dfa->re_str = re_malloc (char, length + 1);
+  strncpy (dfa->re_str, pattern, length + 1);
+#endif
+
+  err = re_string_construct (&regexp, pattern, length, preg->translate,
+                            syntax & RE_ICASE);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      re_free (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+      return err;
+    }
+
+  /* Parse the regular expression, and build a structure tree.  */
+  preg->re_nsub = 0;
+  dfa->str_tree = parse (&regexp, preg, syntax, &err);
+  if (BE (dfa->str_tree == NULL, 0))
+    goto re_compile_internal_free_return;
+
+  /* Analyze the tree and collect information which is necessary to
+     create the dfa.  */
+  err = analyze (dfa);
+  if (BE (err != REG_NOERROR, 0))
+    goto re_compile_internal_free_return;
+
+  /* Then create the initial state of the dfa.  */
+  err = create_initial_state (dfa);
+
+  /* Release work areas.  */
+  free_workarea_compile (preg);
+  re_string_destruct (&regexp);
+
+  if (BE (err != REG_NOERROR, 0))
+    {
+    re_compile_internal_free_return:
+      free_dfa_content (dfa);
+      preg->buffer = NULL;
+      preg->allocated = 0;
+    }
+
+  return err;
+}
+
+/* Initialize DFA.  We use the length of the regular expression PAT_LEN
+   as the initial length of some arrays.  */
+
+static reg_errcode_t
+init_dfa (dfa, pat_len)
+     re_dfa_t *dfa;
+     int pat_len;
+{
+  int table_size;
+
+  memset (dfa, '\0', sizeof (re_dfa_t));
+
+  dfa->nodes_alloc = pat_len + 1;
+  dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
+
+  dfa->states_alloc = pat_len + 1;
+
+  /*  table_size = 2 ^ ceil(log pat_len) */
+  for (table_size = 1; table_size > 0; table_size <<= 1)
+    if (table_size > pat_len)
+      break;
+
+  dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
+  dfa->state_hash_mask = table_size - 1;
+
+  dfa->subexps_alloc = 1;
+  dfa->subexps = re_malloc (re_subexp_t, dfa->subexps_alloc);
+  dfa->word_char = NULL;
+
+  if (BE (dfa->nodes == NULL || dfa->state_table == NULL
+         || dfa->subexps == NULL, 0))
+    {
+      /* We don't bother to free anything which was allocated.  Very
+        soon the process will go down anyway.  */
+      dfa->subexps = NULL;
+      dfa->state_table = NULL;
+      dfa->nodes = NULL;
+      return REG_ESPACE;
+    }
+  return REG_NOERROR;
+}
+
+/* Initialize WORD_CHAR table, which indicate which character is
+   "word".  In this case "word" means that it is the word construction
+   character used by some operators like "\<", "\>", etc.  */
+
+static reg_errcode_t
+init_word_char (dfa)
+     re_dfa_t *dfa;
+{
+  int i, j, ch;
+  dfa->word_char = (re_bitset_ptr_t) calloc (sizeof (bitset), 1);
+  if (BE (dfa->word_char == NULL, 0))
+    return REG_ESPACE;
+  for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+    for (j = 0; j < UINT_BITS; ++j, ++ch)
+      if (isalnum (ch) || ch == '_')
+       dfa->word_char[i] |= 1 << j;
+  return REG_NOERROR;
+}
+
+/* Free the work area which are only used while compiling.  */
+
+static void
+free_workarea_compile (preg)
+     regex_t *preg;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  free_bin_tree (dfa->str_tree);
+  dfa->str_tree = NULL;
+  re_free (dfa->org_indices);
+  dfa->org_indices = NULL;
+}
+
+/* Create initial states for all contexts.  */
+
+static reg_errcode_t
+create_initial_state (dfa)
+     re_dfa_t *dfa;
+{
+  int first, i;
+  reg_errcode_t err;
+  re_node_set init_nodes;
+
+  /* Initial states have the epsilon closure of the node which is
+     the first node of the regular expression.  */
+  first = dfa->str_tree->first;
+  dfa->init_node = first;
+  err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+
+  /* The back-references which are in initial states can epsilon transit,
+     since in this case all of the subexpressions can be null.
+     Then we add epsilon closures of the nodes which are the next nodes of
+     the back-references.  */
+  if (dfa->nbackref > 0)
+    for (i = 0; i < init_nodes.nelem; ++i)
+      {
+       int node_idx = init_nodes.elems[i];
+       re_token_type_t type = dfa->nodes[node_idx].type;
+
+       int clexp_idx;
+       if (type != OP_BACK_REF)
+         continue;
+       for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx)
+         {
+           re_token_t *clexp_node;
+           clexp_node = dfa->nodes + init_nodes.elems[clexp_idx];
+           if (clexp_node->type == OP_CLOSE_SUBEXP
+               && clexp_node->opr.idx + 1 == dfa->nodes[node_idx].opr.idx)
+             break;
+         }
+       if (clexp_idx == init_nodes.nelem)
+         continue;
+
+       if (type == OP_BACK_REF)
+         {
+           int dest_idx = dfa->edests[node_idx].elems[0];
+           if (!re_node_set_contains (&init_nodes, dest_idx))
+             {
+               re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx);
+               i = 0;
+             }
+         }
+      }
+
+  /* It must be the first time to invoke acquire_state.  */
+  dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0);
+  /* We don't check ERR here, since the initial state must not be NULL.  */
+  if (BE (dfa->init_state == NULL, 0))
+    return err;
+  if (dfa->init_state->has_constraint)
+    {
+      dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes,
+                                                      CONTEXT_WORD);
+      dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes,
+                                                    CONTEXT_NEWLINE);
+      dfa->init_state_begbuf = re_acquire_state_context (&err, dfa,
+                                                        &init_nodes,
+                                                        CONTEXT_NEWLINE
+                                                        | CONTEXT_BEGBUF);
+      if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+             || dfa->init_state_begbuf == NULL, 0))
+       return err;
+    }
+  else
+    dfa->init_state_word = dfa->init_state_nl
+      = dfa->init_state_begbuf = dfa->init_state;
+
+  re_node_set_free (&init_nodes);
+  return REG_NOERROR;
+}
+\f
+/* Analyze the structure tree, and calculate "first", "next", "edest",
+   "eclosure", and "inveclosure".  */
+
+static reg_errcode_t
+analyze (dfa)
+     re_dfa_t *dfa;
+{
+  int i;
+  reg_errcode_t ret;
+
+  /* Allocate arrays.  */
+  dfa->nexts = re_malloc (int, dfa->nodes_alloc);
+  dfa->org_indices = re_malloc (int, dfa->nodes_alloc);
+  dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
+  dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+  dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_alloc);
+  if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL
+         || dfa->eclosures == NULL || dfa->inveclosures == NULL, 0))
+    return REG_ESPACE;
+  /* Initialize them.  */
+  for (i = 0; i < dfa->nodes_len; ++i)
+    {
+      dfa->nexts[i] = -1;
+      re_node_set_init_empty (dfa->edests + i);
+      re_node_set_init_empty (dfa->eclosures + i);
+      re_node_set_init_empty (dfa->inveclosures + i);
+    }
+
+  ret = analyze_tree (dfa, dfa->str_tree);
+  if (BE (ret == REG_NOERROR, 1))
+    {
+      ret = calc_eclosure (dfa);
+      if (ret == REG_NOERROR)
+       calc_inveclosure (dfa);
+    }
+  return ret;
+}
+
+/* Helper functions for analyze.
+   This function calculate "first", "next", and "edest" for the subtree
+   whose root is NODE.  */
+
+static reg_errcode_t
+analyze_tree (dfa, node)
+     re_dfa_t *dfa;
+     bin_tree_t *node;
+{
+  reg_errcode_t ret;
+  if (node->first == -1)
+    calc_first (dfa, node);
+  if (node->next == -1)
+    calc_next (dfa, node);
+  if (node->eclosure.nelem == 0)
+    calc_epsdest (dfa, node);
+  /* Calculate "first" etc. for the left child.  */
+  if (node->left != NULL)
+    {
+      ret = analyze_tree (dfa, node->left);
+      if (BE (ret != REG_NOERROR, 0))
+       return ret;
+    }
+  /* Calculate "first" etc. for the right child.  */
+  if (node->right != NULL)
+    {
+      ret = analyze_tree (dfa, node->right);
+      if (BE (ret != REG_NOERROR, 0))
+       return ret;
+    }
+  return REG_NOERROR;
+}
+
+/* Calculate "first" for the node NODE.  */
+static void
+calc_first (dfa, node)
+     re_dfa_t *dfa;
+     bin_tree_t *node;
+{
+  int idx, type;
+  idx = node->node_idx;
+  type = (node->type == 0) ? dfa->nodes[idx].type : node->type;
+
+  switch (type)
+    {
+#ifdef DEBUG
+    case OP_OPEN_BRACKET:
+    case OP_CLOSE_BRACKET:
+    case OP_OPEN_DUP_NUM:
+    case OP_CLOSE_DUP_NUM:
+    case OP_NON_MATCH_LIST:
+    case OP_OPEN_COLL_ELEM:
+    case OP_CLOSE_COLL_ELEM:
+    case OP_OPEN_EQUIV_CLASS:
+    case OP_CLOSE_EQUIV_CLASS:
+    case OP_OPEN_CHAR_CLASS:
+    case OP_CLOSE_CHAR_CLASS:
+      /* These must not be appeared here.  */
+      assert (0);
+#endif
+    case END_OF_RE:
+    case CHARACTER:
+    case OP_PERIOD:
+    case OP_DUP_ASTERISK:
+    case OP_DUP_QUESTION:
+#ifdef RE_ENABLE_I18N
+    case COMPLEX_BRACKET:
+#endif /* RE_ENABLE_I18N */
+    case SIMPLE_BRACKET:
+    case OP_BACK_REF:
+    case ANCHOR:
+    case OP_OPEN_SUBEXP:
+    case OP_CLOSE_SUBEXP:
+      node->first = idx;
+      break;
+    case OP_DUP_PLUS:
+#ifdef DEBUG
+      assert (node->left != NULL);
+#endif
+      if (node->left->first == -1)
+       calc_first (dfa, node->left);
+      node->first = node->left->first;
+      break;
+    case OP_ALT:
+      node->first = idx;
+      break;
+      /* else fall through */
+    default:
+#ifdef DEBUG
+      assert (node->left != NULL);
+#endif
+      if (node->left->first == -1)
+       calc_first (dfa, node->left);
+      node->first = node->left->first;
+      break;
+    }
+}
+
+/* Calculate "next" for the node NODE.  */
+
+static void
+calc_next (dfa, node)
+     re_dfa_t *dfa;
+     bin_tree_t *node;
+{
+  int idx, type;
+  bin_tree_t *parent = node->parent;
+  if (parent == NULL)
+    {
+      node->next = -1;
+      idx = node->node_idx;
+      if (node->type == 0)
+       dfa->nexts[idx] = node->next;
+      return;
+    }
+
+  idx = parent->node_idx;
+  type = (parent->type == 0) ? dfa->nodes[idx].type : parent->type;
+
+  switch (type)
+    {
+    case OP_DUP_ASTERISK:
+    case OP_DUP_PLUS:
+      node->next = idx;
+      break;
+    case CONCAT:
+      if (parent->left == node)
+       {
+         if (parent->right->first == -1)
+           calc_first (dfa, parent->right);
+         node->next = parent->right->first;
+         break;
+       }
+      /* else fall through */
+    default:
+      if (parent->next == -1)
+       calc_next (dfa, parent);
+      node->next = parent->next;
+      break;
+    }
+  idx = node->node_idx;
+  if (node->type == 0)
+    dfa->nexts[idx] = node->next;
+}
+
+/* Calculate "edest" for the node NODE.  */
+
+static void
+calc_epsdest (dfa, node)
+     re_dfa_t *dfa;
+     bin_tree_t *node;
+{
+  int idx;
+  idx = node->node_idx;
+  if (node->type == 0)
+    {
+      if (dfa->nodes[idx].type == OP_DUP_ASTERISK
+         || dfa->nodes[idx].type == OP_DUP_PLUS
+         || dfa->nodes[idx].type == OP_DUP_QUESTION)
+       {
+         if (node->left->first == -1)
+           calc_first (dfa, node->left);
+         if (node->next == -1)
+           calc_next (dfa, node);
+         re_node_set_init_2 (dfa->edests + idx, node->left->first,
+                             node->next);
+       }
+      else if (dfa->nodes[idx].type == OP_ALT)
+       {
+         int left, right;
+         if (node->left != NULL)
+           {
+             if (node->left->first == -1)
+               calc_first (dfa, node->left);
+             left = node->left->first;
+           }
+         else
+           {
+             if (node->next == -1)
+               calc_next (dfa, node);
+             left = node->next;
+           }
+         if (node->right != NULL)
+           {
+             if (node->right->first == -1)
+               calc_first (dfa, node->right);
+             right = node->right->first;
+           }
+         else
+           {
+             if (node->next == -1)
+               calc_next (dfa, node);
+             right = node->next;
+           }
+         re_node_set_init_2 (dfa->edests + idx, left, right);
+       }
+      else if (dfa->nodes[idx].type == ANCHOR
+              || dfa->nodes[idx].type == OP_OPEN_SUBEXP
+              || dfa->nodes[idx].type == OP_CLOSE_SUBEXP
+              || dfa->nodes[idx].type == OP_BACK_REF)
+       re_node_set_init_1 (dfa->edests + idx, node->next);
+    }
+}
+
+/* Duplicate the epsilon closure of the node ROOT_NODE.
+   Note that duplicated nodes have constraint INIT_CONSTRAINT in addition
+   to their own constraint.  */
+
+static reg_errcode_t
+duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
+                       init_constraint)
+     re_dfa_t *dfa;
+     int top_org_node, top_clone_node, root_node;
+     unsigned int init_constraint;
+{
+  reg_errcode_t err;
+  int org_node, clone_node, ret;
+  unsigned int constraint = init_constraint;
+  for (org_node = top_org_node, clone_node = top_clone_node;;)
+    {
+      int org_dest, clone_dest;
+      if (dfa->nodes[org_node].type == OP_BACK_REF)
+       {
+         /* If the back reference epsilon-transit, its destination must
+            also have the constraint.  Then duplicate the epsilon closure
+            of the destination of the back reference, and store it in
+            edests of the back reference.  */
+         org_dest = dfa->nexts[org_node];
+         re_node_set_empty (dfa->edests + clone_node);
+         err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+         dfa->nexts[clone_node] = dfa->nexts[org_node];
+         ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+         if (BE (ret < 0, 0))
+           return REG_ESPACE;
+       }
+      else if (dfa->edests[org_node].nelem == 0)
+       {
+         /* In case of the node can't epsilon-transit, don't duplicate the
+            destination and store the original destination as the
+            destination of the node.  */
+         dfa->nexts[clone_node] = dfa->nexts[org_node];
+         break;
+       }
+      else if (dfa->edests[org_node].nelem == 1)
+       {
+         /* In case of the node can epsilon-transit, and it has only one
+            destination.  */
+         org_dest = dfa->edests[org_node].elems[0];
+         re_node_set_empty (dfa->edests + clone_node);
+         if (dfa->nodes[org_node].type == ANCHOR)
+           {
+             /* In case of the node has another constraint, append it.  */
+             if (org_node == root_node && clone_node != org_node)
+               {
+                 /* ...but if the node is root_node itself, it means the
+                    epsilon closure have a loop, then tie it to the
+                    destination of the root_node.  */
+                 ret = re_node_set_insert (dfa->edests + clone_node,
+                                           org_dest);
+                 if (BE (ret < 0, 0))
+                   return REG_ESPACE;
+                 break;
+               }
+             constraint |= dfa->nodes[org_node].opr.ctx_type;
+           }
+         err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+         ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+         if (BE (ret < 0, 0))
+           return REG_ESPACE;
+       }
+      else /* dfa->edests[org_node].nelem == 2 */
+       {
+         /* In case of the node can epsilon-transit, and it has two
+            destinations. E.g. '|', '*', '+', '?'.   */
+         org_dest = dfa->edests[org_node].elems[0];
+         re_node_set_empty (dfa->edests + clone_node);
+         /* Search for a duplicated node which satisfies the constraint.  */
+         clone_dest = search_duplicated_node (dfa, org_dest, constraint);
+         if (clone_dest == -1)
+           {
+             /* There are no such a duplicated node, create a new one.  */
+             err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
+             if (BE (err != REG_NOERROR, 0))
+               return err;
+             ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+             if (BE (ret < 0, 0))
+               return REG_ESPACE;
+             err = duplicate_node_closure (dfa, org_dest, clone_dest,
+                                           root_node, constraint);
+             if (BE (err != REG_NOERROR, 0))
+               return err;
+           }
+         else
+           {
+             /* There are a duplicated node which satisfy the constraint,
+                use it to avoid infinite loop.  */
+             ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+             if (BE (ret < 0, 0))
+               return REG_ESPACE;
+           }
+
+         org_dest = dfa->edests[org_node].elems[1];
+         err = duplicate_node (&clone_dest, dfa, org_dest, constraint);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+         ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+         if (BE (ret < 0, 0))
+           return REG_ESPACE;
+       }
+      org_node = org_dest;
+      clone_node = clone_dest;
+    }
+  return REG_NOERROR;
+}
+
+/* Search for a node which is duplicated from the node ORG_NODE, and
+   satisfies the constraint CONSTRAINT.  */
+
+static int
+search_duplicated_node (dfa, org_node, constraint)
+     re_dfa_t *dfa;
+     int org_node;
+     unsigned int constraint;
+{
+  int idx;
+  for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx)
+    {
+      if (org_node == dfa->org_indices[idx]
+         && constraint == dfa->nodes[idx].constraint)
+       return idx; /* Found.  */
+    }
+  return -1; /* Not found.  */
+}
+
+/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT.
+   The new index will be stored in NEW_IDX and return REG_NOERROR if succeeded,
+   otherwise return the error code.  */
+
+static reg_errcode_t
+duplicate_node (new_idx, dfa, org_idx, constraint)
+     re_dfa_t *dfa;
+     int *new_idx, org_idx;
+     unsigned int constraint;
+{
+  re_token_t dup;
+  int dup_idx;
+
+  dup = dfa->nodes[org_idx];
+  dup_idx = re_dfa_add_node (dfa, dup, 1);
+  if (BE (dup_idx == -1, 0))
+    return REG_ESPACE;
+  dfa->nodes[dup_idx].constraint = constraint;
+  if (dfa->nodes[org_idx].type == ANCHOR)
+    dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].opr.ctx_type;
+  dfa->nodes[dup_idx].duplicated = 1;
+  re_node_set_init_empty (dfa->edests + dup_idx);
+  re_node_set_init_empty (dfa->eclosures + dup_idx);
+  re_node_set_init_empty (dfa->inveclosures + dup_idx);
+
+  /* Store the index of the original node.  */
+  dfa->org_indices[dup_idx] = org_idx;
+  *new_idx = dup_idx;
+  return REG_NOERROR;
+}
+
+static void
+calc_inveclosure (dfa)
+     re_dfa_t *dfa;
+{
+  int src, idx, dest;
+  for (src = 0; src < dfa->nodes_len; ++src)
+    {
+      for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
+       {
+         dest = dfa->eclosures[src].elems[idx];
+         re_node_set_insert (dfa->inveclosures + dest, src);
+       }
+    }
+}
+
+/* Calculate "eclosure" for all the node in DFA.  */
+
+static reg_errcode_t
+calc_eclosure (dfa)
+     re_dfa_t *dfa;
+{
+  int node_idx, incomplete;
+#ifdef DEBUG
+  assert (dfa->nodes_len > 0);
+#endif
+  incomplete = 0;
+  /* For each nodes, calculate epsilon closure.  */
+  for (node_idx = 0; ; ++node_idx)
+    {
+      reg_errcode_t err;
+      re_node_set eclosure_elem;
+      if (node_idx == dfa->nodes_len)
+       {
+         if (!incomplete)
+           break;
+         incomplete = 0;
+         node_idx = 0;
+       }
+
+#ifdef DEBUG
+      assert (dfa->eclosures[node_idx].nelem != -1);
+#endif
+      /* If we have already calculated, skip it.  */
+      if (dfa->eclosures[node_idx].nelem != 0)
+       continue;
+      /* Calculate epsilon closure of `node_idx'.  */
+      err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+
+      if (dfa->eclosures[node_idx].nelem == 0)
+       {
+         incomplete = 1;
+         re_node_set_free (&eclosure_elem);
+       }
+    }
+  return REG_NOERROR;
+}
+
+/* Calculate epsilon closure of NODE.  */
+
+static reg_errcode_t
+calc_eclosure_iter (new_set, dfa, node, root)
+     re_node_set *new_set;
+     re_dfa_t *dfa;
+     int node, root;
+{
+  reg_errcode_t err;
+  unsigned int constraint;
+  int i, incomplete;
+  re_node_set eclosure;
+  incomplete = 0;
+  err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+
+  /* This indicates that we are calculating this node now.
+     We reference this value to avoid infinite loop.  */
+  dfa->eclosures[node].nelem = -1;
+
+  constraint = ((dfa->nodes[node].type == ANCHOR)
+               ? dfa->nodes[node].opr.ctx_type : 0);
+  /* If the current node has constraints, duplicate all nodes.
+     Since they must inherit the constraints.  */
+  if (constraint && !dfa->nodes[dfa->edests[node].elems[0]].duplicated)
+    {
+      int org_node, cur_node;
+      org_node = cur_node = node;
+      err = duplicate_node_closure (dfa, node, node, node, constraint);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+    }
+
+  /* Expand each epsilon destination nodes.  */
+  if (IS_EPSILON_NODE(dfa->nodes[node].type))
+    for (i = 0; i < dfa->edests[node].nelem; ++i)
+      {
+       re_node_set eclosure_elem;
+       int edest = dfa->edests[node].elems[i];
+       /* If calculating the epsilon closure of `edest' is in progress,
+          return intermediate result.  */
+       if (dfa->eclosures[edest].nelem == -1)
+         {
+           incomplete = 1;
+           continue;
+         }
+       /* If we haven't calculated the epsilon closure of `edest' yet,
+          calculate now. Otherwise use calculated epsilon closure.  */
+       if (dfa->eclosures[edest].nelem == 0)
+         {
+           err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0);
+           if (BE (err != REG_NOERROR, 0))
+             return err;
+         }
+       else
+         eclosure_elem = dfa->eclosures[edest];
+       /* Merge the epsilon closure of `edest'.  */
+       re_node_set_merge (&eclosure, &eclosure_elem);
+       /* If the epsilon closure of `edest' is incomplete,
+          the epsilon closure of this node is also incomplete.  */
+       if (dfa->eclosures[edest].nelem == 0)
+         {
+           incomplete = 1;
+           re_node_set_free (&eclosure_elem);
+         }
+      }
+
+  /* Epsilon closures include itself.  */
+  re_node_set_insert (&eclosure, node);
+  if (incomplete && !root)
+    dfa->eclosures[node].nelem = 0;
+  else
+    dfa->eclosures[node] = eclosure;
+  *new_set = eclosure;
+  return REG_NOERROR;
+}
+\f
+/* Functions for token which are used in the parser.  */
+
+/* Fetch a token from INPUT.
+   We must not use this function inside bracket expressions.  */
+
+static re_token_t
+fetch_token (input, syntax)
+     re_string_t *input;
+     reg_syntax_t syntax;
+{
+  re_token_t token;
+  int consumed_byte;
+  consumed_byte = peek_token (&token, input, syntax);
+  re_string_skip_bytes (input, consumed_byte);
+  return token;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+   We must not use this function inside bracket expressions.  */
+
+static int
+peek_token (token, input, syntax)
+     re_token_t *token;
+     re_string_t *input;
+     reg_syntax_t syntax;
+{
+  unsigned char c;
+
+  if (re_string_eoi (input))
+    {
+      token->type = END_OF_RE;
+      return 0;
+    }
+
+  c = re_string_peek_byte (input, 0);
+  token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+  token->mb_partial = 0;
+  if (MB_CUR_MAX > 1 &&
+      !re_string_first_byte (input, re_string_cur_idx (input)))
+    {
+      token->type = CHARACTER;
+      token->mb_partial = 1;
+      return 1;
+    }
+#endif
+  if (c == '\\')
+    {
+      unsigned char c2;
+      if (re_string_cur_idx (input) + 1 >= re_string_length (input))
+       {
+         token->type = BACK_SLASH;
+         return 1;
+       }
+
+      c2 = re_string_peek_byte_case (input, 1);
+      token->opr.c = c2;
+      token->type = CHARACTER;
+      switch (c2)
+       {
+       case '|':
+         if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR))
+           token->type = OP_ALT;
+         break;
+       case '1': case '2': case '3': case '4': case '5':
+       case '6': case '7': case '8': case '9':
+         if (!(syntax & RE_NO_BK_REFS))
+           {
+             token->type = OP_BACK_REF;
+             token->opr.idx = c2 - '0';
+           }
+         break;
+       case '<':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.idx = WORD_FIRST;
+           }
+         break;
+       case '>':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.idx = WORD_LAST;
+           }
+         break;
+       case 'b':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.idx = WORD_DELIM;
+           }
+         break;
+       case 'B':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.idx = INSIDE_WORD;
+           }
+         break;
+       case 'w':
+         if (!(syntax & RE_NO_GNU_OPS))
+           token->type = OP_WORD;
+         break;
+       case 'W':
+         if (!(syntax & RE_NO_GNU_OPS))
+           token->type = OP_NOTWORD;
+         break;
+       case '`':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.idx = BUF_FIRST;
+           }
+         break;
+       case '\'':
+         if (!(syntax & RE_NO_GNU_OPS))
+           {
+             token->type = ANCHOR;
+             token->opr.idx = BUF_LAST;
+           }
+         break;
+       case '(':
+         if (!(syntax & RE_NO_BK_PARENS))
+           token->type = OP_OPEN_SUBEXP;
+         break;
+       case ')':
+         if (!(syntax & RE_NO_BK_PARENS))
+           token->type = OP_CLOSE_SUBEXP;
+         break;
+       case '+':
+         if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+           token->type = OP_DUP_PLUS;
+         break;
+       case '?':
+         if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
+           token->type = OP_DUP_QUESTION;
+         break;
+       case '{':
+         if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+           token->type = OP_OPEN_DUP_NUM;
+         break;
+       case '}':
+         if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
+           token->type = OP_CLOSE_DUP_NUM;
+         break;
+       default:
+         break;
+       }
+      return 2;
+    }
+
+  token->type = CHARACTER;
+  switch (c)
+    {
+    case '\n':
+      if (syntax & RE_NEWLINE_ALT)
+       token->type = OP_ALT;
+      break;
+    case '|':
+      if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR))
+       token->type = OP_ALT;
+      break;
+    case '*':
+      token->type = OP_DUP_ASTERISK;
+      break;
+    case '+':
+      if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+       token->type = OP_DUP_PLUS;
+      break;
+    case '?':
+      if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
+       token->type = OP_DUP_QUESTION;
+      break;
+    case '{':
+      if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+       token->type = OP_OPEN_DUP_NUM;
+      break;
+    case '}':
+      if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
+       token->type = OP_CLOSE_DUP_NUM;
+      break;
+    case '(':
+      if (syntax & RE_NO_BK_PARENS)
+       token->type = OP_OPEN_SUBEXP;
+      break;
+    case ')':
+      if (syntax & RE_NO_BK_PARENS)
+       token->type = OP_CLOSE_SUBEXP;
+      break;
+    case '[':
+      token->type = OP_OPEN_BRACKET;
+      break;
+    case '.':
+      token->type = OP_PERIOD;
+      break;
+    case '^':
+      if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+         re_string_cur_idx (input) != 0)
+       {
+         char prev = re_string_peek_byte (input, -1);
+         if (prev != '|' && prev != '(' &&
+             (!(syntax & RE_NEWLINE_ALT) || prev != '\n'))
+           break;
+       }
+      token->type = ANCHOR;
+      token->opr.idx = LINE_FIRST;
+      break;
+    case '$':
+      if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+         re_string_cur_idx (input) + 1 != re_string_length (input))
+       {
+         re_token_t next;
+         re_string_skip_bytes (input, 1);
+         peek_token (&next, input, syntax);
+         re_string_skip_bytes (input, -1);
+         if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP)
+           break;
+       }
+      token->type = ANCHOR;
+      token->opr.idx = LINE_LAST;
+      break;
+    default:
+      break;
+    }
+  return 1;
+}
+
+/* Peek a token from INPUT, and return the length of the token.
+   We must not use this function out of bracket expressions.  */
+
+static int
+peek_token_bracket (token, input, syntax)
+     re_token_t *token;
+     re_string_t *input;
+     reg_syntax_t syntax;
+{
+  unsigned char c;
+  if (re_string_eoi (input))
+    {
+      token->type = END_OF_RE;
+      return 0;
+    }
+  c = re_string_peek_byte (input, 0);
+  token->opr.c = c;
+
+#ifdef RE_ENABLE_I18N
+  if (MB_CUR_MAX > 1 &&
+      !re_string_first_byte (input, re_string_cur_idx (input)))
+    {
+      token->type = CHARACTER;
+      return 1;
+    }
+#endif /* RE_ENABLE_I18N */
+
+  if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS))
+    {
+      /* In this case, '\' escape a character.  */
+      unsigned char c2;
+      re_string_skip_bytes (input, 1);
+      c2 = re_string_peek_byte (input, 0);
+      token->opr.c = c2;
+      token->type = CHARACTER;
+      return 1;
+    }
+  if (c == '[') /* '[' is a special char in a bracket exps.  */
+    {
+      unsigned char c2;
+      int token_len;
+      c2 = re_string_peek_byte (input, 1);
+      token->opr.c = c2;
+      token_len = 2;
+      switch (c2)
+       {
+       case '.':
+         token->type = OP_OPEN_COLL_ELEM;
+         break;
+       case '=':
+         token->type = OP_OPEN_EQUIV_CLASS;
+         break;
+       case ':':
+         if (syntax & RE_CHAR_CLASSES)
+           {
+             token->type = OP_OPEN_CHAR_CLASS;
+             break;
+           }
+         /* else fall through.  */
+       default:
+         token->type = CHARACTER;
+         token->opr.c = c;
+         token_len = 1;
+         break;
+       }
+      return token_len;
+    }
+  switch (c)
+    {
+    case '-':
+      token->type = OP_CHARSET_RANGE;
+      break;
+    case ']':
+      token->type = OP_CLOSE_BRACKET;
+      break;
+    case '^':
+      token->type = OP_NON_MATCH_LIST;
+      break;
+    default:
+      token->type = CHARACTER;
+    }
+  return 1;
+}
+\f
+/* Functions for parser.  */
+
+/* Entry point of the parser.
+   Parse the regular expression REGEXP and return the structure tree.
+   If an error is occured, ERR is set by error code, and return NULL.
+   This function build the following tree, from regular expression <reg_exp>:
+          CAT
+          / \
+         /   \
+   <reg_exp>  EOR
+
+   CAT means concatenation.
+   EOR means end of regular expression.  */
+
+static bin_tree_t *
+parse (regexp, preg, syntax, err)
+     re_string_t *regexp;
+     regex_t *preg;
+     reg_syntax_t syntax;
+     reg_errcode_t *err;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree, *eor, *root;
+  re_token_t current_token;
+  int new_idx;
+  current_token = fetch_token (regexp, syntax);
+  tree = parse_reg_exp (regexp, preg, &current_token, syntax, 0, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+  new_idx = re_dfa_add_node (dfa, current_token, 0);
+  eor = create_tree (NULL, NULL, 0, new_idx);
+  if (tree != NULL)
+    root = create_tree (tree, eor, CONCAT, 0);
+  else
+    root = eor;
+  if (BE (new_idx == -1 || eor == NULL || root == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+  return root;
+}
+
+/* This function build the following tree, from regular expression
+   <branch1>|<branch2>:
+          ALT
+          / \
+         /   \
+   <branch1> <branch2>
+
+   ALT means alternative, which represents the operator `|'.  */
+
+static bin_tree_t *
+parse_reg_exp (regexp, preg, token, syntax, nest, err)
+     re_string_t *regexp;
+     regex_t *preg;
+     re_token_t *token;
+     reg_syntax_t syntax;
+     int nest;
+     reg_errcode_t *err;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree, *branch = NULL;
+  int new_idx;
+  tree = parse_branch (regexp, preg, token, syntax, nest, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+
+  while (token->type == OP_ALT)
+    {
+      re_token_t alt_token = *token;
+      new_idx = re_dfa_add_node (dfa, alt_token, 0);
+      *token = fetch_token (regexp, syntax);
+      if (token->type != OP_ALT && token->type != END_OF_RE
+         && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+       {
+         branch = parse_branch (regexp, preg, token, syntax, nest, err);
+         if (BE (*err != REG_NOERROR && branch == NULL, 0))
+           {
+             free_bin_tree (tree);
+             return NULL;
+           }
+       }
+      else
+       branch = NULL;
+      tree = create_tree (tree, branch, 0, new_idx);
+      if (BE (new_idx == -1 || tree == NULL, 0))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+      dfa->has_plural_match = 1;
+    }
+  return tree;
+}
+
+/* This function build the following tree, from regular expression
+   <exp1><exp2>:
+       CAT
+       / \
+       /   \
+   <exp1> <exp2>
+
+   CAT means concatenation.  */
+
+static bin_tree_t *
+parse_branch (regexp, preg, token, syntax, nest, err)
+     re_string_t *regexp;
+     regex_t *preg;
+     re_token_t *token;
+     reg_syntax_t syntax;
+     int nest;
+     reg_errcode_t *err;
+{
+  bin_tree_t *tree, *exp;
+  tree = parse_expression (regexp, preg, token, syntax, nest, err);
+  if (BE (*err != REG_NOERROR && tree == NULL, 0))
+    return NULL;
+
+  while (token->type != OP_ALT && token->type != END_OF_RE
+        && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
+    {
+      exp = parse_expression (regexp, preg, token, syntax, nest, err);
+      if (BE (*err != REG_NOERROR && exp == NULL, 0))
+       {
+         free_bin_tree (tree);
+         return NULL;
+       }
+      if (tree != NULL && exp != NULL)
+       {
+         tree = create_tree (tree, exp, CONCAT, 0);
+         if (tree == NULL)
+           {
+             *err = REG_ESPACE;
+             return NULL;
+           }
+       }
+      else if (tree == NULL)
+       tree = exp;
+      /* Otherwise exp == NULL, we don't need to create new tree.  */
+    }
+  return tree;
+}
+
+/* This function build the following tree, from regular expression a*:
+        *
+        |
+        a
+*/
+
+static bin_tree_t *
+parse_expression (regexp, preg, token, syntax, nest, err)
+     re_string_t *regexp;
+     regex_t *preg;
+     re_token_t *token;
+     reg_syntax_t syntax;
+     int nest;
+     reg_errcode_t *err;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree;
+  int new_idx;
+  switch (token->type)
+    {
+    case CHARACTER:
+      new_idx = re_dfa_add_node (dfa, *token, 0);
+      tree = create_tree (NULL, NULL, 0, new_idx);
+      if (BE (new_idx == -1 || tree == NULL, 0))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+#ifdef RE_ENABLE_I18N
+      if (MB_CUR_MAX > 1)
+       {
+         while (!re_string_eoi (regexp)
+                && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
+           {
+             bin_tree_t *mbc_remain;
+             *token = fetch_token (regexp, syntax);
+             new_idx = re_dfa_add_node (dfa, *token, 0);
+             mbc_remain = create_tree (NULL, NULL, 0, new_idx);
+             tree = create_tree (tree, mbc_remain, CONCAT, 0);
+             if (BE (new_idx == -1 || mbc_remain == NULL || tree == NULL, 0))
+               {
+                 *err = REG_ESPACE;
+                 return NULL;
+               }
+           }
+       }
+#endif
+      break;
+    case OP_OPEN_SUBEXP:
+      tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+       return NULL;
+      break;
+    case OP_OPEN_BRACKET:
+      tree = parse_bracket_exp (regexp, dfa, token, syntax, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+       return NULL;
+      break;
+    case OP_BACK_REF:
+      if (BE (preg->re_nsub < token->opr.idx
+             || dfa->subexps[token->opr.idx - 1].end == -1, 0))
+       {
+         *err = REG_ESUBREG;
+         return NULL;
+       }
+      dfa->used_bkref_map |= 1 << (token->opr.idx - 1);
+      new_idx = re_dfa_add_node (dfa, *token, 0);
+      tree = create_tree (NULL, NULL, 0, new_idx);
+      if (BE (new_idx == -1 || tree == NULL, 0))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+      ++dfa->nbackref;
+      dfa->has_mb_node = 1;
+      break;
+    case OP_DUP_ASTERISK:
+    case OP_DUP_PLUS:
+    case OP_DUP_QUESTION:
+    case OP_OPEN_DUP_NUM:
+      if (syntax & RE_CONTEXT_INVALID_OPS)
+       {
+         *err = REG_BADRPT;
+         return NULL;
+       }
+      else if (syntax & RE_CONTEXT_INDEP_OPS)
+       {
+         *token = fetch_token (regexp, syntax);
+         return parse_expression (regexp, preg, token, syntax, nest, err);
+       }
+      /* else fall through  */
+    case OP_CLOSE_SUBEXP:
+      if ((token->type == OP_CLOSE_SUBEXP) &&
+         !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
+       {
+         *err = REG_ERPAREN;
+         return NULL;
+       }
+      /* else fall through  */
+    case OP_CLOSE_DUP_NUM:
+      /* We treat it as a normal character.  */
+
+      /* Then we can these characters as normal characters.  */
+      token->type = CHARACTER;
+      new_idx = re_dfa_add_node (dfa, *token, 0);
+      tree = create_tree (NULL, NULL, 0, new_idx);
+      if (BE (new_idx == -1 || tree == NULL, 0))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+      break;
+    case ANCHOR:
+      if (dfa->word_char == NULL)
+       {
+         *err = init_word_char (dfa);
+         if (BE (*err != REG_NOERROR, 0))
+           return NULL;
+       }
+      if (token->opr.ctx_type == WORD_DELIM)
+       {
+         bin_tree_t *tree_first, *tree_last;
+         int idx_first, idx_last;
+         token->opr.ctx_type = WORD_FIRST;
+         idx_first = re_dfa_add_node (dfa, *token, 0);
+         tree_first = create_tree (NULL, NULL, 0, idx_first);
+         token->opr.ctx_type = WORD_LAST;
+         idx_last = re_dfa_add_node (dfa, *token, 0);
+         tree_last = create_tree (NULL, NULL, 0, idx_last);
+         token->type = OP_ALT;
+         new_idx = re_dfa_add_node (dfa, *token, 0);
+         tree = create_tree (tree_first, tree_last, 0, new_idx);
+         if (BE (idx_first == -1 || idx_last == -1 || new_idx == -1
+                 || tree_first == NULL || tree_last == NULL
+                 || tree == NULL, 0))
+           {
+             *err = REG_ESPACE;
+             return NULL;
+           }
+       }
+      else
+       {
+         new_idx = re_dfa_add_node (dfa, *token, 0);
+         tree = create_tree (NULL, NULL, 0, new_idx);
+         if (BE (new_idx == -1 || tree == NULL, 0))
+           {
+             *err = REG_ESPACE;
+             return NULL;
+           }
+       }
+      /* We must return here, since ANCHORs can't be followed
+        by repetition operators.
+        eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
+            it must not be "<ANCHOR(^)><REPEAT(*)>".  */
+      *token = fetch_token (regexp, syntax);
+      return tree;
+    case OP_PERIOD:
+      new_idx = re_dfa_add_node (dfa, *token, 0);
+      tree = create_tree (NULL, NULL, 0, new_idx);
+      if (BE (new_idx == -1 || tree == NULL, 0))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+      if (MB_CUR_MAX > 1)
+       dfa->has_mb_node = 1;
+      break;
+    case OP_WORD:
+      tree = build_word_op (dfa, 0, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+       return NULL;
+      break;
+    case OP_NOTWORD:
+      tree = build_word_op (dfa, 1, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+       return NULL;
+      break;
+    case OP_ALT:
+    case END_OF_RE:
+      return NULL;
+    case BACK_SLASH:
+      *err = REG_EESCAPE;
+      return NULL;
+    default:
+      /* Must not happen?  */
+#ifdef DEBUG
+      assert (0);
+#endif
+      return NULL;
+    }
+  *token = fetch_token (regexp, syntax);
+
+  while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
+        || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
+    {
+      tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+       return NULL;
+      dfa->has_plural_match = 1;
+    }
+
+  return tree;
+}
+
+/* This function build the following tree, from regular expression
+   (<reg_exp>):
+        SUBEXP
+           |
+       <reg_exp>
+*/
+
+static bin_tree_t *
+parse_sub_exp (regexp, preg, token, syntax, nest, err)
+     re_string_t *regexp;
+     regex_t *preg;
+     re_token_t *token;
+     reg_syntax_t syntax;
+     int nest;
+     reg_errcode_t *err;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  bin_tree_t *tree, *left_par, *right_par;
+  size_t cur_nsub;
+  int new_idx;
+  cur_nsub = preg->re_nsub++;
+  if (dfa->subexps_alloc < preg->re_nsub)
+    {
+      re_subexp_t *new_array;
+      dfa->subexps_alloc *= 2;
+      new_array = re_realloc (dfa->subexps, re_subexp_t, dfa->subexps_alloc);
+      if (BE (new_array == NULL, 0))
+       {
+         dfa->subexps_alloc /= 2;
+         *err = REG_ESPACE;
+         return NULL;
+       }
+      dfa->subexps = new_array;
+    }
+  dfa->subexps[cur_nsub].start = dfa->nodes_len;
+  dfa->subexps[cur_nsub].end = -1;
+
+  new_idx = re_dfa_add_node (dfa, *token, 0);
+  left_par = create_tree (NULL, NULL, 0, new_idx);
+  if (BE (new_idx == -1 || left_par == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+  dfa->nodes[new_idx].opr.idx = cur_nsub;
+  *token = fetch_token (regexp, syntax);
+
+  /* The subexpression may be a null string.  */
+  if (token->type == OP_CLOSE_SUBEXP)
+    tree = NULL;
+  else
+    {
+      tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
+      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+       return NULL;
+    }
+  if (BE (token->type != OP_CLOSE_SUBEXP, 0))
+    {
+      free_bin_tree (tree);
+      *err = REG_BADPAT;
+      return NULL;
+    }
+  new_idx = re_dfa_add_node (dfa, *token, 0);
+  dfa->subexps[cur_nsub].end = dfa->nodes_len;
+  right_par = create_tree (NULL, NULL, 0, new_idx);
+  tree = ((tree == NULL) ? right_par
+         : create_tree (tree, right_par, CONCAT, 0));
+  tree = create_tree (left_par, tree, CONCAT, 0);
+  if (BE (new_idx == -1 || right_par == NULL || tree == NULL, 0))
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+  dfa->nodes[new_idx].opr.idx = cur_nsub;
+
+  return tree;
+}
+
+/* This function parse repetition operators like "*", "+", "{1,3}" etc.  */
+
+static bin_tree_t *
+parse_dup_op (dup_elem, regexp, dfa, token, syntax, err)
+     bin_tree_t *dup_elem;
+     re_string_t *regexp;
+     re_dfa_t *dfa;
+     re_token_t *token;
+     reg_syntax_t syntax;
+     reg_errcode_t *err;
+{
+  re_token_t dup_token;
+  bin_tree_t *tree = dup_elem, *work_tree;
+  int new_idx, start_idx = re_string_cur_idx (regexp);
+  re_token_t start_token = *token;
+  if (token->type == OP_OPEN_DUP_NUM)
+    {
+      int i;
+      int end = 0;
+      int start = fetch_number (regexp, token, syntax);
+      bin_tree_t *elem;
+      if (start == -1)
+       {
+         if (token->type == CHARACTER && token->opr.c == ',')
+           start = 0; /* We treat "{,m}" as "{0,m}".  */
+         else
+           {
+             *err = REG_BADBR; /* <re>{} is invalid.  */
+             return NULL;
+           }
+       }
+      if (BE (start != -2, 1))
+       {
+         /* We treat "{n}" as "{n,n}".  */
+         end = ((token->type == OP_CLOSE_DUP_NUM) ? start
+                : ((token->type == CHARACTER && token->opr.c == ',')
+                   ? fetch_number (regexp, token, syntax) : -2));
+       }
+      if (BE (start == -2 || end == -2, 0))
+       {
+         /* Invalid sequence.  */
+         if (token->type == OP_CLOSE_DUP_NUM)
+           goto parse_dup_op_invalid_interval;
+         else
+           goto parse_dup_op_ebrace;
+       }
+      if (BE (start == 0 && end == 0, 0))
+       {
+         /* We treat "<re>{0}" and "<re>{0,0}" as null string.  */
+         *token = fetch_token (regexp, syntax);
+         free_bin_tree (dup_elem);
+         return NULL;
+       }
+
+      /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}".  */
+      elem = tree;
+      for (i = 0; i < start; ++i)
+       if (i != 0)
+         {
+           work_tree = duplicate_tree (elem, dfa);
+           tree = create_tree (tree, work_tree, CONCAT, 0);
+           if (BE (work_tree == NULL || tree == NULL, 0))
+             goto parse_dup_op_espace;
+         }
+
+      if (end == -1)
+       {
+         /* We treat "<re>{0,}" as "<re>*".  */
+         dup_token.type = OP_DUP_ASTERISK;
+         if (start > 0)
+           {
+             elem = duplicate_tree (elem, dfa);
+             new_idx = re_dfa_add_node (dfa, dup_token, 0);
+             work_tree = create_tree (elem, NULL, 0, new_idx);
+             tree = create_tree (tree, work_tree, CONCAT, 0);
+             if (BE (elem == NULL || new_idx == -1 || work_tree == NULL
+                     || tree == NULL, 0))
+               goto parse_dup_op_espace;
+           }
+         else
+           {
+             new_idx = re_dfa_add_node (dfa, dup_token, 0);
+             tree = create_tree (elem, NULL, 0, new_idx);
+             if (BE (new_idx == -1 || tree == NULL, 0))
+               goto parse_dup_op_espace;
+           }
+       }
+      else if (end - start > 0)
+       {
+         /* Then extract "<re>{0,m}" to "<re>?<re>?...<re>?".  */
+         dup_token.type = OP_DUP_QUESTION;
+         if (start > 0)
+           {
+             elem = duplicate_tree (elem, dfa);
+             new_idx = re_dfa_add_node (dfa, dup_token, 0);
+             elem = create_tree (elem, NULL, 0, new_idx);
+             tree = create_tree (tree, elem, CONCAT, 0);
+             if (BE (elem == NULL || new_idx == -1 || tree == NULL, 0))
+               goto parse_dup_op_espace;
+           }
+         else
+           {
+             new_idx = re_dfa_add_node (dfa, dup_token, 0);
+             tree = elem = create_tree (elem, NULL, 0, new_idx);
+             if (BE (new_idx == -1 || tree == NULL, 0))
+               goto parse_dup_op_espace;
+           }
+         for (i = 1; i < end - start; ++i)
+           {
+             work_tree = duplicate_tree (elem, dfa);
+             tree = create_tree (tree, work_tree, CONCAT, 0);
+             if (BE (work_tree == NULL || tree == NULL, 0))
+               {
+                 *err = REG_ESPACE;
+                 return NULL;
+               }
+           }
+       }
+    }
+  else
+    {
+      new_idx = re_dfa_add_node (dfa, *token, 0);
+      tree = create_tree (tree, NULL, 0, new_idx);
+      if (BE (new_idx == -1 || tree == NULL, 0))
+       {
+         *err = REG_ESPACE;
+         return NULL;
+       }
+    }
+  *token = fetch_token (regexp, syntax);
+  return tree;
+
+ parse_dup_op_espace:
+  free_bin_tree (tree);
+  *err = REG_ESPACE;
+  return NULL;
+
+ parse_dup_op_ebrace:
+  if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
+    {
+      *err = REG_EBRACE;
+      return NULL;
+    }
+  goto parse_dup_op_rollback;
+ parse_dup_op_invalid_interval:
+  if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
+    {
+      *err = REG_BADBR;
+      return NULL;
+    }
+ parse_dup_op_rollback:
+  re_string_set_index (regexp, start_idx);
+  *token = start_token;
+  token->type = CHARACTER;
+  return dup_elem;
+}
+
+/* Size of the names for collating symbol/equivalence_class/character_class.
+   I'm not sure, but maybe enough.  */
+#define BRACKET_NAME_BUF_SIZE 32
+
+#ifndef _LIBC
+  /* Local function for parse_bracket_exp only used in case of NOT _LIBC.
+     Build the range expression which starts from START_ELEM, and ends
+     at END_ELEM.  The result are written to MBCSET and SBCSET.
+     RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+     mbcset->range_ends, is a pointer argument sinse we may
+     update it.  */
+
+static reg_errcode_t
+# ifdef RE_ENABLE_I18N
+build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
+     re_charset_t *mbcset;
+     int *range_alloc;
+# else /* not RE_ENABLE_I18N */
+build_range_exp (sbcset, start_elem, end_elem)
+# endif /* not RE_ENABLE_I18N */
+     re_bitset_ptr_t sbcset;
+     bracket_elem_t *start_elem, *end_elem;
+{
+  unsigned int start_ch, end_ch;
+  /* Equivalence Classes and Character Classes can't be a range start/end.  */
+  if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+         || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+         0))
+    return REG_ERANGE;
+
+  /* We can handle no multi character collating elements without libc
+     support.  */
+  if (BE ((start_elem->type == COLL_SYM
+          && strlen ((char *) start_elem->opr.name) > 1)
+         || (end_elem->type == COLL_SYM
+             && strlen ((char *) end_elem->opr.name) > 1), 0))
+    return REG_ECOLLATE;
+
+# ifdef RE_ENABLE_I18N
+  {
+    wchar_t wc, start_wc, end_wc;
+    wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+
+    start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
+               : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+                  : 0));
+    end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch
+             : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+                : 0));
+    start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
+               ? __btowc (start_ch) : start_elem->opr.wch);
+    end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
+             ? __btowc (end_ch) : end_elem->opr.wch);
+    cmp_buf[0] = start_wc;
+    cmp_buf[4] = end_wc;
+    if (wcscoll (cmp_buf, cmp_buf + 4) > 0)
+      return REG_ERANGE;
+
+    /* Check the space of the arrays.  */
+    if (*range_alloc == mbcset->nranges)
+      {
+       /* There are not enough space, need realloc.  */
+       wchar_t *new_array_start, *new_array_end;
+       int new_nranges;
+
+       /* +1 in case of mbcset->nranges is 0.  */
+       new_nranges = 2 * mbcset->nranges + 1;
+       /* Use realloc since mbcset->range_starts and mbcset->range_ends
+          are NULL if *range_alloc == 0.  */
+       new_array_start = re_realloc (mbcset->range_starts, wchar_t,
+                                     new_nranges);
+       new_array_end = re_realloc (mbcset->range_ends, wchar_t,
+                                   new_nranges);
+
+       if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+         return REG_ESPACE;
+
+       mbcset->range_starts = new_array_start;
+       mbcset->range_ends = new_array_end;
+       *range_alloc = new_nranges;
+      }
+
+    mbcset->range_starts[mbcset->nranges] = start_wc;
+    mbcset->range_ends[mbcset->nranges++] = end_wc;
+
+    /* Build the table for single byte characters.  */
+    for (wc = 0; wc <= SBC_MAX; ++wc)
+      {
+       cmp_buf[2] = wc;
+       if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+           && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+         bitset_set (sbcset, wc);
+      }
+  }
+# else /* not RE_ENABLE_I18N */
+  {
+    unsigned int ch;
+    start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch
+               : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
+                  : 0));
+    end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch
+             : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
+                : 0));
+    if (start_ch > end_ch)
+      return REG_ERANGE;
+    /* Build the table for single byte characters.  */
+    for (ch = 0; ch <= SBC_MAX; ++ch)
+      if (start_ch <= ch  && ch <= end_ch)
+       bitset_set (sbcset, ch);
+  }
+# endif /* not RE_ENABLE_I18N */
+  return REG_NOERROR;
+}
+#endif /* not _LIBC */
+
+#ifndef _LIBC
+/* Helper function for parse_bracket_exp only used in case of NOT _LIBC..
+   Build the collating element which is represented by NAME.
+   The result are written to MBCSET and SBCSET.
+   COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+   pointer argument since we may update it.  */
+
+static reg_errcode_t
+# ifdef RE_ENABLE_I18N
+build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
+     re_charset_t *mbcset;
+     int *coll_sym_alloc;
+# else /* not RE_ENABLE_I18N */
+build_collating_symbol (sbcset, name)
+# endif /* not RE_ENABLE_I18N */
+     re_bitset_ptr_t sbcset;
+     const unsigned char *name;
+{
+  size_t name_len = strlen ((const char *) name);
+  if (BE (name_len != 1, 0))
+    return REG_ECOLLATE;
+  else
+    {
+      bitset_set (sbcset, name[0]);
+      return REG_NOERROR;
+    }
+}
+#endif /* not _LIBC */
+
+/* This function parse bracket expression like "[abc]", "[a-c]",
+   "[[.a-a.]]" etc.  */
+
+static bin_tree_t *
+parse_bracket_exp (regexp, dfa, token, syntax, err)
+     re_string_t *regexp;
+     re_dfa_t *dfa;
+     re_token_t *token;
+     reg_syntax_t syntax;
+     reg_errcode_t *err;
+{
+#ifdef _LIBC
+  const unsigned char *collseqmb;
+  const char *collseqwc;
+  uint32_t nrules;
+  int32_t table_size;
+  const int32_t *symb_table;
+  const unsigned char *extra;
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Seek the collating symbol entry correspondings to NAME.
+     Return the index of the symbol in the SYMB_TABLE.  */
+
+  static inline int32_t
+  seek_collating_symbol_entry (name, name_len)
+        const unsigned char *name;
+        size_t name_len;
+    {
+      int32_t hash = elem_hash ((const char *) name, name_len);
+      int32_t elem = hash % table_size;
+      int32_t second = hash % (table_size - 2);
+      while (symb_table[2 * elem] != 0)
+       {
+         /* First compare the hashing value.  */
+         if (symb_table[2 * elem] == hash
+             /* Compare the length of the name.  */
+             && name_len == extra[symb_table[2 * elem + 1]]
+             /* Compare the name.  */
+             && memcmp (name, &extra[symb_table[2 * elem + 1] + 1],
+                        name_len) == 0)
+           {
+             /* Yep, this is the entry.  */
+             break;
+           }
+
+         /* Next entry.  */
+         elem += second;
+       }
+      return elem;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Look up the collation sequence value of BR_ELEM.
+     Return the value if succeeded, UINT_MAX otherwise.  */
+
+  static inline unsigned int
+  lookup_collation_sequence_value (br_elem)
+        bracket_elem_t *br_elem;
+    {
+      if (br_elem->type == SB_CHAR)
+       {
+         /*
+         if (MB_CUR_MAX == 1)
+         */
+         if (nrules == 0)
+           return collseqmb[br_elem->opr.ch];
+         else
+           {
+             wint_t wc = __btowc (br_elem->opr.ch);
+             return collseq_table_lookup (collseqwc, wc);
+           }
+       }
+      else if (br_elem->type == MB_CHAR)
+       {
+         return collseq_table_lookup (collseqwc, br_elem->opr.wch);
+       }
+      else if (br_elem->type == COLL_SYM)
+       {
+         size_t sym_name_len = strlen ((char *) br_elem->opr.name);
+         if (nrules != 0)
+           {
+             int32_t elem, idx;
+             elem = seek_collating_symbol_entry (br_elem->opr.name,
+                                                 sym_name_len);
+             if (symb_table[2 * elem] != 0)
+               {
+                 /* We found the entry.  */
+                 idx = symb_table[2 * elem + 1];
+                 /* Skip the name of collating element name.  */
+                 idx += 1 + extra[idx];
+                 /* Skip the byte sequence of the collating element.  */
+                 idx += 1 + extra[idx];
+                 /* Adjust for the alignment.  */
+                 idx = (idx + 3) & ~3;
+                 /* Skip the multibyte collation sequence value.  */
+                 idx += sizeof (unsigned int);
+                 /* Skip the wide char sequence of the collating element.  */
+                 idx += sizeof (unsigned int) *
+                   (1 + *(unsigned int *) (extra + idx));
+                 /* Return the collation sequence value.  */
+                 return *(unsigned int *) (extra + idx);
+               }
+             else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
+               {
+                 /* No valid character.  Match it as a single byte
+                    character.  */
+                 return collseqmb[br_elem->opr.name[0]];
+               }
+           }
+         else if (sym_name_len == 1)
+           return collseqmb[br_elem->opr.name[0]];
+       }
+      return UINT_MAX;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Build the range expression which starts from START_ELEM, and ends
+     at END_ELEM.  The result are written to MBCSET and SBCSET.
+     RANGE_ALLOC is the allocated size of mbcset->range_starts, and
+     mbcset->range_ends, is a pointer argument sinse we may
+     update it.  */
+
+  static inline reg_errcode_t
+# ifdef RE_ENABLE_I18N
+  build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
+        re_charset_t *mbcset;
+        int *range_alloc;
+# else /* not RE_ENABLE_I18N */
+  build_range_exp (sbcset, start_elem, end_elem)
+# endif /* not RE_ENABLE_I18N */
+        re_bitset_ptr_t sbcset;
+        bracket_elem_t *start_elem, *end_elem;
+    {
+      unsigned int ch;
+      uint32_t start_collseq;
+      uint32_t end_collseq;
+
+# ifdef RE_ENABLE_I18N
+      /* Check the space of the arrays.  */
+      if (*range_alloc == mbcset->nranges)
+       {
+         /* There are not enough space, need realloc.  */
+         uint32_t *new_array_start;
+         uint32_t *new_array_end;
+         int new_nranges;
+
+         /* +1 in case of mbcset->nranges is 0.  */
+         new_nranges = 2 * mbcset->nranges + 1;
+         /* Use realloc since mbcset->range_starts and mbcset->range_ends
+            are NULL if *range_alloc == 0.  */
+         new_array_start = re_realloc (mbcset->range_starts, uint32_t,
+                                       new_nranges);
+         new_array_end = re_realloc (mbcset->range_ends, uint32_t,
+                                     new_nranges);
+
+         if (BE (new_array_start == NULL || new_array_end == NULL, 0))
+           return REG_ESPACE;
+
+         mbcset->range_starts = new_array_start;
+         mbcset->range_ends = new_array_end;
+         *range_alloc = new_nranges;
+       }
+# endif /* RE_ENABLE_I18N */
+
+      /* Equivalence Classes and Character Classes can't be a range
+        start/end.  */
+      if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
+             || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
+             0))
+       return REG_ERANGE;
+
+      start_collseq = lookup_collation_sequence_value (start_elem);
+      end_collseq = lookup_collation_sequence_value (end_elem);
+      /* Check start/end collation sequence values.  */
+      if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0))
+       return REG_ECOLLATE;
+      if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0))
+       return REG_ERANGE;
+
+# ifdef RE_ENABLE_I18N
+      /* Got valid collation sequence values, add them as a new entry.  */
+      mbcset->range_starts[mbcset->nranges] = start_collseq;
+      mbcset->range_ends[mbcset->nranges++] = end_collseq;
+# endif /* RE_ENABLE_I18N */
+
+      /* Build the table for single byte characters.  */
+      for (ch = 0; ch <= SBC_MAX; ch++)
+       {
+         uint32_t ch_collseq;
+         /*
+         if (MB_CUR_MAX == 1)
+         */
+         if (nrules == 0)
+           ch_collseq = collseqmb[ch];
+         else
+           ch_collseq = collseq_table_lookup (collseqwc, __btowc (ch));
+         if (start_collseq <= ch_collseq && ch_collseq <= end_collseq)
+           bitset_set (sbcset, ch);
+       }
+      return REG_NOERROR;
+    }
+
+  /* Local function for parse_bracket_exp used in _LIBC environement.
+     Build the collating element which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
+     pointer argument sinse we may update it.  */
+
+  static inline reg_errcode_t
+# ifdef RE_ENABLE_I18N
+  build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
+        re_charset_t *mbcset;
+        int *coll_sym_alloc;
+# else /* not RE_ENABLE_I18N */
+  build_collating_symbol (sbcset, name)
+# endif /* not RE_ENABLE_I18N */
+        re_bitset_ptr_t sbcset;
+        const unsigned char *name;
+    {
+      int32_t elem, idx;
+      size_t name_len = strlen ((const char *) name);
+      if (nrules != 0)
+       {
+         elem = seek_collating_symbol_entry (name, name_len);
+         if (symb_table[2 * elem] != 0)
+           {
+             /* We found the entry.  */
+             idx = symb_table[2 * elem + 1];
+             /* Skip the name of collating element name.  */
+             idx += 1 + extra[idx];
+           }
+         else if (symb_table[2 * elem] == 0 && name_len == 1)
+           {
+             /* No valid character, treat it as a normal
+                character.  */
+             bitset_set (sbcset, name[0]);
+             return REG_NOERROR;
+           }
+         else
+           return REG_ECOLLATE;
+
+# ifdef RE_ENABLE_I18N
+         /* Got valid collation sequence, add it as a new entry.  */
+         /* Check the space of the arrays.  */
+         if (*coll_sym_alloc == mbcset->ncoll_syms)
+           {
+             /* Not enough, realloc it.  */
+             /* +1 in case of mbcset->ncoll_syms is 0.  */
+             *coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
+             /* Use realloc since mbcset->coll_syms is NULL
+                if *alloc == 0.  */
+             mbcset->coll_syms = re_realloc (mbcset->coll_syms, int32_t,
+                                             *coll_sym_alloc);
+             if (BE (mbcset->coll_syms == NULL, 0))
+               return REG_ESPACE;
+           }
+         mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
+# endif /* RE_ENABLE_I18N */
+         return REG_NOERROR;
+       }
+      else
+       {
+         if (BE (name_len != 1, 0))
+           return REG_ECOLLATE;
+         else
+           {
+             bitset_set (sbcset, name[0]);
+             return REG_NOERROR;
+           }
+       }
+    }
+#endif
+
+  re_token_t br_token;
+  re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+  re_charset_t *mbcset;
+  int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
+  int equiv_class_alloc = 0, char_class_alloc = 0;
+#else /* not RE_ENABLE_I18N */
+  int non_match = 0;
+#endif /* not RE_ENABLE_I18N */
+  bin_tree_t *work_tree;
+  int token_len, new_idx;
+#ifdef _LIBC
+  collseqmb = (const unsigned char *)
+    _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+  nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules)
+    {
+      /*
+      if (MB_CUR_MAX > 1)
+      */
+       collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+      table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB);
+      symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+                                                 _NL_COLLATE_SYMB_TABLEMB);
+      extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+                                                  _NL_COLLATE_SYMB_EXTRAMB);
+    }
+#endif
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
+#ifdef RE_ENABLE_I18N
+  mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+#ifdef RE_ENABLE_I18N
+  if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else
+  if (BE (sbcset == NULL, 0))
+#endif /* RE_ENABLE_I18N */
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+
+  token_len = peek_token_bracket (token, regexp, syntax);
+  if (BE (token->type == END_OF_RE, 0))
+    {
+      *err = REG_BADPAT;
+      goto parse_bracket_exp_free_return;
+    }
+  if (token->type == OP_NON_MATCH_LIST)
+    {
+#ifdef RE_ENABLE_I18N
+      int i;
+      mbcset->non_match = 1;
+#else /* not RE_ENABLE_I18N */
+      non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+      if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+       bitset_set (sbcset, '\0');
+      re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+      token_len = peek_token_bracket (token, regexp, syntax);
+      if (BE (token->type == END_OF_RE, 0))
+       {
+         *err = REG_BADPAT;
+         goto parse_bracket_exp_free_return;
+       }
+#ifdef RE_ENABLE_I18N
+      if (MB_CUR_MAX > 1)
+       for (i = 0; i < SBC_MAX; ++i)
+         if (__btowc (i) == WEOF)
+           bitset_set (sbcset, i);
+#endif /* RE_ENABLE_I18N */
+    }
+
+  /* We treat the first ']' as a normal character.  */
+  if (token->type == OP_CLOSE_BRACKET)
+    token->type = CHARACTER;
+
+  while (1)
+    {
+      bracket_elem_t start_elem, end_elem;
+      unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
+      unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
+      reg_errcode_t ret;
+      int token_len2 = 0, is_range_exp = 0;
+      re_token_t token2;
+
+      start_elem.opr.name = start_name_buf;
+      ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
+                                  syntax);
+      if (BE (ret != REG_NOERROR, 0))
+       {
+         *err = ret;
+         goto parse_bracket_exp_free_return;
+       }
+
+      token_len = peek_token_bracket (token, regexp, syntax);
+      if (BE (token->type == END_OF_RE, 0))
+       {
+         *err = REG_BADPAT;
+         goto parse_bracket_exp_free_return;
+       }
+      if (token->type == OP_CHARSET_RANGE)
+       {
+         re_string_skip_bytes (regexp, token_len); /* Skip '-'.  */
+         token_len2 = peek_token_bracket (&token2, regexp, syntax);
+         if (BE (token->type == END_OF_RE, 0))
+           {
+             *err = REG_BADPAT;
+             goto parse_bracket_exp_free_return;
+           }
+         if (token2.type == OP_CLOSE_BRACKET)
+           {
+             /* We treat the last '-' as a normal character.  */
+             re_string_skip_bytes (regexp, -token_len);
+             token->type = CHARACTER;
+           }
+         else
+           is_range_exp = 1;
+       }
+
+      if (is_range_exp == 1)
+       {
+         end_elem.opr.name = end_name_buf;
+         ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
+                                      dfa, syntax);
+         if (BE (ret != REG_NOERROR, 0))
+           {
+             *err = ret;
+             goto parse_bracket_exp_free_return;
+           }
+
+         token_len = peek_token_bracket (token, regexp, syntax);
+         if (BE (token->type == END_OF_RE, 0))
+           {
+             *err = REG_BADPAT;
+             goto parse_bracket_exp_free_return;
+           }
+         *err = build_range_exp (sbcset,
+#ifdef RE_ENABLE_I18N
+                                 mbcset, &range_alloc,
+#endif /* RE_ENABLE_I18N */
+                                 &start_elem, &end_elem);
+         if (BE (*err != REG_NOERROR, 0))
+           goto parse_bracket_exp_free_return;
+       }
+      else
+       {
+         switch (start_elem.type)
+           {
+           case SB_CHAR:
+             bitset_set (sbcset, start_elem.opr.ch);
+             break;
+#ifdef RE_ENABLE_I18N
+           case MB_CHAR:
+             /* Check whether the array has enough space.  */
+             if (mbchar_alloc == mbcset->nmbchars)
+               {
+                 /* Not enough, realloc it.  */
+                 /* +1 in case of mbcset->nmbchars is 0.  */
+                 mbchar_alloc = 2 * mbcset->nmbchars + 1;
+                 /* Use realloc since array is NULL if *alloc == 0.  */
+                 mbcset->mbchars = re_realloc (mbcset->mbchars, wchar_t,
+                                               mbchar_alloc);
+                 if (BE (mbcset->mbchars == NULL, 0))
+                   goto parse_bracket_exp_espace;
+               }
+             mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
+             break;
+#endif /* RE_ENABLE_I18N */
+           case EQUIV_CLASS:
+             *err = build_equiv_class (sbcset,
+#ifdef RE_ENABLE_I18N
+                                       mbcset, &equiv_class_alloc,
+#endif /* RE_ENABLE_I18N */
+                                       start_elem.opr.name);
+             if (BE (*err != REG_NOERROR, 0))
+               goto parse_bracket_exp_free_return;
+             break;
+           case COLL_SYM:
+             *err = build_collating_symbol (sbcset,
+#ifdef RE_ENABLE_I18N
+                                            mbcset, &coll_sym_alloc,
+#endif /* RE_ENABLE_I18N */
+                                            start_elem.opr.name);
+             if (BE (*err != REG_NOERROR, 0))
+               goto parse_bracket_exp_free_return;
+             break;
+           case CHAR_CLASS:
+             *err = build_charclass (sbcset,
+#ifdef RE_ENABLE_I18N
+                                     mbcset, &char_class_alloc,
+#endif /* RE_ENABLE_I18N */
+                                     start_elem.opr.name, syntax);
+             if (BE (*err != REG_NOERROR, 0))
+              goto parse_bracket_exp_free_return;
+             break;
+           default:
+             assert (0);
+             break;
+           }
+       }
+      if (token->type == OP_CLOSE_BRACKET)
+       break;
+    }
+
+  re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+
+  /* If it is non-matching list.  */
+#ifdef RE_ENABLE_I18N
+  if (mbcset->non_match)
+#else /* not RE_ENABLE_I18N */
+  if (non_match)
+#endif /* not RE_ENABLE_I18N */
+    bitset_not (sbcset);
+
+  /* Build a tree for simple bracket.  */
+  br_token.type = SIMPLE_BRACKET;
+  br_token.opr.sbcset = sbcset;
+  new_idx = re_dfa_add_node (dfa, br_token, 0);
+  work_tree = create_tree (NULL, NULL, 0, new_idx);
+  if (BE (new_idx == -1 || work_tree == NULL, 0))
+    goto parse_bracket_exp_espace;
+
+#ifdef RE_ENABLE_I18N
+  if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
+      || mbcset->nranges || (MB_CUR_MAX > 1 && (mbcset->nchar_classes
+                                               || mbcset->non_match)))
+    {
+      re_token_t alt_token;
+      bin_tree_t *mbc_tree;
+      /* Build a tree for complex bracket.  */
+      br_token.type = COMPLEX_BRACKET;
+      br_token.opr.mbcset = mbcset;
+      dfa->has_mb_node = 1;
+      new_idx = re_dfa_add_node (dfa, br_token, 0);
+      mbc_tree = create_tree (NULL, NULL, 0, new_idx);
+      if (BE (new_idx == -1 || mbc_tree == NULL, 0))
+       goto parse_bracket_exp_espace;
+      /* Then join them by ALT node.  */
+      dfa->has_plural_match = 1;
+      alt_token.type = OP_ALT;
+      new_idx = re_dfa_add_node (dfa, alt_token, 0);
+      work_tree = create_tree (work_tree, mbc_tree, 0, new_idx);
+      if (BE (new_idx != -1 && mbc_tree != NULL, 1))
+       return work_tree;
+    }
+  else
+    {
+      free_charset (mbcset);
+      return work_tree;
+    }
+#else /* not RE_ENABLE_I18N */
+  return work_tree;
+#endif /* not RE_ENABLE_I18N */
+
+ parse_bracket_exp_espace:
+  *err = REG_ESPACE;
+ parse_bracket_exp_free_return:
+  re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+  free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+  return NULL;
+}
+
+/* Parse an element in the bracket expression.  */
+
+static reg_errcode_t
+parse_bracket_element (elem, regexp, token, token_len, dfa, syntax)
+     bracket_elem_t *elem;
+     re_string_t *regexp;
+     re_token_t *token;
+     int token_len;
+     re_dfa_t *dfa;
+     reg_syntax_t syntax;
+{
+#ifdef RE_ENABLE_I18N
+  int cur_char_size;
+  cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp));
+  if (cur_char_size > 1)
+    {
+      elem->type = MB_CHAR;
+      elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp));
+      re_string_skip_bytes (regexp, cur_char_size);
+      return REG_NOERROR;
+    }
+#endif /* RE_ENABLE_I18N */
+  re_string_skip_bytes (regexp, token_len); /* Skip a token.  */
+  if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS
+      || token->type == OP_OPEN_EQUIV_CLASS)
+    return parse_bracket_symbol (elem, regexp, token);
+  elem->type = SB_CHAR;
+  elem->opr.ch = token->opr.c;
+  return REG_NOERROR;
+}
+
+/* Parse a bracket symbol in the bracket expression.  Bracket symbols are
+   such as [:<character_class>:], [.<collating_element>.], and
+   [=<equivalent_class>=].  */
+
+static reg_errcode_t
+parse_bracket_symbol (elem, regexp, token)
+     bracket_elem_t *elem;
+     re_string_t *regexp;
+     re_token_t *token;
+{
+  unsigned char ch, delim = token->opr.c;
+  int i = 0;
+  for (;; ++i)
+    {
+      if (re_string_eoi(regexp) || i >= BRACKET_NAME_BUF_SIZE)
+       return REG_EBRACK;
+      if (token->type == OP_OPEN_CHAR_CLASS)
+       ch = re_string_fetch_byte_case (regexp);
+      else
+       ch = re_string_fetch_byte (regexp);
+      if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
+       break;
+      elem->opr.name[i] = ch;
+    }
+  re_string_skip_bytes (regexp, 1);
+  elem->opr.name[i] = '\0';
+  switch (token->type)
+    {
+    case OP_OPEN_COLL_ELEM:
+      elem->type = COLL_SYM;
+      break;
+    case OP_OPEN_EQUIV_CLASS:
+      elem->type = EQUIV_CLASS;
+      break;
+    case OP_OPEN_CHAR_CLASS:
+      elem->type = CHAR_CLASS;
+      break;
+    default:
+      break;
+    }
+  return REG_NOERROR;
+}
+
+  /* Helper function for parse_bracket_exp.
+     Build the equivalence class which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
+     is a pointer argument sinse we may update it.  */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_equiv_class (sbcset, mbcset, equiv_class_alloc, name)
+     re_charset_t *mbcset;
+     int *equiv_class_alloc;
+#else /* not RE_ENABLE_I18N */
+build_equiv_class (sbcset, name)
+#endif /* not RE_ENABLE_I18N */
+     re_bitset_ptr_t sbcset;
+     const unsigned char *name;
+{
+#if defined _LIBC && defined RE_ENABLE_I18N
+  uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules != 0)
+    {
+      const int32_t *table, *indirect;
+      const unsigned char *weights, *extra, *cp;
+      unsigned char char_buf[2];
+      int32_t idx1, idx2;
+      unsigned int ch;
+      size_t len;
+      /* This #include defines a local function!  */
+# include <locale/weight.h>
+      /* Calculate the index for equivalence class.  */
+      cp = name;
+      table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+      weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+                                              _NL_COLLATE_WEIGHTMB);
+      extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
+                                                  _NL_COLLATE_EXTRAMB);
+      indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+                                               _NL_COLLATE_INDIRECTMB);
+      idx1 = findidx (&cp);
+      if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0))
+       /* This isn't a valid character.  */
+       return REG_ECOLLATE;
+
+      /* Build single byte matcing table for this equivalence class.  */
+      char_buf[1] = (unsigned char) '\0';
+      len = weights[idx1];
+      for (ch = 0; ch < SBC_MAX; ++ch)
+       {
+         char_buf[0] = ch;
+         cp = char_buf;
+         idx2 = findidx (&cp);
+/*
+         idx2 = table[ch];
+*/
+         if (idx2 == 0)
+           /* This isn't a valid character.  */
+           continue;
+         if (len == weights[idx2])
+           {
+             int cnt = 0;
+             while (cnt <= len &&
+                    weights[idx1 + 1 + cnt] == weights[idx2 + 1 + cnt])
+               ++cnt;
+
+             if (cnt > len)
+               bitset_set (sbcset, ch);
+           }
+       }
+      /* Check whether the array has enough space.  */
+      if (*equiv_class_alloc == mbcset->nequiv_classes)
+       {
+         /* Not enough, realloc it.  */
+         /* +1 in case of mbcset->nequiv_classes is 0.  */
+         *equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
+         /* Use realloc since the array is NULL if *alloc == 0.  */
+         mbcset->equiv_classes = re_realloc (mbcset->equiv_classes, int32_t,
+                                             *equiv_class_alloc);
+         if (BE (mbcset->equiv_classes == NULL, 0))
+           return REG_ESPACE;
+       }
+      mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
+    }
+  else
+#endif /* _LIBC && RE_ENABLE_I18N */
+    {
+      if (BE (strlen ((const char *) name) != 1, 0))
+       return REG_ECOLLATE;
+      bitset_set (sbcset, *name);
+    }
+  return REG_NOERROR;
+}
+
+  /* Helper function for parse_bracket_exp.
+     Build the character class which is represented by NAME.
+     The result are written to MBCSET and SBCSET.
+     CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
+     is a pointer argument sinse we may update it.  */
+
+static reg_errcode_t
+#ifdef RE_ENABLE_I18N
+build_charclass (sbcset, mbcset, char_class_alloc, class_name, syntax)
+     re_charset_t *mbcset;
+     int *char_class_alloc;
+#else /* not RE_ENABLE_I18N */
+build_charclass (sbcset, class_name, syntax)
+#endif /* not RE_ENABLE_I18N */
+     re_bitset_ptr_t sbcset;
+     const unsigned char *class_name;
+     reg_syntax_t syntax;
+{
+  int i;
+  const char *name = (const char *) class_name;
+
+  /* In case of REG_ICASE "upper" and "lower" match the both of
+     upper and lower cases.  */
+  if ((syntax & RE_ICASE)
+      && (strcmp (name, "upper") == 0 || strcmp (name, "lower") == 0))
+    name = "alpha";
+
+#ifdef RE_ENABLE_I18N
+  /* Check the space of the arrays.  */
+  if (*char_class_alloc == mbcset->nchar_classes)
+    {
+      /* Not enough, realloc it.  */
+      /* +1 in case of mbcset->nchar_classes is 0.  */
+      *char_class_alloc = 2 * mbcset->nchar_classes + 1;
+      /* Use realloc since array is NULL if *alloc == 0.  */
+      mbcset->char_classes = re_realloc (mbcset->char_classes, wctype_t,
+                                        *char_class_alloc);
+      if (BE (mbcset->char_classes == NULL, 0))
+       return REG_ESPACE;
+    }
+  mbcset->char_classes[mbcset->nchar_classes++] = __wctype (name);
+#endif /* RE_ENABLE_I18N */
+
+#define BUILD_CHARCLASS_LOOP(ctype_func)\
+    for (i = 0; i < SBC_MAX; ++i)      \
+      {                                        \
+       if (ctype_func (i))             \
+         bitset_set (sbcset, i);       \
+      }
+
+  if (strcmp (name, "alnum") == 0)
+    BUILD_CHARCLASS_LOOP (isalnum)
+  else if (strcmp (name, "cntrl") == 0)
+    BUILD_CHARCLASS_LOOP (iscntrl)
+  else if (strcmp (name, "lower") == 0)
+    BUILD_CHARCLASS_LOOP (islower)
+  else if (strcmp (name, "space") == 0)
+    BUILD_CHARCLASS_LOOP (isspace)
+  else if (strcmp (name, "alpha") == 0)
+    BUILD_CHARCLASS_LOOP (isalpha)
+  else if (strcmp (name, "digit") == 0)
+    BUILD_CHARCLASS_LOOP (isdigit)
+  else if (strcmp (name, "print") == 0)
+    BUILD_CHARCLASS_LOOP (isprint)
+  else if (strcmp (name, "upper") == 0)
+    BUILD_CHARCLASS_LOOP (isupper)
+  else if (strcmp (name, "blank") == 0)
+    BUILD_CHARCLASS_LOOP (isblank)
+  else if (strcmp (name, "graph") == 0)
+    BUILD_CHARCLASS_LOOP (isgraph)
+  else if (strcmp (name, "punct") == 0)
+    BUILD_CHARCLASS_LOOP (ispunct)
+  else if (strcmp (name, "xdigit") == 0)
+    BUILD_CHARCLASS_LOOP (isxdigit)
+  else
+    return REG_ECTYPE;
+
+  return REG_NOERROR;
+}
+
+static bin_tree_t *
+build_word_op (dfa, not, err)
+     re_dfa_t *dfa;
+     int not;
+     reg_errcode_t *err;
+{
+  re_bitset_ptr_t sbcset;
+#ifdef RE_ENABLE_I18N
+  re_charset_t *mbcset;
+  int alloc = 0;
+#else /* not RE_ENABLE_I18N */
+  int non_match = 0;
+#endif /* not RE_ENABLE_I18N */
+  reg_errcode_t ret;
+  re_token_t br_token;
+  bin_tree_t *tree;
+  int new_idx;
+
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
+#ifdef RE_ENABLE_I18N
+  mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+#endif /* RE_ENABLE_I18N */
+
+#ifdef RE_ENABLE_I18N
+  if (BE (sbcset == NULL || mbcset == NULL, 0))
+#else /* not RE_ENABLE_I18N */
+  if (BE (sbcset == NULL, 0))
+#endif /* not RE_ENABLE_I18N */
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+
+  if (not)
+    {
+#ifdef RE_ENABLE_I18N
+      int i;
+      /*
+      if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
+       bitset_set(cset->sbcset, '\0');
+      */
+      mbcset->non_match = 1;
+      if (MB_CUR_MAX > 1)
+       for (i = 0; i < SBC_MAX; ++i)
+         if (__btowc (i) == WEOF)
+           bitset_set (sbcset, i);
+#else /* not RE_ENABLE_I18N */
+      non_match = 1;
+#endif /* not RE_ENABLE_I18N */
+    }
+
+  /* We don't care the syntax in this case.  */
+  ret = build_charclass (sbcset,
+#ifdef RE_ENABLE_I18N
+                        mbcset, &alloc,
+#endif /* RE_ENABLE_I18N */
+                        (const unsigned char *) "alpha", 0);
+
+  if (BE (ret != REG_NOERROR, 0))
+    {
+      re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+      free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+      *err = ret;
+      return NULL;
+    }
+  /* \w match '_' also.  */
+  bitset_set (sbcset, '_');
+
+  /* If it is non-matching list.  */
+#ifdef RE_ENABLE_I18N
+  if (mbcset->non_match)
+#else /* not RE_ENABLE_I18N */
+  if (non_match)
+#endif /* not RE_ENABLE_I18N */
+    bitset_not (sbcset);
+
+  /* Build a tree for simple bracket.  */
+  br_token.type = SIMPLE_BRACKET;
+  br_token.opr.sbcset = sbcset;
+  new_idx = re_dfa_add_node (dfa, br_token, 0);
+  tree = create_tree (NULL, NULL, 0, new_idx);
+  if (BE (new_idx == -1 || tree == NULL, 0))
+    goto build_word_op_espace;
+
+#ifdef RE_ENABLE_I18N
+  if (MB_CUR_MAX > 1)
+    {
+      re_token_t alt_token;
+      bin_tree_t *mbc_tree;
+      /* Build a tree for complex bracket.  */
+      br_token.type = COMPLEX_BRACKET;
+      br_token.opr.mbcset = mbcset;
+      dfa->has_mb_node = 1;
+      new_idx = re_dfa_add_node (dfa, br_token, 0);
+      mbc_tree = create_tree (NULL, NULL, 0, new_idx);
+      if (BE (new_idx == -1 || mbc_tree == NULL, 0))
+       goto build_word_op_espace;
+      /* Then join them by ALT node.  */
+      alt_token.type = OP_ALT;
+      new_idx = re_dfa_add_node (dfa, alt_token, 0);
+      tree = create_tree (tree, mbc_tree, 0, new_idx);
+      if (BE (new_idx != -1 && mbc_tree != NULL, 1))
+       return tree;
+    }
+  else
+    {
+      free_charset (mbcset);
+      return tree;
+    }
+#else /* not RE_ENABLE_I18N */
+  return tree;
+#endif /* not RE_ENABLE_I18N */
+
+ build_word_op_espace:
+  re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+  free_charset (mbcset);
+#endif /* RE_ENABLE_I18N */
+  *err = REG_ESPACE;
+  return NULL;
+}
+
+/* This is intended for the expressions like "a{1,3}".
+   Fetch a number from `input', and return the number.
+   Return -1, if the number field is empty like "{,1}".
+   Return -2, If an error is occured.  */
+
+static int
+fetch_number (input, token, syntax)
+     re_string_t *input;
+     re_token_t *token;
+     reg_syntax_t syntax;
+{
+  int num = -1;
+  unsigned char c;
+  while (1)
+    {
+      *token = fetch_token (input, syntax);
+      c = token->opr.c;
+      if (BE (token->type == END_OF_RE, 0))
+       return -2;
+      if (token->type == OP_CLOSE_DUP_NUM || c == ',')
+       break;
+      num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2)
+            ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0'));
+      num = (num > RE_DUP_MAX) ? -2 : num;
+    }
+  return num;
+}
+\f
+#ifdef RE_ENABLE_I18N
+static void
+free_charset (re_charset_t *cset)
+{
+  re_free (cset->mbchars);
+# ifdef _LIBC
+  re_free (cset->coll_syms);
+  re_free (cset->equiv_classes);
+  re_free (cset->range_starts);
+  re_free (cset->range_ends);
+# endif
+  re_free (cset->char_classes);
+  re_free (cset);
+}
+#endif /* RE_ENABLE_I18N */
+\f
+/* Functions for binary tree operation.  */
+
+/* Create a node of tree.
+   Note: This function automatically free left and right if malloc fails.  */
+
+static bin_tree_t *
+create_tree (left, right, type, index)
+     bin_tree_t *left;
+     bin_tree_t *right;
+     re_token_type_t type;
+     int index;
+{
+  bin_tree_t *tree;
+  tree = re_malloc (bin_tree_t, 1);
+  if (BE (tree == NULL, 0))
+    {
+      free_bin_tree (left);
+      free_bin_tree (right);
+      return NULL;
+    }
+  tree->parent = NULL;
+  tree->left = left;
+  tree->right = right;
+  tree->type = type;
+  tree->node_idx = index;
+  tree->first = -1;
+  tree->next = -1;
+  re_node_set_init_empty (&tree->eclosure);
+
+  if (left != NULL)
+    left->parent = tree;
+  if (right != NULL)
+    right->parent = tree;
+  return tree;
+}
+
+/* Free the sub tree pointed by TREE.  */
+
+static void
+free_bin_tree (tree)
+     bin_tree_t *tree;
+{
+  if (tree == NULL)
+    return;
+  /*re_node_set_free (&tree->eclosure);*/
+  free_bin_tree (tree->left);
+  free_bin_tree (tree->right);
+  re_free (tree);
+}
+
+/* Duplicate the node SRC, and return new node.  */
+
+static bin_tree_t *
+duplicate_tree (src, dfa)
+     const bin_tree_t *src;
+     re_dfa_t *dfa;
+{
+  bin_tree_t *left = NULL, *right = NULL, *new_tree;
+  int new_node_idx;
+  /* Since node indies must be according to Post-order of the tree,
+     we must duplicate the left at first.  */
+  if (src->left != NULL)
+    {
+      left = duplicate_tree (src->left, dfa);
+      if (left == NULL)
+       return NULL;
+    }
+
+  /* Secondaly, duplicate the right.  */
+  if (src->right != NULL)
+    {
+      right = duplicate_tree (src->right, dfa);
+      if (right == NULL)
+       {
+         free_bin_tree (left);
+         return NULL;
+       }
+    }
+
+  /* At last, duplicate itself.  */
+  if (src->type == NON_TYPE)
+    {
+      new_node_idx = re_dfa_add_node (dfa, dfa->nodes[src->node_idx], 0);
+      dfa->nodes[new_node_idx].duplicated = 1;
+      if (BE (new_node_idx == -1, 0))
+       {
+         free_bin_tree (left);
+         free_bin_tree (right);
+         return NULL;
+       }
+    }
+  else
+    new_node_idx = src->type;
+
+  new_tree = create_tree (left, right, src->type, new_node_idx);
+  if (BE (new_tree == NULL, 0))
+    {
+      free_bin_tree (left);
+      free_bin_tree (right);
+    }
+  return new_tree;
+}
diff --git a/posix/regex.c b/posix/regex.c
new file mode 100644 (file)
index 0000000..98d86e1
--- /dev/null
@@ -0,0 +1,61 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   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.  */
+
+#ifdef _LIBC
+/* We have to keep the namespace clean.  */
+#  define regfree(preg) __regfree (preg)
+#  define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
+#  define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
+#  define regerror(errcode, preg, errbuf, errbuf_size) \
+       __regerror(errcode, preg, errbuf, errbuf_size)
+#  define re_set_registers(bu, re, nu, st, en) \
+       __re_set_registers (bu, re, nu, st, en)
+#  define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
+       __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
+#  define re_match(bufp, string, size, pos, regs) \
+       __re_match (bufp, string, size, pos, regs)
+#  define re_search(bufp, string, size, startpos, range, regs) \
+       __re_search (bufp, string, size, startpos, range, regs)
+#  define re_compile_pattern(pattern, length, bufp) \
+       __re_compile_pattern (pattern, length, bufp)
+#  define re_set_syntax(syntax) __re_set_syntax (syntax)
+#  define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
+       __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
+#  define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
+#endif
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+   <regex.h>.  */
+#include <sys/types.h>
+#include <regex.h>
+#include "regex_internal.h"
+
+#include "regex_internal.c"
+#include "regcomp.c"
+#include "regexec.c"
+
+/* Binary backward compatibility.  */
+#if _LIBC
+# include <shlib-compat.h>
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3)
+link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
+int re_max_failures = 2000;
+# endif
+#endif
diff --git a/posix/regex.h b/posix/regex.h
new file mode 100644 (file)
index 0000000..314292b
--- /dev/null
@@ -0,0 +1,574 @@
+/* Definitions for data structures and routines for the regular
+   expression library.
+   Copyright (C) 1985,1989-93,1995-98,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.  */
+
+#ifndef _REGEX_H
+#define _REGEX_H 1
+
+/* Allow the use in C++ code.  */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* POSIX says that <sys/types.h> must be included (by the caller) before
+   <regex.h>.  */
+
+#if !defined _POSIX_C_SOURCE && !defined _POSIX_SOURCE && (defined VMS || defined _MSC_VER)
+/* VMS doesn't have `size_t' in <sys/types.h>, even though POSIX says it
+   should be there. Same for Microsoft Visual C++ 6.0 */
+# include <stddef.h>
+#endif
+
+/* The following two types have to be signed and unsigned integer type
+   wide enough to hold a value of a pointer.  For most ANSI compilers
+   ptrdiff_t and size_t should be likely OK.  Still size of these two
+   types is 2 for Microsoft C.  Ugh... */
+typedef long int s_reg_t;
+typedef unsigned long int active_reg_t;
+
+/* The following bits are used to determine the regexp syntax we
+   recognize.  The set/not-set meanings are chosen so that Emacs syntax
+   remains the value 0.  The bits are given in alphabetical order, and
+   the definitions shifted by one from the previous bit; thus, when we
+   add or remove a bit, only one other definition need change.  */
+typedef unsigned long int reg_syntax_t;
+
+/* If this bit is not set, then \ inside a bracket expression is literal.
+   If set, then such a \ quotes the following character.  */
+#define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
+
+/* If this bit is not set, then + and ? are operators, and \+ and \? are
+     literals.
+   If set, then \+ and \? are operators and + and ? are literals.  */
+#define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
+
+/* If this bit is set, then character classes are supported.  They are:
+     [:alpha:], [:upper:], [:lower:],  [:digit:], [:alnum:], [:xdigit:],
+     [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
+   If not set, then character classes are not supported.  */
+#define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
+
+/* If this bit is set, then ^ and $ are always anchors (outside bracket
+     expressions, of course).
+   If this bit is not set, then it depends:
+        ^  is an anchor if it is at the beginning of a regular
+           expression or after an open-group or an alternation operator;
+        $  is an anchor if it is at the end of a regular expression, or
+           before a close-group or an alternation operator.
+
+   This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
+   POSIX draft 11.2 says that * etc. in leading positions is undefined.
+   We already implemented a previous draft which made those constructs
+   invalid, though, so we haven't changed the code back.  */
+#define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
+
+/* If this bit is set, then special characters are always special
+     regardless of where they are in the pattern.
+   If this bit is not set, then special characters are special only in
+     some contexts; otherwise they are ordinary.  Specifically,
+     * + ? and intervals are only special when not after the beginning,
+     open-group, or alternation operator.  */
+#define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
+
+/* If this bit is set, then *, +, ?, and { cannot be first in an re or
+     immediately after an alternation or begin-group operator.  */
+#define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
+
+/* If this bit is set, then . matches newline.
+   If not set, then it doesn't.  */
+#define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
+
+/* If this bit is set, then . doesn't match NUL.
+   If not set, then it does.  */
+#define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
+
+/* If this bit is set, nonmatching lists [^...] do not match newline.
+   If not set, they do.  */
+#define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
+
+/* If this bit is set, either \{...\} or {...} defines an
+     interval, depending on RE_NO_BK_BRACES.
+   If not set, \{, \}, {, and } are literals.  */
+#define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
+
+/* If this bit is set, +, ? and | aren't recognized as operators.
+   If not set, they are.  */
+#define RE_LIMITED_OPS (RE_INTERVALS << 1)
+
+/* If this bit is set, newline is an alternation operator.
+   If not set, newline is literal.  */
+#define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
+
+/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+     are literals.
+  If not set, then `\{...\}' defines an interval.  */
+#define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
+
+/* If this bit is set, (...) defines a group, and \( and \) are literals.
+   If not set, \(...\) defines a group, and ( and ) are literals.  */
+#define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
+
+/* If this bit is set, then \<digit> matches <digit>.
+   If not set, then \<digit> is a back-reference.  */
+#define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
+
+/* If this bit is set, then | is an alternation operator, and \| is literal.
+   If not set, then \| is an alternation operator, and | is literal.  */
+#define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
+
+/* If this bit is set, then an ending range point collating higher
+     than the starting range point, as in [z-a], is invalid.
+   If not set, then when ending range point collates higher than the
+     starting range point, the range is ignored.  */
+#define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
+
+/* If this bit is set, then an unmatched ) is ordinary.
+   If not set, then an unmatched ) is invalid.  */
+#define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
+
+/* If this bit is set, succeed as soon as we match the whole pattern,
+   without further backtracking.  */
+#define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
+
+/* If this bit is set, do not process the GNU regex operators.
+   If not set, then the GNU regex operators are recognized. */
+#define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
+
+/* If this bit is set, turn on internal regex debugging.
+   If not set, and debugging was on, turn it off.
+   This only works if regex.c is compiled -DDEBUG.
+   We define this bit always, so that all that's needed to turn on
+   debugging is to recompile regex.c; the calling code can always have
+   this bit set, and it won't affect anything in the normal case. */
+#define RE_DEBUG (RE_NO_GNU_OPS << 1)
+
+/* If this bit is set, a syntactically invalid interval is treated as
+   a string of ordinary characters.  For example, the ERE 'a{1' is
+   treated as 'a\{1'.  */
+#define RE_INVALID_INTERVAL_ORD (RE_DEBUG << 1)
+
+/* If this bit is set, then ignore case when matching.
+   If not set, then case is significant.  */
+#define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
+
+/* This global variable defines the particular regexp syntax to use (for
+   some interfaces).  When a regexp is compiled, the syntax used is
+   stored in the pattern buffer, so changing this does not affect
+   already-compiled regexps.  */
+extern reg_syntax_t re_syntax_options;
+\f
+/* Define combinations of the above bits for the standard possibilities.
+   (The [[[ comments delimit what gets put into the Texinfo file, so
+   don't delete them!)  */
+/* [[[begin syntaxes]]] */
+#define RE_SYNTAX_EMACS 0
+
+#define RE_SYNTAX_AWK                                                  \
+  (RE_BACKSLASH_ESCAPE_IN_LISTS   | RE_DOT_NOT_NULL                    \
+   | RE_NO_BK_PARENS              | RE_NO_BK_REFS                      \
+   | RE_NO_BK_VBAR                | RE_NO_EMPTY_RANGES                 \
+   | RE_DOT_NEWLINE              | RE_CONTEXT_INDEP_ANCHORS            \
+   | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GNU_AWK                                              \
+  ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DEBUG)        \
+   & ~(RE_DOT_NOT_NULL | RE_INTERVALS | RE_CONTEXT_INDEP_OPS           \
+       | RE_CONTEXT_INVALID_OPS ))
+
+#define RE_SYNTAX_POSIX_AWK                                            \
+  (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS             \
+   | RE_INTERVALS          | RE_NO_GNU_OPS)
+
+#define RE_SYNTAX_GREP                                                 \
+  (RE_BK_PLUS_QM              | RE_CHAR_CLASSES                                \
+   | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS                           \
+   | RE_NEWLINE_ALT)
+
+#define RE_SYNTAX_EGREP                                                        \
+  (RE_CHAR_CLASSES        | RE_CONTEXT_INDEP_ANCHORS                   \
+   | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE                   \
+   | RE_NEWLINE_ALT       | RE_NO_BK_PARENS                            \
+   | RE_NO_BK_VBAR)
+
+#define RE_SYNTAX_POSIX_EGREP                                          \
+  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES                    \
+   | RE_INVALID_INTERVAL_ORD)
+
+/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff.  */
+#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+
+#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
+
+/* Syntax bits common to both basic and extended POSIX regex syntax.  */
+#define _RE_SYNTAX_POSIX_COMMON                                                \
+  (RE_CHAR_CLASSES | RE_DOT_NEWLINE      | RE_DOT_NOT_NULL             \
+   | RE_INTERVALS  | RE_NO_EMPTY_RANGES)
+
+#define RE_SYNTAX_POSIX_BASIC                                          \
+  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM)
+
+/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
+   RE_LIMITED_OPS, i.e., \? \+ \| are not recognized.  Actually, this
+   isn't minimal, since other operators, such as \`, aren't disabled.  */
+#define RE_SYNTAX_POSIX_MINIMAL_BASIC                                  \
+  (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
+
+#define RE_SYNTAX_POSIX_EXTENDED                                       \
+  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS                 \
+   | RE_CONTEXT_INDEP_OPS   | RE_NO_BK_BRACES                          \
+   | RE_NO_BK_PARENS        | RE_NO_BK_VBAR                            \
+   | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)
+
+/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
+   removed and RE_NO_BK_REFS is added.  */
+#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED                               \
+  (_RE_SYNTAX_POSIX_COMMON  | RE_CONTEXT_INDEP_ANCHORS                 \
+   | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES                          \
+   | RE_NO_BK_PARENS        | RE_NO_BK_REFS                            \
+   | RE_NO_BK_VBAR         | RE_UNMATCHED_RIGHT_PAREN_ORD)
+/* [[[end syntaxes]]] */
+\f
+/* Maximum number of duplicates an interval can allow.  Some systems
+   (erroneously) define this in other header files, but we want our
+   value, so remove any previous define.  */
+#ifdef RE_DUP_MAX
+# undef RE_DUP_MAX
+#endif
+/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows.  */
+#define RE_DUP_MAX (0x7fff)
+
+
+/* POSIX `cflags' bits (i.e., information for `regcomp').  */
+
+/* If this bit is set, then use extended regular expression syntax.
+   If not set, then use basic regular expression syntax.  */
+#define REG_EXTENDED 1
+
+/* If this bit is set, then ignore case when matching.
+   If not set, then case is significant.  */
+#define REG_ICASE (REG_EXTENDED << 1)
+
+/* If this bit is set, then anchors do not match at newline
+     characters in the string.
+   If not set, then anchors do match at newlines.  */
+#define REG_NEWLINE (REG_ICASE << 1)
+
+/* If this bit is set, then report only success or fail in regexec.
+   If not set, then returns differ between not matching and errors.  */
+#define REG_NOSUB (REG_NEWLINE << 1)
+
+
+/* POSIX `eflags' bits (i.e., information for regexec).  */
+
+/* If this bit is set, then the beginning-of-line operator doesn't match
+     the beginning of the string (presumably because it's not the
+     beginning of a line).
+   If not set, then the beginning-of-line operator does match the
+     beginning of the string.  */
+#define REG_NOTBOL 1
+
+/* Like REG_NOTBOL, except for the end-of-line.  */
+#define REG_NOTEOL (1 << 1)
+
+
+/* If any error codes are removed, changed, or added, update the
+   `re_error_msg' table in regex.c.  */
+typedef enum
+{
+#ifdef _XOPEN_SOURCE
+  REG_ENOSYS = -1,     /* This will never happen for this implementation.  */
+#endif
+
+  REG_NOERROR = 0,     /* Success.  */
+  REG_NOMATCH,         /* Didn't find a match (for regexec).  */
+
+  /* POSIX regcomp return error codes.  (In the order listed in the
+     standard.)  */
+  REG_BADPAT,          /* Invalid pattern.  */
+  REG_ECOLLATE,                /* Not implemented.  */
+  REG_ECTYPE,          /* Invalid character class name.  */
+  REG_EESCAPE,         /* Trailing backslash.  */
+  REG_ESUBREG,         /* Invalid back reference.  */
+  REG_EBRACK,          /* Unmatched left bracket.  */
+  REG_EPAREN,          /* Parenthesis imbalance.  */
+  REG_EBRACE,          /* Unmatched \{.  */
+  REG_BADBR,           /* Invalid contents of \{\}.  */
+  REG_ERANGE,          /* Invalid range end.  */
+  REG_ESPACE,          /* Ran out of memory.  */
+  REG_BADRPT,          /* No preceding re for repetition op.  */
+
+  /* Error codes we've added.  */
+  REG_EEND,            /* Premature end.  */
+  REG_ESIZE,           /* Compiled pattern bigger than 2^16 bytes.  */
+  REG_ERPAREN          /* Unmatched ) or \); not returned from regcomp.  */
+} reg_errcode_t;
+\f
+/* This data structure represents a compiled pattern.  Before calling
+   the pattern compiler, the fields `buffer', `allocated', `fastmap',
+   `translate', and `no_sub' can be set.  After the pattern has been
+   compiled, the `re_nsub' field is available.  All other fields are
+   private to the regex routines.  */
+
+#ifndef RE_TRANSLATE_TYPE
+# define RE_TRANSLATE_TYPE char *
+#endif
+
+struct re_pattern_buffer
+{
+/* [[[begin pattern_buffer]]] */
+       /* Space that holds the compiled pattern.  It is declared as
+          `unsigned char *' because its elements are
+           sometimes used as array indexes.  */
+  unsigned char *buffer;
+
+       /* Number of bytes to which `buffer' points.  */
+  unsigned long int allocated;
+
+       /* Number of bytes actually used in `buffer'.  */
+  unsigned long int used;
+
+        /* Syntax setting with which the pattern was compiled.  */
+  reg_syntax_t syntax;
+
+        /* Pointer to a fastmap, if any, otherwise zero.  re_search uses
+           the fastmap, if there is one, to skip over impossible
+           starting points for matches.  */
+  char *fastmap;
+
+        /* Either a translate table to apply to all characters before
+           comparing them, or zero for no translation.  The translation
+           is applied to a pattern when it is compiled and to a string
+           when it is matched.  */
+  RE_TRANSLATE_TYPE translate;
+
+       /* Number of subexpressions found by the compiler.  */
+  size_t re_nsub;
+
+        /* Zero if this pattern cannot match the empty string, one else.
+           Well, in truth it's used only in `re_search_2', to see
+           whether or not we should use the fastmap, so we don't set
+           this absolutely perfectly; see `re_compile_fastmap' (the
+           `duplicate' case).  */
+  unsigned can_be_null : 1;
+
+        /* If REGS_UNALLOCATED, allocate space in the `regs' structure
+             for `max (RE_NREGS, re_nsub + 1)' groups.
+           If REGS_REALLOCATE, reallocate space if necessary.
+           If REGS_FIXED, use what's there.  */
+#define REGS_UNALLOCATED 0
+#define REGS_REALLOCATE 1
+#define REGS_FIXED 2
+  unsigned regs_allocated : 2;
+
+        /* Set to zero when `regex_compile' compiles a pattern; set to one
+           by `re_compile_fastmap' if it updates the fastmap.  */
+  unsigned fastmap_accurate : 1;
+
+        /* If set, `re_match_2' does not return information about
+           subexpressions.  */
+  unsigned no_sub : 1;
+
+        /* If set, a beginning-of-line anchor doesn't match at the
+           beginning of the string.  */
+  unsigned not_bol : 1;
+
+        /* Similarly for an end-of-line anchor.  */
+  unsigned not_eol : 1;
+
+        /* If true, an anchor at a newline matches.  */
+  unsigned newline_anchor : 1;
+
+/* [[[end pattern_buffer]]] */
+};
+
+typedef struct re_pattern_buffer regex_t;
+\f
+/* Type for byte offsets within the string.  POSIX mandates this.  */
+typedef int regoff_t;
+
+
+/* This is the structure we store register match data in.  See
+   regex.texinfo for a full description of what registers match.  */
+struct re_registers
+{
+  unsigned num_regs;
+  regoff_t *start;
+  regoff_t *end;
+};
+
+
+/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
+   `re_match_2' returns information about at least this many registers
+   the first time a `regs' structure is passed.  */
+#ifndef RE_NREGS
+# define RE_NREGS 30
+#endif
+
+
+/* POSIX specification for registers.  Aside from the different names than
+   `re_registers', POSIX uses an array of structures, instead of a
+   structure of arrays.  */
+typedef struct
+{
+  regoff_t rm_so;  /* Byte offset from string's start to substring's start.  */
+  regoff_t rm_eo;  /* Byte offset from string's start to substring's end.  */
+} regmatch_t;
+\f
+/* Declarations for routines.  */
+
+/* To avoid duplicating every routine declaration -- once with a
+   prototype (if we are ANSI), and once without (if we aren't) -- we
+   use the following macro to declare argument types.  This
+   unfortunately clutters up the declarations a bit, but I think it's
+   worth it.  */
+
+#if __STDC__
+
+# define _RE_ARGS(args) args
+
+#else /* not __STDC__ */
+
+# define _RE_ARGS(args) ()
+
+#endif /* not __STDC__ */
+
+/* Sets the current default syntax to SYNTAX, and return the old syntax.
+   You can also simply assign to the `re_syntax_options' variable.  */
+extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
+
+/* Compile the regular expression PATTERN, with length LENGTH
+   and syntax given by the global `re_syntax_options', into the buffer
+   BUFFER.  Return NULL if successful, and an error string if not.  */
+extern const char *re_compile_pattern
+  _RE_ARGS ((const char *pattern, size_t length,
+             struct re_pattern_buffer *buffer));
+
+
+/* Compile a fastmap for the compiled pattern in BUFFER; used to
+   accelerate searches.  Return 0 if successful and -2 if was an
+   internal error.  */
+extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
+
+
+/* Search in the string STRING (with length LENGTH) for the pattern
+   compiled into BUFFER.  Start searching at position START, for RANGE
+   characters.  Return the starting position of the match, -1 for no
+   match, or -2 for an internal error.  Also return register
+   information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
+extern int re_search
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+            int length, int start, int range, struct re_registers *regs));
+
+
+/* Like `re_search', but search in the concatenation of STRING1 and
+   STRING2.  Also, stop searching at index START + STOP.  */
+extern int re_search_2
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+             int length1, const char *string2, int length2,
+             int start, int range, struct re_registers *regs, int stop));
+
+
+/* Like `re_search', but return how many characters in STRING the regexp
+   in BUFFER matched, starting at position START.  */
+extern int re_match
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
+             int length, int start, struct re_registers *regs));
+
+
+/* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
+extern int re_match_2
+  _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
+             int length1, const char *string2, int length2,
+             int start, struct re_registers *regs, int stop));
+
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using BUFFER and REGS will use this memory
+   for recording register information.  STARTS and ENDS must be
+   allocated with malloc, and must each be at least `NUM_REGS * sizeof
+   (regoff_t)' bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+extern void re_set_registers
+  _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
+             unsigned num_regs, regoff_t *starts, regoff_t *ends));
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+# ifndef _CRAY
+/* 4.2 bsd compatibility.  */
+extern char *re_comp _RE_ARGS ((const char *));
+extern int re_exec _RE_ARGS ((const char *));
+# endif
+#endif
+
+/* GCC 2.95 and later have "__restrict"; C99 compilers have
+   "restrict", and "configure" may have defined "restrict".  */
+#ifndef __restrict
+# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
+#  if defined restrict || 199901L <= __STDC_VERSION__
+#   define __restrict restrict
+#  else
+#   define __restrict
+#  endif
+# endif
+#endif
+/* gcc 3.1 and up support the [restrict] syntax.  */
+#ifndef __restrict_arr
+# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)
+#  define __restrict_arr __restrict
+# else
+#  define __restrict_arr
+# endif
+#endif
+
+/* POSIX compatibility.  */
+extern int regcomp _RE_ARGS ((regex_t *__restrict __preg,
+                             const char *__restrict __pattern,
+                             int __cflags));
+
+extern int regexec _RE_ARGS ((const regex_t *__restrict __preg,
+                             const char *__restrict __string, size_t __nmatch,
+                             regmatch_t __pmatch[__restrict_arr],
+                             int __eflags));
+
+extern size_t regerror _RE_ARGS ((int __errcode, const regex_t *__preg,
+                                 char *__errbuf, size_t __errbuf_size));
+
+extern void regfree _RE_ARGS ((regex_t *__preg));
+
+
+#ifdef __cplusplus
+}
+#endif /* C++ */
+
+#endif /* regex.h */
+\f
+/*
+Local variables:
+make-backup-files: t
+version-control: t
+trim-versions-without-asking: nil
+End:
+*/
diff --git a/posix/regex_internal.c b/posix/regex_internal.c
new file mode 100644 (file)
index 0000000..f969c7c
--- /dev/null
@@ -0,0 +1,1263 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   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.  */
+
+static void re_string_construct_common (const char *str, int len,
+                                       re_string_t *pstr,
+                                       RE_TRANSLATE_TYPE trans, int icase);
+#ifdef RE_ENABLE_I18N
+static int re_string_skip_chars (re_string_t *pstr, int new_raw_idx,
+                                wint_t *last_wc);
+#endif /* RE_ENABLE_I18N */
+static re_dfastate_t *create_newstate_common (re_dfa_t *dfa,
+                                             const re_node_set *nodes,
+                                             unsigned int hash);
+static reg_errcode_t register_state (re_dfa_t *dfa, re_dfastate_t *newstate,
+                                    unsigned int hash);
+static re_dfastate_t *create_ci_newstate (re_dfa_t *dfa,
+                                         const re_node_set *nodes,
+                                         unsigned int hash);
+static re_dfastate_t *create_cd_newstate (re_dfa_t *dfa,
+                                         const re_node_set *nodes,
+                                         unsigned int context,
+                                         unsigned int hash);
+static unsigned int inline calc_state_hash (const re_node_set *nodes,
+                                           unsigned int context);
+\f
+/* Functions for string operation.  */
+
+/* This function allocate the buffers.  It is necessary to call
+   re_string_reconstruct before using the object.  */
+
+static reg_errcode_t
+re_string_allocate (pstr, str, len, init_len, trans, icase)
+     re_string_t *pstr;
+     const char *str;
+     int len, init_len, icase;
+     RE_TRANSLATE_TYPE trans;
+{
+  reg_errcode_t ret;
+  int init_buf_len = (len + 1 < init_len) ? len + 1: init_len;
+  re_string_construct_common (str, len, pstr, trans, icase);
+  pstr->stop = pstr->len;
+
+  ret = re_string_realloc_buffers (pstr, init_buf_len);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  pstr->mbs_case = (MBS_CASE_ALLOCATED (pstr) ? pstr->mbs_case
+                   : (unsigned char *) str);
+  pstr->mbs = MBS_ALLOCATED (pstr) ? pstr->mbs : pstr->mbs_case;
+  pstr->valid_len = (MBS_CASE_ALLOCATED (pstr) || MBS_ALLOCATED (pstr)
+                    || MB_CUR_MAX > 1) ? pstr->valid_len : len;
+  return REG_NOERROR;
+}
+
+/* This function allocate the buffers, and initialize them.  */
+
+static reg_errcode_t
+re_string_construct (pstr, str, len, trans, icase)
+     re_string_t *pstr;
+     const char *str;
+     int len, icase;
+     RE_TRANSLATE_TYPE trans;
+{
+  reg_errcode_t ret;
+  re_string_construct_common (str, len, pstr, trans, icase);
+  pstr->stop = pstr->len;
+  /* Set 0 so that this function can initialize whole buffers.  */
+  pstr->valid_len = 0;
+
+  if (len > 0)
+    {
+      ret = re_string_realloc_buffers (pstr, len + 1);
+      if (BE (ret != REG_NOERROR, 0))
+       return ret;
+    }
+  pstr->mbs_case = (MBS_CASE_ALLOCATED (pstr) ? pstr->mbs_case
+                   : (unsigned char *) str);
+  pstr->mbs = MBS_ALLOCATED (pstr) ? pstr->mbs : pstr->mbs_case;
+
+  if (icase)
+    {
+#ifdef RE_ENABLE_I18N
+      if (MB_CUR_MAX > 1)
+       build_wcs_upper_buffer (pstr);
+      else
+#endif /* RE_ENABLE_I18N  */
+       build_upper_buffer (pstr);
+    }
+  else
+    {
+#ifdef RE_ENABLE_I18N
+      if (MB_CUR_MAX > 1)
+       build_wcs_buffer (pstr);
+      else
+#endif /* RE_ENABLE_I18N  */
+       {
+         if (trans != NULL)
+           re_string_translate_buffer (pstr);
+         else
+           pstr->valid_len = len;
+       }
+    }
+
+  /* Initialized whole buffers, then valid_len == bufs_len.  */
+  pstr->valid_len = pstr->bufs_len;
+  return REG_NOERROR;
+}
+
+/* Helper functions for re_string_allocate, and re_string_construct.  */
+
+static reg_errcode_t
+re_string_realloc_buffers (pstr, new_buf_len)
+     re_string_t *pstr;
+     int new_buf_len;
+{
+#ifdef RE_ENABLE_I18N
+  if (MB_CUR_MAX > 1)
+    {
+      wint_t *new_array = re_realloc (pstr->wcs, wint_t, new_buf_len);
+      if (BE (new_array == NULL, 0))
+       return REG_ESPACE;
+      pstr->wcs = new_array;
+    }
+#endif /* RE_ENABLE_I18N  */
+  if (MBS_ALLOCATED (pstr))
+    {
+      unsigned char *new_array = re_realloc (pstr->mbs, unsigned char,
+                                            new_buf_len);
+      if (BE (new_array == NULL, 0))
+       return REG_ESPACE;
+      pstr->mbs = new_array;
+    }
+  if (MBS_CASE_ALLOCATED (pstr))
+    {
+      unsigned char *new_array = re_realloc (pstr->mbs_case, unsigned char,
+                                            new_buf_len);
+      if (BE (new_array == NULL, 0))
+       return REG_ESPACE;
+      pstr->mbs_case = new_array;
+      if (!MBS_ALLOCATED (pstr))
+       pstr->mbs = pstr->mbs_case;
+    }
+  pstr->bufs_len = new_buf_len;
+  return REG_NOERROR;
+}
+
+
+static void
+re_string_construct_common (str, len, pstr, trans, icase)
+     const char *str;
+     int len;
+     re_string_t *pstr;
+     RE_TRANSLATE_TYPE trans;
+     int icase;
+{
+  memset (pstr, '\0', sizeof (re_string_t));
+  pstr->raw_mbs = (const unsigned char *) str;
+  pstr->len = len;
+  pstr->trans = trans;
+  pstr->icase = icase ? 1 : 0;
+}
+
+#ifdef RE_ENABLE_I18N
+
+/* Build wide character buffer PSTR->WCS.
+   If the byte sequence of the string are:
+     <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3>
+   Then wide character buffer will be:
+     <wc1>   , WEOF    , <wc2>   , WEOF    , <wc3>
+   We use WEOF for padding, they indicate that the position isn't
+   a first byte of a multibyte character.
+
+   Note that this function assumes PSTR->VALID_LEN elements are already
+   built and starts from PSTR->VALID_LEN.  */
+
+static void
+build_wcs_buffer (pstr)
+     re_string_t *pstr;
+{
+  mbstate_t prev_st;
+  int byte_idx, end_idx, mbclen, remain_len;
+  /* Build the buffers from pstr->valid_len to either pstr->len or
+     pstr->bufs_len.  */
+  end_idx = (pstr->bufs_len > pstr->len)? pstr->len : pstr->bufs_len;
+  for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
+    {
+      wchar_t wc;
+      remain_len = end_idx - byte_idx;
+      prev_st = pstr->cur_state;
+      mbclen = mbrtowc (&wc, ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
+                             + byte_idx), remain_len, &pstr->cur_state);
+      if (BE (mbclen == (size_t) -2, 0))
+       {
+         /* The buffer doesn't have enough space, finish to build.  */
+         pstr->cur_state = prev_st;
+         break;
+       }
+      else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0))
+       {
+         /* We treat these cases as a singlebyte character.  */
+         mbclen = 1;
+         wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+         pstr->cur_state = prev_st;
+       }
+
+      /* Apply the translateion if we need.  */
+      if (pstr->trans != NULL && mbclen == 1)
+       {
+         int ch = pstr->trans[pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]];
+         pstr->mbs_case[byte_idx] = ch;
+       }
+      /* Write wide character and padding.  */
+      pstr->wcs[byte_idx++] = wc;
+      /* Write paddings.  */
+      for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+       pstr->wcs[byte_idx++] = WEOF;
+    }
+  pstr->valid_len = byte_idx;
+}
+
+/* Build wide character buffer PSTR->WCS like build_wcs_buffer,
+   but for REG_ICASE.  */
+
+static void
+build_wcs_upper_buffer (pstr)
+     re_string_t *pstr;
+{
+  mbstate_t prev_st;
+  int byte_idx, end_idx, mbclen, remain_len;
+  /* Build the buffers from pstr->valid_len to either pstr->len or
+     pstr->bufs_len.  */
+  end_idx = (pstr->bufs_len > pstr->len)? pstr->len : pstr->bufs_len;
+  for (byte_idx = pstr->valid_len; byte_idx < end_idx;)
+    {
+      wchar_t wc;
+      remain_len = end_idx - byte_idx;
+      prev_st = pstr->cur_state;
+      mbclen = mbrtowc (&wc, ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx
+                             + byte_idx), remain_len, &pstr->cur_state);
+      if (BE (mbclen == (size_t) -2, 0))
+       {
+         /* The buffer doesn't have enough space, finish to build.  */
+         pstr->cur_state = prev_st;
+         break;
+       }
+      else if (mbclen == 1 || mbclen == (size_t) -1 || mbclen == 0)
+       {
+         /* In case of a singlebyte character.  */
+         int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+         /* Apply the translateion if we need.  */
+         if (pstr->trans != NULL && mbclen == 1)
+           {
+             ch = pstr->trans[ch];
+             pstr->mbs_case[byte_idx] = ch;
+           }
+         pstr->wcs[byte_idx] = iswlower (wc) ? toupper (wc) : wc;
+         pstr->mbs[byte_idx++] = islower (ch) ? toupper (ch) : ch;
+         if (BE (mbclen == (size_t) -1, 0))
+           pstr->cur_state = prev_st;
+       }
+      else /* mbclen > 1 */
+       {
+         if (iswlower (wc))
+           wcrtomb ((char *) pstr->mbs + byte_idx, towupper (wc), &prev_st);
+         else
+           memcpy (pstr->mbs + byte_idx,
+                   pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen);
+         pstr->wcs[byte_idx++] = iswlower (wc) ? toupper (wc) : wc;
+         /* Write paddings.  */
+         for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+           pstr->wcs[byte_idx++] = WEOF;
+       }
+    }
+  pstr->valid_len = byte_idx;
+}
+
+/* Skip characters until the index becomes greater than NEW_RAW_IDX.
+   Return the index.  */
+
+static int
+re_string_skip_chars (pstr, new_raw_idx, last_wc)
+     re_string_t *pstr;
+     int new_raw_idx;
+     wint_t *last_wc;
+{
+  mbstate_t prev_st;
+  int rawbuf_idx, mbclen;
+  wchar_t wc = 0;
+
+  /* Skip the characters which are not necessary to check.  */
+  for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_len;
+       rawbuf_idx < new_raw_idx;)
+    {
+      int remain_len;
+      remain_len = pstr->len - rawbuf_idx;
+      prev_st = pstr->cur_state;
+      mbclen = mbrtowc (&wc, (const char *) pstr->raw_mbs + rawbuf_idx,
+                       remain_len, &pstr->cur_state);
+      if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0))
+       {
+         /* We treat these cases as a singlebyte character.  */
+         mbclen = 1;
+         pstr->cur_state = prev_st;
+       }
+      /* Then proceed the next character.  */
+      rawbuf_idx += mbclen;
+    }
+  *last_wc = (wint_t) wc;
+  return rawbuf_idx;
+}
+#endif /* RE_ENABLE_I18N  */
+
+/* Build the buffer PSTR->MBS, and apply the translation if we need.
+   This function is used in case of REG_ICASE.  */
+
+static void
+build_upper_buffer (pstr)
+     re_string_t *pstr;
+{
+  int char_idx, end_idx;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx)
+    {
+      int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx];
+      if (pstr->trans != NULL)
+       {
+         ch =  pstr->trans[ch];
+         pstr->mbs_case[char_idx] = ch;
+       }
+      if (islower (ch))
+       pstr->mbs[char_idx] = toupper (ch);
+      else
+       pstr->mbs[char_idx] = ch;
+    }
+  pstr->valid_len = char_idx;
+}
+
+/* Apply TRANS to the buffer in PSTR.  */
+
+static void
+re_string_translate_buffer (pstr)
+     re_string_t *pstr;
+{
+  int buf_idx, end_idx;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx)
+    {
+      int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx];
+      pstr->mbs_case[buf_idx] = pstr->trans[ch];
+    }
+
+  pstr->valid_len = buf_idx;
+}
+
+/* This function re-construct the buffers.
+   Concretely, convert to wide character in case of MB_CUR_MAX > 1,
+   convert to upper case in case of REG_ICASE, apply translation.  */
+
+static reg_errcode_t
+re_string_reconstruct (pstr, idx, eflags, newline)
+     re_string_t *pstr;
+     int idx, eflags, newline;
+{
+  int offset = idx - pstr->raw_mbs_idx;
+  if (offset < 0)
+    {
+      /* Reset buffer.  */
+#ifdef RE_ENABLE_I18N
+      if (MB_CUR_MAX > 1)
+       memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+#endif /* RE_ENABLE_I18N */
+      pstr->len += pstr->raw_mbs_idx;
+      pstr->stop += pstr->raw_mbs_idx;
+      pstr->valid_len = pstr->raw_mbs_idx = 0;
+      pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+                          : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
+      if (!MBS_CASE_ALLOCATED (pstr))
+       pstr->mbs_case = (unsigned char *) pstr->raw_mbs;
+      if (!MBS_ALLOCATED (pstr) && !MBS_CASE_ALLOCATED (pstr))
+       pstr->mbs = (unsigned char *) pstr->raw_mbs;
+      offset = idx;
+    }
+
+  if (offset != 0)
+    {
+      /* Are the characters which are already checked remain?  */
+      if (offset < pstr->valid_len)
+       {
+         /* Yes, move them to the front of the buffer.  */
+         pstr->tip_context = re_string_context_at (pstr, offset - 1, eflags,
+                                                   newline);
+#ifdef RE_ENABLE_I18N
+         if (MB_CUR_MAX > 1)
+           memmove (pstr->wcs, pstr->wcs + offset,
+                    (pstr->valid_len - offset) * sizeof (wint_t));
+#endif /* RE_ENABLE_I18N */
+         if (MBS_ALLOCATED (pstr))
+           memmove (pstr->mbs, pstr->mbs + offset,
+                    pstr->valid_len - offset);
+         if (MBS_CASE_ALLOCATED (pstr))
+           memmove (pstr->mbs_case, pstr->mbs_case + offset,
+                    pstr->valid_len - offset);
+         pstr->valid_len -= offset;
+#if DEBUG
+         assert (pstr->valid_len > 0);
+#endif
+       }
+      else
+       {
+         /* No, skip all characters until IDX.  */
+         pstr->valid_len = 0;
+#ifdef RE_ENABLE_I18N
+         if (MB_CUR_MAX > 1)
+           {
+             int wcs_idx;
+             wint_t wc;
+             pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx;
+             for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx)
+               pstr->wcs[wcs_idx] = WEOF;
+             if (pstr->trans && wc <= 0xff)
+               wc = pstr->trans[wc];
+             pstr->tip_context = (IS_WIDE_WORD_CHAR (wc) ? CONTEXT_WORD
+                                  : ((newline && IS_WIDE_NEWLINE (wc))
+                                     ? CONTEXT_NEWLINE : 0));
+           }
+         else
+#endif /* RE_ENABLE_I18N */
+           {
+             int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1];
+             if (pstr->trans)
+               c = pstr->trans[c];
+             pstr->tip_context = (IS_WORD_CHAR (c) ? CONTEXT_WORD
+                                  : ((newline && IS_NEWLINE (c))
+                                     ? CONTEXT_NEWLINE : 0));
+           }
+       }
+      if (!MBS_CASE_ALLOCATED (pstr))
+       {
+         pstr->mbs_case += offset;
+         /* In case of !MBS_ALLOCATED && !MBS_CASE_ALLOCATED.  */
+         if (!MBS_ALLOCATED (pstr))
+           pstr->mbs += offset;
+       }
+    }
+  pstr->raw_mbs_idx = idx;
+  pstr->len -= offset;
+  pstr->stop -= offset;
+
+  /* Then build the buffers.  */
+#ifdef RE_ENABLE_I18N
+  if (MB_CUR_MAX > 1)
+    {
+      if (pstr->icase)
+       build_wcs_upper_buffer (pstr);
+      else
+       build_wcs_buffer (pstr);
+    }
+  else
+#endif /* RE_ENABLE_I18N */
+    {
+      if (pstr->icase)
+       build_upper_buffer (pstr);
+      else if (pstr->trans != NULL)
+       re_string_translate_buffer (pstr);
+    }
+  pstr->cur_idx = 0;
+
+  return REG_NOERROR;
+}
+
+static void
+re_string_destruct (pstr)
+     re_string_t *pstr;
+{
+#ifdef RE_ENABLE_I18N
+  re_free (pstr->wcs);
+#endif /* RE_ENABLE_I18N  */
+  if (MBS_ALLOCATED (pstr))
+    re_free (pstr->mbs);
+  if (MBS_CASE_ALLOCATED (pstr))
+    re_free (pstr->mbs_case);
+}
+
+/* Return the context at IDX in INPUT.  */
+
+static unsigned int
+re_string_context_at (input, idx, eflags, newline_anchor)
+     const re_string_t *input;
+     int idx, eflags, newline_anchor;
+{
+  int c;
+  if (idx < 0 || idx == input->len)
+    {
+      if (idx < 0)
+       /* In this case, we use the value stored in input->tip_context,
+          since we can't know the character in input->mbs[-1] here.  */
+       return input->tip_context;
+      else /* (idx == input->len) */
+       return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF
+               : CONTEXT_NEWLINE | CONTEXT_ENDBUF);
+    }
+#ifdef RE_ENABLE_I18N
+  if (MB_CUR_MAX > 1)
+    {
+      wint_t wc;
+      int wc_idx = idx;
+      while(input->wcs[wc_idx] == WEOF)
+       {
+#ifdef DEBUG
+         /* It must not happen.  */
+         assert (wc_idx >= 0);
+#endif
+         --wc_idx;
+         if (wc_idx < 0)
+           return input->tip_context;
+       }
+      wc = input->wcs[wc_idx];
+      if (IS_WIDE_WORD_CHAR (wc))
+       return CONTEXT_WORD;
+      return (newline_anchor && IS_WIDE_NEWLINE (wc)) ? CONTEXT_NEWLINE : 0;
+    }
+  else
+#endif
+    {
+      c = re_string_byte_at (input, idx);
+      if (IS_WORD_CHAR (c))
+       return CONTEXT_WORD;
+      return (newline_anchor && IS_NEWLINE (c)) ? CONTEXT_NEWLINE : 0;
+    }
+}
+\f
+/* Functions for set operation.  */
+
+static reg_errcode_t
+re_node_set_alloc (set, size)
+     re_node_set *set;
+     int size;
+{
+  set->alloc = size;
+  set->nelem = 0;
+  set->elems = re_malloc (int, size);
+  if (BE (set->elems == NULL, 0))
+    return REG_ESPACE;
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_1 (set, elem)
+     re_node_set *set;
+     int elem;
+{
+  set->alloc = 1;
+  set->nelem = 1;
+  set->elems = re_malloc (int, 1);
+  if (BE (set->elems == NULL, 0))
+    {
+      set->alloc = set->nelem = 0;
+      return REG_ESPACE;
+    }
+  set->elems[0] = elem;
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_2 (set, elem1, elem2)
+     re_node_set *set;
+     int elem1, elem2;
+{
+  set->alloc = 2;
+  set->elems = re_malloc (int, 2);
+  if (BE (set->elems == NULL, 0))
+    return REG_ESPACE;
+  if (elem1 == elem2)
+    {
+      set->nelem = 1;
+      set->elems[0] = elem1;
+    }
+  else
+    {
+      set->nelem = 2;
+      if (elem1 < elem2)
+       {
+         set->elems[0] = elem1;
+         set->elems[1] = elem2;
+       }
+      else
+       {
+         set->elems[0] = elem2;
+         set->elems[1] = elem1;
+       }
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+re_node_set_init_copy (dest, src)
+     re_node_set *dest;
+     const re_node_set *src;
+{
+  dest->nelem = src->nelem;
+  if (src->nelem > 0)
+    {
+      dest->alloc = dest->nelem;
+      dest->elems = re_malloc (int, dest->alloc);
+      if (BE (dest->elems == NULL, 0))
+       {
+         dest->alloc = dest->nelem = 0;
+         return REG_ESPACE;
+       }
+      memcpy (dest->elems, src->elems, src->nelem * sizeof (int));
+    }
+  else
+    re_node_set_init_empty (dest);
+  return REG_NOERROR;
+}
+
+/* Calculate the intersection of the sets SRC1 and SRC2. And merge it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.
+   Note: We assume dest->elems is NULL, when dest->alloc is 0.  */
+
+static reg_errcode_t
+re_node_set_add_intersect (dest, src1, src2)
+     re_node_set *dest;
+     const re_node_set *src1, *src2;
+{
+  int i1, i2, id;
+  if (src1->nelem > 0 && src2->nelem > 0)
+    {
+      if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
+       {
+         dest->alloc = src1->nelem + src2->nelem + dest->nelem;
+         dest->elems = re_realloc (dest->elems, int, dest->alloc);
+         if (BE (dest->elems == NULL, 0))
+           return REG_ESPACE;
+       }
+    }
+  else
+    return REG_NOERROR;
+
+  for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+    {
+      if (src1->elems[i1] > src2->elems[i2])
+       {
+         ++i2;
+         continue;
+       }
+      if (src1->elems[i1] == src2->elems[i2])
+       {
+         while (id < dest->nelem && dest->elems[id] < src2->elems[i2])
+           ++id;
+         if (id < dest->nelem && dest->elems[id] == src2->elems[i2])
+           ++id;
+         else
+           {
+             memmove (dest->elems + id + 1, dest->elems + id,
+                      sizeof (int) * (dest->nelem - id));
+             dest->elems[id++] = src2->elems[i2++];
+             ++dest->nelem;
+           }
+       }
+      ++i1;
+    }
+  return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets SRC1 and SRC2. And store it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.  */
+
+static reg_errcode_t
+re_node_set_init_union (dest, src1, src2)
+     re_node_set *dest;
+     const re_node_set *src1, *src2;
+{
+  int i1, i2, id;
+  if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0)
+    {
+      dest->alloc = src1->nelem + src2->nelem;
+      dest->elems = re_malloc (int, dest->alloc);
+      if (BE (dest->elems == NULL, 0))
+       return REG_ESPACE;
+    }
+  else
+    {
+      if (src1 != NULL && src1->nelem > 0)
+       return re_node_set_init_copy (dest, src1);
+      else if (src2 != NULL && src2->nelem > 0)
+       return re_node_set_init_copy (dest, src2);
+      else
+       re_node_set_init_empty (dest);
+      return REG_NOERROR;
+    }
+  for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+    {
+      if (src1->elems[i1] > src2->elems[i2])
+       {
+         dest->elems[id++] = src2->elems[i2++];
+         continue;
+       }
+      if (src1->elems[i1] == src2->elems[i2])
+       ++i2;
+      dest->elems[id++] = src1->elems[i1++];
+    }
+  if (i1 < src1->nelem)
+    {
+      memcpy (dest->elems + id, src1->elems + i1,
+            (src1->nelem - i1) * sizeof (int));
+      id += src1->nelem - i1;
+    }
+  else if (i2 < src2->nelem)
+    {
+      memcpy (dest->elems + id, src2->elems + i2,
+            (src2->nelem - i2) * sizeof (int));
+      id += src2->nelem - i2;
+    }
+  dest->nelem = id;
+  return REG_NOERROR;
+}
+
+/* Calculate the union set of the sets DEST and SRC. And store it to
+   DEST. Return value indicate the error code or REG_NOERROR if succeeded.  */
+
+static reg_errcode_t
+re_node_set_merge (dest, src)
+     re_node_set *dest;
+     const re_node_set *src;
+{
+  int si, di;
+  if (src == NULL || src->nelem == 0)
+    return REG_NOERROR;
+  if (dest->alloc < src->nelem + dest->nelem)
+    {
+      int *new_buffer;
+      dest->alloc = 2 * (src->nelem + dest->alloc);
+      new_buffer = re_realloc (dest->elems, int, dest->alloc);
+      if (BE (new_buffer == NULL, 0))
+       return REG_ESPACE;
+      dest->elems = new_buffer;
+    }
+
+  for (si = 0, di = 0 ; si < src->nelem && di < dest->nelem ;)
+    {
+      int cp_from, ncp, mid, right, src_elem = src->elems[si];
+      /* Binary search the spot we will add the new element.  */
+      right = dest->nelem;
+      while (di < right)
+       {
+         mid = (di + right) / 2;
+         if (dest->elems[mid] < src_elem)
+           di = mid + 1;
+         else
+           right = mid;
+       }
+      if (di >= dest->nelem)
+       break;
+
+      if (dest->elems[di] == src_elem)
+       {
+         /* Skip since, DEST already has the element.  */
+         ++di;
+         ++si;
+         continue;
+       }
+
+      /* Skip the src elements which are less than dest->elems[di].  */
+      cp_from = si;
+      while (si < src->nelem && src->elems[si] < dest->elems[di])
+       ++si;
+      /* Copy these src elements.  */
+      ncp = si - cp_from;
+      memmove (dest->elems + di + ncp, dest->elems + di,
+              sizeof (int) * (dest->nelem - di));
+      memcpy (dest->elems + di, src->elems + cp_from,
+             sizeof (int) * ncp);
+      /* Update counters.  */
+      di += ncp;
+      dest->nelem += ncp;
+    }
+
+  /* Copy remaining src elements.  */
+  if (si < src->nelem)
+    {
+      memcpy (dest->elems + di, src->elems + si,
+             sizeof (int) * (src->nelem - si));
+      dest->nelem += src->nelem - si;
+    }
+  return REG_NOERROR;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+   return 0 if SET already has ELEM,
+   return -1 if an error is occured, return 1 otherwise.  */
+
+static int
+re_node_set_insert (set, elem)
+     re_node_set *set;
+     int elem;
+{
+  int idx, right, mid;
+  /* In case of the set is empty.  */
+  if (set->elems == NULL || set->alloc == 0)
+    {
+      if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1))
+       return 1;
+      else
+       return -1;
+    }
+
+  /* Binary search the spot we will add the new element.  */
+  idx = 0;
+  right = set->nelem;
+  while (idx < right)
+    {
+      mid = (idx + right) / 2;
+      if (set->elems[mid] < elem)
+       idx = mid + 1;
+      else
+       right = mid;
+    }
+
+  /* Realloc if we need.  */
+  if (set->alloc < set->nelem + 1)
+    {
+      int *new_array;
+      set->alloc = set->alloc * 2;
+      new_array = re_malloc (int, set->alloc);
+      if (BE (new_array == NULL, 0))
+       return -1;
+      /* Copy the elements they are followed by the new element.  */
+      if (idx > 0)
+       memcpy (new_array, set->elems, sizeof (int) * (idx));
+      /* Copy the elements which follows the new element.  */
+      if (set->nelem - idx > 0)
+       memcpy (new_array + idx + 1, set->elems + idx,
+               sizeof (int) * (set->nelem - idx));
+      re_free (set->elems);
+      set->elems = new_array;
+    }
+  else
+    {
+      /* Move the elements which follows the new element.  */
+      if (set->nelem - idx > 0)
+       memmove (set->elems + idx + 1, set->elems + idx,
+                sizeof (int) * (set->nelem - idx));
+    }
+  /* Insert the new element.  */
+  set->elems[idx] = elem;
+  ++set->nelem;
+  return 1;
+}
+
+/* Compare two node sets SET1 and SET2.
+   return 1 if SET1 and SET2 are equivalent, retrun 0 otherwise.  */
+
+static int
+re_node_set_compare (set1, set2)
+     const re_node_set *set1, *set2;
+{
+  int i;
+  if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
+    return 0;
+  for (i = 0 ; i < set1->nelem ; i++)
+    if (set1->elems[i] != set2->elems[i])
+      return 0;
+  return 1;
+}
+
+/* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise.  */
+
+static int
+re_node_set_contains (set, elem)
+     const re_node_set *set;
+     int elem;
+{
+  int idx, right, mid;
+  if (set->nelem <= 0)
+    return 0;
+
+  /* Binary search the element.  */
+  idx = 0;
+  right = set->nelem - 1;
+  while (idx < right)
+    {
+      mid = (idx + right) / 2;
+      if (set->elems[mid] < elem)
+       idx = mid + 1;
+      else
+       right = mid;
+    }
+  return set->elems[idx] == elem ? idx + 1 : 0;
+}
+
+static void
+re_node_set_remove_at (set, idx)
+     re_node_set *set;
+     int idx;
+{
+  if (idx < 0 || idx >= set->nelem)
+    return;
+  if (idx < set->nelem - 1)
+    memmove (set->elems + idx, set->elems + idx + 1,
+            sizeof (int) * (set->nelem - idx - 1));
+  --set->nelem;
+}
+\f
+
+/* Add the token TOKEN to dfa->nodes, and return the index of the token.
+   Or return -1, if an error will be occured.  */
+
+static int
+re_dfa_add_node (dfa, token, mode)
+     re_dfa_t *dfa;
+     re_token_t token;
+     int mode;
+{
+  if (dfa->nodes_len >= dfa->nodes_alloc)
+    {
+      re_token_t *new_array;
+      dfa->nodes_alloc *= 2;
+      new_array = re_realloc (dfa->nodes, re_token_t, dfa->nodes_alloc);
+      if (BE (new_array == NULL, 0))
+       return -1;
+      else
+       dfa->nodes = new_array;
+      if (mode)
+       {
+         int *new_nexts, *new_indices;
+         re_node_set *new_edests, *new_eclosures, *new_inveclosures;
+
+         new_nexts = re_realloc (dfa->nexts, int, dfa->nodes_alloc);
+         new_indices = re_realloc (dfa->org_indices, int, dfa->nodes_alloc);
+         new_edests = re_realloc (dfa->edests, re_node_set, dfa->nodes_alloc);
+         new_eclosures = re_realloc (dfa->eclosures, re_node_set,
+                                     dfa->nodes_alloc);
+         new_inveclosures = re_realloc (dfa->inveclosures, re_node_set,
+                                        dfa->nodes_alloc);
+         if (BE (new_nexts == NULL || new_indices == NULL
+                 || new_edests == NULL || new_eclosures == NULL
+                 || new_inveclosures == NULL, 0))
+           return -1;
+         dfa->nexts = new_nexts;
+         dfa->org_indices = new_indices;
+         dfa->edests = new_edests;
+         dfa->eclosures = new_eclosures;
+         dfa->inveclosures = new_inveclosures;
+       }
+    }
+  dfa->nodes[dfa->nodes_len] = token;
+  dfa->nodes[dfa->nodes_len].duplicated = 0;
+  dfa->nodes[dfa->nodes_len].constraint = 0;
+  return dfa->nodes_len++;
+}
+
+static unsigned int inline
+calc_state_hash (nodes, context)
+     const re_node_set *nodes;
+     unsigned int context;
+{
+  unsigned int hash = nodes->nelem + context;
+  int i;
+  for (i = 0 ; i < nodes->nelem ; i++)
+    hash += nodes->elems[i];
+  return hash;
+}
+
+/* Search for the state whose node_set is equivalent to NODES.
+   Return the pointer to the state, if we found it in the DFA.
+   Otherwise create the new one and return it.  In case of an error
+   return NULL and set the error code in ERR.
+   Note: - We assume NULL as the invalid state, then it is possible that
+          return value is NULL and ERR is REG_NOERROR.
+        - We never return non-NULL value in case of any errors, it is for
+          optimization.  */
+
+static re_dfastate_t*
+re_acquire_state (err, dfa, nodes)
+     reg_errcode_t *err;
+     re_dfa_t *dfa;
+     const re_node_set *nodes;
+{
+  unsigned int hash;
+  re_dfastate_t *new_state;
+  struct re_state_table_entry *spot;
+  int i;
+  if (BE (nodes->nelem == 0, 0))
+    {
+      *err = REG_NOERROR;
+      return NULL;
+    }
+  hash = calc_state_hash (nodes, 0);
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+  for (i = 0 ; i < spot->num ; i++)
+    {
+      re_dfastate_t *state = spot->array[i];
+      if (hash != state->hash)
+       continue;
+      if (re_node_set_compare (&state->nodes, nodes))
+       return state;
+    }
+
+  /* There are no appropriate state in the dfa, create the new one.  */
+  new_state = create_ci_newstate (dfa, nodes, hash);
+  if (BE (new_state != NULL, 1))
+    return new_state;
+  else
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+}
+
+/* Search for the state whose node_set is equivalent to NODES and
+   whose context is equivalent to CONTEXT.
+   Return the pointer to the state, if we found it in the DFA.
+   Otherwise create the new one and return it.  In case of an error
+   return NULL and set the error code in ERR.
+   Note: - We assume NULL as the invalid state, then it is possible that
+          return value is NULL and ERR is REG_NOERROR.
+        - We never return non-NULL value in case of any errors, it is for
+          optimization.  */
+
+static re_dfastate_t*
+re_acquire_state_context (err, dfa, nodes, context)
+     reg_errcode_t *err;
+     re_dfa_t *dfa;
+     const re_node_set *nodes;
+     unsigned int context;
+{
+  unsigned int hash;
+  re_dfastate_t *new_state;
+  struct re_state_table_entry *spot;
+  int i;
+  if (nodes->nelem == 0)
+    {
+      *err = REG_NOERROR;
+      return NULL;
+    }
+  hash = calc_state_hash (nodes, context);
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+  for (i = 0 ; i < spot->num ; i++)
+    {
+      re_dfastate_t *state = spot->array[i];
+      if (hash != state->hash)
+       continue;
+      if (re_node_set_compare (state->entrance_nodes, nodes)
+         && state->context == context)
+       return state;
+    }
+  /* There are no appropriate state in `dfa', create the new one.  */
+  new_state = create_cd_newstate (dfa, nodes, context, hash);
+  if (BE (new_state != NULL, 1))
+    return new_state;
+  else
+    {
+      *err = REG_ESPACE;
+      return NULL;
+    }
+}
+
+/* Allocate memory for DFA state and initialize common properties.
+   Return the new state if succeeded, otherwise return NULL.  */
+
+static re_dfastate_t *
+create_newstate_common (dfa, nodes, hash)
+     re_dfa_t *dfa;
+     const re_node_set *nodes;
+     unsigned int hash;
+{
+  re_dfastate_t *newstate;
+  reg_errcode_t err;
+  newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
+  if (BE (newstate == NULL, 0))
+    return NULL;
+  err = re_node_set_init_copy (&newstate->nodes, nodes);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      re_free (newstate);
+      return NULL;
+    }
+  newstate->trtable = NULL;
+  newstate->trtable_search = NULL;
+  newstate->hash = hash;
+  return newstate;
+}
+
+/* Store the new state NEWSTATE whose hash value is HASH in appropriate
+   position.  Return value indicate the error code if failed.  */
+
+static reg_errcode_t
+register_state (dfa, newstate, hash)
+     re_dfa_t *dfa;
+     re_dfastate_t *newstate;
+     unsigned int hash;
+{
+  struct re_state_table_entry *spot;
+  spot = dfa->state_table + (hash & dfa->state_hash_mask);
+
+  if (spot->alloc <= spot->num)
+    {
+      re_dfastate_t **new_array;
+      spot->alloc = 2 * spot->num + 2;
+      new_array = re_realloc (spot->array, re_dfastate_t *, spot->alloc);
+      if (BE (new_array == NULL, 0))
+       return REG_ESPACE;
+      spot->array = new_array;
+    }
+  spot->array[spot->num++] = newstate;
+  return REG_NOERROR;
+}
+
+/* Create the new state which is independ of contexts.
+   Return the new state if succeeded, otherwise return NULL.  */
+
+static re_dfastate_t *
+create_ci_newstate (dfa, nodes, hash)
+     re_dfa_t *dfa;
+     const re_node_set *nodes;
+     unsigned int hash;
+{
+  int i;
+  reg_errcode_t err;
+  re_dfastate_t *newstate;
+  newstate = create_newstate_common (dfa, nodes, hash);
+  if (BE (newstate == NULL, 0))
+    return NULL;
+  newstate->entrance_nodes = &newstate->nodes;
+
+  for (i = 0 ; i < nodes->nelem ; i++)
+    {
+      re_token_t *node = dfa->nodes + nodes->elems[i];
+      re_token_type_t type = node->type;
+      if (type == CHARACTER && !node->constraint)
+       continue;
+
+      /* If the state has the halt node, the state is a halt state.  */
+      else if (type == END_OF_RE)
+       newstate->halt = 1;
+#ifdef RE_ENABLE_I18N
+      else if (type == COMPLEX_BRACKET
+              || (type == OP_PERIOD && MB_CUR_MAX > 1))
+       newstate->accept_mb = 1;
+#endif /* RE_ENABLE_I18N */
+      else if (type == OP_BACK_REF)
+       newstate->has_backref = 1;
+      else if (type == ANCHOR || node->constraint)
+       newstate->has_constraint = 1;
+    }
+  err = register_state (dfa, newstate, hash);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_state (newstate);
+      newstate = NULL;
+    }
+  return newstate;
+}
+
+/* Create the new state which is depend on the context CONTEXT.
+   Return the new state if succeeded, otherwise return NULL.  */
+
+static re_dfastate_t *
+create_cd_newstate (dfa, nodes, context, hash)
+     re_dfa_t *dfa;
+     const re_node_set *nodes;
+     unsigned int context, hash;
+{
+  int i, nctx_nodes = 0;
+  reg_errcode_t err;
+  re_dfastate_t *newstate;
+
+  newstate = create_newstate_common (dfa, nodes, hash);
+  if (BE (newstate == NULL, 0))
+    return NULL;
+  newstate->context = context;
+  newstate->entrance_nodes = &newstate->nodes;
+
+  for (i = 0 ; i < nodes->nelem ; i++)
+    {
+      unsigned int constraint = 0;
+      re_token_t *node = dfa->nodes + nodes->elems[i];
+      re_token_type_t type = node->type;
+      if (node->constraint)
+       constraint = node->constraint;
+
+      if (type == CHARACTER && !constraint)
+       continue;
+      /* If the state has the halt node, the state is a halt state.  */
+      else if (type == END_OF_RE)
+       newstate->halt = 1;
+#ifdef RE_ENABLE_I18N
+      else if (type == COMPLEX_BRACKET
+              || (type == OP_PERIOD && MB_CUR_MAX > 1))
+       newstate->accept_mb = 1;
+#endif /* RE_ENABLE_I18N */
+      else if (type == OP_BACK_REF)
+       newstate->has_backref = 1;
+      else if (type == ANCHOR)
+       constraint = node->opr.ctx_type;
+
+      if (constraint)
+       {
+         if (newstate->entrance_nodes == &newstate->nodes)
+           {
+             newstate->entrance_nodes = re_malloc (re_node_set, 1);
+             if (BE (newstate->entrance_nodes == NULL, 0))
+               {
+                 free_state (newstate);
+                 return NULL;
+               }
+             re_node_set_init_copy (newstate->entrance_nodes, nodes);
+             nctx_nodes = 0;
+             newstate->has_constraint = 1;
+           }
+
+         if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context))
+           {
+             re_node_set_remove_at (&newstate->nodes, i - nctx_nodes);
+             ++nctx_nodes;
+           }
+       }
+    }
+  err = register_state (dfa, newstate, hash);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      free_state (newstate);
+      newstate = NULL;
+    }
+  return  newstate;
+}
+
+static void
+free_state (state)
+     re_dfastate_t *state;
+{
+  if (state->entrance_nodes != &state->nodes)
+    {
+      re_node_set_free (state->entrance_nodes);
+      re_free (state->entrance_nodes);
+    }
+  re_node_set_free (&state->nodes);
+  re_free (state->trtable);
+  re_free (state->trtable_search);
+  re_free (state);
+}
diff --git a/posix/regex_internal.h b/posix/regex_internal.h
new file mode 100644 (file)
index 0000000..bf84ad6
--- /dev/null
@@ -0,0 +1,742 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   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 _REGEX_INTERNAL_H
+#define _REGEX_INTERNAL_H 1
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined HAVE_LOCALE_H || defined _LIBC
+# include <locale.h>
+#endif
+#if defined HAVE_WCHAR_H || defined _LIBC
+# include <wchar.h>
+#endif /* HAVE_WCHAR_H || _LIBC */
+#if defined HAVE_WCTYPE_H || defined _LIBC
+# include <wctype.h>
+#endif /* HAVE_WCTYPE_H || _LIBC */
+
+/* In case that the system doesn't have isblank().  */
+#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank
+# define isblank(ch) ((ch) == ' ' || (ch) == '\t')
+#endif
+
+#ifdef _LIBC
+# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
+#  define _RE_DEFINE_LOCALE_FUNCTIONS 1
+#   include <locale/localeinfo.h>
+#   include <locale/elem-hash.h>
+#   include <locale/coll-lookup.h>
+# endif
+#endif
+
+/* This is for other GNU distributions with internationalized messages.  */
+#if HAVE_LIBINTL_H || defined _LIBC
+# include <libintl.h>
+# ifdef _LIBC
+#  undef gettext
+#  define gettext(msgid) \
+  INTUSE(__dcgettext) (INTUSE(_libc_intl_domainname), msgid, LC_MESSAGES)
+# endif
+#else
+# define gettext(msgid) (msgid)
+#endif
+
+#ifndef gettext_noop
+/* This define is so xgettext can find the internationalizable
+   strings.  */
+# define gettext_noop(String) String
+#endif
+
+#if (defined MB_CUR_MAX && HAVE_LOCALE_H && HAVE_WCTYPE_H && HAVE_WCHAR_H && HAVE_WCRTOMB && HAVE_MBRTOWC && HAVE_WCSCOLL) || _LIBC
+# define RE_ENABLE_I18N
+#endif
+
+#if __GNUC__ >= 3
+# define BE(expr, val) __builtin_expect (expr, val)
+#else
+# define BE(expr, val) (expr)
+# define inline
+#endif
+
+/* Number of bits in a byte.  */
+#define BYTE_BITS 8
+/* Number of single byte character.  */
+#define SBC_MAX 256
+
+#define COLL_ELEM_LEN_MAX 8
+
+/* The character which represents newline.  */
+#define NEWLINE_CHAR '\n'
+#define WIDE_NEWLINE_CHAR L'\n'
+
+/* Rename to standard API for using out of glibc.  */
+#ifndef _LIBC
+# define __wctype wctype
+# define __iswctype iswctype
+# define __btowc btowc
+# define __mempcpy mempcpy
+# define __wcrtomb wcrtomb
+# define attribute_hidden
+#endif /* not _LIBC */
+
+extern const char __re_error_msgid[] attribute_hidden;
+extern const size_t __re_error_msgid_idx[] attribute_hidden;
+
+/* Number of bits in an unsinged int.  */
+#define UINT_BITS (sizeof (unsigned int) * BYTE_BITS)
+/* Number of unsigned int in an bit_set.  */
+#define BITSET_UINTS ((SBC_MAX + UINT_BITS - 1) / UINT_BITS)
+typedef unsigned int bitset[BITSET_UINTS];
+typedef unsigned int *re_bitset_ptr_t;
+
+#define bitset_set(set,i) (set[i / UINT_BITS] |= 1 << i % UINT_BITS)
+#define bitset_clear(set,i) (set[i / UINT_BITS] &= ~(1 << i % UINT_BITS))
+#define bitset_contain(set,i) (set[i / UINT_BITS] & (1 << i % UINT_BITS))
+#define bitset_empty(set) memset (set, 0, sizeof (unsigned int) * BITSET_UINTS)
+#define bitset_set_all(set) \
+  memset (set, 255, sizeof (unsigned int) * BITSET_UINTS)
+#define bitset_copy(dest,src) \
+  memcpy (dest, src, sizeof (unsigned int) * BITSET_UINTS)
+static inline void bitset_not (bitset set);
+static inline void bitset_merge (bitset dest, const bitset src);
+static inline void bitset_not_merge (bitset dest, const bitset src);
+
+#define PREV_WORD_CONSTRAINT 0x0001
+#define PREV_NOTWORD_CONSTRAINT 0x0002
+#define NEXT_WORD_CONSTRAINT 0x0004
+#define NEXT_NOTWORD_CONSTRAINT 0x0008
+#define PREV_NEWLINE_CONSTRAINT 0x0010
+#define NEXT_NEWLINE_CONSTRAINT 0x0020
+#define PREV_BEGBUF_CONSTRAINT 0x0040
+#define NEXT_ENDBUF_CONSTRAINT 0x0080
+#define DUMMY_CONSTRAINT 0x0100
+
+typedef enum
+{
+  INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+  WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
+  WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
+  LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
+  LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
+  BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
+  BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
+  WORD_DELIM = DUMMY_CONSTRAINT
+} re_context_type;
+
+typedef struct
+{
+  int alloc;
+  int nelem;
+  int *elems;
+} re_node_set;
+
+typedef enum
+{
+  NON_TYPE = 0,
+
+  /* Token type, these are used only by token.  */
+  OP_OPEN_BRACKET,
+  OP_CLOSE_BRACKET,
+  OP_CHARSET_RANGE,
+  OP_OPEN_DUP_NUM,
+  OP_CLOSE_DUP_NUM,
+  OP_NON_MATCH_LIST,
+  OP_OPEN_COLL_ELEM,
+  OP_CLOSE_COLL_ELEM,
+  OP_OPEN_EQUIV_CLASS,
+  OP_CLOSE_EQUIV_CLASS,
+  OP_OPEN_CHAR_CLASS,
+  OP_CLOSE_CHAR_CLASS,
+  OP_WORD,
+  OP_NOTWORD,
+  BACK_SLASH,
+
+  /* Tree type, these are used only by tree. */
+  CONCAT,
+  ALT,
+  SUBEXP,
+  SIMPLE_BRACKET,
+#ifdef RE_ENABLE_I18N
+  COMPLEX_BRACKET,
+#endif /* RE_ENABLE_I18N */
+
+  /* Node type, These are used by token, node, tree.  */
+  OP_OPEN_SUBEXP,
+  OP_CLOSE_SUBEXP,
+  OP_PERIOD,
+  CHARACTER,
+  END_OF_RE,
+  OP_ALT,
+  OP_DUP_ASTERISK,
+  OP_DUP_PLUS,
+  OP_DUP_QUESTION,
+  OP_BACK_REF,
+  ANCHOR,
+
+  /* Dummy marker.  */
+  END_OF_RE_TOKEN_T
+} re_token_type_t;
+
+#ifdef RE_ENABLE_I18N
+typedef struct
+{
+  /* Multibyte characters.  */
+  wchar_t *mbchars;
+
+  /* Collating symbols.  */
+# ifdef _LIBC
+  int32_t *coll_syms;
+# endif
+
+  /* Equivalence classes. */
+# ifdef _LIBC
+  int32_t *equiv_classes;
+# endif
+
+  /* Range expressions. */
+# ifdef _LIBC
+  uint32_t *range_starts;
+  uint32_t *range_ends;
+# else /* not _LIBC */
+  wchar_t *range_starts;
+  wchar_t *range_ends;
+# endif /* not _LIBC */
+
+  /* Character classes. */
+  wctype_t *char_classes;
+
+  /* If this character set is the non-matching list.  */
+  unsigned int non_match : 1;
+
+  /* # of multibyte characters.  */
+  int nmbchars;
+
+  /* # of collating symbols.  */
+  int ncoll_syms;
+
+  /* # of equivalence classes. */
+  int nequiv_classes;
+
+  /* # of range expressions. */
+  int nranges;
+
+  /* # of character classes. */
+  int nchar_classes;
+} re_charset_t;
+#endif /* RE_ENABLE_I18N */
+
+typedef struct
+{
+  union
+  {
+    unsigned char c;           /* for CHARACTER */
+    re_bitset_ptr_t sbcset;    /* for SIMPLE_BRACKET */
+#ifdef RE_ENABLE_I18N
+    re_charset_t *mbcset;      /* for COMPLEX_BRACKET */
+#endif /* RE_ENABLE_I18N */
+    int idx;                   /* for BACK_REF */
+    re_context_type ctx_type;  /* for ANCHOR */
+  } opr;
+#if __GNUC__ >= 2
+  re_token_type_t type : 8;
+#else
+  re_token_type_t type;
+#endif
+  unsigned int constraint : 10;        /* context constraint */
+  unsigned int duplicated : 1;
+#ifdef RE_ENABLE_I18N
+  unsigned int mb_partial : 1;
+#endif
+} re_token_t;
+
+#define IS_EPSILON_NODE(type) \
+  ((type) == OP_ALT || (type) == OP_DUP_ASTERISK || (type) == OP_DUP_PLUS \
+   || (type) == OP_DUP_QUESTION || (type) == ANCHOR \
+   || (type) == OP_OPEN_SUBEXP || (type) == OP_CLOSE_SUBEXP)
+
+#define ACCEPT_MB_NODE(type) \
+  ((type) == COMPLEX_BRACKET || (type) == OP_PERIOD)
+
+struct re_string_t
+{
+  /* Indicate the raw buffer which is the original string passed as an
+     argument of regexec(), re_search(), etc..  */
+  const unsigned char *raw_mbs;
+  /* Store the multibyte string.  In case of "case insensitive mode" like
+     REG_ICASE, upper cases of the string are stored, otherwise MBS points
+     the same address that RAW_MBS points.  */
+  unsigned char *mbs;
+  /* Store the case sensitive multibyte string.  In case of
+     "case insensitive mode", the original string are stored,
+     otherwise MBS_CASE points the same address that MBS points.  */
+  unsigned char *mbs_case;
+#ifdef RE_ENABLE_I18N
+  /* Store the wide character string which is corresponding to MBS.  */
+  wint_t *wcs;
+  mbstate_t cur_state;
+#endif
+  /* Index in RAW_MBS.  Each character mbs[i] corresponds to
+     raw_mbs[raw_mbs_idx + i].  */
+  int raw_mbs_idx;
+  /* The length of the valid characters in the buffers.  */
+  int valid_len;
+  /* The length of the buffers MBS, MBS_CASE, and WCS.  */
+  int bufs_len;
+  /* The index in MBS, which is updated by re_string_fetch_byte.  */
+  int cur_idx;
+  /* This is length_of_RAW_MBS - RAW_MBS_IDX.  */
+  int len;
+  /* End of the buffer may be shorter than its length in the cases such
+     as re_match_2, re_search_2.  Then, we use STOP for end of the buffer
+     instead of LEN.  */
+  int stop;
+
+  /* The context of mbs[0].  We store the context independently, since
+     the context of mbs[0] may be different from raw_mbs[0], which is
+     the beginning of the input string.  */
+  unsigned int tip_context;
+  /* The translation passed as a part of an argument of re_compile_pattern.  */
+  RE_TRANSLATE_TYPE trans;
+  /* 1 if REG_ICASE.  */
+  unsigned int icase : 1;
+};
+typedef struct re_string_t re_string_t;
+/* In case of REG_ICASE, we allocate the buffer dynamically for mbs.  */
+#define MBS_ALLOCATED(pstr) (pstr->icase)
+/* In case that we need translation, we allocate the buffer dynamically
+   for mbs_case.  Note that mbs == mbs_case if not REG_ICASE.  */
+#define MBS_CASE_ALLOCATED(pstr) (pstr->trans != NULL)
+
+
+static reg_errcode_t re_string_allocate (re_string_t *pstr, const char *str,
+                                        int len, int init_len,
+                                        RE_TRANSLATE_TYPE trans, int icase);
+static reg_errcode_t re_string_construct (re_string_t *pstr, const char *str,
+                                         int len, RE_TRANSLATE_TYPE trans,
+                                         int icase);
+static reg_errcode_t re_string_reconstruct (re_string_t *pstr, int idx,
+                                           int eflags, int newline);
+static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
+                                               int new_buf_len);
+#ifdef RE_ENABLE_I18N
+static void build_wcs_buffer (re_string_t *pstr);
+static void build_wcs_upper_buffer (re_string_t *pstr);
+#endif /* RE_ENABLE_I18N */
+static void build_upper_buffer (re_string_t *pstr);
+static void re_string_translate_buffer (re_string_t *pstr);
+static void re_string_destruct (re_string_t *pstr);
+#ifdef RE_ENABLE_I18N
+static int re_string_elem_size_at (const re_string_t *pstr, int idx);
+static inline int re_string_char_size_at (const re_string_t *pstr, int idx);
+static inline wint_t re_string_wchar_at (const re_string_t *pstr, int idx);
+#endif /* RE_ENABLE_I18N */
+static unsigned int re_string_context_at (const re_string_t *input, int idx,
+                                         int eflags, int newline_anchor);
+#define re_string_peek_byte(pstr, offset) \
+  ((pstr)->mbs[(pstr)->cur_idx + offset])
+#define re_string_peek_byte_case(pstr, offset) \
+  ((pstr)->mbs_case[(pstr)->cur_idx + offset])
+#define re_string_fetch_byte(pstr) \
+  ((pstr)->mbs[(pstr)->cur_idx++])
+#define re_string_fetch_byte_case(pstr) \
+  ((pstr)->mbs_case[(pstr)->cur_idx++])
+#define re_string_first_byte(pstr, idx) \
+  ((idx) == (pstr)->len || (pstr)->wcs[idx] != WEOF)
+#define re_string_is_single_byte_char(pstr, idx) \
+  ((pstr)->wcs[idx] != WEOF && ((pstr)->len == (idx) \
+                               || (pstr)->wcs[(idx) + 1] != WEOF))
+#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
+#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
+#define re_string_get_buffer(pstr) ((pstr)->mbs)
+#define re_string_length(pstr) ((pstr)->len)
+#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx])
+#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
+#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
+
+#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
+#define re_realloc(p,t,n) ((t *) realloc (p, (n) * sizeof (t)))
+#define re_free(p) free (p)
+
+struct bin_tree_t
+{
+  struct bin_tree_t *parent;
+  struct bin_tree_t *left;
+  struct bin_tree_t *right;
+
+  /* `node_idx' is the index in dfa->nodes, if `type' == 0.
+     Otherwise `type' indicate the type of this node.  */
+  re_token_type_t type;
+  int node_idx;
+
+  int first;
+  int next;
+  re_node_set eclosure;
+};
+typedef struct bin_tree_t bin_tree_t;
+
+
+#define CONTEXT_WORD 1
+#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
+#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
+#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
+
+#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
+#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
+#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
+#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
+#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
+
+#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
+#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
+#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_')
+#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
+
+#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
+ ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+  || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+  || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
+  || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
+
+#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
+ ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
+  || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
+  || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
+  || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
+
+struct re_dfastate_t
+{
+  unsigned int hash;
+  re_node_set nodes;
+  re_node_set *entrance_nodes;
+  struct re_dfastate_t **trtable;
+  struct re_dfastate_t **trtable_search;
+  /* If this state is a special state.
+     A state is a special state if the state is the halt state, or
+     a anchor.  */
+  unsigned int context : 2;
+  unsigned int halt : 1;
+  /* If this state can accept `multi byte'.
+     Note that we refer to multibyte characters, and multi character
+     collating elements as `multi byte'.  */
+  unsigned int accept_mb : 1;
+  /* If this state has backreference node(s).  */
+  unsigned int has_backref : 1;
+  unsigned int has_constraint : 1;
+};
+typedef struct re_dfastate_t re_dfastate_t;
+
+typedef struct
+{
+  /* start <= node < end  */
+  int start;
+  int end;
+} re_subexp_t;
+
+struct re_state_table_entry
+{
+  int num;
+  int alloc;
+  re_dfastate_t **array;
+};
+
+/* Array type used in re_sub_match_last_t and re_sub_match_top_t.  */
+
+typedef struct
+{
+  int next_idx;
+  int alloc;
+  re_dfastate_t **array;
+} state_array_t;
+
+/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP.  */
+
+typedef struct
+{
+  int node;
+  int str_idx; /* The position NODE match at.  */
+  state_array_t path;
+} re_sub_match_last_t;
+
+/* Store information about the node NODE whose type is OP_OPEN_SUBEXP.
+   And information about the node, whose type is OP_CLOSE_SUBEXP,
+   corresponding to NODE is stored in LASTS.  */
+
+typedef struct
+{
+  int str_idx;
+  int node;
+  int next_last_offset;
+  state_array_t *path;
+  int alasts; /* Allocation size of LASTS.  */
+  int nlasts; /* The number of LASTS.  */
+  re_sub_match_last_t **lasts;
+} re_sub_match_top_t;
+
+struct re_backref_cache_entry
+{
+  int node;
+  int str_idx;
+  int subexp_from;
+  int subexp_to;
+  int flag;
+};
+
+typedef struct
+{
+  /* EFLAGS of the argument of regexec.  */
+  int eflags;
+  /* Where the matching ends.  */
+  int match_last;
+  int last_node;
+  /* The string object corresponding to the input string.  */
+  re_string_t *input;
+  /* The state log used by the matcher.  */
+  re_dfastate_t **state_log;
+  int state_log_top;
+  /* Back reference cache.  */
+  int nbkref_ents;
+  int abkref_ents;
+  struct re_backref_cache_entry *bkref_ents;
+  int max_mb_elem_len;
+  int nsub_tops;
+  int asub_tops;
+  re_sub_match_top_t **sub_tops;
+} re_match_context_t;
+
+typedef struct
+{
+  int cur_bkref;
+  int cls_subexp_idx;
+
+  re_dfastate_t **sifted_states;
+  re_dfastate_t **limited_states;
+
+  re_node_set limits;
+
+  int last_node;
+  int last_str_idx;
+  int check_subexp;
+} re_sift_context_t;
+
+struct re_fail_stack_ent_t
+{
+  int idx;
+  int node;
+  regmatch_t *regs;
+  re_node_set eps_via_nodes;
+};
+
+struct re_fail_stack_t
+{
+  int num;
+  int alloc;
+  struct re_fail_stack_ent_t *stack;
+};
+
+struct re_dfa_t
+{
+  re_bitset_ptr_t word_char;
+
+  /* number of subexpressions `re_nsub' is in regex_t.  */
+  int subexps_alloc;
+  re_subexp_t *subexps;
+
+  re_token_t *nodes;
+  int nodes_alloc;
+  int nodes_len;
+  bin_tree_t *str_tree;
+  int *nexts;
+  int *org_indices;
+  re_node_set *edests;
+  re_node_set *eclosures;
+  re_node_set *inveclosures;
+  struct re_state_table_entry *state_table;
+  unsigned int state_hash_mask;
+  re_dfastate_t *init_state;
+  re_dfastate_t *init_state_word;
+  re_dfastate_t *init_state_nl;
+  re_dfastate_t *init_state_begbuf;
+  int states_alloc;
+  int init_node;
+  int nbackref; /* The number of backreference in this dfa.  */
+  /* Bitmap expressing which backreference is used.  */
+  unsigned int used_bkref_map;
+#ifdef DEBUG
+  char* re_str;
+#endif
+  unsigned int has_plural_match : 1;
+  /* If this dfa has "multibyte node", which is a backreference or
+     a node which can accept multibyte character or multi character
+     collating element.  */
+  unsigned int has_mb_node : 1;
+};
+typedef struct re_dfa_t re_dfa_t;
+
+static reg_errcode_t re_node_set_alloc (re_node_set *set, int size);
+static reg_errcode_t re_node_set_init_1 (re_node_set *set, int elem);
+static reg_errcode_t re_node_set_init_2 (re_node_set *set, int elem1,
+                                        int elem2);
+#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
+static reg_errcode_t re_node_set_init_copy (re_node_set *dest,
+                                           const re_node_set *src);
+static reg_errcode_t re_node_set_add_intersect (re_node_set *dest,
+                                               const re_node_set *src1,
+                                               const re_node_set *src2);
+static reg_errcode_t re_node_set_init_union (re_node_set *dest,
+                                            const re_node_set *src1,
+                                            const re_node_set *src2);
+static reg_errcode_t re_node_set_merge (re_node_set *dest,
+                                       const re_node_set *src);
+static int re_node_set_insert (re_node_set *set, int elem);
+static int re_node_set_compare (const re_node_set *set1,
+                               const re_node_set *set2);
+static int re_node_set_contains (const re_node_set *set, int elem);
+static void re_node_set_remove_at (re_node_set *set, int idx);
+#define re_node_set_remove(set,id) \
+  (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1))
+#define re_node_set_empty(p) ((p)->nelem = 0)
+#define re_node_set_free(set) re_free ((set)->elems)
+static int re_dfa_add_node (re_dfa_t *dfa, re_token_t token, int mode);
+static re_dfastate_t *re_acquire_state (reg_errcode_t *err, re_dfa_t *dfa,
+                                       const re_node_set *nodes);
+static re_dfastate_t *re_acquire_state_context (reg_errcode_t *err,
+                                               re_dfa_t *dfa,
+                                               const re_node_set *nodes,
+                                               unsigned int context);
+static void free_state (re_dfastate_t *state);
+\f
+
+typedef enum
+{
+  SB_CHAR,
+  MB_CHAR,
+  EQUIV_CLASS,
+  COLL_SYM,
+  CHAR_CLASS
+} bracket_elem_type;
+
+typedef struct
+{
+  bracket_elem_type type;
+  union
+  {
+    unsigned char ch;
+    unsigned char *name;
+    wchar_t wch;
+  } opr;
+} bracket_elem_t;
+
+
+/* Inline functions for bitset operation.  */
+static inline void
+bitset_not (set)
+     bitset set;
+{
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
+    set[bitset_i] = ~set[bitset_i];
+}
+
+static inline void
+bitset_merge (dest, src)
+     bitset dest;
+     const bitset src;
+{
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
+    dest[bitset_i] |= src[bitset_i];
+}
+
+static inline void
+bitset_not_merge (dest, src)
+     bitset dest;
+     const bitset src;
+{
+  int i;
+  for (i = 0; i < BITSET_UINTS; ++i)
+    dest[i] |= ~src[i];
+}
+
+#ifdef RE_ENABLE_I18N
+/* Inline functions for re_string.  */
+static inline int
+re_string_char_size_at (pstr, idx)
+     const re_string_t *pstr;
+     int idx;
+{
+  int byte_idx;
+  if (MB_CUR_MAX == 1)
+    return 1;
+  for (byte_idx = 1; idx + byte_idx < pstr->len; ++byte_idx)
+    if (pstr->wcs[idx + byte_idx] != WEOF)
+      break;
+  return byte_idx;
+}
+
+static inline wint_t
+re_string_wchar_at (pstr, idx)
+     const re_string_t *pstr;
+     int idx;
+{
+  if (MB_CUR_MAX == 1)
+    return (wint_t) pstr->mbs[idx];
+  return (wint_t) pstr->wcs[idx];
+}
+
+static int
+re_string_elem_size_at (pstr, idx)
+     const re_string_t *pstr;
+     int idx;
+{
+#ifdef _LIBC
+  const unsigned char *p, *extra;
+  const int32_t *table, *indirect;
+  int32_t tmp;
+# include <locale/weight.h>
+  uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+
+  if (nrules != 0)
+    {
+      table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+      extra = (const unsigned char *)
+       _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+      indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
+                                               _NL_COLLATE_INDIRECTMB);
+      p = pstr->mbs + idx;
+      tmp = findidx (&p);
+      return p - pstr->mbs - idx;
+    }
+  else
+#endif /* _LIBC */
+    return 1;
+}
+#endif /* RE_ENABLE_I18N */
+
+#endif /*  _REGEX_INTERNAL_H */
diff --git a/posix/regexec.c b/posix/regexec.c
new file mode 100644 (file)
index 0000000..6ea14a6
--- /dev/null
@@ -0,0 +1,3977 @@
+/* Extended regular expression matching and search library.
+   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
+
+   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.  */
+
+static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
+                                    re_string_t *input, int n);
+static void match_ctx_clean (re_match_context_t *mctx);
+static void match_ctx_free (re_match_context_t *cache);
+static void match_ctx_free_subtops (re_match_context_t *mctx);
+static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node,
+                                         int str_idx, int from, int to);
+static int search_cur_bkref_entry (re_match_context_t *mctx, int str_idx);
+static void match_ctx_clear_flag (re_match_context_t *mctx);
+static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node,
+                                          int str_idx);
+static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop,
+                                                  int node, int str_idx);
+static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+                          re_dfastate_t **limited_sts, int last_node,
+                          int last_str_idx, int check_subexp);
+static reg_errcode_t re_search_internal (const regex_t *preg,
+                                        const char *string, int length,
+                                        int start, int range, int stop,
+                                        size_t nmatch, regmatch_t pmatch[],
+                                        int eflags);
+static int re_search_2_stub (struct re_pattern_buffer *bufp,
+                            const char *string1, int length1,
+                            const char *string2, int length2,
+                            int start, int range, struct re_registers *regs,
+                            int stop, int ret_len);
+static int re_search_stub (struct re_pattern_buffer *bufp,
+                          const char *string, int length, int start,
+                          int range, int stop, struct re_registers *regs,
+                          int ret_len);
+static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch,
+                             int nregs, int regs_allocated);
+static inline re_dfastate_t *acquire_init_state_context (reg_errcode_t *err,
+                                                        const regex_t *preg,
+                                                        const re_match_context_t *mctx,
+                                                        int idx);
+static reg_errcode_t prune_impossible_nodes (const regex_t *preg,
+                                            re_match_context_t *mctx);
+static int check_matching (const regex_t *preg, re_match_context_t *mctx,
+                          int fl_search, int fl_longest_match);
+static int check_halt_node_context (const re_dfa_t *dfa, int node,
+                                   unsigned int context);
+static int check_halt_state_context (const regex_t *preg,
+                                    const re_dfastate_t *state,
+                                    const re_match_context_t *mctx, int idx);
+static void update_regs (re_dfa_t *dfa, regmatch_t *pmatch, int cur_node,
+                        int cur_idx, int nmatch);
+static int proceed_next_node (const regex_t *preg, int nregs, regmatch_t *regs,
+                             const re_match_context_t *mctx,
+                             int *pidx, int node, re_node_set *eps_via_nodes,
+                             struct re_fail_stack_t *fs);
+static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs,
+                                     int str_idx, int *dests, int nregs,
+                                     regmatch_t *regs,
+                                     re_node_set *eps_via_nodes);
+static int pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs,
+                          regmatch_t *regs, re_node_set *eps_via_nodes);
+static reg_errcode_t set_regs (const regex_t *preg,
+                              const re_match_context_t *mctx,
+                              size_t nmatch, regmatch_t *pmatch,
+                              int fl_backtrack);
+static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs);
+
+#ifdef RE_ENABLE_I18N
+static int sift_states_iter_mb (const regex_t *preg,
+                               const re_match_context_t *mctx,
+                               re_sift_context_t *sctx,
+                               int node_idx, int str_idx, int max_str_idx);
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t sift_states_backward (const regex_t *preg,
+                                          re_match_context_t *mctx,
+                                          re_sift_context_t *sctx);
+static reg_errcode_t update_cur_sifted_state (const regex_t *preg,
+                                             re_match_context_t *mctx,
+                                             re_sift_context_t *sctx,
+                                             int str_idx,
+                                             re_node_set *dest_nodes);
+static reg_errcode_t add_epsilon_src_nodes (re_dfa_t *dfa,
+                                           re_node_set *dest_nodes,
+                                           const re_node_set *candidates);
+static reg_errcode_t sub_epsilon_src_nodes (re_dfa_t *dfa, int node,
+                                           re_node_set *dest_nodes,
+                                           const re_node_set *and_nodes);
+static int check_dst_limits (re_dfa_t *dfa, re_node_set *limits,
+                            re_match_context_t *mctx, int dst_node,
+                            int dst_idx, int src_node, int src_idx);
+static int check_dst_limits_calc_pos (re_dfa_t *dfa, re_match_context_t *mctx,
+                                     int limit, re_node_set *eclosures,
+                                     int subexp_idx, int node, int str_idx);
+static reg_errcode_t check_subexp_limits (re_dfa_t *dfa,
+                                         re_node_set *dest_nodes,
+                                         const re_node_set *candidates,
+                                         re_node_set *limits,
+                                         struct re_backref_cache_entry *bkref_ents,
+                                         int str_idx);
+static reg_errcode_t sift_states_bkref (const regex_t *preg,
+                                       re_match_context_t *mctx,
+                                       re_sift_context_t *sctx,
+                                       int str_idx, re_node_set *dest_nodes);
+static reg_errcode_t clean_state_log_if_need (re_match_context_t *mctx,
+                                             int next_state_log_idx);
+static reg_errcode_t merge_state_array (re_dfa_t *dfa, re_dfastate_t **dst,
+                                       re_dfastate_t **src, int num);
+static re_dfastate_t *transit_state (reg_errcode_t *err, const regex_t *preg,
+                                    re_match_context_t *mctx,
+                                    re_dfastate_t *state, int fl_search);
+static reg_errcode_t check_subexp_matching_top (re_dfa_t *dfa,
+                                               re_match_context_t *mctx,
+                                               re_node_set *cur_nodes,
+                                               int str_idx);
+static re_dfastate_t *transit_state_sb (reg_errcode_t *err, const regex_t *preg,
+                                       re_dfastate_t *pstate,
+                                       int fl_search,
+                                       re_match_context_t *mctx);
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t transit_state_mb (const regex_t *preg,
+                                      re_dfastate_t *pstate,
+                                      re_match_context_t *mctx);
+#endif /* RE_ENABLE_I18N */
+static reg_errcode_t transit_state_bkref (const regex_t *preg,
+                                         re_node_set *nodes,
+                                         re_match_context_t *mctx);
+static reg_errcode_t get_subexp (const regex_t *preg, re_match_context_t *mctx,
+                                int bkref_node, int bkref_str_idx);
+static reg_errcode_t get_subexp_sub (const regex_t *preg,
+                                    re_match_context_t *mctx,
+                                    re_sub_match_top_t *sub_top,
+                                    re_sub_match_last_t *sub_last,
+                                    int bkref_node, int bkref_str);
+static int find_subexp_node (re_dfa_t *dfa, re_node_set *nodes,
+                            int subexp_idx, int fl_open);
+static reg_errcode_t check_arrival (const regex_t *preg,
+                                   re_match_context_t *mctx,
+                                   state_array_t *path, int top_node,
+                                   int top_str, int last_node, int last_str,
+                                   int fl_open);
+static reg_errcode_t check_arrival_add_next_nodes (const regex_t *preg,
+                                                  re_dfa_t *dfa,
+                                                  re_match_context_t *mctx,
+                                                  int str_idx,
+                                                  re_node_set *cur_nodes,
+                                                  re_node_set *next_nodes);
+static reg_errcode_t check_arrival_expand_ecl (re_dfa_t *dfa,
+                                              re_node_set *cur_nodes,
+                                              int ex_subexp, int fl_open);
+static reg_errcode_t check_arrival_expand_ecl_sub (re_dfa_t *dfa,
+                                                  re_node_set *dst_nodes,
+                                                  int target, int ex_subexp,
+                                                  int fl_open);
+static reg_errcode_t expand_bkref_cache (const regex_t *preg,
+                                        re_match_context_t *mctx,
+                                        re_node_set *cur_nodes, int cur_str,
+                                        int last_str, int subexp_num,
+                                        int fl_open);
+static re_dfastate_t **build_trtable (const regex_t *dfa,
+                                     const re_dfastate_t *state,
+                                     int fl_search);
+#ifdef RE_ENABLE_I18N
+static int check_node_accept_bytes (const regex_t *preg, int node_idx,
+                                   const re_string_t *input, int idx);
+# ifdef _LIBC
+static unsigned int find_collation_sequence_value (const unsigned char *mbs,
+                                                  size_t name_len);
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+static int group_nodes_into_DFAstates (const regex_t *dfa,
+                                      const re_dfastate_t *state,
+                                      re_node_set *states_node,
+                                      bitset *states_ch);
+static int check_node_accept (const regex_t *preg, const re_token_t *node,
+                             const re_match_context_t *mctx, int idx);
+static reg_errcode_t extend_buffers (re_match_context_t *mctx);
+\f
+/* Entry point for POSIX code.  */
+
+/* regexec searches for a given pattern, specified by PREG, in the
+   string STRING.
+
+   If NMATCH is zero or REG_NOSUB was set in the cflags argument to
+   `regcomp', we ignore PMATCH.  Otherwise, we assume PMATCH has at
+   least NMATCH elements, and we set them to the offsets of the
+   corresponding matched substrings.
+
+   EFLAGS specifies `execution flags' which affect matching: if
+   REG_NOTBOL is set, then ^ does not match at the beginning of the
+   string; if REG_NOTEOL is set, then $ does not match at the end.
+
+   We return 0 if we find a match and REG_NOMATCH if not.  */
+
+int
+regexec (preg, string, nmatch, pmatch, eflags)
+    const regex_t *__restrict preg;
+    const char *__restrict string;
+    size_t nmatch;
+    regmatch_t pmatch[];
+    int eflags;
+{
+  reg_errcode_t err;
+  int length = strlen (string);
+  if (preg->no_sub)
+    err = re_search_internal (preg, string, length, 0, length, length, 0,
+                             NULL, eflags);
+  else
+    err = re_search_internal (preg, string, length, 0, length, length, nmatch,
+                             pmatch, eflags);
+  return err != REG_NOERROR;
+}
+#ifdef _LIBC
+weak_alias (__regexec, regexec)
+#endif
+
+/* Entry points for GNU code.  */
+
+/* re_match, re_search, re_match_2, re_search_2
+
+   The former two functions operate on STRING with length LENGTH,
+   while the later two operate on concatenation of STRING1 and STRING2
+   with lengths LENGTH1 and LENGTH2, respectively.
+
+   re_match() matches the compiled pattern in BUFP against the string,
+   starting at index START.
+
+   re_search() first tries matching at index START, then it tries to match
+   starting from index START + 1, and so on.  The last start position tried
+   is START + RANGE.  (Thus RANGE = 0 forces re_search to operate the same
+   way as re_match().)
+
+   The parameter STOP of re_{match,search}_2 specifies that no match exceeding
+   the first STOP characters of the concatenation of the strings should be
+   concerned.
+
+   If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match
+   and all groups is stroed in REGS.  (For the "_2" variants, the offsets are
+   computed relative to the concatenation, not relative to the individual
+   strings.)
+
+   On success, re_match* functions return the length of the match, re_search*
+   return the position of the start of the match.  Return value -1 means no
+   match was found and -2 indicates an internal error.  */
+
+int
+re_match (bufp, string, length, start, regs)
+    struct re_pattern_buffer *bufp;
+    const char *string;
+    int length, start;
+    struct re_registers *regs;
+{
+  return re_search_stub (bufp, string, length, start, 0, length, regs, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match, re_match)
+#endif
+
+int
+re_search (bufp, string, length, start, range, regs)
+    struct re_pattern_buffer *bufp;
+    const char *string;
+    int length, start, range;
+    struct re_registers *regs;
+{
+  return re_search_stub (bufp, string, length, start, range, length, regs, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search, re_search)
+#endif
+
+int
+re_match_2 (bufp, string1, length1, string2, length2, start, regs, stop)
+    struct re_pattern_buffer *bufp;
+    const char *string1, *string2;
+    int length1, length2, start, stop;
+    struct re_registers *regs;
+{
+  return re_search_2_stub (bufp, string1, length1, string2, length2,
+                          start, 0, regs, stop, 1);
+}
+#ifdef _LIBC
+weak_alias (__re_match_2, re_match_2)
+#endif
+
+int
+re_search_2 (bufp, string1, length1, string2, length2, start, range, regs, stop)
+    struct re_pattern_buffer *bufp;
+    const char *string1, *string2;
+    int length1, length2, start, range, stop;
+    struct re_registers *regs;
+{
+  return re_search_2_stub (bufp, string1, length1, string2, length2,
+                          start, range, regs, stop, 0);
+}
+#ifdef _LIBC
+weak_alias (__re_search_2, re_search_2)
+#endif
+
+static int
+re_search_2_stub (bufp, string1, length1, string2, length2, start, range, regs,
+                 stop, ret_len)
+    struct re_pattern_buffer *bufp;
+    const char *string1, *string2;
+    int length1, length2, start, range, stop, ret_len;
+    struct re_registers *regs;
+{
+  const char *str;
+  int rval;
+  int len = length1 + length2;
+  int free_str = 0;
+
+  if (BE (length1 < 0 || length2 < 0 || stop < 0, 0))
+    return -2;
+
+  /* Concatenate the strings.  */
+  if (length2 > 0)
+    if (length1 > 0)
+      {
+       char *s = re_malloc (char, len);
+
+       if (BE (s == NULL, 0))
+         return -2;
+       memcpy (s, string1, length1);
+       memcpy (s + length1, string2, length2);
+       str = s;
+       free_str = 1;
+      }
+    else
+      str = string2;
+  else
+    str = string1;
+
+  rval = re_search_stub (bufp, str, len, start, range, stop, regs,
+                        ret_len);
+  if (free_str)
+    re_free ((char *) str);
+  return rval;
+}
+
+/* The parameters have the same meaning as those of re_search.
+   Additional parameters:
+   If RET_LEN is nonzero the length of the match is returned (re_match style);
+   otherwise the position of the match is returned.  */
+
+static int
+re_search_stub (bufp, string, length, start, range, stop, regs, ret_len)
+    struct re_pattern_buffer *bufp;
+    const char *string;
+    int length, start, range, stop, ret_len;
+    struct re_registers *regs;
+{
+  reg_errcode_t result;
+  regmatch_t *pmatch;
+  int nregs, rval;
+  int eflags = 0;
+
+  /* Check for out-of-range.  */
+  if (BE (start < 0 || start > length, 0))
+    return -1;
+  if (BE (start + range > length, 0))
+    range = length - start;
+  else if (BE (start + range < 0, 0))
+    range = -start;
+
+  eflags |= (bufp->not_bol) ? REG_NOTBOL : 0;
+  eflags |= (bufp->not_eol) ? REG_NOTEOL : 0;
+
+  /* Compile fastmap if we haven't yet.  */
+  if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate)
+    re_compile_fastmap (bufp);
+
+  if (BE (bufp->no_sub, 0))
+    regs = NULL;
+
+  /* We need at least 1 register.  */
+  if (regs == NULL)
+    nregs = 1;
+  else if (BE (bufp->regs_allocated == REGS_FIXED &&
+              regs->num_regs < bufp->re_nsub + 1, 0))
+    {
+      nregs = regs->num_regs;
+      if (BE (nregs < 1, 0))
+       {
+         /* Nothing can be copied to regs.  */
+         regs = NULL;
+         nregs = 1;
+       }
+    }
+  else
+    nregs = bufp->re_nsub + 1;
+  pmatch = re_malloc (regmatch_t, nregs);
+  if (BE (pmatch == NULL, 0))
+    return -2;
+
+  result = re_search_internal (bufp, string, length, start, range, stop,
+                              nregs, pmatch, eflags);
+
+  rval = 0;
+
+  /* I hope we needn't fill ther regs with -1's when no match was found.  */
+  if (result != REG_NOERROR)
+    rval = -1;
+  else if (regs != NULL)
+    {
+      /* If caller wants register contents data back, copy them.  */
+      bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs,
+                                          bufp->regs_allocated);
+      if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0))
+       rval = -2;
+    }
+
+  if (BE (rval == 0, 1))
+    {
+      if (ret_len)
+       {
+         assert (pmatch[0].rm_so == start);
+         rval = pmatch[0].rm_eo - start;
+       }
+      else
+       rval = pmatch[0].rm_so;
+    }
+  re_free (pmatch);
+  return rval;
+}
+
+static unsigned
+re_copy_regs (regs, pmatch, nregs, regs_allocated)
+    struct re_registers *regs;
+    regmatch_t *pmatch;
+    int nregs, regs_allocated;
+{
+  int rval = REGS_REALLOCATE;
+  int i;
+  int need_regs = nregs + 1;
+  /* We need one extra element beyond `num_regs' for the `-1' marker GNU code
+     uses.  */
+
+  /* Have the register data arrays been allocated?  */
+  if (regs_allocated == REGS_UNALLOCATED)
+    { /* No.  So allocate them with malloc.  */
+      regs->start = re_malloc (regoff_t, need_regs);
+      if (BE (regs->start == NULL, 0))
+       return REGS_UNALLOCATED;
+      regs->end = re_malloc (regoff_t, need_regs);
+      if (BE (regs->end == NULL, 0))
+       {
+         re_free (regs->start);
+         return REGS_UNALLOCATED;
+       }
+      regs->num_regs = need_regs;
+    }
+  else if (regs_allocated == REGS_REALLOCATE)
+    { /* Yes.  If we need more elements than were already
+        allocated, reallocate them.  If we need fewer, just
+        leave it alone.  */
+      if (need_regs > regs->num_regs)
+       {
+         regs->start = re_realloc (regs->start, regoff_t, need_regs);
+         if (BE (regs->start == NULL, 0))
+           {
+             if (regs->end != NULL)
+               re_free (regs->end);
+             return REGS_UNALLOCATED;
+           }
+         regs->end = re_realloc (regs->end, regoff_t, need_regs);
+         if (BE (regs->end == NULL, 0))
+           {
+             re_free (regs->start);
+             return REGS_UNALLOCATED;
+           }
+         regs->num_regs = need_regs;
+       }
+    }
+  else
+    {
+      assert (regs_allocated == REGS_FIXED);
+      /* This function may not be called with REGS_FIXED and nregs too big.  */
+      assert (regs->num_regs >= nregs);
+      rval = REGS_FIXED;
+    }
+
+  /* Copy the regs.  */
+  for (i = 0; i < nregs; ++i)
+    {
+      regs->start[i] = pmatch[i].rm_so;
+      regs->end[i] = pmatch[i].rm_eo;
+    }
+  for ( ; i < regs->num_regs; ++i)
+    regs->start[i] = regs->end[i] = -1;
+
+  return rval;
+}
+
+/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
+   ENDS.  Subsequent matches using PATTERN_BUFFER and REGS will use
+   this memory for recording register information.  STARTS and ENDS
+   must be allocated using the malloc library routine, and must each
+   be at least NUM_REGS * sizeof (regoff_t) bytes long.
+
+   If NUM_REGS == 0, then subsequent matches should allocate their own
+   register data.
+
+   Unless this function is called, the first search or match using
+   PATTERN_BUFFER will allocate its own register data, without
+   freeing the old data.  */
+
+void
+re_set_registers (bufp, regs, num_regs, starts, ends)
+    struct re_pattern_buffer *bufp;
+    struct re_registers *regs;
+    unsigned num_regs;
+    regoff_t *starts, *ends;
+{
+  if (num_regs)
+    {
+      bufp->regs_allocated = REGS_REALLOCATE;
+      regs->num_regs = num_regs;
+      regs->start = starts;
+      regs->end = ends;
+    }
+  else
+    {
+      bufp->regs_allocated = REGS_UNALLOCATED;
+      regs->num_regs = 0;
+      regs->start = regs->end = (regoff_t *) 0;
+    }
+}
+#ifdef _LIBC
+weak_alias (__re_set_registers, re_set_registers)
+#endif
+\f
+/* Entry points compatible with 4.2 BSD regex library.  We don't define
+   them unless specifically requested.  */
+
+#if defined _REGEX_RE_COMP || defined _LIBC
+int
+# ifdef _LIBC
+weak_function
+# endif
+re_exec (s)
+     const char *s;
+{
+  return 0 == regexec (&re_comp_buf, s, 0, NULL, 0);
+}
+#endif /* _REGEX_RE_COMP */
+\f
+static re_node_set empty_set;
+
+/* Internal entry point.  */
+
+/* Searches for a compiled pattern PREG in the string STRING, whose
+   length is LENGTH.  NMATCH, PMATCH, and EFLAGS have the same
+   mingings with regexec.  START, and RANGE have the same meanings
+   with re_search.
+   Return REG_NOERROR if we find a match, and REG_NOMATCH if not,
+   otherwise return the error code.
+   Note: We assume front end functions already check ranges.
+   (START + RANGE >= 0 && START + RANGE <= LENGTH)  */
+
+static reg_errcode_t
+re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch,
+                   eflags)
+    const regex_t *preg;
+    const char *string;
+    int length, start, range, stop, eflags;
+    size_t nmatch;
+    regmatch_t pmatch[];
+{
+  reg_errcode_t err;
+  re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+  re_string_t input;
+  int left_lim, right_lim, incr;
+  int fl_longest_match, match_first, match_last = -1;
+  int fast_translate, sb;
+  re_match_context_t mctx;
+  char *fastmap = ((preg->fastmap != NULL && preg->fastmap_accurate
+                   && range && !preg->can_be_null) ? preg->fastmap : NULL);
+
+  /* Check if the DFA haven't been compiled.  */
+  if (BE (preg->used == 0 || dfa->init_state == NULL
+         || dfa->init_state_word == NULL || dfa->init_state_nl == NULL
+         || dfa->init_state_begbuf == NULL, 0))
+    return REG_NOMATCH;
+
+  re_node_set_init_empty (&empty_set);
+  memset (&mctx, '\0', sizeof (re_match_context_t));
+
+  /* We must check the longest matching, if nmatch > 0.  */
+  fl_longest_match = (nmatch != 0 || dfa->nbackref);
+
+  err = re_string_allocate (&input, string, length, dfa->nodes_len + 1,
+                           preg->translate, preg->syntax & RE_ICASE);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+  input.stop = stop;
+
+  err = match_ctx_init (&mctx, eflags, &input, dfa->nbackref * 2);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+
+  /* We will log all the DFA states through which the dfa pass,
+     if nmatch > 1, or this dfa has "multibyte node", which is a
+     back-reference or a node which can accept multibyte character or
+     multi character collating element.  */
+  if (nmatch > 1 || dfa->has_mb_node)
+    {
+      mctx.state_log = re_malloc (re_dfastate_t *, dfa->nodes_len + 1);
+      if (BE (mctx.state_log == NULL, 0))
+       {
+         err = REG_ESPACE;
+         goto free_return;
+       }
+    }
+  else
+    mctx.state_log = NULL;
+
+#ifdef DEBUG
+  /* We assume front-end functions already check them.  */
+  assert (start + range >= 0 && start + range <= length);
+#endif
+
+  match_first = start;
+  input.tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+                      : CONTEXT_NEWLINE | CONTEXT_BEGBUF);
+
+  /* Check incrementally whether of not the input string match.  */
+  incr = (range < 0) ? -1 : 1;
+  left_lim = (range < 0) ? start + range : start;
+  right_lim = (range < 0) ? start : start + range;
+  sb = MB_CUR_MAX == 1;
+  fast_translate = sb || !(preg->syntax & RE_ICASE || preg->translate);
+
+  for (;;)
+    {
+      /* At first get the current byte from input string.  */
+      if (fastmap)
+       {
+         if (BE (fast_translate, 1))
+           {
+             unsigned RE_TRANSLATE_TYPE t
+               = (unsigned RE_TRANSLATE_TYPE) preg->translate;
+             if (BE (range >= 0, 1))
+               {
+                 if (BE (t != NULL, 0))
+                   {
+                     while (BE (match_first < right_lim, 1)
+                            && !fastmap[t[(unsigned char) string[match_first]]])
+                       ++match_first;
+                   }
+                 else
+                   {
+                     while (BE (match_first < right_lim, 1)
+                            && !fastmap[(unsigned char) string[match_first]])
+                       ++match_first;
+                   }
+                 if (BE (match_first == right_lim, 0))
+                   {
+                     int ch = match_first >= length
+                              ? 0 : (unsigned char) string[match_first];
+                     if (!fastmap[t ? t[ch] : ch])
+                       break;
+                   }
+               }
+             else
+               {
+                 while (match_first >= left_lim)
+                   {
+                     int ch = match_first >= length
+                              ? 0 : (unsigned char) string[match_first];
+                     if (fastmap[t ? t[ch] : ch])
+                       break;
+                     --match_first;
+                   }
+                 if (match_first < left_lim)
+                   break;
+               }
+           }
+         else
+           {
+             int ch;
+
+             do
+               {
+                 /* In this case, we can't determine easily the current byte,
+                    since it might be a component byte of a multibyte
+                    character.  Then we use the constructed buffer
+                    instead.  */
+                 /* If MATCH_FIRST is out of the valid range, reconstruct the
+                    buffers.  */
+                 if (input.raw_mbs_idx + input.valid_len <= match_first
+                     || match_first < input.raw_mbs_idx)
+                   {
+                     err = re_string_reconstruct (&input, match_first, eflags,
+                                                  preg->newline_anchor);
+                     if (BE (err != REG_NOERROR, 0))
+                       goto free_return;
+                   }
+                 /* If MATCH_FIRST is out of the buffer, leave it as '\0'.
+                    Note that MATCH_FIRST must not be smaller than 0.  */
+                 ch = ((match_first >= length) ? 0
+                      : re_string_byte_at (&input,
+                                           match_first - input.raw_mbs_idx));
+                 if (fastmap[ch])
+                   break;
+                 match_first += incr;
+               }
+             while (match_first >= left_lim && match_first <= right_lim);
+             if (! fastmap[ch])
+               break;
+           }
+       }
+
+      /* Reconstruct the buffers so that the matcher can assume that
+        the matching starts from the begining of the buffer.  */
+      err = re_string_reconstruct (&input, match_first, eflags,
+                                  preg->newline_anchor);
+      if (BE (err != REG_NOERROR, 0))
+       goto free_return;
+#ifdef RE_ENABLE_I18N
+     /* Eliminate it when it is a component of a multibyte character
+        and isn't the head of a multibyte character.  */
+      if (sb || re_string_first_byte (&input, 0))
+#endif
+       {
+         /* It seems to be appropriate one, then use the matcher.  */
+         /* We assume that the matching starts from 0.  */
+         mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0;
+         match_last = check_matching (preg, &mctx, 0, fl_longest_match);
+         if (match_last != -1)
+           {
+             if (BE (match_last == -2, 0))
+               {
+                 err = REG_ESPACE;
+                 goto free_return;
+               }
+             else
+               {
+                 mctx.match_last = match_last;
+                 if ((!preg->no_sub && nmatch > 1) || dfa->nbackref)
+                   {
+                     re_dfastate_t *pstate = mctx.state_log[match_last];
+                     mctx.last_node = check_halt_state_context (preg, pstate,
+                                                                &mctx, match_last);
+                   }
+                 if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match)
+                     || dfa->nbackref)
+                   {
+                     err = prune_impossible_nodes (preg, &mctx);
+                     if (err == REG_NOERROR)
+                       break;
+                     if (BE (err != REG_NOMATCH, 0))
+                       goto free_return;
+                   }
+                 else
+                   break; /* We found a matching.  */
+               }
+           }
+         match_ctx_clean (&mctx);
+       }
+      /* Update counter.  */
+      match_first += incr;
+      if (match_first < left_lim || right_lim < match_first)
+       break;
+    }
+
+  /* Set pmatch[] if we need.  */
+  if (match_last != -1 && nmatch > 0)
+    {
+      int reg_idx;
+
+      /* Initialize registers.  */
+      for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+       pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1;
+
+      /* Set the points where matching start/end.  */
+      pmatch[0].rm_so = 0;
+      pmatch[0].rm_eo = mctx.match_last;
+
+      if (!preg->no_sub && nmatch > 1)
+       {
+         err = set_regs (preg, &mctx, nmatch, pmatch,
+                         dfa->has_plural_match && dfa->nbackref > 0);
+         if (BE (err != REG_NOERROR, 0))
+           goto free_return;
+       }
+
+      /* At last, add the offset to the each registers, since we slided
+        the buffers so that We can assume that the matching starts from 0.  */
+      for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+       if (pmatch[reg_idx].rm_so != -1)
+         {
+           pmatch[reg_idx].rm_so += match_first;
+           pmatch[reg_idx].rm_eo += match_first;
+         }
+    }
+  err = (match_last == -1) ? REG_NOMATCH : REG_NOERROR;
+ free_return:
+  re_free (mctx.state_log);
+  if (dfa->nbackref)
+    match_ctx_free (&mctx);
+  re_string_destruct (&input);
+  return err;
+}
+
+static reg_errcode_t
+prune_impossible_nodes (preg, mctx)
+     const regex_t *preg;
+     re_match_context_t *mctx;
+{
+  int halt_node, match_last;
+  reg_errcode_t ret;
+  re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+  re_dfastate_t **sifted_states;
+  re_dfastate_t **lim_states = NULL;
+  re_sift_context_t sctx;
+#ifdef DEBUG
+  assert (mctx->state_log != NULL);
+#endif
+  match_last = mctx->match_last;
+  halt_node = mctx->last_node;
+  sifted_states = re_malloc (re_dfastate_t *, match_last + 1);
+  if (BE (sifted_states == NULL, 0))
+    {
+      ret = REG_ESPACE;
+      goto free_return;
+    }
+  if (dfa->nbackref)
+    {
+      lim_states = re_malloc (re_dfastate_t *, match_last + 1);
+      if (BE (lim_states == NULL, 0))
+       {
+         ret = REG_ESPACE;
+         goto free_return;
+       }
+      while (1)
+       {
+         memset (lim_states, '\0',
+                 sizeof (re_dfastate_t *) * (match_last + 1));
+         match_ctx_clear_flag (mctx);
+         sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
+                        match_last, 0);
+         ret = sift_states_backward (preg, mctx, &sctx);
+         re_node_set_free (&sctx.limits);
+         if (BE (ret != REG_NOERROR, 0))
+             goto free_return;
+         if (sifted_states[0] != NULL || lim_states[0] != NULL)
+           break;
+         do
+           {
+             --match_last;
+             if (match_last < 0)
+               {
+                 ret = REG_NOMATCH;
+                 goto free_return;
+               }
+           } while (!mctx->state_log[match_last]->halt);
+         halt_node = check_halt_state_context (preg,
+                                               mctx->state_log[match_last],
+                                               mctx, match_last);
+       }
+      ret = merge_state_array (dfa, sifted_states, lim_states,
+                              match_last + 1);
+      re_free (lim_states);
+      lim_states = NULL;
+      if (BE (ret != REG_NOERROR, 0))
+       goto free_return;
+    }
+  else
+    {
+      sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
+                    match_last, 0);
+      ret = sift_states_backward (preg, mctx, &sctx);
+      re_node_set_free (&sctx.limits);
+      if (BE (ret != REG_NOERROR, 0))
+       goto free_return;
+    }
+  re_free (mctx->state_log);
+  mctx->state_log = sifted_states;
+  sifted_states = NULL;
+  mctx->last_node = halt_node;
+  mctx->match_last = match_last;
+  ret = REG_NOERROR;
+ free_return:
+  re_free (sifted_states);
+  re_free (lim_states);
+  return ret;
+}
+
+/* Acquire an initial state and return it.
+   We must select appropriate initial state depending on the context,
+   since initial states may have constraints like "\<", "^", etc..  */
+
+static inline re_dfastate_t *
+acquire_init_state_context (err, preg, mctx, idx)
+     reg_errcode_t *err;
+     const regex_t *preg;
+     const re_match_context_t *mctx;
+     int idx;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+
+  *err = REG_NOERROR;
+  if (dfa->init_state->has_constraint)
+    {
+      unsigned int context;
+      context =  re_string_context_at (mctx->input, idx - 1, mctx->eflags,
+                                      preg->newline_anchor);
+      if (IS_WORD_CONTEXT (context))
+       return dfa->init_state_word;
+      else if (IS_ORDINARY_CONTEXT (context))
+       return dfa->init_state;
+      else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context))
+       return dfa->init_state_begbuf;
+      else if (IS_NEWLINE_CONTEXT (context))
+       return dfa->init_state_nl;
+      else if (IS_BEGBUF_CONTEXT (context))
+       {
+         /* It is relatively rare case, then calculate on demand.  */
+         return  re_acquire_state_context (err, dfa,
+                                           dfa->init_state->entrance_nodes,
+                                           context);
+       }
+      else
+       /* Must not happen?  */
+       return dfa->init_state;
+    }
+  else
+    return dfa->init_state;
+}
+
+/* Check whether the regular expression match input string INPUT or not,
+   and return the index where the matching end, return -1 if not match,
+   or return -2 in case of an error.
+   FL_SEARCH means we must search where the matching starts,
+   FL_LONGEST_MATCH means we want the POSIX longest matching.
+   Note that the matcher assume that the maching starts from the current
+   index of the buffer.  */
+
+static int
+check_matching (preg, mctx, fl_search, fl_longest_match)
+    const regex_t *preg;
+    re_match_context_t *mctx;
+    int fl_search, fl_longest_match;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  reg_errcode_t err;
+  int match = 0;
+  int match_last = -1;
+  int cur_str_idx = re_string_cur_idx (mctx->input);
+  re_dfastate_t *cur_state;
+
+  cur_state = acquire_init_state_context (&err, preg, mctx, cur_str_idx);
+  /* An initial state must not be NULL(invalid state).  */
+  if (BE (cur_state == NULL, 0))
+    return -2;
+  if (mctx->state_log != NULL)
+    mctx->state_log[cur_str_idx] = cur_state;
+
+  /* Check OP_OPEN_SUBEXP in the initial state in case that we use them
+     later.  E.g. Processing back references.  */
+  if (dfa->nbackref)
+    {
+      err = check_subexp_matching_top (dfa, mctx, &cur_state->nodes, 0);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+    }
+
+  if (cur_state->has_backref)
+    {
+      err = transit_state_bkref (preg, &cur_state->nodes, mctx);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+    }
+
+  /* If the RE accepts NULL string.  */
+  if (cur_state->halt)
+    {
+      if (!cur_state->has_constraint
+         || check_halt_state_context (preg, cur_state, mctx, cur_str_idx))
+       {
+         if (!fl_longest_match)
+           return cur_str_idx;
+         else
+           {
+             match_last = cur_str_idx;
+             match = 1;
+           }
+       }
+    }
+
+  while (!re_string_eoi (mctx->input))
+    {
+      cur_state = transit_state (&err, preg, mctx, cur_state,
+                                fl_search && !match);
+      if (cur_state == NULL) /* Reached at the invalid state or an error.  */
+       {
+         cur_str_idx = re_string_cur_idx (mctx->input);
+         if (BE (err != REG_NOERROR, 0))
+           return -2;
+         if (fl_search && !match)
+           {
+             /* Restart from initial state, since we are searching
+                the point from where matching start.  */
+#ifdef RE_ENABLE_I18N
+             if (MB_CUR_MAX == 1
+                 || re_string_first_byte (mctx->input, cur_str_idx))
+#endif /* RE_ENABLE_I18N */
+               cur_state = acquire_init_state_context (&err, preg, mctx,
+                                                       cur_str_idx);
+             if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+               return -2;
+             if (mctx->state_log != NULL)
+               mctx->state_log[cur_str_idx] = cur_state;
+           }
+         else if (!fl_longest_match && match)
+           break;
+         else /* (fl_longest_match && match) || (!fl_search && !match)  */
+           {
+             if (mctx->state_log == NULL)
+               break;
+             else
+               {
+                 int max = mctx->state_log_top;
+                 for (; cur_str_idx <= max; ++cur_str_idx)
+                   if (mctx->state_log[cur_str_idx] != NULL)
+                     break;
+                 if (cur_str_idx > max)
+                   break;
+               }
+           }
+       }
+
+      if (cur_state != NULL && cur_state->halt)
+       {
+         /* Reached at a halt state.
+            Check the halt state can satisfy the current context.  */
+         if (!cur_state->has_constraint
+             || check_halt_state_context (preg, cur_state, mctx,
+                                          re_string_cur_idx (mctx->input)))
+           {
+             /* We found an appropriate halt state.  */
+             match_last = re_string_cur_idx (mctx->input);
+             match = 1;
+             if (!fl_longest_match)
+               break;
+           }
+       }
+   }
+  return match_last;
+}
+
+/* Check NODE match the current context.  */
+
+static int check_halt_node_context (dfa, node, context)
+    const re_dfa_t *dfa;
+    int node;
+    unsigned int context;
+{
+  re_token_type_t type = dfa->nodes[node].type;
+  unsigned int constraint = dfa->nodes[node].constraint;
+  if (type != END_OF_RE)
+    return 0;
+  if (!constraint)
+    return 1;
+  if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context))
+    return 0;
+  return 1;
+}
+
+/* Check the halt state STATE match the current context.
+   Return 0 if not match, if the node, STATE has, is a halt node and
+   match the context, return the node.  */
+
+static int
+check_halt_state_context (preg, state, mctx, idx)
+    const regex_t *preg;
+    const re_dfastate_t *state;
+    const re_match_context_t *mctx;
+    int idx;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  int i;
+  unsigned int context;
+#ifdef DEBUG
+  assert (state->halt);
+#endif
+  context = re_string_context_at (mctx->input, idx, mctx->eflags,
+                                 preg->newline_anchor);
+  for (i = 0; i < state->nodes.nelem; ++i)
+    if (check_halt_node_context (dfa, state->nodes.elems[i], context))
+      return state->nodes.elems[i];
+  return 0;
+}
+
+/* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA
+   corresponding to the DFA).
+   Return the destination node, and update EPS_VIA_NODES, return -1 in case
+   of errors.  */
+
+static int
+proceed_next_node (preg, nregs, regs, mctx, pidx, node, eps_via_nodes, fs)
+    const regex_t *preg;
+    regmatch_t *regs;
+    const re_match_context_t *mctx;
+    int nregs, *pidx, node;
+    re_node_set *eps_via_nodes;
+    struct re_fail_stack_t *fs;
+{
+  re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+  int i, err, dest_node;
+  dest_node = -1;
+  if (IS_EPSILON_NODE (dfa->nodes[node].type))
+    {
+      re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes;
+      int ndest, dest_nodes[2];
+      err = re_node_set_insert (eps_via_nodes, node);
+      if (BE (err < 0, 0))
+       return -1;
+      /* Pick up valid destinations.  */
+      for (ndest = 0, i = 0; i < dfa->edests[node].nelem; ++i)
+       {
+         int candidate = dfa->edests[node].elems[i];
+         if (!re_node_set_contains (cur_nodes, candidate))
+           continue;
+         dest_nodes[0] = (ndest == 0) ? candidate : dest_nodes[0];
+         dest_nodes[1] = (ndest == 1) ? candidate : dest_nodes[1];
+         ++ndest;
+       }
+      if (ndest <= 1)
+       return ndest == 0 ? -1 : (ndest == 1 ? dest_nodes[0] : 0);
+      /* In order to avoid infinite loop like "(a*)*".  */
+      if (re_node_set_contains (eps_via_nodes, dest_nodes[0]))
+       return dest_nodes[1];
+      if (fs != NULL)
+       push_fail_stack (fs, *pidx, dest_nodes, nregs, regs, eps_via_nodes);
+      return dest_nodes[0];
+    }
+  else
+    {
+      int naccepted = 0;
+      re_token_type_t type = dfa->nodes[node].type;
+
+#ifdef RE_ENABLE_I18N
+      if (ACCEPT_MB_NODE (type))
+       naccepted = check_node_accept_bytes (preg, node, mctx->input, *pidx);
+      else
+#endif /* RE_ENABLE_I18N */
+      if (type == OP_BACK_REF)
+       {
+         int subexp_idx = dfa->nodes[node].opr.idx;
+         naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so;
+         if (fs != NULL)
+           {
+             if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1)
+               return -1;
+             else if (naccepted)
+               {
+                 char *buf = (char *) re_string_get_buffer (mctx->input);
+                 if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
+                             naccepted) != 0)
+                   return -1;
+               }
+           }
+
+         if (naccepted == 0)
+           {
+             err = re_node_set_insert (eps_via_nodes, node);
+             if (BE (err < 0, 0))
+               return -2;
+             dest_node = dfa->edests[node].elems[0];
+             if (re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+                                       dest_node))
+               return dest_node;
+           }
+       }
+
+      if (naccepted != 0
+         || check_node_accept (preg, dfa->nodes + node, mctx, *pidx))
+       {
+         dest_node = dfa->nexts[node];
+         *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted;
+         if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL
+                    || !re_node_set_contains (&mctx->state_log[*pidx]->nodes,
+                                              dest_node)))
+           return -1;
+         re_node_set_empty (eps_via_nodes);
+         return dest_node;
+       }
+    }
+  return -1;
+}
+
+static reg_errcode_t
+push_fail_stack (fs, str_idx, dests, nregs, regs, eps_via_nodes)
+     struct re_fail_stack_t *fs;
+     int str_idx, *dests, nregs;
+     regmatch_t *regs;
+     re_node_set *eps_via_nodes;
+{
+  reg_errcode_t err;
+  int num = fs->num++;
+  if (fs->num == fs->alloc)
+    {
+      struct re_fail_stack_ent_t *new_array;
+      fs->alloc *= 2;
+      new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t)
+                                      * fs->alloc));
+      if (new_array == NULL)
+       return REG_ESPACE;
+      fs->stack = new_array;
+    }
+  fs->stack[num].idx = str_idx;
+  fs->stack[num].node = dests[1];
+  fs->stack[num].regs = re_malloc (regmatch_t, nregs);
+  memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs);
+  err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes);
+  return err;
+}
+
+static int
+pop_fail_stack (fs, pidx, nregs, regs, eps_via_nodes)
+     struct re_fail_stack_t *fs;
+     int *pidx, nregs;
+     regmatch_t *regs;
+     re_node_set *eps_via_nodes;
+{
+  int num = --fs->num;
+  assert (num >= 0);
+ *pidx = fs->stack[num].idx;
+  memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs);
+  re_node_set_free (eps_via_nodes);
+  re_free (fs->stack[num].regs);
+  *eps_via_nodes = fs->stack[num].eps_via_nodes;
+  return fs->stack[num].node;
+}
+
+/* Set the positions where the subexpressions are starts/ends to registers
+   PMATCH.
+   Note: We assume that pmatch[0] is already set, and
+   pmatch[i].rm_so == pmatch[i].rm_eo == -1 (i > 1).  */
+
+static reg_errcode_t
+set_regs (preg, mctx, nmatch, pmatch, fl_backtrack)
+     const regex_t *preg;
+     const re_match_context_t *mctx;
+     size_t nmatch;
+     regmatch_t *pmatch;
+     int fl_backtrack;
+{
+  re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+  int idx, cur_node, real_nmatch;
+  re_node_set eps_via_nodes;
+  struct re_fail_stack_t *fs;
+  struct re_fail_stack_t fs_body = {0, 2, NULL};
+#ifdef DEBUG
+  assert (nmatch > 1);
+  assert (mctx->state_log != NULL);
+#endif
+  if (fl_backtrack)
+    {
+      fs = &fs_body;
+      fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
+    }
+  else
+    fs = NULL;
+  cur_node = dfa->init_node;
+  real_nmatch = (nmatch <= preg->re_nsub) ? nmatch : preg->re_nsub + 1;
+  re_node_set_init_empty (&eps_via_nodes);
+  for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
+    {
+      update_regs (dfa, pmatch, cur_node, idx, real_nmatch);
+      if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
+       {
+         int reg_idx;
+         if (fs)
+           {
+             for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+               if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1)
+                 break;
+             if (reg_idx == nmatch)
+               {
+                 re_node_set_free (&eps_via_nodes);
+                 return free_fail_stack_return (fs);
+               }
+             cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+                                        &eps_via_nodes);
+           }
+         else
+           {
+             re_node_set_free (&eps_via_nodes);
+             return REG_NOERROR;
+           }
+       }
+
+      /* Proceed to next node.  */
+      cur_node = proceed_next_node (preg, nmatch, pmatch, mctx, &idx, cur_node,
+                                   &eps_via_nodes, fs);
+
+      if (BE (cur_node < 0, 0))
+       {
+         if (cur_node == -2)
+           return REG_ESPACE;
+         if (fs)
+           cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
+                                      &eps_via_nodes);
+         else
+           {
+             re_node_set_free (&eps_via_nodes);
+             return REG_NOMATCH;
+           }
+       }
+    }
+  re_node_set_free (&eps_via_nodes);
+  return free_fail_stack_return (fs);
+}
+
+static reg_errcode_t
+free_fail_stack_return (fs)
+     struct re_fail_stack_t *fs;
+{
+  if (fs)
+    {
+      int fs_idx;
+      for (fs_idx = 0; fs_idx < fs->num; ++fs_idx)
+       {
+         re_node_set_free (&fs->stack[fs_idx].eps_via_nodes);
+         re_free (fs->stack[fs_idx].regs);
+       }
+      re_free (fs->stack);
+    }
+  return REG_NOERROR;
+}
+
+static void
+update_regs (dfa, pmatch, cur_node, cur_idx, nmatch)
+     re_dfa_t *dfa;
+     regmatch_t *pmatch;
+     int cur_node, cur_idx, nmatch;
+{
+  int type = dfa->nodes[cur_node].type;
+  int reg_num;
+  if (type != OP_OPEN_SUBEXP && type != OP_CLOSE_SUBEXP)
+    return;
+  reg_num = dfa->nodes[cur_node].opr.idx + 1;
+  if (reg_num >= nmatch)
+    return;
+  if (type == OP_OPEN_SUBEXP)
+    {
+      /* We are at the first node of this sub expression.  */
+      pmatch[reg_num].rm_so = cur_idx;
+      pmatch[reg_num].rm_eo = -1;
+    }
+  else if (type == OP_CLOSE_SUBEXP)
+    /* We are at the first node of this sub expression.  */
+    pmatch[reg_num].rm_eo = cur_idx;
+}
+
+#define NUMBER_OF_STATE 1
+
+/* This function checks the STATE_LOG from the SCTX->last_str_idx to 0
+   and sift the nodes in each states according to the following rules.
+   Updated state_log will be wrote to STATE_LOG.
+
+   Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if...
+     1. When STR_IDX == MATCH_LAST(the last index in the state_log):
+       If `a' isn't the LAST_NODE and `a' can't epsilon transit to
+       the LAST_NODE, we throw away the node `a'.
+     2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts
+       string `s' and transit to `b':
+       i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw
+          away the node `a'.
+       ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is
+           throwed away, we throw away the node `a'.
+     3. When 0 <= STR_IDX < n and 'a' epsilon transit to 'b':
+       i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the
+          node `a'.
+       ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is throwed away,
+           we throw away the node `a'.  */
+
+#define STATE_NODE_CONTAINS(state,node) \
+  ((state) != NULL && re_node_set_contains (&(state)->nodes, node))
+
+static reg_errcode_t
+sift_states_backward (preg, mctx, sctx)
+     const regex_t *preg;
+     re_match_context_t *mctx;
+     re_sift_context_t *sctx;
+{
+  reg_errcode_t err;
+  re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+  int null_cnt = 0;
+  int str_idx = sctx->last_str_idx;
+  re_node_set cur_dest;
+  re_node_set *cur_src; /* Points the state_log[str_idx]->nodes  */
+
+#ifdef DEBUG
+  assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL);
+#endif
+  cur_src = &mctx->state_log[str_idx]->nodes;
+
+  /* Build sifted state_log[str_idx].  It has the nodes which can epsilon
+     transit to the last_node and the last_node itself.  */
+  err = re_node_set_init_1 (&cur_dest, sctx->last_node);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  err = update_cur_sifted_state (preg, mctx, sctx, str_idx, &cur_dest);
+  if (BE (err != REG_NOERROR, 0))
+    goto free_return;
+
+  /* Then check each states in the state_log.  */
+  while (str_idx > 0)
+    {
+      int i, ret;
+      /* Update counters.  */
+      null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0;
+      if (null_cnt > mctx->max_mb_elem_len)
+       {
+         memset (sctx->sifted_states, '\0',
+                 sizeof (re_dfastate_t *) * str_idx);
+         re_node_set_free (&cur_dest);
+         return REG_NOERROR;
+       }
+      re_node_set_empty (&cur_dest);
+      --str_idx;
+      cur_src = ((mctx->state_log[str_idx] == NULL) ? &empty_set
+                : &mctx->state_log[str_idx]->nodes);
+
+      /* Then build the next sifted state.
+        We build the next sifted state on `cur_dest', and update
+        `sifted_states[str_idx]' with `cur_dest'.
+        Note:
+        `cur_dest' is the sifted state from `state_log[str_idx + 1]'.
+        `cur_src' points the node_set of the old `state_log[str_idx]'.  */
+      for (i = 0; i < cur_src->nelem; i++)
+       {
+         int prev_node = cur_src->elems[i];
+         int naccepted = 0;
+         re_token_type_t type = dfa->nodes[prev_node].type;
+
+         if (IS_EPSILON_NODE(type))
+           continue;
+#ifdef RE_ENABLE_I18N
+         /* If the node may accept `multi byte'.  */
+         if (ACCEPT_MB_NODE (type))
+           naccepted = sift_states_iter_mb (preg, mctx, sctx, prev_node,
+                                            str_idx, sctx->last_str_idx);
+
+#endif /* RE_ENABLE_I18N */
+         /* We don't check backreferences here.
+            See update_cur_sifted_state().  */
+
+         if (!naccepted
+             && check_node_accept (preg, dfa->nodes + prev_node, mctx,
+                                   str_idx)
+             && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1],
+                                     dfa->nexts[prev_node]))
+           naccepted = 1;
+
+         if (naccepted == 0)
+           continue;
+
+         if (sctx->limits.nelem)
+           {
+             int to_idx = str_idx + naccepted;
+             if (check_dst_limits (dfa, &sctx->limits, mctx,
+                                   dfa->nexts[prev_node], to_idx,
+                                   prev_node, str_idx))
+               continue;
+           }
+         ret = re_node_set_insert (&cur_dest, prev_node);
+         if (BE (ret == -1, 0))
+           {
+             err = REG_ESPACE;
+             goto free_return;
+           }
+       }
+
+      /* Add all the nodes which satisfy the following conditions:
+        - It can epsilon transit to a node in CUR_DEST.
+        - It is in CUR_SRC.
+        And update state_log.  */
+      err = update_cur_sifted_state (preg, mctx, sctx, str_idx, &cur_dest);
+      if (BE (err != REG_NOERROR, 0))
+       goto free_return;
+    }
+  err = REG_NOERROR;
+ free_return:
+  re_node_set_free (&cur_dest);
+  return err;
+}
+
+/* Helper functions.  */
+
+static inline reg_errcode_t
+clean_state_log_if_need (mctx, next_state_log_idx)
+    re_match_context_t *mctx;
+    int next_state_log_idx;
+{
+  int top = mctx->state_log_top;
+
+  if (next_state_log_idx >= mctx->input->bufs_len
+      || (next_state_log_idx >= mctx->input->valid_len
+         && mctx->input->valid_len < mctx->input->len))
+    {
+      reg_errcode_t err;
+      err = extend_buffers (mctx);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+    }
+
+  if (top < next_state_log_idx)
+    {
+      memset (mctx->state_log + top + 1, '\0',
+             sizeof (re_dfastate_t *) * (next_state_log_idx - top));
+      mctx->state_log_top = next_state_log_idx;
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+merge_state_array (dfa, dst, src, num)
+     re_dfa_t *dfa;
+     re_dfastate_t **dst;
+     re_dfastate_t **src;
+     int num;
+{
+  int st_idx;
+  reg_errcode_t err;
+  for (st_idx = 0; st_idx < num; ++st_idx)
+    {
+      if (dst[st_idx] == NULL)
+       dst[st_idx] = src[st_idx];
+      else if (src[st_idx] != NULL)
+       {
+         re_node_set merged_set;
+         err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes,
+                                       &src[st_idx]->nodes);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+         dst[st_idx] = re_acquire_state (&err, dfa, &merged_set);
+         re_node_set_free (&merged_set);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+       }
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+update_cur_sifted_state (preg, mctx, sctx, str_idx, dest_nodes)
+     const regex_t *preg;
+     re_match_context_t *mctx;
+     re_sift_context_t *sctx;
+     int str_idx;
+     re_node_set *dest_nodes;
+{
+  reg_errcode_t err;
+  re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+  const re_node_set *candidates;
+  candidates = ((mctx->state_log[str_idx] == NULL) ? &empty_set
+               : &mctx->state_log[str_idx]->nodes);
+
+  /* At first, add the nodes which can epsilon transit to a node in
+     DEST_NODE.  */
+  if (dest_nodes->nelem)
+    {
+      err = add_epsilon_src_nodes (dfa, dest_nodes, candidates);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+    }
+
+  /* Then, check the limitations in the current sift_context.  */
+  if (dest_nodes->nelem && sctx->limits.nelem)
+    {
+      err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits,
+                                mctx->bkref_ents, str_idx);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+    }
+
+  /* Update state_log.  */
+  sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes);
+  if (BE (sctx->sifted_states[str_idx] == NULL && err != REG_NOERROR, 0))
+    return err;
+
+  if ((mctx->state_log[str_idx] != NULL
+       && mctx->state_log[str_idx]->has_backref))
+    {
+      err = sift_states_bkref (preg, mctx, sctx, str_idx, dest_nodes);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+add_epsilon_src_nodes (dfa, dest_nodes, candidates)
+     re_dfa_t *dfa;
+     re_node_set *dest_nodes;
+     const re_node_set *candidates;
+{
+  reg_errcode_t err;
+  int src_idx;
+  re_node_set src_copy;
+
+  err = re_node_set_init_copy (&src_copy, dest_nodes);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  for (src_idx = 0; src_idx < src_copy.nelem; ++src_idx)
+    {
+      err = re_node_set_add_intersect (dest_nodes, candidates,
+                                      dfa->inveclosures
+                                      + src_copy.elems[src_idx]);
+      if (BE (err != REG_NOERROR, 0))
+       {
+         re_node_set_free (&src_copy);
+         return err;
+       }
+    }
+  re_node_set_free (&src_copy);
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+sub_epsilon_src_nodes (dfa, node, dest_nodes, candidates)
+     re_dfa_t *dfa;
+     int node;
+     re_node_set *dest_nodes;
+     const re_node_set *candidates;
+{
+    int ecl_idx;
+    reg_errcode_t err;
+    re_node_set *inv_eclosure = dfa->inveclosures + node;
+    re_node_set except_nodes;
+    re_node_set_init_empty (&except_nodes);
+    for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+      {
+       int cur_node = inv_eclosure->elems[ecl_idx];
+       if (cur_node == node)
+         continue;
+       if (IS_EPSILON_NODE (dfa->nodes[cur_node].type))
+         {
+           int edst1 = dfa->edests[cur_node].elems[0];
+           int edst2 = ((dfa->edests[cur_node].nelem > 1)
+                        ? dfa->edests[cur_node].elems[1] : -1);
+           if ((!re_node_set_contains (inv_eclosure, edst1)
+                && re_node_set_contains (dest_nodes, edst1))
+               || (edst2 > 0
+                   && !re_node_set_contains (inv_eclosure, edst2)
+                   && re_node_set_contains (dest_nodes, edst2)))
+             {
+               err = re_node_set_add_intersect (&except_nodes, candidates,
+                                                dfa->inveclosures + cur_node);
+               if (BE (err != REG_NOERROR, 0))
+                 {
+                   re_node_set_free (&except_nodes);
+                   return err;
+                 }
+             }
+         }
+      }
+    for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
+      {
+       int cur_node = inv_eclosure->elems[ecl_idx];
+       if (!re_node_set_contains (&except_nodes, cur_node))
+         {
+           int idx = re_node_set_contains (dest_nodes, cur_node) - 1;
+           re_node_set_remove_at (dest_nodes, idx);
+         }
+      }
+    re_node_set_free (&except_nodes);
+    return REG_NOERROR;
+}
+
+static int
+check_dst_limits (dfa, limits, mctx, dst_node, dst_idx, src_node, src_idx)
+     re_dfa_t *dfa;
+     re_node_set *limits;
+     re_match_context_t *mctx;
+     int dst_node, dst_idx, src_node, src_idx;
+{
+  int lim_idx, src_pos, dst_pos;
+
+  for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+    {
+      int subexp_idx;
+      struct re_backref_cache_entry *ent;
+      ent = mctx->bkref_ents + limits->elems[lim_idx];
+      subexp_idx = dfa->nodes[ent->node].opr.idx - 1;
+
+      dst_pos = check_dst_limits_calc_pos (dfa, mctx, limits->elems[lim_idx],
+                                          dfa->eclosures + dst_node,
+                                          subexp_idx, dst_node, dst_idx);
+      src_pos = check_dst_limits_calc_pos (dfa, mctx, limits->elems[lim_idx],
+                                          dfa->eclosures + src_node,
+                                          subexp_idx, src_node, src_idx);
+
+      /* In case of:
+        <src> <dst> ( <subexp> )
+        ( <subexp> ) <src> <dst>
+        ( <subexp1> <src> <subexp2> <dst> <subexp3> )  */
+      if (src_pos == dst_pos)
+       continue; /* This is unrelated limitation.  */
+      else
+       return 1;
+    }
+  return 0;
+}
+
+static int
+check_dst_limits_calc_pos (dfa, mctx, limit, eclosures, subexp_idx, node,
+                          str_idx)
+     re_dfa_t *dfa;
+     re_match_context_t *mctx;
+     re_node_set *eclosures;
+     int limit, subexp_idx, node, str_idx;
+{
+  struct re_backref_cache_entry *lim = mctx->bkref_ents + limit;
+  int pos = (str_idx < lim->subexp_from ? -1
+            : (lim->subexp_to < str_idx ? 1 : 0));
+  if (pos == 0
+      && (str_idx == lim->subexp_from || str_idx == lim->subexp_to))
+    {
+      int node_idx;
+      for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
+       {
+         int node = eclosures->elems[node_idx];
+         re_token_type_t type= dfa->nodes[node].type;
+         if (type == OP_BACK_REF)
+           {
+             int bi = search_cur_bkref_entry (mctx, str_idx);
+             for (; bi < mctx->nbkref_ents; ++bi)
+               {
+                 struct re_backref_cache_entry *ent = mctx->bkref_ents + bi;
+                 if (ent->str_idx > str_idx)
+                   break;
+                 if (ent->node == node && ent->subexp_from == ent->subexp_to)
+                   {
+                     int cpos, dst;
+                     dst = dfa->edests[node].elems[0];
+                     cpos = check_dst_limits_calc_pos (dfa, mctx, limit,
+                                                       dfa->eclosures + dst,
+                                                       subexp_idx, dst,
+                                                       str_idx);
+                     if ((str_idx == lim->subexp_from && cpos == -1)
+                         || (str_idx == lim->subexp_to && cpos == 0))
+                       return cpos;
+                   }
+               }
+           }
+         if (type == OP_OPEN_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx
+             && str_idx == lim->subexp_from)
+           {
+             pos = -1;
+             break;
+           }
+         if (type == OP_CLOSE_SUBEXP && subexp_idx == dfa->nodes[node].opr.idx
+             && str_idx == lim->subexp_to)
+           break;
+       }
+      if (node_idx == eclosures->nelem && str_idx == lim->subexp_to)
+       pos = 1;
+    }
+  return pos;
+}
+
+/* Check the limitations of sub expressions LIMITS, and remove the nodes
+   which are against limitations from DEST_NODES. */
+
+static reg_errcode_t
+check_subexp_limits (dfa, dest_nodes, candidates, limits, bkref_ents, str_idx)
+     re_dfa_t *dfa;
+     re_node_set *dest_nodes;
+     const re_node_set *candidates;
+     re_node_set *limits;
+     struct re_backref_cache_entry *bkref_ents;
+     int str_idx;
+{
+  reg_errcode_t err;
+  int node_idx, lim_idx;
+
+  for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
+    {
+      int subexp_idx;
+      struct re_backref_cache_entry *ent;
+      ent = bkref_ents + limits->elems[lim_idx];
+
+      if (str_idx <= ent->subexp_from || ent->str_idx < str_idx)
+       continue; /* This is unrelated limitation.  */
+
+      subexp_idx = dfa->nodes[ent->node].opr.idx - 1;
+      if (ent->subexp_to == str_idx)
+       {
+         int ops_node = -1;
+         int cls_node = -1;
+         for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+           {
+             int node = dest_nodes->elems[node_idx];
+             re_token_type_t type= dfa->nodes[node].type;
+             if (type == OP_OPEN_SUBEXP
+                 && subexp_idx == dfa->nodes[node].opr.idx)
+               ops_node = node;
+             else if (type == OP_CLOSE_SUBEXP
+                      && subexp_idx == dfa->nodes[node].opr.idx)
+               cls_node = node;
+           }
+
+         /* Check the limitation of the open subexpression.  */
+         /* Note that (ent->subexp_to = str_idx != ent->subexp_from).  */
+         if (ops_node >= 0)
+           {
+             err = sub_epsilon_src_nodes(dfa, ops_node, dest_nodes,
+                                         candidates);
+             if (BE (err != REG_NOERROR, 0))
+               return err;
+           }
+         /* Check the limitation of the close subexpression.  */
+         for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+           {
+             int node = dest_nodes->elems[node_idx];
+             if (!re_node_set_contains (dfa->inveclosures + node, cls_node)
+                 && !re_node_set_contains (dfa->eclosures + node, cls_node))
+               {
+                 /* It is against this limitation.
+                    Remove it form the current sifted state.  */
+                 err = sub_epsilon_src_nodes(dfa, node, dest_nodes,
+                                             candidates);
+                 if (BE (err != REG_NOERROR, 0))
+                   return err;
+                 --node_idx;
+               }
+           }
+       }
+      else /* (ent->subexp_to != str_idx)  */
+       {
+         for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+           {
+             int node = dest_nodes->elems[node_idx];
+             re_token_type_t type= dfa->nodes[node].type;
+             if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP)
+               {
+                 if (subexp_idx != dfa->nodes[node].opr.idx)
+                   continue;
+                 if ((type == OP_CLOSE_SUBEXP && ent->subexp_to != str_idx)
+                     || (type == OP_OPEN_SUBEXP))
+                   {
+                     /* It is against this limitation.
+                        Remove it form the current sifted state.  */
+                     err = sub_epsilon_src_nodes(dfa, node, dest_nodes,
+                                                 candidates);
+                     if (BE (err != REG_NOERROR, 0))
+                       return err;
+                   }
+               }
+           }
+       }
+    }
+  return REG_NOERROR;
+}
+
+static reg_errcode_t
+sift_states_bkref (preg, mctx, sctx, str_idx, dest_nodes)
+     const regex_t *preg;
+     re_match_context_t *mctx;
+     re_sift_context_t *sctx;
+     int str_idx;
+     re_node_set *dest_nodes;
+{
+  reg_errcode_t err;
+  re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+  int node_idx, node;
+  re_sift_context_t local_sctx;
+  const re_node_set *candidates;
+  candidates = ((mctx->state_log[str_idx] == NULL) ? &empty_set
+               : &mctx->state_log[str_idx]->nodes);
+  local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized.  */
+
+  for (node_idx = 0; node_idx < candidates->nelem; ++node_idx)
+    {
+      int cur_bkref_idx = re_string_cur_idx (mctx->input);
+      re_token_type_t type;
+      node = candidates->elems[node_idx];
+      type = dfa->nodes[node].type;
+      if (node == sctx->cur_bkref && str_idx == cur_bkref_idx)
+       continue;
+      /* Avoid infinite loop for the REs like "()\1+".  */
+      if (node == sctx->last_node && str_idx == sctx->last_str_idx)
+       continue;
+      if (type == OP_BACK_REF)
+       {
+         int enabled_idx = search_cur_bkref_entry (mctx, str_idx);
+         for (; enabled_idx < mctx->nbkref_ents; ++enabled_idx)
+           {
+             int disabled_idx, subexp_len, to_idx, dst_node;
+             struct re_backref_cache_entry *entry;
+             entry = mctx->bkref_ents + enabled_idx;
+             if (entry->str_idx > str_idx)
+               break;
+             if (entry->node != node)
+                 continue;
+             subexp_len = entry->subexp_to - entry->subexp_from;
+             to_idx = str_idx + subexp_len;
+             dst_node = (subexp_len ? dfa->nexts[node]
+                         : dfa->edests[node].elems[0]);
+
+             if (to_idx > sctx->last_str_idx
+                 || sctx->sifted_states[to_idx] == NULL
+                 || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx],
+                                          dst_node)
+                 || check_dst_limits (dfa, &sctx->limits, mctx, node,
+                                      str_idx, dst_node, to_idx))
+               continue;
+               {
+                 re_dfastate_t *cur_state;
+                 entry->flag = 0;
+                 for (disabled_idx = enabled_idx + 1;
+                      disabled_idx < mctx->nbkref_ents; ++disabled_idx)
+                   {
+                     struct re_backref_cache_entry *entry2;
+                     entry2 = mctx->bkref_ents + disabled_idx;
+                     if (entry2->str_idx > str_idx)
+                       break;
+                     entry2->flag = (entry2->node == node) ? 1 : entry2->flag;
+                   }
+
+                 if (local_sctx.sifted_states == NULL)
+                   {
+                     local_sctx = *sctx;
+                     err = re_node_set_init_copy (&local_sctx.limits,
+                                                  &sctx->limits);
+                     if (BE (err != REG_NOERROR, 0))
+                       goto free_return;
+                   }
+                 local_sctx.last_node = node;
+                 local_sctx.last_str_idx = str_idx;
+                 err = re_node_set_insert (&local_sctx.limits, enabled_idx);
+                 if (BE (err < 0, 0))
+                   {
+                     err = REG_ESPACE;
+                     goto free_return;
+                   }
+                 cur_state = local_sctx.sifted_states[str_idx];
+                 err = sift_states_backward (preg, mctx, &local_sctx);
+                 if (BE (err != REG_NOERROR, 0))
+                   goto free_return;
+                 if (sctx->limited_states != NULL)
+                   {
+                     err = merge_state_array (dfa, sctx->limited_states,
+                                              local_sctx.sifted_states,
+                                              str_idx + 1);
+                     if (BE (err != REG_NOERROR, 0))
+                       goto free_return;
+                   }
+                 local_sctx.sifted_states[str_idx] = cur_state;
+                 re_node_set_remove (&local_sctx.limits, enabled_idx);
+                 /* We must not use the variable entry here, since
+                    mctx->bkref_ents might be realloced.  */
+                 mctx->bkref_ents[enabled_idx].flag = 1;
+               }
+           }
+         enabled_idx = search_cur_bkref_entry (mctx, str_idx);
+         for (; enabled_idx < mctx->nbkref_ents; ++enabled_idx)
+           {
+             struct re_backref_cache_entry *entry;
+             entry = mctx->bkref_ents + enabled_idx;
+             if (entry->str_idx > str_idx)
+               break;
+             if (entry->node == node)
+               entry->flag = 0;
+           }
+       }
+    }
+  err = REG_NOERROR;
+ free_return:
+  if (local_sctx.sifted_states != NULL)
+    {
+      re_node_set_free (&local_sctx.limits);
+    }
+
+  return err;
+}
+
+
+#ifdef RE_ENABLE_I18N
+static int
+sift_states_iter_mb (preg, mctx, sctx, node_idx, str_idx, max_str_idx)
+    const regex_t *preg;
+    const re_match_context_t *mctx;
+    re_sift_context_t *sctx;
+    int node_idx, str_idx, max_str_idx;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  int naccepted;
+  /* Check the node can accept `multi byte'.  */
+  naccepted = check_node_accept_bytes (preg, node_idx, mctx->input, str_idx);
+  if (naccepted > 0 && str_idx + naccepted <= max_str_idx &&
+      !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted],
+                           dfa->nexts[node_idx]))
+    /* The node can't accept the `multi byte', or the
+       destination was already throwed away, then the node
+       could't accept the current input `multi byte'.   */
+    naccepted = 0;
+  /* Otherwise, it is sure that the node could accept
+     `naccepted' bytes input.  */
+  return naccepted;
+}
+#endif /* RE_ENABLE_I18N */
+
+\f
+/* Functions for state transition.  */
+
+/* Return the next state to which the current state STATE will transit by
+   accepting the current input byte, and update STATE_LOG if necessary.
+   If STATE can accept a multibyte char/collating element/back reference
+   update the destination of STATE_LOG.  */
+
+static re_dfastate_t *
+transit_state (err, preg, mctx, state, fl_search)
+     reg_errcode_t *err;
+     const regex_t *preg;
+     re_match_context_t *mctx;
+     re_dfastate_t *state;
+     int fl_search;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  re_dfastate_t **trtable, *next_state;
+  unsigned char ch;
+  int cur_idx;
+
+  if (re_string_cur_idx (mctx->input) + 1 >= mctx->input->bufs_len
+      || (re_string_cur_idx (mctx->input) + 1 >= mctx->input->valid_len
+         && mctx->input->valid_len < mctx->input->len))
+    {
+      *err = extend_buffers (mctx);
+      if (BE (*err != REG_NOERROR, 0))
+       return NULL;
+    }
+
+  *err = REG_NOERROR;
+  if (state == NULL)
+    {
+      next_state = state;
+      re_string_skip_bytes (mctx->input, 1);
+    }
+  else
+    {
+#ifdef RE_ENABLE_I18N
+      /* If the current state can accept multibyte.  */
+      if (state->accept_mb)
+       {
+         *err = transit_state_mb (preg, state, mctx);
+         if (BE (*err != REG_NOERROR, 0))
+           return NULL;
+       }
+#endif /* RE_ENABLE_I18N */
+
+      /* Then decide the next state with the single byte.  */
+      if (1)
+       {
+         /* Use transition table  */
+         ch = re_string_fetch_byte (mctx->input);
+         trtable = fl_search ? state->trtable_search : state->trtable;
+         if (trtable == NULL)
+           {
+             trtable = build_trtable (preg, state, fl_search);
+             if (fl_search)
+               state->trtable_search = trtable;
+             else
+               state->trtable = trtable;
+           }
+         next_state = trtable[ch];
+       }
+      else
+       {
+         /* don't use transition table  */
+         next_state = transit_state_sb (err, preg, state, fl_search, mctx);
+         if (BE (next_state == NULL && err != REG_NOERROR, 0))
+           return NULL;
+       }
+    }
+
+  cur_idx = re_string_cur_idx (mctx->input);
+  /* Update the state_log if we need.  */
+  if (mctx->state_log != NULL)
+    {
+      if (cur_idx > mctx->state_log_top)
+       {
+         mctx->state_log[cur_idx] = next_state;
+         mctx->state_log_top = cur_idx;
+       }
+      else if (mctx->state_log[cur_idx] == 0)
+       {
+         mctx->state_log[cur_idx] = next_state;
+       }
+      else
+       {
+         re_dfastate_t *pstate;
+         unsigned int context;
+         re_node_set next_nodes, *log_nodes, *table_nodes = NULL;
+         /* If (state_log[cur_idx] != 0), it implies that cur_idx is
+            the destination of a multibyte char/collating element/
+            back reference.  Then the next state is the union set of
+            these destinations and the results of the transition table.  */
+         pstate = mctx->state_log[cur_idx];
+         log_nodes = pstate->entrance_nodes;
+         if (next_state != NULL)
+           {
+             table_nodes = next_state->entrance_nodes;
+             *err = re_node_set_init_union (&next_nodes, table_nodes,
+                                            log_nodes);
+             if (BE (*err != REG_NOERROR, 0))
+               return NULL;
+           }
+         else
+           next_nodes = *log_nodes;
+         /* Note: We already add the nodes of the initial state,
+                  then we don't need to add them here.  */
+
+         context = re_string_context_at (mctx->input,
+                                         re_string_cur_idx (mctx->input) - 1,
+                                         mctx->eflags, preg->newline_anchor);
+         next_state = mctx->state_log[cur_idx]
+           = re_acquire_state_context (err, dfa, &next_nodes, context);
+         /* We don't need to check errors here, since the return value of
+            this function is next_state and ERR is already set.  */
+
+         if (table_nodes != NULL)
+           re_node_set_free (&next_nodes);
+       }
+    }
+
+  /* Check OP_OPEN_SUBEXP in the current state in case that we use them
+     later.  We must check them here, since the back references in the
+     next state might use them.  */
+  if (dfa->nbackref && next_state/* && fl_process_bkref */)
+    {
+      *err = check_subexp_matching_top (dfa, mctx, &next_state->nodes,
+                                       cur_idx);
+      if (BE (*err != REG_NOERROR, 0))
+       return NULL;
+    }
+
+  /* If the next state has back references.  */
+  if (next_state != NULL && next_state->has_backref)
+    {
+      *err = transit_state_bkref (preg, &next_state->nodes, mctx);
+      if (BE (*err != REG_NOERROR, 0))
+       return NULL;
+      next_state = mctx->state_log[cur_idx];
+    }
+  return next_state;
+}
+
+/* Helper functions for transit_state.  */
+
+/* From the node set CUR_NODES, pick up the nodes whose types are
+   OP_OPEN_SUBEXP and which have corresponding back references in the regular
+   expression. And register them to use them later for evaluating the
+   correspoding back references.  */
+
+static reg_errcode_t
+check_subexp_matching_top (dfa, mctx, cur_nodes, str_idx)
+     re_dfa_t *dfa;
+     re_match_context_t *mctx;
+     re_node_set *cur_nodes;
+     int str_idx;
+{
+  int node_idx;
+  reg_errcode_t err;
+
+  /* TODO: This isn't efficient.
+          Because there might be more than one nodes whose types are
+          OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+          nodes.
+          E.g. RE: (a){2}  */
+  for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx)
+    {
+      int node = cur_nodes->elems[node_idx];
+      if (dfa->nodes[node].type == OP_OPEN_SUBEXP
+         && dfa->used_bkref_map & (1 << dfa->nodes[node].opr.idx))
+       {
+         err = match_ctx_add_subtop (mctx, node, str_idx);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+       }
+    }
+  return REG_NOERROR;
+}
+
+/* Return the next state to which the current state STATE will transit by
+   accepting the current input byte.  */
+
+static re_dfastate_t *
+transit_state_sb (err, preg, state, fl_search, mctx)
+     reg_errcode_t *err;
+     const regex_t *preg;
+     re_dfastate_t *state;
+     int fl_search;
+     re_match_context_t *mctx;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  re_node_set next_nodes;
+  re_dfastate_t *next_state;
+  int node_cnt, cur_str_idx = re_string_cur_idx (mctx->input);
+  unsigned int context;
+
+  *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1);
+  if (BE (*err != REG_NOERROR, 0))
+    return NULL;
+  for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt)
+    {
+      int cur_node = state->nodes.elems[node_cnt];
+      if (check_node_accept (preg, dfa->nodes + cur_node, mctx, cur_str_idx))
+       {
+         *err = re_node_set_merge (&next_nodes,
+                                   dfa->eclosures + dfa->nexts[cur_node]);
+         if (BE (*err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return NULL;
+           }
+       }
+    }
+  if (fl_search)
+    {
+#ifdef RE_ENABLE_I18N
+      int not_initial = 0;
+      if (MB_CUR_MAX > 1)
+       for (node_cnt = 0; node_cnt < next_nodes.nelem; ++node_cnt)
+         if (dfa->nodes[next_nodes.elems[node_cnt]].type == CHARACTER)
+           {
+             not_initial = dfa->nodes[next_nodes.elems[node_cnt]].mb_partial;
+             break;
+           }
+      if (!not_initial)
+#endif
+       {
+         *err = re_node_set_merge (&next_nodes,
+                                   dfa->init_state->entrance_nodes);
+         if (BE (*err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return NULL;
+           }
+       }
+    }
+  context = re_string_context_at (mctx->input, cur_str_idx, mctx->eflags,
+                                 preg->newline_anchor);
+  next_state = re_acquire_state_context (err, dfa, &next_nodes, context);
+  /* We don't need to check errors here, since the return value of
+     this function is next_state and ERR is already set.  */
+
+  re_node_set_free (&next_nodes);
+  re_string_skip_bytes (mctx->input, 1);
+  return next_state;
+}
+
+#ifdef RE_ENABLE_I18N
+static reg_errcode_t
+transit_state_mb (preg, pstate, mctx)
+    const regex_t *preg;
+    re_dfastate_t *pstate;
+    re_match_context_t *mctx;
+{
+  reg_errcode_t err;
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  int i;
+
+  for (i = 0; i < pstate->nodes.nelem; ++i)
+    {
+      re_node_set dest_nodes, *new_nodes;
+      int cur_node_idx = pstate->nodes.elems[i];
+      int naccepted = 0, dest_idx;
+      unsigned int context;
+      re_dfastate_t *dest_state;
+
+      if (dfa->nodes[cur_node_idx].constraint)
+       {
+         context = re_string_context_at (mctx->input,
+                                         re_string_cur_idx (mctx->input),
+                                         mctx->eflags, preg->newline_anchor);
+         if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint,
+                                          context))
+           continue;
+       }
+
+      /* How many bytes the node can accepts?  */
+      if (ACCEPT_MB_NODE (dfa->nodes[cur_node_idx].type))
+       naccepted = check_node_accept_bytes (preg, cur_node_idx, mctx->input,
+                                            re_string_cur_idx (mctx->input));
+      if (naccepted == 0)
+       continue;
+
+      /* The node can accepts `naccepted' bytes.  */
+      dest_idx = re_string_cur_idx (mctx->input) + naccepted;
+      mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted
+                              : mctx->max_mb_elem_len);
+      err = clean_state_log_if_need (mctx, dest_idx);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+#ifdef DEBUG
+      assert (dfa->nexts[cur_node_idx] != -1);
+#endif
+      /* `cur_node_idx' may point the entity of the OP_CONTEXT_NODE,
+        then we use pstate->nodes.elems[i] instead.  */
+      new_nodes = dfa->eclosures + dfa->nexts[pstate->nodes.elems[i]];
+
+      dest_state = mctx->state_log[dest_idx];
+      if (dest_state == NULL)
+       dest_nodes = *new_nodes;
+      else
+       {
+         err = re_node_set_init_union (&dest_nodes,
+                                       dest_state->entrance_nodes, new_nodes);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+       }
+      context = re_string_context_at (mctx->input, dest_idx - 1, mctx->eflags,
+                                     preg->newline_anchor);
+      mctx->state_log[dest_idx]
+       = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+      if (dest_state != NULL)
+       re_node_set_free (&dest_nodes);
+      if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0))
+       return err;
+    }
+  return REG_NOERROR;
+}
+#endif /* RE_ENABLE_I18N */
+
+static reg_errcode_t
+transit_state_bkref (preg, nodes, mctx)
+    const regex_t *preg;
+    re_node_set *nodes;
+    re_match_context_t *mctx;
+{
+  reg_errcode_t err;
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  int i;
+  int cur_str_idx = re_string_cur_idx (mctx->input);
+
+  for (i = 0; i < nodes->nelem; ++i)
+    {
+      int dest_str_idx, prev_nelem, bkc_idx;
+      int node_idx = nodes->elems[i];
+      unsigned int context;
+      re_token_t *node = dfa->nodes + node_idx;
+      re_node_set *new_dest_nodes;
+
+      /* Check whether `node' is a backreference or not.  */
+      if (node->type != OP_BACK_REF)
+       continue;
+
+      if (node->constraint)
+       {
+         context = re_string_context_at (mctx->input, cur_str_idx,
+                                         mctx->eflags, preg->newline_anchor);
+         if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+           continue;
+       }
+
+      /* `node' is a backreference.
+        Check the substring which the substring matched.  */
+      bkc_idx = mctx->nbkref_ents;
+      err = get_subexp (preg, mctx, node_idx, cur_str_idx);
+      if (BE (err != REG_NOERROR, 0))
+       goto free_return;
+
+      /* And add the epsilon closures (which is `new_dest_nodes') of
+        the backreference to appropriate state_log.  */
+#ifdef DEBUG
+      assert (dfa->nexts[node_idx] != -1);
+#endif
+      for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx)
+       {
+         int subexp_len;
+         re_dfastate_t *dest_state;
+         struct re_backref_cache_entry *bkref_ent;
+         bkref_ent = mctx->bkref_ents + bkc_idx;
+         if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx)
+           continue;
+         subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from;
+         new_dest_nodes = (subexp_len == 0
+                           ? dfa->eclosures + dfa->edests[node_idx].elems[0]
+                           : dfa->eclosures + dfa->nexts[node_idx]);
+         dest_str_idx = (cur_str_idx + bkref_ent->subexp_to
+                         - bkref_ent->subexp_from);
+         context = re_string_context_at (mctx->input, dest_str_idx - 1,
+                                         mctx->eflags, preg->newline_anchor);
+         dest_state = mctx->state_log[dest_str_idx];
+         prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0
+                       : mctx->state_log[cur_str_idx]->nodes.nelem);
+         /* Add `new_dest_node' to state_log.  */
+         if (dest_state == NULL)
+           {
+             mctx->state_log[dest_str_idx]
+               = re_acquire_state_context (&err, dfa, new_dest_nodes,
+                                           context);
+             if (BE (mctx->state_log[dest_str_idx] == NULL
+                     && err != REG_NOERROR, 0))
+               goto free_return;
+           }
+         else
+           {
+             re_node_set dest_nodes;
+             err = re_node_set_init_union (&dest_nodes,
+                                           dest_state->entrance_nodes,
+                                           new_dest_nodes);
+             if (BE (err != REG_NOERROR, 0))
+               {
+                 re_node_set_free (&dest_nodes);
+                 goto free_return;
+               }
+             mctx->state_log[dest_str_idx]
+               = re_acquire_state_context (&err, dfa, &dest_nodes, context);
+             re_node_set_free (&dest_nodes);
+             if (BE (mctx->state_log[dest_str_idx] == NULL
+                     && err != REG_NOERROR, 0))
+               goto free_return;
+           }
+         /* We need to check recursively if the backreference can epsilon
+            transit.  */
+         if (subexp_len == 0
+             && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem)
+           {
+             err = check_subexp_matching_top (dfa, mctx, new_dest_nodes,
+                                              cur_str_idx);
+             if (BE (err != REG_NOERROR, 0))
+               goto free_return;
+             err = transit_state_bkref (preg, new_dest_nodes, mctx);
+             if (BE (err != REG_NOERROR, 0))
+               goto free_return;
+           }
+       }
+    }
+  err = REG_NOERROR;
+ free_return:
+  return err;
+}
+
+/* Enumerate all the candidates which the backreference BKREF_NODE can match
+   at BKREF_STR_IDX, and register them by match_ctx_add_entry().
+   Note that we might collect inappropriate candidates here.
+   However, the cost of checking them strictly here is too high, then we
+   delay these checking for prune_impossible_nodes().  */
+
+static reg_errcode_t
+get_subexp (preg, mctx, bkref_node, bkref_str_idx)
+     const regex_t *preg;
+     re_match_context_t *mctx;
+     int bkref_node, bkref_str_idx;
+{
+  int subexp_num, sub_top_idx;
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  char *buf = (char *) re_string_get_buffer (mctx->input);
+  /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX.  */
+  int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx);
+  for (; cache_idx < mctx->nbkref_ents; ++cache_idx)
+    {
+      struct re_backref_cache_entry *entry = mctx->bkref_ents + cache_idx;
+      if (entry->str_idx > bkref_str_idx)
+       break;
+      if (entry->node == bkref_node)
+       return REG_NOERROR; /* We already checked it.  */
+    }
+  subexp_num = dfa->nodes[bkref_node].opr.idx - 1;
+
+  /* For each sub expression  */
+  for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx)
+    {
+      reg_errcode_t err;
+      re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx];
+      re_sub_match_last_t *sub_last;
+      int sub_last_idx, sl_str;
+      char *bkref_str;
+
+      if (dfa->nodes[sub_top->node].opr.idx != subexp_num)
+       continue; /* It isn't related.  */
+
+      sl_str = sub_top->str_idx;
+      bkref_str = buf + bkref_str_idx;
+      /* At first, check the last node of sub expressions we already
+        evaluated.  */
+      for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx)
+       {
+         int sl_str_diff;
+         sub_last = sub_top->lasts[sub_last_idx];
+         sl_str_diff = sub_last->str_idx - sl_str;
+         /* The matched string by the sub expression match with the substring
+            at the back reference?  */
+         if (sl_str_diff > 0
+             && memcmp (bkref_str, buf + sl_str, sl_str_diff) != 0)
+           break; /* We don't need to search this sub expression any more.  */
+         bkref_str += sl_str_diff;
+         sl_str += sl_str_diff;
+         err = get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node,
+                               bkref_str_idx);
+         if (err == REG_NOMATCH)
+           continue;
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+       }
+      if (sub_last_idx < sub_top->nlasts)
+       continue;
+      if (sub_last_idx > 0)
+       ++sl_str;
+      /* Then, search for the other last nodes of the sub expression.  */
+      for (; sl_str <= bkref_str_idx; ++sl_str)
+       {
+         int cls_node, sl_str_off;
+         re_node_set *nodes;
+         sl_str_off = sl_str - sub_top->str_idx;
+         /* The matched string by the sub expression match with the substring
+            at the back reference?  */
+         if (sl_str_off > 0
+             && memcmp (bkref_str++, buf + sl_str - 1, 1) != 0)
+           break; /* We don't need to search this sub expression any more.  */
+         if (mctx->state_log[sl_str] == NULL)
+           continue;
+         /* Does this state have a ')' of the sub expression?  */
+         nodes = &mctx->state_log[sl_str]->nodes;
+         cls_node = find_subexp_node (dfa, nodes, subexp_num, 0);
+         if (cls_node == -1)
+           continue; /* No.  */
+         if (sub_top->path == NULL)
+           {
+             sub_top->path = calloc (sizeof (state_array_t),
+                                     sl_str - sub_top->str_idx + 1);
+             if (sub_top->path == NULL)
+               return REG_ESPACE;
+           }
+         /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node
+            in the current context?  */
+         err = check_arrival (preg, mctx, sub_top->path, sub_top->node,
+                              sub_top->str_idx, cls_node, sl_str, 0);
+         if (err == REG_NOMATCH)
+             continue;
+         if (BE (err != REG_NOERROR, 0))
+             return err;
+         sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str);
+         if (BE (sub_last == NULL, 0))
+           return REG_ESPACE;
+         err = get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node,
+                               bkref_str_idx);
+         if (err == REG_NOMATCH)
+           continue;
+       }
+    }
+  return REG_NOERROR;
+}
+
+/* Helper functions for get_subexp().  */
+
+/* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR.
+   If it can arrive, register the sub expression expressed with SUB_TOP
+   and SUB_LAST.  */
+
+static reg_errcode_t
+get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node, bkref_str)
+     const regex_t *preg;
+     re_match_context_t *mctx;
+     re_sub_match_top_t *sub_top;
+     re_sub_match_last_t *sub_last;
+     int bkref_node, bkref_str;
+{
+  reg_errcode_t err;
+  int to_idx;
+  /* Can the subexpression arrive the back reference?  */
+  err = check_arrival (preg, mctx, &sub_last->path, sub_last->node,
+                      sub_last->str_idx, bkref_node, bkref_str, 1);
+  if (err != REG_NOERROR)
+    return err;
+  err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx,
+                            sub_last->str_idx);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx;
+  clean_state_log_if_need (mctx, to_idx);
+  return REG_NOERROR;
+}
+
+/* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX.
+   Search '(' if FL_OPEN, or search ')' otherwise.
+   TODO: This function isn't efficient...
+        Because there might be more than one nodes whose types are
+        OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all
+        nodes.
+        E.g. RE: (a){2}  */
+
+static int
+find_subexp_node (dfa, nodes, subexp_idx, fl_open)
+     re_dfa_t *dfa;
+     re_node_set *nodes;
+     int subexp_idx, fl_open;
+{
+  int cls_idx;
+  for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx)
+    {
+      int cls_node = nodes->elems[cls_idx];
+      re_token_t *node = dfa->nodes + cls_node;
+      if (((fl_open && node->type == OP_OPEN_SUBEXP)
+         || (!fl_open && node->type == OP_CLOSE_SUBEXP))
+         && node->opr.idx == subexp_idx)
+       return cls_node;
+    }
+  return -1;
+}
+
+/* Check whether the node TOP_NODE at TOP_STR can arrive to the node
+   LAST_NODE at LAST_STR.  We record the path onto PATH since it will be
+   heavily reused.
+   Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise.  */
+
+static reg_errcode_t
+check_arrival (preg, mctx, path, top_node, top_str, last_node, last_str,
+              fl_open)
+     const regex_t *preg;
+     re_match_context_t *mctx;
+     state_array_t *path;
+     int top_node, top_str, last_node, last_str, fl_open;
+{
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  reg_errcode_t err;
+  int subexp_num, backup_cur_idx, str_idx, null_cnt;
+  re_dfastate_t *cur_state = NULL;
+  re_node_set *cur_nodes, next_nodes;
+  re_dfastate_t **backup_state_log;
+  unsigned int context;
+
+  subexp_num = dfa->nodes[top_node].opr.idx;
+  /* Extend the buffer if we need.  */
+  if (path->alloc < last_str + mctx->max_mb_elem_len + 1)
+    {
+      re_dfastate_t **new_array;
+      int old_alloc = path->alloc;
+      path->alloc += last_str + mctx->max_mb_elem_len + 1;
+      new_array = re_realloc (path->array, re_dfastate_t *, path->alloc);
+      if (new_array == NULL)
+       return REG_ESPACE;
+      path->array = new_array;
+      memset (new_array + old_alloc, '\0',
+             sizeof (re_dfastate_t *) * (path->alloc - old_alloc));
+    }
+
+  str_idx = path->next_idx == 0 ? top_str : path->next_idx;
+
+  /* Temporary modify MCTX.  */
+  backup_state_log = mctx->state_log;
+  backup_cur_idx = mctx->input->cur_idx;
+  mctx->state_log = path->array;
+  mctx->input->cur_idx = str_idx;
+
+  /* Setup initial node set.  */
+  context = re_string_context_at (mctx->input, str_idx - 1, mctx->eflags,
+                                 preg->newline_anchor);
+  if (str_idx == top_str)
+    {
+      err = re_node_set_init_1 (&next_nodes, top_node);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+      err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, fl_open);
+      if (BE (err != REG_NOERROR, 0))
+       {
+         re_node_set_free (&next_nodes);
+         return err;
+       }
+    }
+  else
+    {
+      cur_state = mctx->state_log[str_idx];
+      if (cur_state && cur_state->has_backref)
+       {
+         err = re_node_set_init_copy (&next_nodes, &cur_state->nodes);
+         if (BE ( err != REG_NOERROR, 0))
+           return err;
+       }
+      else
+       re_node_set_init_empty (&next_nodes);
+    }
+  if (str_idx == top_str || (cur_state && cur_state->has_backref))
+    {
+      if (next_nodes.nelem)
+       {
+         err = expand_bkref_cache (preg, mctx, &next_nodes, str_idx, last_str,
+                                   subexp_num, fl_open);
+         if (BE ( err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return err;
+           }
+       }
+      cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+      if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+       {
+         re_node_set_free (&next_nodes);
+         return err;
+       }
+      mctx->state_log[str_idx] = cur_state;
+    }
+
+  for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;)
+    {
+      re_node_set_empty (&next_nodes);
+      if (mctx->state_log[str_idx + 1])
+       {
+         err = re_node_set_merge (&next_nodes,
+                                  &mctx->state_log[str_idx + 1]->nodes);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return err;
+           }
+       }
+      if (cur_state)
+       {
+         err = check_arrival_add_next_nodes(preg, dfa, mctx, str_idx,
+                                            &cur_state->nodes, &next_nodes);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return err;
+           }
+       }
+      ++str_idx;
+      if (next_nodes.nelem)
+       {
+         err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num,
+                                         fl_open);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return err;
+           }
+         err = expand_bkref_cache (preg, mctx, &next_nodes, str_idx, last_str,
+                                   subexp_num, fl_open);
+         if (BE ( err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&next_nodes);
+             return err;
+           }
+       }
+      context = re_string_context_at (mctx->input, str_idx - 1, mctx->eflags,
+                                     preg->newline_anchor);
+      cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
+      if (BE (cur_state == NULL && err != REG_NOERROR, 0))
+       {
+         re_node_set_free (&next_nodes);
+         return err;
+       }
+      mctx->state_log[str_idx] = cur_state;
+      null_cnt = cur_state == NULL ? null_cnt + 1 : 0;
+    }
+  re_node_set_free (&next_nodes);
+  cur_nodes = (mctx->state_log[last_str] == NULL ? NULL
+              : &mctx->state_log[last_str]->nodes);
+  path->next_idx = str_idx;
+
+  /* Fix MCTX.  */
+  mctx->state_log = backup_state_log;
+  mctx->input->cur_idx = backup_cur_idx;
+
+  if (cur_nodes == NULL)
+    return REG_NOMATCH;
+  /* Then check the current node set has the node LAST_NODE.  */
+  return (re_node_set_contains (cur_nodes, last_node)
+         || re_node_set_contains (cur_nodes, last_node) ? REG_NOERROR
+         : REG_NOMATCH);
+}
+
+/* Helper functions for check_arrival.  */
+
+/* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them
+   to NEXT_NODES.
+   TODO: This function is similar to the functions transit_state*(),
+        however this function has many additional works.
+        Can't we unify them?  */
+
+static reg_errcode_t
+check_arrival_add_next_nodes (preg, dfa, mctx, str_idx, cur_nodes, next_nodes)
+     const regex_t *preg;
+     re_dfa_t *dfa;
+     re_match_context_t *mctx;
+     int str_idx;
+     re_node_set *cur_nodes, *next_nodes;
+{
+  int cur_idx;
+  reg_errcode_t err;
+  re_node_set union_set;
+  re_node_set_init_empty (&union_set);
+  for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx)
+    {
+      int naccepted = 0;
+      int cur_node = cur_nodes->elems[cur_idx];
+      re_token_type_t type = dfa->nodes[cur_node].type;
+      if (IS_EPSILON_NODE(type))
+       continue;
+#ifdef RE_ENABLE_I18N
+      /* If the node may accept `multi byte'.  */
+      if (ACCEPT_MB_NODE (type))
+       {
+         naccepted = check_node_accept_bytes (preg, cur_node, mctx->input,
+                                              str_idx);
+         if (naccepted > 1)
+           {
+             re_dfastate_t *dest_state;
+             int next_node = dfa->nexts[cur_node];
+             int next_idx = str_idx + naccepted;
+             dest_state = mctx->state_log[next_idx];
+             re_node_set_empty (&union_set);
+             if (dest_state)
+               {
+                 err = re_node_set_merge (&union_set, &dest_state->nodes);
+                 if (BE (err != REG_NOERROR, 0))
+                   {
+                     re_node_set_free (&union_set);
+                     return err;
+                   }
+                 err = re_node_set_insert (&union_set, next_node);
+                 if (BE (err < 0, 0))
+                   {
+                     re_node_set_free (&union_set);
+                     return REG_ESPACE;
+                   }
+               }
+             else
+               {
+                 err = re_node_set_insert (&union_set, next_node);
+                 if (BE (err < 0, 0))
+                   {
+                     re_node_set_free (&union_set);
+                     return REG_ESPACE;
+                   }
+               }
+             mctx->state_log[next_idx] = re_acquire_state (&err, dfa,
+                                                           &union_set);
+             if (BE (mctx->state_log[next_idx] == NULL
+                     && err != REG_NOERROR, 0))
+               {
+                 re_node_set_free (&union_set);
+                 return err;
+               }
+           }
+       }
+#endif /* RE_ENABLE_I18N */
+      if (naccepted
+         || check_node_accept (preg, dfa->nodes + cur_node, mctx,
+                               str_idx))
+       {
+         err = re_node_set_insert (next_nodes, dfa->nexts[cur_node]);
+         if (BE (err < 0, 0))
+           {
+             re_node_set_free (&union_set);
+             return REG_ESPACE;
+           }
+       }
+    }
+  re_node_set_free (&union_set);
+  return REG_NOERROR;
+}
+
+/* For all the nodes in CUR_NODES, add the epsilon closures of them to
+   CUR_NODES, however exclude the nodes which are:
+    - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN.
+    - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN.
+*/
+
+static reg_errcode_t
+check_arrival_expand_ecl (dfa, cur_nodes, ex_subexp, fl_open)
+     re_dfa_t *dfa;
+     re_node_set *cur_nodes;
+     int ex_subexp, fl_open;
+{
+  reg_errcode_t err;
+  int idx, outside_node;
+  re_node_set new_nodes;
+#ifdef DEBUG
+  assert (cur_nodes->nelem);
+#endif
+  err = re_node_set_alloc (&new_nodes, cur_nodes->nelem);
+  if (BE (err != REG_NOERROR, 0))
+    return err;
+  /* Create a new node set NEW_NODES with the nodes which are epsilon
+     closures of the node in CUR_NODES.  */
+
+  for (idx = 0; idx < cur_nodes->nelem; ++idx)
+    {
+      int cur_node = cur_nodes->elems[idx];
+      re_node_set *eclosure = dfa->eclosures + cur_node;
+      outside_node = find_subexp_node (dfa, eclosure, ex_subexp, fl_open);
+      if (outside_node == -1)
+       {
+         /* There are no problematic nodes, just merge them.  */
+         err = re_node_set_merge (&new_nodes, eclosure);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&new_nodes);
+             return err;
+           }
+       }
+      else
+       {
+         /* There are problematic nodes, re-calculate incrementally.  */
+         err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node,
+                                             ex_subexp, fl_open);
+         if (BE (err != REG_NOERROR, 0))
+           {
+             re_node_set_free (&new_nodes);
+             return err;
+           }
+       }
+    }
+  re_node_set_free (cur_nodes);
+  *cur_nodes = new_nodes;
+  return REG_NOERROR;
+}
+
+/* Helper function for check_arrival_expand_ecl.
+   Check incrementally the epsilon closure of TARGET, and if it isn't
+   problematic append it to DST_NODES.  */
+
+static reg_errcode_t
+check_arrival_expand_ecl_sub (dfa, dst_nodes, target, ex_subexp, fl_open)
+     re_dfa_t *dfa;
+     int target, ex_subexp, fl_open;
+     re_node_set *dst_nodes;
+{
+  int cur_node, type;
+  for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);)
+    {
+      int err;
+      type = dfa->nodes[cur_node].type;
+
+      if (((type == OP_OPEN_SUBEXP && fl_open)
+          || (type == OP_CLOSE_SUBEXP && !fl_open))
+         && dfa->nodes[cur_node].opr.idx == ex_subexp)
+       {
+         if (!fl_open)
+           {
+             err = re_node_set_insert (dst_nodes, cur_node);
+             if (BE (err == -1, 0))
+               return REG_ESPACE;
+           }
+         break;
+       }
+      err = re_node_set_insert (dst_nodes, cur_node);
+      if (BE (err == -1, 0))
+       return REG_ESPACE;
+      if (dfa->edests[cur_node].nelem == 0)
+       break;
+      if (dfa->edests[cur_node].nelem == 2)
+       {
+         err = check_arrival_expand_ecl_sub (dfa, dst_nodes,
+                                             dfa->edests[cur_node].elems[1],
+                                             ex_subexp, fl_open);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+       }
+      cur_node = dfa->edests[cur_node].elems[0];
+    }
+  return REG_NOERROR;
+}
+
+
+/* For all the back references in the current state, calculate the
+   destination of the back references by the appropriate entry
+   in MCTX->BKREF_ENTS.  */
+
+static reg_errcode_t
+expand_bkref_cache (preg, mctx, cur_nodes, cur_str, last_str, subexp_num,
+                   fl_open)
+     const regex_t *preg;
+     re_match_context_t *mctx;
+     int cur_str, last_str, subexp_num, fl_open;
+     re_node_set *cur_nodes;
+{
+  reg_errcode_t err;
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  int cache_idx, cache_idx_start;
+  /* The current state.  */
+
+  cache_idx_start = search_cur_bkref_entry (mctx, cur_str);
+  for (cache_idx = cache_idx_start; cache_idx < mctx->nbkref_ents; ++cache_idx)
+    {
+      int to_idx, next_node;
+      struct re_backref_cache_entry *ent = mctx->bkref_ents + cache_idx;
+      if (ent->str_idx > cur_str)
+       break;
+      /* Is this entry ENT is appropriate?  */
+      if (!re_node_set_contains (cur_nodes, ent->node))
+       continue; /* No.  */
+
+      to_idx = cur_str + ent->subexp_to - ent->subexp_from;
+      /* Calculate the destination of the back reference, and append it
+        to MCTX->STATE_LOG.  */
+      if (to_idx == cur_str)
+       {
+         /* The backreference did epsilon transit, we must re-check all the
+            node in the current state.  */
+         re_node_set new_dests;
+         reg_errcode_t err2, err3;
+         next_node = dfa->edests[ent->node].elems[0];
+         if (re_node_set_contains (cur_nodes, next_node))
+           continue;
+         err = re_node_set_init_1 (&new_dests, next_node);
+         err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num,
+                                          fl_open);
+         err3 = re_node_set_merge (cur_nodes, &new_dests);
+         re_node_set_free (&new_dests);
+         if (BE (err != REG_NOERROR || err2 != REG_NOERROR
+                 || err3 != REG_NOERROR, 0))
+           {
+             err = (err != REG_NOERROR ? err
+                    : (err2 != REG_NOERROR ? err2 : err3));
+             return err;
+           }
+         /* TODO: It is still inefficient...  */
+         cache_idx = cache_idx_start - 1;
+         continue;
+       }
+      else
+       {
+         re_node_set union_set;
+         next_node = dfa->nexts[ent->node];
+         if (mctx->state_log[to_idx])
+           {
+             int ret;
+             if (re_node_set_contains (&mctx->state_log[to_idx]->nodes,
+                                       next_node))
+               continue;
+             err = re_node_set_init_copy (&union_set,
+                                          &mctx->state_log[to_idx]->nodes);
+             ret = re_node_set_insert (&union_set, next_node);
+             if (BE (err != REG_NOERROR || ret < 0, 0))
+               {
+                 re_node_set_free (&union_set);
+                 err = err != REG_NOERROR ? err : REG_ESPACE;
+                 return err;
+               }
+           }
+         else
+           {
+             err = re_node_set_init_1 (&union_set, next_node);
+             if (BE (err != REG_NOERROR, 0))
+               return err;
+           }
+         mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set);
+         re_node_set_free (&union_set);
+         if (BE (mctx->state_log[to_idx] == NULL
+                 && err != REG_NOERROR, 0))
+           return err;
+       }
+    }
+  return REG_NOERROR;
+}
+
+/* Build transition table for the state.
+   Return the new table if succeeded, otherwise return NULL.  */
+
+static re_dfastate_t **
+build_trtable (preg, state, fl_search)
+    const regex_t *preg;
+    const re_dfastate_t *state;
+    int fl_search;
+{
+  reg_errcode_t err;
+  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  int i, j, k, ch;
+  int dests_node_malloced = 0, dest_states_malloced = 0;
+  int ndests; /* Number of the destination states from `state'.  */
+  re_dfastate_t **trtable;
+  re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl;
+  re_node_set follows, *dests_node;
+  bitset *dests_ch;
+  bitset acceptable;
+
+  /* We build DFA states which corresponds to the destination nodes
+     from `state'.  `dests_node[i]' represents the nodes which i-th
+     destination state contains, and `dests_ch[i]' represents the
+     characters which i-th destination state accepts.  */
+#ifdef _LIBC
+  if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX))
+    dests_node = (re_node_set *)
+                alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX);
+  else
+#endif
+    {
+      dests_node = (re_node_set *)
+                  malloc ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX);
+      if (BE (dests_node == NULL, 0))
+       return NULL;
+      dests_node_malloced = 1;
+    }
+  dests_ch = (bitset *) (dests_node + SBC_MAX);
+
+  /* Initialize transiton table.  */
+  trtable = (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+  if (BE (trtable == NULL, 0))
+    {
+      if (dests_node_malloced)
+       free (dests_node);
+      return NULL;
+    }
+
+  /* At first, group all nodes belonging to `state' into several
+     destinations.  */
+  ndests = group_nodes_into_DFAstates (preg, state, dests_node, dests_ch);
+  if (BE (ndests <= 0, 0))
+    {
+      if (dests_node_malloced)
+       free (dests_node);
+      /* Return NULL in case of an error, trtable otherwise.  */
+      if (ndests == 0)
+       return trtable;
+      free (trtable);
+      return NULL;
+    }
+
+  err = re_node_set_alloc (&follows, ndests + 1);
+  if (BE (err != REG_NOERROR, 0))
+    goto out_free;
+
+#ifdef _LIBC
+  if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset)) * SBC_MAX
+                        + ndests * 3 * sizeof (re_dfastate_t *)))
+    dest_states = (re_dfastate_t **)
+                 alloca (ndests * 3 * sizeof (re_dfastate_t *));
+  else
+#endif
+    {
+      dest_states = (re_dfastate_t **)
+                   malloc (ndests * 3 * sizeof (re_dfastate_t *));
+      if (BE (dest_states == NULL, 0))
+       {
+out_free:
+         if (dest_states_malloced)
+           free (dest_states);
+         re_node_set_free (&follows);
+         for (i = 0; i < ndests; ++i)
+           re_node_set_free (dests_node + i);
+         free (trtable);
+         if (dests_node_malloced)
+           free (dests_node);
+         return NULL;
+       }
+      dest_states_malloced = 1;
+    }
+  dest_states_word = dest_states + ndests;
+  dest_states_nl = dest_states_word + ndests;
+  bitset_empty (acceptable);
+
+  /* Then build the states for all destinations.  */
+  for (i = 0; i < ndests; ++i)
+    {
+      int next_node;
+      re_node_set_empty (&follows);
+      /* Merge the follows of this destination states.  */
+      for (j = 0; j < dests_node[i].nelem; ++j)
+       {
+         next_node = dfa->nexts[dests_node[i].elems[j]];
+         if (next_node != -1)
+           {
+             err = re_node_set_merge (&follows, dfa->eclosures + next_node);
+             if (BE (err != REG_NOERROR, 0))
+               goto out_free;
+           }
+       }
+      /* If search flag is set, merge the initial state.  */
+      if (fl_search)
+       {
+#ifdef RE_ENABLE_I18N
+         int not_initial = 0;
+         for (j = 0; j < follows.nelem; ++j)
+           if (dfa->nodes[follows.elems[j]].type == CHARACTER)
+             {
+               not_initial = dfa->nodes[follows.elems[j]].mb_partial;
+               break;
+             }
+         if (!not_initial)
+#endif
+           {
+             err = re_node_set_merge (&follows,
+                                      dfa->init_state->entrance_nodes);
+             if (BE (err != REG_NOERROR, 0))
+               goto out_free;
+           }
+       }
+      dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0);
+      if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0))
+       goto out_free;
+      /* If the new state has context constraint,
+        build appropriate states for these contexts.  */
+      if (dest_states[i]->has_constraint)
+       {
+         dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows,
+                                                         CONTEXT_WORD);
+         if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0))
+           goto out_free;
+         dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows,
+                                                       CONTEXT_NEWLINE);
+         if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0))
+           goto out_free;
+       }
+      else
+       {
+         dest_states_word[i] = dest_states[i];
+         dest_states_nl[i] = dest_states[i];
+       }
+      bitset_merge (acceptable, dests_ch[i]);
+    }
+
+  /* Update the transition table.  */
+  /* For all characters ch...:  */
+  for (i = 0, ch = 0; i < BITSET_UINTS; ++i)
+    for (j = 0; j < UINT_BITS; ++j, ++ch)
+      if ((acceptable[i] >> j) & 1)
+       {
+         /* The current state accepts the character ch.  */
+         if (IS_WORD_CHAR (ch))
+           {
+             for (k = 0; k < ndests; ++k)
+               if ((dests_ch[k][i] >> j) & 1)
+                 {
+                   /* k-th destination accepts the word character ch.  */
+                   trtable[ch] = dest_states_word[k];
+                   /* There must be only one destination which accepts
+                      character ch.  See group_nodes_into_DFAstates.  */
+                   break;
+                 }
+           }
+         else /* not WORD_CHAR */
+           {
+             for (k = 0; k < ndests; ++k)
+               if ((dests_ch[k][i] >> j) & 1)
+                 {
+                   /* k-th destination accepts the non-word character ch.  */
+                   trtable[ch] = dest_states[k];
+                   /* There must be only one destination which accepts
+                      character ch.  See group_nodes_into_DFAstates.  */
+                   break;
+                 }
+           }
+       }
+  /* new line */
+  if (bitset_contain (acceptable, NEWLINE_CHAR))
+    {
+      /* The current state accepts newline character.  */
+      for (k = 0; k < ndests; ++k)
+       if (bitset_contain (dests_ch[k], NEWLINE_CHAR))
+         {
+           /* k-th destination accepts newline character.  */
+           trtable[NEWLINE_CHAR] = dest_states_nl[k];
+           /* There must be only one destination which accepts
+              newline.  See group_nodes_into_DFAstates.  */
+           break;
+         }
+    }
+
+  if (dest_states_malloced)
+    free (dest_states);
+
+  re_node_set_free (&follows);
+  for (i = 0; i < ndests; ++i)
+    re_node_set_free (dests_node + i);
+
+  if (dests_node_malloced)
+    free (dests_node);
+
+  return trtable;
+}
+
+/* Group all nodes belonging to STATE into several destinations.
+   Then for all destinations, set the nodes belonging to the destination
+   to DESTS_NODE[i] and set the characters accepted by the destination
+   to DEST_CH[i].  This function return the number of destinations.  */
+
+static int
+group_nodes_into_DFAstates (preg, state, dests_node, dests_ch)
+    const regex_t *preg;
+    const re_dfastate_t *state;
+    re_node_set *dests_node;
+    bitset *dests_ch;
+{
+  reg_errcode_t err;
+  const re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  int i, j, k;
+  int ndests; /* Number of the destinations from `state'.  */
+  bitset accepts; /* Characters a node can accept.  */
+  const re_node_set *cur_nodes = &state->nodes;
+  bitset_empty (accepts);
+  ndests = 0;
+
+  /* For all the nodes belonging to `state',  */
+  for (i = 0; i < cur_nodes->nelem; ++i)
+    {
+      re_token_t *node = &dfa->nodes[cur_nodes->elems[i]];
+      re_token_type_t type = node->type;
+      unsigned int constraint = node->constraint;
+
+      /* Enumerate all single byte character this node can accept.  */
+      if (type == CHARACTER)
+       bitset_set (accepts, node->opr.c);
+      else if (type == SIMPLE_BRACKET)
+       {
+         bitset_merge (accepts, node->opr.sbcset);
+       }
+      else if (type == OP_PERIOD)
+       {
+         bitset_set_all (accepts);
+         if (!(preg->syntax & RE_DOT_NEWLINE))
+           bitset_clear (accepts, '\n');
+         if (preg->syntax & RE_DOT_NOT_NULL)
+           bitset_clear (accepts, '\0');
+       }
+      else
+       continue;
+
+      /* Check the `accepts' and sift the characters which are not
+        match it the context.  */
+      if (constraint)
+       {
+         if (constraint & NEXT_WORD_CONSTRAINT)
+           for (j = 0; j < BITSET_UINTS; ++j)
+             accepts[j] &= dfa->word_char[j];
+         if (constraint & NEXT_NOTWORD_CONSTRAINT)
+           for (j = 0; j < BITSET_UINTS; ++j)
+             accepts[j] &= ~dfa->word_char[j];
+         if (constraint & NEXT_NEWLINE_CONSTRAINT)
+           {
+             int accepts_newline = bitset_contain (accepts, NEWLINE_CHAR);
+             bitset_empty (accepts);
+             if (accepts_newline)
+               bitset_set (accepts, NEWLINE_CHAR);
+             else
+               continue;
+           }
+       }
+
+      /* Then divide `accepts' into DFA states, or create a new
+        state.  */
+      for (j = 0; j < ndests; ++j)
+       {
+         bitset intersec; /* Intersection sets, see below.  */
+         bitset remains;
+         /* Flags, see below.  */
+         int has_intersec, not_subset, not_consumed;
+
+         /* Optimization, skip if this state doesn't accept the character.  */
+         if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c))
+           continue;
+
+         /* Enumerate the intersection set of this state and `accepts'.  */
+         has_intersec = 0;
+         for (k = 0; k < BITSET_UINTS; ++k)
+           has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k];
+         /* And skip if the intersection set is empty.  */
+         if (!has_intersec)
+           continue;
+
+         /* Then check if this state is a subset of `accepts'.  */
+         not_subset = not_consumed = 0;
+         for (k = 0; k < BITSET_UINTS; ++k)
+           {
+             not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k];
+             not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k];
+           }
+
+         /* If this state isn't a subset of `accepts', create a
+            new group state, which has the `remains'. */
+         if (not_subset)
+           {
+             bitset_copy (dests_ch[ndests], remains);
+             bitset_copy (dests_ch[j], intersec);
+             err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]);
+             if (BE (err != REG_NOERROR, 0))
+               goto error_return;
+             ++ndests;
+           }
+
+         /* Put the position in the current group. */
+         err = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
+         if (BE (err < 0, 0))
+           goto error_return;
+
+         /* If all characters are consumed, go to next node. */
+         if (!not_consumed)
+           break;
+       }
+      /* Some characters remain, create a new group. */
+      if (j == ndests)
+       {
+         bitset_copy (dests_ch[ndests], accepts);
+         err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]);
+         if (BE (err != REG_NOERROR, 0))
+           goto error_return;
+         ++ndests;
+         bitset_empty (accepts);
+       }
+    }
+  return ndests;
+ error_return:
+  for (j = 0; j < ndests; ++j)
+    re_node_set_free (dests_node + j);
+  return -1;
+}
+
+#ifdef RE_ENABLE_I18N
+/* Check how many bytes the node `dfa->nodes[node_idx]' accepts.
+   Return the number of the bytes the node accepts.
+   STR_IDX is the current index of the input string.
+
+   This function handles the nodes which can accept one character, or
+   one collating element like '.', '[a-z]', opposite to the other nodes
+   can only accept one byte.  */
+
+static int
+check_node_accept_bytes (preg, node_idx, input, str_idx)
+    const regex_t *preg;
+    int node_idx, str_idx;
+    const re_string_t *input;
+{
+  const re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  const re_token_t *node = dfa->nodes + node_idx;
+  int elem_len = re_string_elem_size_at (input, str_idx);
+  int char_len = re_string_char_size_at (input, str_idx);
+  int i;
+# ifdef _LIBC
+  int j;
+  uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+# endif /* _LIBC */
+  if (elem_len <= 1 && char_len <= 1)
+    return 0;
+  if (node->type == OP_PERIOD)
+    {
+      /* '.' accepts any one character except the following two cases.  */
+      if ((!(preg->syntax & RE_DOT_NEWLINE) &&
+          re_string_byte_at (input, str_idx) == '\n') ||
+         ((preg->syntax & RE_DOT_NOT_NULL) &&
+          re_string_byte_at (input, str_idx) == '\0'))
+       return 0;
+      return char_len;
+    }
+  else if (node->type == COMPLEX_BRACKET)
+    {
+      const re_charset_t *cset = node->opr.mbcset;
+# ifdef _LIBC
+      const unsigned char *pin = ((char *) re_string_get_buffer (input)
+                                 + str_idx);
+# endif /* _LIBC */
+      int match_len = 0;
+      wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars)
+                   ? re_string_wchar_at (input, str_idx) : 0);
+
+      /* match with multibyte character?  */
+      for (i = 0; i < cset->nmbchars; ++i)
+       if (wc == cset->mbchars[i])
+         {
+           match_len = char_len;
+           goto check_node_accept_bytes_match;
+         }
+      /* match with character_class?  */
+      for (i = 0; i < cset->nchar_classes; ++i)
+       {
+         wctype_t wt = cset->char_classes[i];
+         if (__iswctype (wc, wt))
+           {
+             match_len = char_len;
+             goto check_node_accept_bytes_match;
+           }
+       }
+
+# ifdef _LIBC
+      if (nrules != 0)
+       {
+         unsigned int in_collseq = 0;
+         const int32_t *table, *indirect;
+         const unsigned char *weights, *extra;
+         const char *collseqwc;
+         int32_t idx;
+         /* This #include defines a local function!  */
+#  include <locale/weight.h>
+
+         /* match with collating_symbol?  */
+         if (cset->ncoll_syms)
+           extra = (const unsigned char *)
+             _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+         for (i = 0; i < cset->ncoll_syms; ++i)
+           {
+             const unsigned char *coll_sym = extra + cset->coll_syms[i];
+             /* Compare the length of input collating element and
+                the length of current collating element.  */
+             if (*coll_sym != elem_len)
+               continue;
+             /* Compare each bytes.  */
+             for (j = 0; j < *coll_sym; j++)
+               if (pin[j] != coll_sym[1 + j])
+                 break;
+             if (j == *coll_sym)
+               {
+                 /* Match if every bytes is equal.  */
+                 match_len = j;
+                 goto check_node_accept_bytes_match;
+               }
+           }
+
+         if (cset->nranges)
+           {
+             if (elem_len <= char_len)
+               {
+                 collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+                 in_collseq = collseq_table_lookup (collseqwc, wc);
+               }
+             else
+               in_collseq = find_collation_sequence_value (pin, elem_len);
+           }
+         /* match with range expression?  */
+         for (i = 0; i < cset->nranges; ++i)
+           if (cset->range_starts[i] <= in_collseq
+               && in_collseq <= cset->range_ends[i])
+             {
+               match_len = elem_len;
+               goto check_node_accept_bytes_match;
+             }
+
+         /* match with equivalence_class?  */
+         if (cset->nequiv_classes)
+           {
+             const unsigned char *cp = pin;
+             table = (const int32_t *)
+               _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
+             weights = (const unsigned char *)
+               _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB);
+             extra = (const unsigned char *)
+               _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
+             indirect = (const int32_t *)
+               _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
+             idx = findidx (&cp);
+             if (idx > 0)
+               for (i = 0; i < cset->nequiv_classes; ++i)
+                 {
+                   int32_t equiv_class_idx = cset->equiv_classes[i];
+                   size_t weight_len = weights[idx];
+                   if (weight_len == weights[equiv_class_idx])
+                     {
+                       int cnt = 0;
+                       while (cnt <= weight_len
+                              && (weights[equiv_class_idx + 1 + cnt]
+                                  == weights[idx + 1 + cnt]))
+                         ++cnt;
+                       if (cnt > weight_len)
+                         {
+                           match_len = elem_len;
+                           goto check_node_accept_bytes_match;
+                         }
+                     }
+                 }
+           }
+       }
+      else
+# endif /* _LIBC */
+       {
+         /* match with range expression?  */
+#if __GNUC__ >= 2
+         wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'};
+#else
+         wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
+         cmp_buf[2] = wc;
+#endif
+         for (i = 0; i < cset->nranges; ++i)
+           {
+             cmp_buf[0] = cset->range_starts[i];
+             cmp_buf[4] = cset->range_ends[i];
+             if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
+                 && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
+               {
+                 match_len = char_len;
+                 goto check_node_accept_bytes_match;
+               }
+           }
+       }
+    check_node_accept_bytes_match:
+      if (!cset->non_match)
+       return match_len;
+      else
+       {
+         if (match_len > 0)
+           return 0;
+         else
+           return (elem_len > char_len) ? elem_len : char_len;
+       }
+    }
+  return 0;
+}
+
+# ifdef _LIBC
+static unsigned int
+find_collation_sequence_value (mbs, mbs_len)
+    const unsigned char *mbs;
+    size_t mbs_len;
+{
+  uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
+  if (nrules == 0)
+    {
+      if (mbs_len == 1)
+       {
+         /* No valid character.  Match it as a single byte character.  */
+         const unsigned char *collseq = (const unsigned char *)
+           _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
+         return collseq[mbs[0]];
+       }
+      return UINT_MAX;
+    }
+  else
+    {
+      int32_t idx;
+      const unsigned char *extra = (const unsigned char *)
+       _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+
+      for (idx = 0; ;)
+       {
+         int mbs_cnt, found = 0;
+         int32_t elem_mbs_len;
+         /* Skip the name of collating element name.  */
+         idx = idx + extra[idx] + 1;
+         elem_mbs_len = extra[idx++];
+         if (mbs_len == elem_mbs_len)
+           {
+             for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt)
+               if (extra[idx + mbs_cnt] != mbs[mbs_cnt])
+                 break;
+             if (mbs_cnt == elem_mbs_len)
+               /* Found the entry.  */
+               found = 1;
+           }
+         /* Skip the byte sequence of the collating element.  */
+         idx += elem_mbs_len;
+         /* Adjust for the alignment.  */
+         idx = (idx + 3) & ~3;
+         /* Skip the collation sequence value.  */
+         idx += sizeof (uint32_t);
+         /* Skip the wide char sequence of the collating element.  */
+         idx = idx + sizeof (uint32_t) * (extra[idx] + 1);
+         /* If we found the entry, return the sequence value.  */
+         if (found)
+           return *(uint32_t *) (extra + idx);
+         /* Skip the collation sequence value.  */
+         idx += sizeof (uint32_t);
+       }
+    }
+}
+# endif /* _LIBC */
+#endif /* RE_ENABLE_I18N */
+
+/* Check whether the node accepts the byte which is IDX-th
+   byte of the INPUT.  */
+
+static int
+check_node_accept (preg, node, mctx, idx)
+    const regex_t *preg;
+    const re_token_t *node;
+    const re_match_context_t *mctx;
+    int idx;
+{
+  unsigned char ch;
+  if (node->constraint)
+    {
+      /* The node has constraints.  Check whether the current context
+        satisfies the constraints.  */
+      unsigned int context = re_string_context_at (mctx->input, idx,
+                                                  mctx->eflags,
+                                                  preg->newline_anchor);
+      if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
+       return 0;
+    }
+  ch = re_string_byte_at (mctx->input, idx);
+  if (node->type == CHARACTER)
+    return node->opr.c == ch;
+  else if (node->type == SIMPLE_BRACKET)
+    return bitset_contain (node->opr.sbcset, ch);
+  else if (node->type == OP_PERIOD)
+    return !((ch == '\n' && !(preg->syntax & RE_DOT_NEWLINE))
+            || (ch == '\0' && (preg->syntax & RE_DOT_NOT_NULL)));
+  else
+    return 0;
+}
+
+/* Extend the buffers, if the buffers have run out.  */
+
+static reg_errcode_t
+extend_buffers (mctx)
+     re_match_context_t *mctx;
+{
+  reg_errcode_t ret;
+  re_string_t *pstr = mctx->input;
+
+  /* Double the lengthes of the buffers.  */
+  ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  if (mctx->state_log != NULL)
+    {
+      /* And double the length of state_log.  */
+      re_dfastate_t **new_array;
+      new_array = re_realloc (mctx->state_log, re_dfastate_t *,
+                             pstr->bufs_len * 2);
+      if (BE (new_array == NULL, 0))
+       return REG_ESPACE;
+      mctx->state_log = new_array;
+    }
+
+  /* Then reconstruct the buffers.  */
+  if (pstr->icase)
+    {
+#ifdef RE_ENABLE_I18N
+      if (MB_CUR_MAX > 1)
+       build_wcs_upper_buffer (pstr);
+      else
+#endif /* RE_ENABLE_I18N  */
+       build_upper_buffer (pstr);
+    }
+  else
+    {
+#ifdef RE_ENABLE_I18N
+      if (MB_CUR_MAX > 1)
+       build_wcs_buffer (pstr);
+      else
+#endif /* RE_ENABLE_I18N  */
+       {
+         if (pstr->trans != NULL)
+           re_string_translate_buffer (pstr);
+         else
+           pstr->valid_len = pstr->bufs_len;
+       }
+    }
+  return REG_NOERROR;
+}
+
+\f
+/* Functions for matching context.  */
+
+/* Initialize MCTX.  */
+
+static reg_errcode_t
+match_ctx_init (mctx, eflags, input, n)
+    re_match_context_t *mctx;
+    int eflags, n;
+    re_string_t *input;
+{
+  mctx->eflags = eflags;
+  mctx->input = input;
+  mctx->match_last = -1;
+  if (n > 0)
+    {
+      mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n);
+      mctx->sub_tops = re_malloc (re_sub_match_top_t *, n);
+      if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0))
+       return REG_ESPACE;
+    }
+  else
+    mctx->bkref_ents = NULL;
+  mctx->nbkref_ents = 0;
+  mctx->abkref_ents = n;
+  mctx->max_mb_elem_len = 1;
+  mctx->nsub_tops = 0;
+  mctx->asub_tops = n;
+  return REG_NOERROR;
+}
+
+/* Clean the entries which depend on the current input in MCTX.
+   This function must be invoked when the matcher changes the start index
+   of the input, or changes the input string.  */
+
+static void
+match_ctx_clean (mctx)
+    re_match_context_t *mctx;
+{
+  match_ctx_free_subtops (mctx);
+  mctx->nsub_tops = 0;
+  mctx->nbkref_ents = 0;
+}
+
+/* Free all the memory associated with MCTX.  */
+
+static void
+match_ctx_free (mctx)
+    re_match_context_t *mctx;
+{
+  match_ctx_free_subtops (mctx);
+  re_free (mctx->sub_tops);
+  re_free (mctx->bkref_ents);
+}
+
+/* Free all the memory associated with MCTX->SUB_TOPS.  */
+
+static void
+match_ctx_free_subtops (mctx)
+     re_match_context_t *mctx;
+{
+  int st_idx;
+  for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx)
+    {
+      int sl_idx;
+      re_sub_match_top_t *top = mctx->sub_tops[st_idx];
+      for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx)
+       {
+         re_sub_match_last_t *last = top->lasts[sl_idx];
+         re_free (last->path.array);
+         re_free (last);
+       }
+      re_free (top->lasts);
+      if (top->path)
+       {
+         re_free (top->path->array);
+         re_free (top->path);
+       }
+      free (top);
+    }
+}
+
+/* Add a new backreference entry to MCTX.
+   Note that we assume that caller never call this function with duplicate
+   entry, and call with STR_IDX which isn't smaller than any existing entry.
+*/
+
+static reg_errcode_t
+match_ctx_add_entry (mctx, node, str_idx, from, to)
+     re_match_context_t *mctx;
+     int node, str_idx, from, to;
+{
+  if (mctx->nbkref_ents >= mctx->abkref_ents)
+    {
+      struct re_backref_cache_entry* new_entry;
+      new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry,
+                             mctx->abkref_ents * 2);
+      if (BE (new_entry == NULL, 0))
+       {
+         re_free (mctx->bkref_ents);
+         return REG_ESPACE;
+       }
+      mctx->bkref_ents = new_entry;
+      memset (mctx->bkref_ents + mctx->nbkref_ents, '\0',
+             sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
+      mctx->abkref_ents *= 2;
+    }
+  mctx->bkref_ents[mctx->nbkref_ents].node = node;
+  mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx;
+  mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from;
+  mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to;
+  mctx->bkref_ents[mctx->nbkref_ents++].flag = 0;
+  if (mctx->max_mb_elem_len < to - from)
+    mctx->max_mb_elem_len = to - from;
+  return REG_NOERROR;
+}
+
+/* Search for the first entry which has the same str_idx.
+   Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX.  */
+
+static int
+search_cur_bkref_entry (mctx, str_idx)
+     re_match_context_t *mctx;
+     int str_idx;
+{
+  int left, right, mid;
+  right = mctx->nbkref_ents;
+  for (left = 0; left < right;)
+    {
+      mid = (left + right) / 2;
+      if (mctx->bkref_ents[mid].str_idx < str_idx)
+       left = mid + 1;
+      else
+       right = mid;
+    }
+  return left;
+}
+
+static void
+match_ctx_clear_flag (mctx)
+     re_match_context_t *mctx;
+{
+  int i;
+  for (i = 0; i < mctx->nbkref_ents; ++i)
+    {
+      mctx->bkref_ents[i].flag = 0;
+    }
+}
+
+/* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches
+   at STR_IDX.  */
+
+static reg_errcode_t
+match_ctx_add_subtop (mctx, node, str_idx)
+     re_match_context_t *mctx;
+     int node, str_idx;
+{
+#ifdef DEBUG
+  assert (mctx->sub_tops != NULL);
+  assert (mctx->asub_tops > 0);
+#endif
+  if (mctx->nsub_tops == mctx->asub_tops)
+    {
+      re_sub_match_top_t **new_array;
+      mctx->asub_tops *= 2;
+      new_array = re_realloc (mctx->sub_tops, re_sub_match_top_t *,
+                             mctx->asub_tops);
+      if (BE (new_array == NULL, 0))
+       return REG_ESPACE;
+      mctx->sub_tops = new_array;
+    }
+  mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t));
+  if (mctx->sub_tops[mctx->nsub_tops] == NULL)
+    return REG_ESPACE;
+  mctx->sub_tops[mctx->nsub_tops]->node = node;
+  mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx;
+  return REG_NOERROR;
+}
+
+/* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches
+   at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP.  */
+
+static re_sub_match_last_t *
+match_ctx_add_sublast (subtop, node, str_idx)
+     re_sub_match_top_t *subtop;
+     int node, str_idx;
+{
+  re_sub_match_last_t *new_entry;
+  if (subtop->nlasts == subtop->alasts)
+    {
+      re_sub_match_last_t **new_array;
+      subtop->alasts = 2 * subtop->alasts + 1;
+      new_array = re_realloc (subtop->lasts, re_sub_match_last_t *,
+                             subtop->alasts);
+      if (BE (new_array == NULL, 0))
+       return NULL;
+      subtop->lasts = new_array;
+    }
+  new_entry = calloc (1, sizeof (re_sub_match_last_t));
+  if (BE (new_entry == NULL, 0))
+    return NULL;
+  subtop->lasts[subtop->nlasts] = new_entry;
+  new_entry->node = node;
+  new_entry->str_idx = str_idx;
+  ++subtop->nlasts;
+  return new_entry;
+}
+
+static void
+sift_ctx_init (sctx, sifted_sts, limited_sts, last_node, last_str_idx,
+              check_subexp)
+    re_sift_context_t *sctx;
+    re_dfastate_t **sifted_sts, **limited_sts;
+    int last_node, last_str_idx, check_subexp;
+{
+  sctx->sifted_states = sifted_sts;
+  sctx->limited_states = limited_sts;
+  sctx->last_node = last_node;
+  sctx->last_str_idx = last_str_idx;
+  sctx->check_subexp = check_subexp;
+  sctx->cur_bkref = -1;
+  sctx->cls_subexp_idx = -1;
+  re_node_set_init_empty (&sctx->limits);
+}
diff --git a/scsicmds.c b/scsicmds.c
new file mode 100644 (file)
index 0000000..36ae6dc
--- /dev/null
@@ -0,0 +1,2097 @@
+/*
+ * scsicmds.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * Additional SCSI work:
+ * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ *
+ * In the SCSI world "SMART" is a dead or withdrawn standard. In recent
+ * SCSI standards (since SCSI-3) it goes under the awkward name of
+ * "Informational Exceptions" ["IE" or "IEC" (with the "C" for "control")].
+ * The relevant information is spread around several SCSI draft
+ * standards available at http://www.t10.org . Reference is made in the
+ * code to the following acronyms:
+ *      - SAM [SCSI Architectural model, versions 2 or 3]
+ *      - SPC [SCSI Primary commands, versions 2 or 3]
+ *      - SBC [SCSI Block commands, versions 2]
+ *
+ * Some SCSI disk vendors have snippets of "SMART" information in their
+ * product manuals.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+#include "int64.h"
+#include "extern.h"
+#include "scsicmds.h"
+#include "utility.h"
+
+const char *scsicmds_c_cvsid="$Id: scsicmds.c,v 1.85 2006/04/12 14:54:28 ballen4705 Exp $"
+CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+
+/* for passing global control variables */
+extern smartmonctrl *con;
+
+/* output binary in hex and optionally ascii */
+void dStrHex(const char* str, int len, int no_ascii)
+{
+    const char* p = str;
+    unsigned char c;
+    char buff[82];
+    int a = 0;
+    const int bpstart = 5;
+    const int cpstart = 60;
+    int cpos = cpstart;
+    int bpos = bpstart;
+    int i, k;
+    
+    if (len <= 0) return;
+    memset(buff,' ',80);
+    buff[80]='\0';
+    k = sprintf(buff + 1, "%.2x", a);
+    buff[k + 1] = ' ';
+    if (bpos >= ((bpstart + (9 * 3))))
+        bpos++;
+
+    for(i = 0; i < len; i++)
+    {
+        c = *p++;
+        bpos += 3;
+        if (bpos == (bpstart + (9 * 3)))
+            bpos++;
+        sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c);
+        buff[bpos + 2] = ' ';
+        if (no_ascii)
+            buff[cpos++] = ' ';
+        else {
+            if ((c < ' ') || (c >= 0x7f))
+                c='.';
+            buff[cpos++] = c;
+        }
+        if (cpos > (cpstart+15))
+        {
+            pout("%s\n", buff);
+            bpos = bpstart;
+            cpos = cpstart;
+            a += 16;
+            memset(buff,' ',80);
+            k = sprintf(buff + 1, "%.2x", a);
+            buff[k + 1] = ' ';
+        }
+    }
+    if (cpos > cpstart)
+    {
+        pout("%s\n", buff);
+    }
+}
+
+struct scsi_opcode_name {
+    UINT8 opcode;
+    const char * name;
+};
+
+static struct scsi_opcode_name opcode_name_arr[] = {
+    /* in ascending opcode order */
+    {TEST_UNIT_READY, "test unit ready"},       /* 0x00 */
+    {REQUEST_SENSE, "request sense"},           /* 0x03 */
+    {INQUIRY, "inquiry"},                       /* 0x12 */
+    {MODE_SELECT, "mode select"},               /* 0x15 */
+    {MODE_SENSE, "mode sense"},                 /* 0x1a */
+    {RECEIVE_DIAGNOSTIC, "receive diagnostic"}, /* 0x1c */
+    {SEND_DIAGNOSTIC, "send diagnostic"},       /* 0x1d */
+    {READ_DEFECT_10, "read defect list(10)"},   /* 0x37 */
+    {LOG_SENSE, "log sense"},                   /* 0x4d */
+    {MODE_SELECT_10, "mode select(10)"},        /* 0x55 */
+    {MODE_SENSE_10, "mode sense(10)"},          /* 0x5a */
+};
+
+const char * scsi_get_opcode_name(UINT8 opcode)
+{
+    int k;
+    int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]);
+    struct scsi_opcode_name * onp;
+
+    for (k = 0; k < len; ++k) {
+        onp = &opcode_name_arr[k];
+        if (opcode == onp->opcode)
+            return onp->name;
+        else if (opcode < onp->opcode)
+            return NULL;
+    }
+    return NULL;
+}
+
+
+void scsi_do_sense_disect(const struct scsi_cmnd_io * io_buf,
+                          struct scsi_sense_disect * out)
+{
+    memset(out, 0, sizeof(struct scsi_sense_disect));
+    if ((SCSI_STATUS_CHECK_CONDITION == io_buf->scsi_status) && 
+        (io_buf->resp_sense_len > 7)) {  
+        out->error_code = (io_buf->sensep[0] & 0x7f);
+        out->sense_key = (io_buf->sensep[2] & 0xf);
+        if (io_buf->resp_sense_len > 13) {
+            out->asc = io_buf->sensep[12];
+            out->ascq = io_buf->sensep[13];
+        }
+    }
+}
+
+static int scsiSimpleSenseFilter(const struct scsi_sense_disect * sinfo)
+{
+    switch (sinfo->sense_key) {
+    case SCSI_SK_NOT_READY:
+        if (SCSI_ASC_NO_MEDIUM == sinfo->asc) 
+            return SIMPLE_ERR_NO_MEDIUM;
+        else if (SCSI_ASC_NOT_READY == sinfo->asc) {
+            if (0x1 == sinfo->ascq)
+                return SIMPLE_ERR_BECOMING_READY;
+            else
+                return SIMPLE_ERR_NOT_READY;
+        } else
+            return SIMPLE_ERR_NOT_READY;
+    case SCSI_SK_MEDIUM_ERROR:
+    case SCSI_SK_HARDWARE_ERROR:
+        return SIMPLE_ERR_MEDIUM_HARDWARE;
+    case SCSI_SK_ILLEGAL_REQUEST:
+        if (SCSI_ASC_UNKNOWN_OPCODE == sinfo->asc)
+            return SIMPLE_ERR_BAD_OPCODE;
+        else if (SCSI_ASC_UNKNOWN_FIELD == sinfo->asc)
+            return SIMPLE_ERR_BAD_FIELD;
+        else if (SCSI_ASC_UNKNOWN_PARAM == sinfo->asc)
+            return SIMPLE_ERR_BAD_PARAM;
+        else
+            return SIMPLE_ERR_BAD_PARAM;    /* all other illegal request */
+    case SCSI_SK_UNIT_ATTENTION:
+        return SIMPLE_ERR_TRY_AGAIN;
+    default:
+        return SIMPLE_NO_ERROR;
+    }
+}
+
+const char * scsiErrString(int scsiErr)
+{
+    if (scsiErr < 0)
+        return strerror(-scsiErr);
+    switch (scsiErr) {
+        case SIMPLE_NO_ERROR: 
+            return "no error";
+        case SIMPLE_ERR_NOT_READY: 
+            return "device not ready";
+        case SIMPLE_ERR_BAD_OPCODE: 
+            return "unsupported scsi opcode";
+        case SIMPLE_ERR_BAD_FIELD: 
+            return "unsupported field in scsi command";
+        case SIMPLE_ERR_BAD_PARAM: 
+            return "badly formed scsi parameters";
+        case SIMPLE_ERR_BAD_RESP: 
+            return "scsi response fails sanity test";
+        case SIMPLE_ERR_NO_MEDIUM: 
+            return "no medium present";
+        case SIMPLE_ERR_BECOMING_READY: 
+            return "device will be ready soon";
+        case SIMPLE_ERR_TRY_AGAIN: 
+            return "unit attention reported, try again";
+        case SIMPLE_ERR_MEDIUM_HARDWARE: 
+            return "medium or hardware error (serious)";
+        default:
+            return "unknown error";
+    }
+}
+
+/* Sends LOG SENSE command. Returns 0 if ok, 1 if device NOT READY, 2 if
+   command not supported, 3 if field (within command) not supported or
+   returns negated errno.  SPC-3 sections 6.6 and 7.2 (rec 22a).
+   N.B. Sets PC==1 to fetch "current cumulative" log pages.
+   If known_resp_len > 0 then a single fetch is done for this response
+   length. If known_resp_len == 0 then twin fetches are performed, the
+   first to deduce the response length, then send the same command again
+   requesting the deduced response length. This protects certain fragile 
+   HBAs. The twin fetch technique should not be used with the TapeAlert
+   log page since it clears its state flags after each fetch. */
+int scsiLogSense(int device, int pagenum, UINT8 *pBuf, int bufLen,
+                 int known_resp_len)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[10];
+    UINT8 sense[32];
+    int pageLen;
+    int status, res;
+
+    if (known_resp_len > bufLen)
+        return -EIO;
+    if (known_resp_len > 0)
+        pageLen = known_resp_len;
+    else {
+        /* Starting twin fetch strategy: first fetch to find respone length */
+        pageLen = 4;
+        if (pageLen > bufLen)
+            return -EIO;
+        else
+            memset(pBuf, 0, pageLen);
+
+        memset(&io_hdr, 0, sizeof(io_hdr));
+        memset(cdb, 0, sizeof(cdb));
+        io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+        io_hdr.dxfer_len = pageLen;
+        io_hdr.dxferp = pBuf;
+        cdb[0] = LOG_SENSE;
+        cdb[2] = 0x40 | (pagenum & 0x3f);  /* Page control (PC)==1 */
+        cdb[7] = (pageLen >> 8) & 0xff;
+        cdb[8] = pageLen & 0xff;
+        io_hdr.cmnd = cdb;
+        io_hdr.cmnd_len = sizeof(cdb);
+        io_hdr.sensep = sense;
+        io_hdr.max_sense_len = sizeof(sense);
+        io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+    
+        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+        if (0 != status)
+            return status;
+        scsi_do_sense_disect(&io_hdr, &sinfo);
+        if ((res = scsiSimpleSenseFilter(&sinfo)))
+            return res;
+        /* sanity check on response */
+        if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum))
+            return SIMPLE_ERR_BAD_RESP;
+        if (0 == ((pBuf[2] << 8) + pBuf[3]))
+            return SIMPLE_ERR_BAD_RESP;
+        pageLen = (pBuf[2] << 8) + pBuf[3] + 4;
+        /* some SCSI HBA don't like "odd" length transfers */
+        if (pageLen % 2)
+            pageLen += 1;   
+        if (pageLen > bufLen)
+            pageLen = bufLen;
+    }
+    memset(pBuf, 0, 4);
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = pageLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = LOG_SENSE;
+    cdb[2] = 0x40 | (pagenum & 0x3f);  /* Page control (PC)==1 */
+    cdb[7] = (pageLen >> 8) & 0xff;
+    cdb[8] = pageLen & 0xff;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    status = scsiSimpleSenseFilter(&sinfo);
+    if (0 != status)
+        return status;
+    /* sanity check on response */
+    if ((SUPPORTED_LPAGES != pagenum) && (pBuf[0] != pagenum))
+        return SIMPLE_ERR_BAD_RESP;
+    if (0 == ((pBuf[2] << 8) + pBuf[3]))
+        return SIMPLE_ERR_BAD_RESP;
+    return 0;
+}
+
+/* Send MODE SENSE (6 byte) command. Returns 0 if ok, 1 if NOT READY,
+ * 2 if command not supported (then MODE SENSE(10) should be supported),
+ * 3 if field in command not supported or returns negated errno. 
+ * SPC-3 sections 6.9 and 7.4 (rev 22a) [mode subpage==0] */
+int scsiModeSense(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status;
+
+    if ((bufLen < 0) || (bufLen > 255))
+        return -EINVAL;
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = MODE_SENSE;
+    cdb[2] = (pc << 6) | (pagenum & 0x3f);
+    cdb[4] = bufLen;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    status = scsiSimpleSenseFilter(&sinfo);
+    if (SIMPLE_ERR_TRY_AGAIN == status) {
+        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+        if (0 != status)
+            return status;
+        scsi_do_sense_disect(&io_hdr, &sinfo);
+        status = scsiSimpleSenseFilter(&sinfo);
+    }
+    if ((0 == status) && (ALL_MODE_PAGES != pagenum)) {
+        int offset;
+
+        offset = scsiModePageOffset(pBuf, bufLen, 0);
+        if (offset < 0)
+            return SIMPLE_ERR_BAD_RESP;
+        else if (pagenum != (pBuf[offset] & 0x3f))
+            return SIMPLE_ERR_BAD_RESP;
+    }
+    return status;
+}
+
+/* Sends a 6 byte MODE SELECT command. Assumes given pBuf is the response
+ * from a corresponding 6 byte MODE SENSE command. Such a response should
+ * have a 4 byte header followed by 0 or more 8 byte block descriptors
+ * (normally 1) and then 1 mode page. Returns 0 if ok, 1 if NOT READY,
+ * 2 if command not supported (then MODE SELECT(10) may be supported), 
+ * 3 if field in command not supported, 4 if bad parameter to command
+ * or returns negated errno. SPC-3 sections 6.7 and 7.4 (rev 22a) */
+int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status, pg_offset, pg_len, hdr_plus_1_pg;
+
+    pg_offset = 4 + pBuf[3];
+    if (pg_offset + 2 >= bufLen)
+        return -EINVAL;
+    pg_len = pBuf[pg_offset + 1] + 2;
+    hdr_plus_1_pg = pg_offset + pg_len;
+    if (hdr_plus_1_pg > bufLen)
+        return -EINVAL;
+    pBuf[0] = 0;    /* Length of returned mode sense data reserved for SELECT */
+    pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_TO_DEVICE;
+    io_hdr.dxfer_len = hdr_plus_1_pg;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = MODE_SELECT;
+    cdb[1] = 0x10 | (sp & 1);      /* set PF (page format) bit always */
+    cdb[4] = hdr_plus_1_pg; /* make sure only one page sent */
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* MODE SENSE (10 byte). Returns 0 if ok, 1 if NOT READY, 2 if command 
+ * not supported (then MODE SENSE(6) might be supported), 3 if field in
+ * command not supported or returns negated errno.  
+ * SPC-3 sections 6.10 and 7.4 (rev 22a) [mode subpage==0] */
+int scsiModeSense10(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[10];
+    UINT8 sense[32];
+    int status;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = MODE_SENSE_10;
+    cdb[2] = (pc << 6) | (pagenum & 0x3f);
+    cdb[7] = (bufLen >> 8) & 0xff;
+    cdb[8] = bufLen & 0xff;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    status = scsiSimpleSenseFilter(&sinfo);
+    if (SIMPLE_ERR_TRY_AGAIN == status) {
+        status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+        if (0 != status)
+            return status;
+        scsi_do_sense_disect(&io_hdr, &sinfo);
+        status = scsiSimpleSenseFilter(&sinfo);
+    }
+    if ((0 == status) && (ALL_MODE_PAGES != pagenum)) {
+        int offset;
+
+        offset = scsiModePageOffset(pBuf, bufLen, 1);
+        if (offset < 0)
+            return SIMPLE_ERR_BAD_RESP;
+        else if (pagenum != (pBuf[offset] & 0x3f))
+            return SIMPLE_ERR_BAD_RESP;
+    }
+    return status;
+}
+
+/* Sends a 10 byte MODE SELECT command. Assumes given pBuf is the response
+ * from a corresponding 10 byte MODE SENSE command. Such a response should
+ * have a 8 byte header followed by 0 or more 8 byte block descriptors
+ * (normally 1) and then 1 mode page. Returns 0 if ok, 1 NOT REAFY, 2 if 
+ * command not supported (then MODE SELECT(6) may be supported), 3 if field
+ * in command not supported, 4 if bad parameter to command or returns
+ * negated errno. SPC-3 sections 6.8 and 7.4 (rev 22a) */
+int scsiModeSelect10(int device, int sp, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[10];
+    UINT8 sense[32];
+    int status, pg_offset, pg_len, hdr_plus_1_pg;
+
+    pg_offset = 8 + (pBuf[6] << 8) + pBuf[7];
+    if (pg_offset + 2 >= bufLen)
+        return -EINVAL;
+    pg_len = pBuf[pg_offset + 1] + 2;
+    hdr_plus_1_pg = pg_offset + pg_len;
+    if (hdr_plus_1_pg > bufLen)
+        return -EINVAL;
+    pBuf[0] = 0;    
+    pBuf[1] = 0; /* Length of returned mode sense data reserved for SELECT */
+    pBuf[pg_offset] &= 0x7f;    /* Mask out PS bit from byte 0 of page data */
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_TO_DEVICE;
+    io_hdr.dxfer_len = hdr_plus_1_pg;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = MODE_SELECT_10;
+    cdb[1] = 0x10 | (sp & 1);      /* set PF (page format) bit always */
+    cdb[8] = hdr_plus_1_pg; /* make sure only one page sent */
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* Standard INQUIRY returns 0 for ok, anything else is a major problem.
+ * bufLen should be 36 for unsafe devices (like USB mass storage stuff)
+ * otherwise they can lock up! SPC-3 sections 6.4 and 7.6 (rev 22a) */
+int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_sense_disect sinfo;
+    struct scsi_cmnd_io io_hdr;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status;
+
+    if ((bufLen < 0) || (bufLen > 255))
+        return -EINVAL;
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = INQUIRY;
+    cdb[4] = bufLen;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* INQUIRY to fetch Vital Page Data.  Returns 0 if ok, 1 if NOT READY
+ * (unlikely), 2 if command not supported, 3 if field in command not 
+ * supported, 5 if response indicates that EVPD bit ignored or returns
+ * negated errno. SPC-3 section 6.4 and 7.6 (rev 22a) */
+int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status, res;
+
+    if ((bufLen < 0) || (bufLen > 255))
+        return -EINVAL;
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    if (bufLen > 1)
+        pBuf[1] = 0x0;
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = INQUIRY;
+    cdb[1] = 0x1;       /* set EVPD bit (enable Vital Product Data) */
+    cdb[2] = vpd_page;
+    cdb[4] = bufLen;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    if ((res = scsiSimpleSenseFilter(&sinfo)))
+        return res;
+    /* Guard against devices that ignore EVPD bit and do standard INQUIRY */
+    if (bufLen > 1) {
+        if (vpd_page == pBuf[1]) {
+            if ((0x80 == vpd_page) && (bufLen > 2) && (0x0 != pBuf[2]))
+                return SIMPLE_ERR_BAD_RESP;
+        } else
+            return SIMPLE_ERR_BAD_RESP;
+    }
+    return 0;
+}
+
+/* REQUEST SENSE command. Returns 0 if ok, anything else major problem.
+ * SPC-3 section 6.27 (rev 22a) */
+int scsiRequestSense(int device, struct scsi_sense_disect * sense_info)
+{
+    struct scsi_cmnd_io io_hdr;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    UINT8 buff[18];
+    int status, len;
+    UINT8 ecode;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = sizeof(buff);
+    io_hdr.dxferp = buff;
+    cdb[0] = REQUEST_SENSE;
+    cdb[4] = sizeof(buff);
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if ((0 == status) && (sense_info)) {
+        ecode = buff[0] & 0x7f;
+        sense_info->error_code = ecode;
+        sense_info->sense_key = buff[2] & 0xf;
+        sense_info->asc = 0;
+        sense_info->ascq = 0;
+        if ((0x70 == ecode) || (0x71 == ecode)) {
+            len = buff[7] + 8;
+            if (len > 13) {
+                sense_info->asc = buff[12];
+                sense_info->ascq = buff[13];
+            }
+        }
+    }
+    return status;
+}
+
+/* SEND DIAGNOSTIC command.  Returns 0 if ok, 1 if NOT READY, 2 if command
+ * not supported, 3 if field in command not supported or returns negated
+ * errno. SPC-3 section 6.28 (rev 22a) */
+int scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = bufLen ? DXFER_TO_DEVICE: DXFER_NONE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = SEND_DIAGNOSTIC;
+    if (SCSI_DIAG_DEF_SELF_TEST == functioncode)
+        cdb[1] = 0x4;  /* SelfTest bit */
+    else if (SCSI_DIAG_NO_SELF_TEST != functioncode)
+        cdb[1] = (functioncode & 0x7) << 5; /* SelfTest _code_ */
+    else   /* SCSI_DIAG_NO_SELF_TEST == functioncode */
+        cdb[1] = 0x10;  /* PF bit */
+    cdb[3] = (bufLen >> 8) & 0xff;
+    cdb[4] = bufLen & 0xff;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    /* worst case is an extended foreground self test on a big disk */
+    io_hdr.timeout = SCSI_TIMEOUT_SELF_TEST;
+    
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* RECEIVE DIAGNOSTIC command. Returns 0 if ok, 1 if NOT READY, 2 if
+ * command not supported, 3 if field in command not supported or returns
+ * negated errno. SPC-3 section 6.18 (rev 22a) */
+int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf, 
+                      int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = RECEIVE_DIAGNOSTIC;
+    cdb[1] = pcv;
+    cdb[2] = pagenum;
+    cdb[3] = (bufLen >> 8) & 0xff;
+    cdb[4] = bufLen & 0xff;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* TEST UNIT READY command. SPC-3 section 6.33 (rev 22a) */
+static int _testunitready(int device, struct scsi_sense_disect * sinfo)
+{
+    struct scsi_cmnd_io io_hdr;
+    UINT8 cdb[6];
+    UINT8 sense[32];
+    int status;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_NONE;
+    io_hdr.dxfer_len = 0;
+    io_hdr.dxferp = NULL;
+    cdb[0] = TEST_UNIT_READY;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, sinfo);
+    return 0;
+}
+
+/* Returns 0 for device responds and media ready, 1 for device responds and
+   media not ready, or returns a negated errno value */
+int scsiTestUnitReady(int device)
+{
+    struct scsi_sense_disect sinfo;
+    int status;
+
+    status = _testunitready(device, &sinfo);
+    if (0 != status)
+        return status;
+    status = scsiSimpleSenseFilter(&sinfo);
+    if (SIMPLE_ERR_TRY_AGAIN == status) {
+        /* power on reset, media changed, ok ... try again */
+        status = _testunitready(device, &sinfo);        
+        if (0 != status)
+            return status;
+        status = scsiSimpleSenseFilter(&sinfo);
+    }
+    return status;
+}
+
+/* READ DEFECT (10) command. Returns 0 if ok, 1 if NOT READY, 2 if
+ * command not supported, 3 if field in command not supported or returns
+ * negated errno. SBC-2 section 5.12 (rev 16) */
+int scsiReadDefect10(int device, int req_plist, int req_glist, int dl_format,
+                     UINT8 *pBuf, int bufLen)
+{
+    struct scsi_cmnd_io io_hdr;
+    struct scsi_sense_disect sinfo;
+    UINT8 cdb[10];
+    UINT8 sense[32];
+    int status;
+
+    memset(&io_hdr, 0, sizeof(io_hdr));
+    memset(cdb, 0, sizeof(cdb));
+    io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
+    io_hdr.dxfer_len = bufLen;
+    io_hdr.dxferp = pBuf;
+    cdb[0] = READ_DEFECT_10;
+    cdb[2] = (unsigned char)(((req_plist << 4) & 0x10) |
+               ((req_glist << 3) & 0x8) | (dl_format & 0x7));
+    cdb[7] = (bufLen >> 8) & 0xff;
+    cdb[8] = bufLen & 0xff;
+    io_hdr.cmnd = cdb;
+    io_hdr.cmnd_len = sizeof(cdb);
+    io_hdr.sensep = sense;
+    io_hdr.max_sense_len = sizeof(sense);
+    io_hdr.timeout = SCSI_TIMEOUT_DEFAULT;
+
+    status = do_scsi_cmnd_io(device, &io_hdr, con->reportscsiioctl);
+    if (0 != status)
+        return status;
+    scsi_do_sense_disect(&io_hdr, &sinfo);
+    return scsiSimpleSenseFilter(&sinfo);
+}
+
+/* Offset into mode sense (6 or 10 byte) response that actual mode page
+ * starts at (relative to resp[0]). Returns -1 if problem */
+int scsiModePageOffset(const UINT8 * resp, int len, int modese_len)
+{
+    int resp_len, bd_len;
+    int offset = -1;
+
+    if (resp) {
+        if (10 == modese_len) {
+            resp_len = (resp[0] << 8) + resp[1] + 2;
+            bd_len = (resp[6] << 8) + resp[7];
+            offset = bd_len + 8;
+        } else {
+            resp_len = resp[0] + 1;
+            bd_len = resp[3];
+            offset = bd_len + 4;
+        }
+        if ((offset + 2) > len) {
+            pout("scsiModePageOffset: raw_curr too small, offset=%d "
+                 "resp_len=%d bd_len=%d\n", offset, resp_len, bd_len);
+            offset = -1;
+        } else if ((offset + 2) > resp_len) {
+             if ((resp_len > 2) || con->reportscsiioctl)
+                pout("scsiModePageOffset: response length too short, "
+                     "resp_len=%d offset=%d bd_len=%d\n", resp_len,
+                     offset, bd_len);
+            offset = -1;
+        }
+    }
+    return offset;
+}
+
+/* IEC mode page byte 2 bit masks */
+#define DEXCPT_ENABLE   0x08
+#define EWASC_ENABLE    0x10
+#define DEXCPT_DISABLE  0xf7
+#define EWASC_DISABLE   0xef
+#define TEST_DISABLE    0xfb
+
+/* Fetches the Informational Exceptions Control mode page. First tries
+ * the 6 byte MODE SENSE command and if that fails with an illegal opcode
+ * tries a 10 byte MODE SENSE command. Returns 0 if successful, a positive
+ * number if a known error (see  SIMPLE_ERR_ ...) or a negative errno
+ * value. */
+int scsiFetchIECmpage(int device, struct scsi_iec_mode_page *iecp, int modese_len)
+{
+    int err = 0;
+
+    memset(iecp, 0, sizeof(*iecp));
+    iecp->modese_len = modese_len;
+    iecp->requestedCurrent = 1;
+    if (iecp->modese_len <= 6) {
+        if ((err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, 
+                                 MPAGE_CONTROL_CURRENT, 
+                                 iecp->raw_curr, sizeof(iecp->raw_curr)))) {
+            if (SIMPLE_ERR_BAD_OPCODE == err)
+                iecp->modese_len = 10;
+            else {
+                iecp->modese_len = 0;
+                return err;
+            }
+        } else if (0 == iecp->modese_len)
+            iecp->modese_len = 6;
+    }
+    if (10 == iecp->modese_len) {
+        err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
+                              MPAGE_CONTROL_CURRENT, 
+                              iecp->raw_curr, sizeof(iecp->raw_curr));
+        if (err) {
+            iecp->modese_len = 0;
+            return err;
+        }
+    } 
+    iecp->gotCurrent = 1;
+    iecp->requestedChangeable = 1;
+    if (10 == iecp->modese_len)
+        err = scsiModeSense10(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE,
+                                 MPAGE_CONTROL_CHANGEABLE,
+                                 iecp->raw_chg, sizeof(iecp->raw_chg));
+    else if (6 == iecp->modese_len)
+        err = scsiModeSense(device, INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE, 
+                            MPAGE_CONTROL_CHANGEABLE, 
+                            iecp->raw_chg, sizeof(iecp->raw_chg));
+    if (err)
+        return err;
+    iecp->gotChangeable = 1;
+    return 0;
+}
+
+int scsi_IsExceptionControlEnabled(const struct scsi_iec_mode_page *iecp)
+{
+    int offset;
+
+    if (iecp && iecp->gotCurrent) {
+        offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
+                                    iecp->modese_len);
+        if (offset >= 0)
+            return (iecp->raw_curr[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
+        else
+            return 0;
+    } else
+        return 0;
+}
+
+int scsi_IsWarningEnabled(const struct scsi_iec_mode_page *iecp)
+{
+    int offset;
+
+    if (iecp && iecp->gotCurrent) {
+        offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
+                                    iecp->modese_len);
+        if (offset >= 0)
+            return (iecp->raw_curr[offset + 2] & EWASC_ENABLE) ? 1 : 0;
+        else
+            return 0;
+    } else
+        return 0;
+}
+
+/* set EWASC and clear PERF, EBF, DEXCPT TEST and LOGERR */
+#define SCSI_IEC_MP_BYTE2_ENABLED 0x10 
+#define SCSI_IEC_MP_BYTE2_TEST_MASK 0x4
+/* exception/warning via an unrequested REQUEST SENSE command */
+#define SCSI_IEC_MP_MRIE 6      
+#define SCSI_IEC_MP_INTERVAL_T 0
+#define SCSI_IEC_MP_REPORT_COUNT 1
+
+/* Try to set (or clear) both Exception Control and Warning in the IE
+ * mode page subject to the "changeable" mask. The object pointed to
+ * by iecp is (possibly) inaccurate after this call, therefore
+ * scsiFetchIECmpage() should be called again if the IEC mode page
+ * is to be re-examined.
+ * When -r ioctl is invoked 3 or more time on 'smartctl -s on ...'
+ * then set the TEST bit (causes asc,ascq pair of 0x5d,0xff). */
+int scsiSetExceptionControlAndWarning(int device, int enabled,
+                                      const struct scsi_iec_mode_page *iecp)
+{
+    int k, offset, resp_len;
+    int err = 0;
+    UINT8 rout[SCSI_IECMP_RAW_LEN];
+    int sp, eCEnabled, wEnabled;
+
+    if ((! iecp) || (! iecp->gotCurrent))
+        return -EINVAL;
+    offset = scsiModePageOffset(iecp->raw_curr, sizeof(iecp->raw_curr),
+                                iecp->modese_len);
+    if (offset < 0)
+        return -EINVAL;
+    memcpy(rout, iecp->raw_curr, SCSI_IECMP_RAW_LEN);
+    if (10 == iecp->modese_len) {
+        resp_len = (rout[0] << 8) + rout[1] + 2;
+        rout[3] &= 0xef;    /* for disks mask out DPOFUA bit */
+    } else {
+        resp_len = rout[0] + 1;
+        rout[2] &= 0xef;    /* for disks mask out DPOFUA bit */
+    }
+    sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
+    if (enabled) {
+        rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED;
+        if (con->reportscsiioctl > 2)
+            rout[offset + 2] |= SCSI_IEC_MP_BYTE2_TEST_MASK;
+        rout[offset + 3] = SCSI_IEC_MP_MRIE;
+        rout[offset + 4] = (SCSI_IEC_MP_INTERVAL_T >> 24) & 0xff;
+        rout[offset + 5] = (SCSI_IEC_MP_INTERVAL_T >> 16) & 0xff;
+        rout[offset + 6] = (SCSI_IEC_MP_INTERVAL_T >> 8) & 0xff;
+        rout[offset + 7] = SCSI_IEC_MP_INTERVAL_T & 0xff;
+        rout[offset + 8] = (SCSI_IEC_MP_REPORT_COUNT >> 24) & 0xff;
+        rout[offset + 9] = (SCSI_IEC_MP_REPORT_COUNT >> 16) & 0xff;
+        rout[offset + 10] = (SCSI_IEC_MP_REPORT_COUNT >> 8) & 0xff;
+        rout[offset + 11] = SCSI_IEC_MP_REPORT_COUNT & 0xff;
+        if (iecp->gotChangeable) {
+            UINT8 chg2 = iecp->raw_chg[offset + 2];
+
+            rout[offset + 2] = chg2 ? (rout[offset + 2] & chg2) :
+                                      iecp->raw_curr[offset + 2];
+            for (k = 3; k < 12; ++k) {
+                if (0 == iecp->raw_chg[offset + k])
+                    rout[offset + k] = iecp->raw_curr[offset + k];
+            }
+        }
+        if (0 == memcmp(&rout[offset + 2], &iecp->raw_chg[offset + 2], 10)) {
+            if (con->reportscsiioctl > 0)
+                pout("scsiSetExceptionControlAndWarning: already enabled\n");
+            return 0;
+        }
+    } else { /* disabling Exception Control and (temperature) Warnings */
+        eCEnabled = (rout[offset + 2] & DEXCPT_ENABLE) ? 0 : 1;
+        wEnabled = (rout[offset + 2] & EWASC_ENABLE) ? 1 : 0;
+        if ((! eCEnabled) && (! wEnabled)) {
+            if (con->reportscsiioctl > 0)
+                pout("scsiSetExceptionControlAndWarning: already disabled\n");
+            return 0;   /* nothing to do, leave other setting alone */
+        }
+        if (wEnabled) 
+            rout[offset + 2] &= EWASC_DISABLE;
+        if (eCEnabled) {
+            if (iecp->gotChangeable && 
+                (iecp->raw_chg[offset + 2] & DEXCPT_ENABLE))
+                rout[offset + 2] |= DEXCPT_ENABLE;
+                rout[offset + 2] &= TEST_DISABLE;/* clear TEST bit for spec */
+        }
+    }
+    if (10 == iecp->modese_len)
+        err = scsiModeSelect10(device, sp, rout, resp_len);
+    else if (6 == iecp->modese_len)
+        err = scsiModeSelect(device, sp, rout, resp_len);
+    return err;
+}
+
+int scsiGetTemp(int device, UINT8 *currenttemp, UINT8 *triptemp)
+{
+    UINT8 tBuf[252];
+    int err;
+
+    memset(tBuf, 0, sizeof(tBuf));
+    if ((err = scsiLogSense(device, TEMPERATURE_LPAGE, tBuf, 
+                            sizeof(tBuf), 0))) {
+        *currenttemp = 0;
+        *triptemp = 0;
+        pout("Log Sense for temperature failed [%s]\n", scsiErrString(err));
+        return err;
+    }
+    *currenttemp = tBuf[9];
+    *triptemp = tBuf[15];
+    return 0;
+}
+
+/* Read informational exception log page or Request Sense response.
+ * Fetching asc/ascq code potentially flagging an exception or warning.
+ * Returns 0 if ok, else error number. A current temperature of 255
+ * (Celsius) implies that the temperature not available. */
+int scsiCheckIE(int device, int hasIELogPage, int hasTempLogPage,
+                UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp,
+                UINT8 *triptemp)
+{
+    UINT8 tBuf[252];
+    struct scsi_sense_disect sense_info;
+    int err;
+    int temperatureSet = 0;
+    unsigned short pagesize;
+    UINT8 currTemp, trTemp;
+    *asc = 0;
+    *ascq = 0;
+    *currenttemp = 0;
+    *triptemp = 0;
+    memset(tBuf,0,sizeof(tBuf)); // need to clear stack space of junk
+    memset(&sense_info, 0, sizeof(sense_info));
+    if (hasIELogPage) {
+        if ((err = scsiLogSense(device, IE_LPAGE, tBuf, 
+                                sizeof(tBuf), 0))) {
+            pout("Log Sense failed, IE page [%s]\n", scsiErrString(err));
+            return err;
+        }
+        // pull out page size from response, don't forget to add 4
+        pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4; 
+        if ((pagesize < 4) || tBuf[4] || tBuf[5]) {
+            pout("Log Sense failed, IE page, bad parameter code or length\n");
+            return SIMPLE_ERR_BAD_PARAM;
+        }
+        if (tBuf[7] > 1) {
+            sense_info.asc = tBuf[8]; 
+            sense_info.ascq = tBuf[9];
+            if (! hasTempLogPage) {
+                if (tBuf[7] > 2) 
+                    *currenttemp = tBuf[10];
+                if (tBuf[7] > 3)        /* IBM extension in SMART (IE) lpage */
+                    *triptemp = tBuf[11];
+            }
+        } 
+    }
+    if (0 == sense_info.asc) {    
+        /* ties in with MRIE field of 6 in IEC mode page (0x1c) */
+        if ((err = scsiRequestSense(device, &sense_info))) {
+            pout("Request Sense failed, [%s]\n", scsiErrString(err));
+            return err;
+        }
+    }
+    *asc = sense_info.asc;
+    *ascq = sense_info.ascq;
+    if ((! temperatureSet) && hasTempLogPage) {
+        if (0 == scsiGetTemp(device, &currTemp, &trTemp)) {
+            *currenttemp = currTemp;
+            *triptemp = trTemp;
+        }
+    }
+    return 0;
+}
+
+// The first character (W, C, I) tells the severity
+static const char * TapeAlertsMessageTable[]= {  
+    " ",
+    /* 0x01 */
+   "W: The tape drive is having problems reading data. No data has been lost,\n"
+       "  but there has been a reduction in the performance of the tape.",
+    /* 0x02 */
+   "W: The tape drive is having problems writing data. No data has been lost,\n"
+       "  but there has been a reduction in the capacity of the tape.",
+    /* 0x03 */
+   "W: The operation has stopped because an error has occurred while reading\n"
+       "  or writing data that the drive cannot correct.",
+    /* 0x04 */
+   "C: Your data is at risk:\n"
+       "  1. Copy any data you require from this tape. \n"
+       "  2. Do not use this tape again.\n"
+       "  3. Restart the operation with a different tape.",
+    /* 0x05 */
+   "C: The tape is damaged or the drive is faulty. Call the tape drive\n"
+       "  supplier helpline.",
+    /* 0x06 */
+   "C: The tape is from a faulty batch or the tape drive is faulty:\n"
+       "  1. Use a good tape to test the drive.\n"
+       "  2. If problem persists, call the tape drive supplier helpline.",
+    /* 0x07 */
+   "W: The tape cartridge has reached the end of its calculated useful life:\n"
+       "  1. Copy data you need to another tape.\n"
+       "  2. Discard the old tape.",
+    /* 0x08 */
+   "W: The tape cartridge is not data-grade. Any data you back up to the tape\n"
+       "  is at risk. Replace the cartridge with a data-grade tape.",
+    /* 0x09 */
+   "C: You are trying to write to a write-protected cartridge. Remove the\n"
+       "  write-protection or use another tape.",
+    /* 0x0a */
+   "I: You cannot eject the cartridge because the tape drive is in use. Wait\n"
+       "  until the operation is complete before ejecting the cartridge.",
+    /* 0x0b */
+   "I: The tape in the drive is a cleaning cartridge.",
+    /* 0x0c */
+   "I: You have tried to load a cartridge of a type which is not supported\n"
+       "  by this drive.",
+    /* 0x0d */
+   "C: The operation has failed because the tape in the drive has experienced\n"
+       "  a mechanical failure:\n"
+       "  1. Discard the old tape.\n"
+       "  2. Restart the operation with a different tape.",
+    /* 0x0e */
+   "C: The operation has failed because the tape in the drive has experienced\n"
+       "  a mechanical failure:\n"
+       "  1. Do not attempt to extract the tape cartridge\n"
+       "  2. Call the tape drive supplier helpline.",
+    /* 0x0f */
+   "W: The memory in the tape cartridge has failed, which reduces\n"
+       "  performance. Do not use the cartridge for further write operations.",
+    /* 0x10 */
+   "C: The operation has failed because the tape cartridge was manually\n"
+       "  de-mounted while the tape drive was actively writing or reading.",
+    /* 0x11 */
+   "W: You have loaded a cartridge of a type that is read-only in this drive.\n"
+       "  The cartridge will appear as write-protected.",
+    /* 0x12 */
+   "W: The tape directory on the tape cartridge has been corrupted. File\n"
+       "  search performance will be degraded. The tape directory can be rebuilt\n"
+       "  by reading all the data on the cartridge.",
+    /* 0x13 */
+   "I: The tape cartridge is nearing the end of its calculated life. It is\n"
+       "  recommended that you:\n"
+       "  1. Use another tape cartridge for your next backup.\n"
+       "  2. Store this tape in a safe place in case you need to restore "
+       "  data from it.",
+    /* 0x14 */
+   "C: The tape drive needs cleaning:\n"
+       "  1. If the operation has stopped, eject the tape and clean the drive.\n"
+       "  2. If the operation has not stopped, wait for it to finish and then\n"
+       "  clean the drive.\n"
+       "  Check the tape drive users manual for device specific cleaning instructions.",
+    /* 0x15 */
+   "W: The tape drive is due for routine cleaning:\n"
+       "  1. Wait for the current operation to finish.\n"
+       "  2. The use a cleaning cartridge.\n"
+       "  Check the tape drive users manual for device specific cleaning instructions.",
+    /* 0x16 */
+   "C: The last cleaning cartridge used in the tape drive has worn out:\n"
+       "  1. Discard the worn out cleaning cartridge.\n"
+       "  2. Wait for the current operation to finish.\n"
+       "  3. Then use a new cleaning cartridge.",
+    /* 0x17 */
+   "C: The last cleaning cartridge used in the tape drive was an invalid\n"
+       "  type:\n"
+       "  1. Do not use this cleaning cartridge in this drive.\n"
+       "  2. Wait for the current operation to finish.\n"
+       "  3. Then use a new cleaning cartridge.",
+    /* 0x18 */
+   "W: The tape drive has requested a retention operation",
+    /* 0x19 */
+   "W: A redundant interface port on the tape drive has failed",
+    /* 0x1a */
+   "W: A tape drive cooling fan has failed",
+    /* 0x1b */
+   "W: A redundant power supply has failed inside the tape drive enclosure.\n"
+       "  Check the enclosure users manual for instructions on replacing the\n"
+       "  failed power supply.",
+    /* 0x1c */
+   "W: The tape drive power consumption is outside the specified range.",
+    /* 0x1d */
+   "W: Preventive maintenance of the tape drive is required. Check the tape\n"
+       "  drive users manual for device specific preventive maintenance\n"
+       "  tasks or call the tape drive supplier helpline.",
+    /* 0x1e */
+   "C: The tape drive has a hardware fault:\n"
+       "  1. Eject the tape or magazine.\n"
+       "  2. Reset the drive.\n"
+       "  3. Restart the operation.",
+    /* 0x1f */
+   "C: The tape drive has a hardware fault:\n"
+       "  1. Turn the tape drive off and then on again.\n"
+       "  2. Restart the operation.\n"
+    "  3. If the problem persists, call the tape drive supplier helpline.",
+    /* 0x20 */
+   "W: The tape drive has a problem with the application client interface:\n"
+       "  1. Check the cables and cable connections.\n"
+       "  2. Restart the operation.",
+    /* 0x21 */
+   "C: The operation has failed:\n"
+       "  1. Eject the tape or magazine.\n"
+       "  2. Insert the tape or magazine again.\n"
+       "  3. Restart the operation.",
+    /* 0x22 */
+   "W: The firmware download has failed because you have tried to use the\n"
+       "  incorrect firmware for this tape drive. Obtain the correct\n"
+       "  firmware and try again.",
+    /* 0x23 */
+   "W: Environmental conditions inside the tape drive are outside the\n"
+       "  specified humidity range.",
+    /* 0x24 */
+   "W: Environmental conditions inside the tape drive are outside the\n"
+       "  specified temperature range.",
+    /* 0x25 */
+   "W: The voltage supply to the tape drive is outside the specified range.",
+    /* 0x26 */
+   "C: A hardware failure of the tape drive is predicted. Call the tape\n"
+       "  drive supplier helpline.",
+    /* 0x27 */
+   "W: The tape drive may have a hardware fault. Run extended diagnostics to\n"
+       "  verify and diagnose the problem. Check the tape drive users manual for\n"
+       "  device specific instructions on running extended diagnostic tests.",
+    /* 0x28 */
+   "C: The changer mechanism is having difficulty communicating with the tape\n"
+       "  drive:\n"
+       "  1. Turn the autoloader off then on.\n"
+       "  2. Restart the operation.\n"
+       "  3. If problem persists, call the tape drive supplier helpline.",
+    /* 0x29 */
+   "C: A tape has been left in the autoloader by a previous hardware fault:\n"
+       "  1. Insert an empty magazine to clear the fault.\n"
+       "  2. If the fault does not clear, turn the autoloader off and then\n"
+       "  on again.\n"
+       "  3. If the problem persists, call the tape drive supplier helpline.",
+    /* 0x2a */
+   "W: There is a problem with the autoloader mechanism.",
+    /* 0x2b */
+   "C: The operation has failed because the autoloader door is open:\n"
+       "  1. Clear any obstructions from the autoloader door.\n"
+       "  2. Eject the magazine and then insert it again.\n"
+       "  3. If the fault does not clear, turn the autoloader off and then\n"
+       "  on again.\n"
+       "  4. If the problem persists, call the tape drive supplier helpline.",
+    /* 0x2c */
+   "C: The autoloader has a hardware fault:\n"
+       "  1. Turn the autoloader off and then on again.\n"
+       "  2. Restart the operation.\n"
+       "  3. If the problem persists, call the tape drive supplier helpline.\n"
+       "  Check the autoloader users manual for device specific instructions\n"
+       "  on turning the device power on and off.",
+    /* 0x2d */
+   "C: The autoloader cannot operate without the magazine,\n"
+       "  1. Insert the magazine into the autoloader.\n"
+       "  2. Restart the operation.",
+    /* 0x2e */
+   "W: A hardware failure of the changer mechanism is predicted. Call the\n"
+       "  tape drive supplier helpline.",
+    /* 0x2f */
+   "I: Reserved.",
+    /* 0x30 */
+   "I: Reserved.",
+    /* 0x31 */
+   "I: Reserved.",
+    /* 0x32 */
+   "W: Media statistics have been lost at some time in the past",
+    /* 0x33 */
+   "W: The tape directory on the tape cartridge just unloaded has been\n"
+       "  corrupted. File search performance will be degraded. The tape\n"
+       "  directory can be rebuilt by reading all the data.",
+    /* 0x34 */
+   "C: The tape just unloaded could not write its system area successfully:\n"
+       "  1. Copy data to another tape cartridge.\n"
+       "  2. Discard the old cartridge.",
+    /* 0x35 */
+   "C: The tape system are could not be read successfully at load time:\n"
+    "  1. Copy data to another tape cartridge.\n",
+    /* 0x36 */
+   "C: The start or data could not be found on the tape:\n"
+       "  1. Check you are using the correct format tape.\n"
+       "  2. Discard the tape or return the tape to your supplier",
+    /* 0x37 */
+    "C: The operation has failed because the media cannot be loaded\n"
+        "  and threaded.\n"
+        "  1. Remove the cartridge, inspect it as specified in the product\n"
+        "  manual, and retry the operation.\n"
+        "  2. If the problem persists, call the tape drive supplier help line.",
+    /* 0x38 */
+    "C: The operation has failed because the medium cannot be unloaded:\n"
+        "  1. Do not attempt to extract the tape cartridge.\n"
+        "  2. Call the tape driver supplier help line.",
+    /* 0x39 */
+    "C: The tape drive has a problem with the automation interface:\n"
+        "  1. Check the power to the automation system.\n"
+        "  2. Check the cables and cable connections.\n"
+        "  3. Call the supplier help line if problem persists.",
+    /* 0x3a */
+    "W: The tape drive has reset itself due to a detected firmware\n"
+        "  fault. If problem persists, call the supplier help line.",
+    };
+
+const char * scsiTapeAlertsTapeDevice(unsigned short code)
+{
+    const int num = sizeof(TapeAlertsMessageTable) /
+                        sizeof(TapeAlertsMessageTable[0]);
+
+    return (code < num) ?  TapeAlertsMessageTable[code] : "Unknown Alert"; 
+}
+
+// The first character (W, C, I) tells the severity
+static const char * ChangerTapeAlertsMessageTable[]= {  
+    " ",
+    /* 0x01 */
+    "C: The library mechanism is having difficulty communicating with the\n"
+        "  drive:\n"
+        "  1. Turn the library off then on.\n"
+        "  2. Restart the operation.\n"
+        "  3. If the problem persists, call the library supplier help line.",
+    /* 0x02 */
+    "W: There is a problem with the library mechanism. If problem persists,\n"
+        "  call the library supplier help line.",
+    /* 0x03 */
+    "C: The library has a hardware fault:\n"
+        "  1. Reset the library.\n"
+        "  2. Restart the operation.\n"
+        "  Check the library users manual for device specific instructions on resetting\n"
+        "  the device.",
+    /* 0x04 */
+    "C: The library has a hardware fault:\n"
+        "  1. Turn the library off then on again.\n"
+        "  2. Restart the operation.\n"
+        "  3. If the problem persists, call the library supplier help line.\n"
+        "  Check the library users manual for device specific instructions on turning the\n"
+        "  device power on and off.",
+    /* 0x05 */
+    "W: The library mechanism may have a hardware fault.\n"
+        "  Run extended diagnostics to verify and diagnose the problem. Check the library\n"
+        "  users manual for device specific instructions on running extended diagnostic\n"
+        "  tests.",
+    /* 0x06 */
+    "C: The library has a problem with the host interface:\n"
+        "  1. Check the cables and connections.\n"
+        "  2. Restart the operation.",
+    /* 0x07 */
+    "W: A hardware failure of the library is predicted. Call the library\n"
+        "  supplier help line.",
+    /* 0x08 */
+    "W: Preventive maintenance of the library is required.\n"
+        "  Check the library users manual for device specific preventative maintenance\n"
+        "  tasks, or call your library supplier help line.",
+    /* 0x09 */
+    "C: General environmental conditions inside the library are outside the\n"
+        "  specified humidity range.",
+    /* 0x0a */
+    "C: General environmental conditions inside the library are outside the\n"
+        "  specified temperature range.",
+    /* 0x0b */
+    "C: The voltage supply to the library is outside the specified range.\n"
+        "  There is a potential problem with the power supply or failure of\n"
+        "  a redundant power supply.",
+    /* 0x0c */
+    "C: A cartridge has been left inside the library by a previous hardware\n"
+        "  fault:\n"
+        "  1. Insert an empty magazine to clear the fault.\n"
+        "  2. If the fault does not clear, turn the library off and then on again.\n"
+        "  3. If the problem persists, call the library supplier help line.",
+    /* 0x0d */
+    "W: There is a potential problem with the drive ejecting cartridges or\n"
+        "  with the library mechanism picking a cartridge from a slot.\n"
+        "  1. No action needs to be taken at this time.\n"
+        "  2. If the problem persists, call the library supplier help line.",
+    /* 0x0e */
+    "W: There is a potential problem with the library mechanism placing a\n"
+        "  cartridge into a slot.\n"
+        "  1. No action needs to be taken at this time.\n"
+        "  2. If the problem persists, call the library supplier help line.",
+    /* 0x0f */
+    "W: There is a potential problem with the drive or the library mechanism\n"
+        "  loading cartridges, or an incompatible cartridge.",
+    /* 0x10 */
+    "C: The library has failed because the door is open:\n"
+        "  1. Clear any obstructions from the library door.\n"
+        "  2. Close the library door.\n"
+        "  3. If the problem persists, call the library supplier help line.",
+    /* 0x11 */
+    "C: There is a mechanical problem with the library media import/export\n"
+        "  mailslot.",
+    /* 0x12 */
+    "C: The library cannot operate without the magazine.\n"
+        "  1. Insert the magazine into the library.\n"
+        "  2. Restart the operation.",
+    /* 0x13 */
+    "W: Library security has been compromised.",
+    /* 0x14 */
+    "I: The library security mode has been changed.\n"
+        "  The library has either been put into secure mode, or the library has exited\n"
+        "  the secure mode.\n"
+        "  This is for information purposes only. No action is required.",
+    /* 0x15 */
+    "I: The library has been manually turned offline and is unavailable for use.",
+    /* 0x16 */
+    "I: A drive inside the library has been taken offline.\n"
+        "  This is for information purposes only. No action is required.",
+    /* 0x17 */
+    "W: There is a potential problem with the bar code label or the scanner\n"
+        "  hardware in the library mechanism.\n"
+        "  1. No action needs to be taken at this time.\n"
+        "  2. If the problem persists, call the library supplier help line.",
+    /* 0x18 */
+    "C: The library has detected an inconsistency in its inventory.\n"
+        "  1. Redo the library inventory to correct inconsistency.\n"
+        "  2. Restart the operation.\n"
+        "  Check the applications users manual or the hardware users manual for\n"
+        "  specific instructions on redoing the library inventory.",
+    /* 0x19 */
+    "W: A library operation has been attempted that is invalid at this time.",
+    /* 0x1a */
+    "W: A redundant interface port on the library has failed.",
+    /* 0x1b */
+    "W: A library cooling fan has failed.",
+    /* 0x1c */
+    "W: A redundant power supply has failed inside the library. Check the\n"
+        "  library users manual for instructions on replacing the failed power supply.",
+    /* 0x1d */
+    "W: The library power consumption is outside the specified range.",
+    /* 0x1e */
+    "C: A failure has occurred in the cartridge pass-through mechanism between\n"
+        "  two library modules.",
+    /* 0x1f */
+    "C: A cartridge has been left in the pass-through mechanism from a\n"
+        "  previous hardware fault. Check the library users guide for instructions on\n"
+        "  clearing this fault.",
+    /* 0x20 */
+    "I: The library was unable to read the bar code on a cartridge.",
+};
+
+const char * scsiTapeAlertsChangerDevice(unsigned short code)
+{
+    const int num = sizeof(ChangerTapeAlertsMessageTable) /
+                        sizeof(ChangerTapeAlertsMessageTable[0]);
+
+    return (code < num) ?  ChangerTapeAlertsMessageTable[code] : "Unknown Alert"; 
+}
+
+
+/* this is a subset of the SCSI additional sense code strings indexed
+ * by "ascq" for the case when asc==SCSI_ASC_IMPENDING_FAILURE (0x5d)
+ */
+static const char * strs_for_asc_5d[] = {
+   /* 0x00 */   "FAILURE PREDICTION THRESHOLD EXCEEDED",
+        "MEDIA FAILURE PREDICTION THRESHOLD EXCEEDED",
+        "LOGICAL UNIT FAILURE PREDICTION THRESHOLD EXCEEDED",
+        "SPARE AREA EXHAUSTION PREDICTION THRESHOLD EXCEEDED",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+        "",
+   /* 0x10 */   "HARDWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "HARDWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "HARDWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "HARDWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "HARDWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "HARDWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "HARDWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "HARDWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "HARDWARE IMPENDING FAILURE CONTROLLER DETECTED",
+        "HARDWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "HARDWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "HARDWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
+        "HARDWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
+        "",
+        "",
+        "",
+   /* 0x20 */   "CONTROLLER IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "CONTROLLER IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "CONTROLLER IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "CONTROLLER IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "CONTROLLER IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "CONTROLLER IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "CONTROLLER IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "CONTROLLER IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "CONTROLLER IMPENDING FAILURE CONTROLLER DETECTED",
+        "CONTROLLER IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "CONTROLLER IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "CONTROLLER IMPENDING FAILURE SPIN-UP RETRY COUNT",
+        "CONTROLLER IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
+        "",
+        "",
+        "",
+   /* 0x30 */   "DATA CHANNEL IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "DATA CHANNEL IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "DATA CHANNEL IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "DATA CHANNEL IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "DATA CHANNEL IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "DATA CHANNEL IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "DATA CHANNEL IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "DATA CHANNEL IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "DATA CHANNEL IMPENDING FAILURE CONTROLLER DETECTED",
+        "DATA CHANNEL IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "DATA CHANNEL IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "DATA CHANNEL IMPENDING FAILURE SPIN-UP RETRY COUNT",
+        "DATA CHANNEL IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
+        "",
+        "",
+        "",
+   /* 0x40 */   "SERVO IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "SERVO IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "SERVO IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "SERVO IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "SERVO IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "SERVO IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "SERVO IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "SERVO IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "SERVO IMPENDING FAILURE CONTROLLER DETECTED",
+        "SERVO IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "SERVO IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "SERVO IMPENDING FAILURE SPIN-UP RETRY COUNT",
+        "SERVO IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
+        "",
+        "",
+        "",
+   /* 0x50 */   "SPINDLE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "SPINDLE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "SPINDLE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "SPINDLE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "SPINDLE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "SPINDLE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "SPINDLE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "SPINDLE IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "SPINDLE IMPENDING FAILURE CONTROLLER DETECTED",
+        "SPINDLE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "SPINDLE IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "SPINDLE IMPENDING FAILURE SPIN-UP RETRY COUNT",
+        "SPINDLE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT",
+        "",
+        "",
+        "",
+   /* 0x60 */   "FIRMWARE IMPENDING FAILURE GENERAL HARD DRIVE FAILURE",
+        "FIRMWARE IMPENDING FAILURE DRIVE ERROR RATE TOO HIGH",
+        "FIRMWARE IMPENDING FAILURE DATA ERROR RATE TOO HIGH",
+        "FIRMWARE IMPENDING FAILURE SEEK ERROR RATE TOO HIGH",
+        "FIRMWARE IMPENDING FAILURE TOO MANY BLOCK REASSIGNS",
+        "FIRMWARE IMPENDING FAILURE ACCESS TIMES TOO HIGH",
+        "FIRMWARE IMPENDING FAILURE START UNIT TIMES TOO HIGH",
+        "FIRMWARE IMPENDING FAILURE CHANNEL PARAMETRICS",
+        "FIRMWARE IMPENDING FAILURE CONTROLLER DETECTED",
+        "FIRMWARE IMPENDING FAILURE THROUGHPUT PERFORMANCE",
+        "FIRMWARE IMPENDING FAILURE SEEK TIME PERFORMANCE",
+        "FIRMWARE IMPENDING FAILURE SPIN-UP RETRY COUNT",
+   /* 0x6c */   "FIRMWARE IMPENDING FAILURE DRIVE CALIBRATION RETRY COUNT"};
+
+
+/* this is a subset of the SCSI additional sense code strings indexed
+ *  * by "ascq" for the case when asc==SCSI_ASC_WARNING (0xb)
+ *   */
+static const char * strs_for_asc_b[] = {
+       /* 0x00 */   "WARNING",
+               "WARNING - SPECIFIED TEMPERATURE EXCEEDED",
+               "WARNING - ENCLOSURE DEGRADED"};
+
+static char spare_buff[128];
+
+const char * scsiGetIEString(UINT8 asc, UINT8 ascq)
+{
+    const char * rp;
+
+    if (SCSI_ASC_IMPENDING_FAILURE == asc) {
+        if (ascq == 0xff)
+            return "FAILURE PREDICTION THRESHOLD EXCEEDED (FALSE)";
+        else if (ascq < 
+                 (sizeof(strs_for_asc_5d) / sizeof(strs_for_asc_5d[0]))) {
+            rp = strs_for_asc_5d[ascq];
+            if (strlen(rp) > 0)
+                return rp;
+        }
+        snprintf(spare_buff, sizeof(spare_buff),
+                 "FAILURE PREDICTION THRESHOLD EXCEEDED: ascq=0x%x", ascq);
+        return spare_buff;
+    } else if (SCSI_ASC_WARNING == asc) {
+        if (ascq < (sizeof(strs_for_asc_b) / sizeof(strs_for_asc_b[0]))) {
+            rp = strs_for_asc_b[ascq];
+            if (strlen(rp) > 0)
+                return rp;
+        }
+        snprintf(spare_buff, sizeof(spare_buff), "WARNING: ascq=0x%x", ascq);
+        return spare_buff;
+    }
+    return NULL;        /* not a IE additional sense code */
+}
+
+
+/* This is not documented in t10.org, page 0x80 is vendor specific */
+/* Some IBM disks do an offline read-scan when they get this command. */
+int scsiSmartIBMOfflineTest(int device)
+{       
+    UINT8 tBuf[256];
+    int res;
+        
+    memset(tBuf, 0, sizeof(tBuf));
+    /* Build SMART Off-line Immediate Diag Header */
+    tBuf[0] = 0x80; /* Page Code */
+    tBuf[1] = 0x00; /* Reserved */
+    tBuf[2] = 0x00; /* Page Length MSB */
+    tBuf[3] = 0x04; /* Page Length LSB */
+    tBuf[4] = 0x03; /* SMART Revision */
+    tBuf[5] = 0x00; /* Reserved */
+    tBuf[6] = 0x00; /* Off-line Immediate Time MSB */
+    tBuf[7] = 0x00; /* Off-line Immediate Time LSB */
+    res = scsiSendDiagnostic(device, SCSI_DIAG_NO_SELF_TEST, tBuf, 8);
+    if (res)
+        pout("IBM offline test failed [%s]\n", scsiErrString(res));
+    return res;
+}
+
+int scsiSmartDefaultSelfTest(int device)
+{       
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_DEF_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Default self test failed [%s]\n", scsiErrString(res));
+    return res;
+}
+
+int scsiSmartShortSelfTest(int device)
+{       
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_BG_SHORT_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Short offline self test failed [%s]\n", scsiErrString(res));
+    return res;
+}
+
+int scsiSmartExtendSelfTest(int device)
+{       
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_BG_EXTENDED_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Long (extended) offline self test failed [%s]\n",
+             scsiErrString(res));
+    return res;
+}
+
+int scsiSmartShortCapSelfTest(int device)
+{       
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_FG_SHORT_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Short foreground self test failed [%s]\n", scsiErrString(res));
+    return res;
+}
+
+int scsiSmartExtendCapSelfTest(int device)
+{
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_FG_EXTENDED_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Long (extended) foreground self test failed [%s]\n",
+             scsiErrString(res));
+    return res;
+}
+
+int scsiSmartSelfTestAbort(int device)
+{
+    int res;
+
+    res = scsiSendDiagnostic(device, SCSI_DIAG_ABORT_SELF_TEST, NULL, 0);
+    if (res)
+        pout("Abort self test failed [%s]\n", scsiErrString(res));
+    return res;
+}
+
+/* Returns 0 and the expected duration of an extended self test (in seconds)
+   if successful; any other return value indicates a failure. */
+int scsiFetchExtendedSelfTestTime(int device, int * durationSec, int modese_len)
+{
+    int err, offset, res;
+    UINT8 buff[64];
+
+    memset(buff, 0, sizeof(buff));
+    if (modese_len <= 6) {
+        if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 
+                                 MPAGE_CONTROL_CURRENT, 
+                                 buff, sizeof(buff)))) {
+            if (SIMPLE_ERR_BAD_OPCODE == err)
+                modese_len = 10;
+            else
+                return err;
+        } else if (0 == modese_len)
+            modese_len = 6;
+    }
+    if (10 == modese_len) {
+        err = scsiModeSense10(device, CONTROL_MODE_PAGE, 
+                              MPAGE_CONTROL_CURRENT, 
+                              buff, sizeof(buff));
+        if (err)
+            return err;
+    } 
+    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
+    if (offset < 0)
+        return -EINVAL;
+    if (buff[offset + 1] >= 0xa) {
+        res = (buff[offset + 10] << 8) | buff[offset + 11];
+        *durationSec = res;
+        return 0;
+    }
+    else
+        return -EINVAL;
+}
+
+void scsiDecodeErrCounterPage(unsigned char * resp, 
+                              struct scsiErrorCounter *ecp)
+{
+    int k, j, num, pl, pc;
+    unsigned char * ucp;
+    unsigned char * xp;
+    uint64_t * ullp;
+
+    memset(ecp, 0, sizeof(*ecp));
+    num = (resp[2] << 8) | resp[3];
+    ucp = &resp[0] + 4;
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        switch (pc) {
+            case 0: 
+            case 1: 
+            case 2: 
+            case 3: 
+            case 4: 
+            case 5: 
+            case 6: 
+                ecp->gotPC[pc] = 1;
+                ullp = &ecp->counter[pc];
+                break;
+        default: 
+                ecp->gotExtraPC = 1;
+                ullp = &ecp->counter[7];
+                break;
+        }
+        k = pl - 4;
+        xp = ucp + 4;
+        if (k > (int)sizeof(*ullp)) {
+            xp += (k - sizeof(*ullp));
+            k = sizeof(*ullp);
+        }
+        *ullp = 0;
+        for (j = 0; j < k; ++j) {
+            if (j > 0)
+                *ullp <<= 8;
+            *ullp |= xp[j];
+        }
+        num -= pl;
+        ucp += pl;
+    }
+}
+
+void scsiDecodeNonMediumErrPage(unsigned char *resp, 
+                                struct scsiNonMediumError *nmep)
+{
+    int k, j, num, pl, pc, szof;
+    unsigned char * ucp;
+    unsigned char * xp;
+
+    memset(nmep, 0, sizeof(*nmep));
+    num = (resp[2] << 8) | resp[3];
+    ucp = &resp[0] + 4;
+    szof = sizeof(nmep->counterPC0);
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        switch (pc) {
+            case 0: 
+                nmep->gotPC0 = 1;
+                k = pl - 4;
+                xp = ucp + 4;
+                if (k > szof) {
+                    xp += (k - szof);
+                    k = szof;
+                }
+                nmep->counterPC0 = 0;
+                for (j = 0; j < k; ++j) {
+                    if (j > 0)
+                        nmep->counterPC0 <<= 8;
+                    nmep->counterPC0 |= xp[j];
+                }
+                break;
+            case 0x8009: 
+                nmep->gotTFE_H = 1;
+                k = pl - 4;
+                xp = ucp + 4;
+                if (k > szof) {
+                    xp += (k - szof);
+                    k = szof;
+                }
+                nmep->counterTFE_H = 0;
+                for (j = 0; j < k; ++j) {
+                    if (j > 0)
+                        nmep->counterTFE_H <<= 8;
+                    nmep->counterTFE_H |= xp[j];
+                }
+                break;
+            case 0x8015: 
+                nmep->gotPE_H = 1;
+                k = pl - 4;
+                xp = ucp + 4;
+                if (k > szof) {
+                    xp += (k - szof);
+                    k = szof;
+                }
+                nmep->counterPE_H = 0;
+                for (j = 0; j < k; ++j) {
+                    if (j > 0)
+                        nmep->counterPE_H <<= 8;
+                    nmep->counterPE_H |= xp[j];
+                }
+                break;
+        default: 
+                nmep->gotExtraPC = 1;
+                break;
+        }
+        num -= pl;
+        ucp += pl;
+    }
+}
+
+/* Counts number of failed self-tests. Also encodes the poweron_hour
+   of the most recent failed self-test. Return value is negative if
+   this function has a problem (typically -1), otherwise the bottom 8
+   bits are the number of failed self tests and the 16 bits above that
+   are the poweron hour of the most recent failure. Note: aborted self
+   tests (typically by the user) and self tests in progress are not 
+   considered failures. See Working Draft SCSI Primary Commands - 3 
+   (SPC-3) section 7.2.10 T10/1416-D (rev 22a) */
+int scsiCountFailedSelfTests(int fd, int noisy)
+{
+    int num, k, n, err, res, fails, fail_hour;
+    UINT8 * ucp;
+    unsigned char resp[LOG_RESP_SELF_TEST_LEN];
+
+    if ((err = scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, resp, 
+                            LOG_RESP_SELF_TEST_LEN, 0))) {
+        if (noisy)
+            pout("scsiCountSelfTests Failed [%s]\n", scsiErrString(err));
+        return -1;
+    }
+    if (resp[0] != SELFTEST_RESULTS_LPAGE) {
+        if (noisy)
+            pout("Self-test Log Sense Failed, page mismatch\n");
+        return -1;
+    }
+    // compute page length
+    num = (resp[2] << 8) + resp[3];
+    // Log sense page length 0x190 bytes
+    if (num != 0x190) {
+        if (noisy)
+            pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n", num);
+        return -1;
+    }
+    fails = 0;
+    fail_hour = 0;
+    // loop through the twenty possible entries
+    for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) {
+
+        // timestamp in power-on hours (or zero if test in progress)
+        n = (ucp[6] << 8) | ucp[7];
+
+        // The spec says "all 20 bytes will be zero if no test" but
+        // DG has found otherwise.  So this is a heuristic.
+        if ((0 == n) && (0 == ucp[4]))
+            break;
+        res = ucp[4] & 0xf;
+        if ((res > 2) && (res < 8)) {
+            fails++;
+            if (1 == fails) 
+                fail_hour = (ucp[6] << 8) + ucp[7];
+        }
+    }
+    return (fail_hour << 8) + fails;
+}
+
+/* Returns 0 if able to read self test log page; then outputs 1 into
+   *inProgress if self test still in progress, else outputs 0. */
+int scsiSelfTestInProgress(int fd, int * inProgress)
+{
+    int num;
+    UINT8 * ucp;
+    unsigned char resp[LOG_RESP_SELF_TEST_LEN];
+
+    if (scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, resp, 
+                     LOG_RESP_SELF_TEST_LEN, 0))
+        return -1;
+    if (resp[0] != SELFTEST_RESULTS_LPAGE)
+        return -1;
+    // compute page length
+    num = (resp[2] << 8) + resp[3];
+    // Log sense page length 0x190 bytes
+    if (num != 0x190) {
+        return -1;
+    }
+    ucp = resp + 4;
+    if (inProgress)
+        *inProgress = (0xf == (ucp[4] & 0xf)) ? 1 : 0;
+    return 0;
+}
+
+/* Returns a negative value if failed to fetch Contol mode page or it was
+   malformed. Returns 0 if GLTSD bit is zero and returns 1 if the GLTSD
+   bit is set. Examines default mode page when current==0 else examines
+   current mode page. */
+int scsiFetchControlGLTSD(int device, int modese_len, int current)
+{
+    int err, offset;
+    UINT8 buff[64];
+    int pc = current ? MPAGE_CONTROL_CURRENT : MPAGE_CONTROL_DEFAULT;
+
+    memset(buff, 0, sizeof(buff));
+    if (modese_len <= 6) {
+        if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, pc, 
+                                 buff, sizeof(buff)))) {
+            if (SIMPLE_ERR_BAD_OPCODE == err)
+                modese_len = 10;
+            else
+                return -EINVAL;
+        } else if (0 == modese_len)
+            modese_len = 6;
+    }
+    if (10 == modese_len) {
+        err = scsiModeSense10(device, CONTROL_MODE_PAGE, pc,
+                              buff, sizeof(buff));
+        if (err)
+            return -EINVAL;
+    } 
+    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
+    if ((offset >= 0) && (buff[offset + 1] >= 0xa))
+        return (buff[offset + 2] & 2) ? 1 : 0;
+    return -EINVAL;
+}
+
+/* Attempts to set or clear GLTSD bit in Control mode page. If enabled is
+   0 attempts to clear GLTSD otherwise it attempts to set it. Returns 0 if
+   successful, negative if low level error, > 0 if higher level error (e.g.
+   SIMPLE_ERR_BAD_PARAM if GLTSD bit is not changeable). */
+int scsiSetControlGLTSD(int device, int enabled, int modese_len)
+{
+    int err, offset, resp_len, sp;
+    UINT8 buff[64];
+    UINT8 ch_buff[64];
+
+    memset(buff, 0, sizeof(buff));
+    if (modese_len <= 6) {
+        if ((err = scsiModeSense(device, CONTROL_MODE_PAGE, 
+                                 MPAGE_CONTROL_CURRENT, 
+                                 buff, sizeof(buff)))) {
+            if (SIMPLE_ERR_BAD_OPCODE == err)
+                modese_len = 10;
+            else
+                return err;
+        } else if (0 == modese_len)
+            modese_len = 6;
+    }
+    if (10 == modese_len) {
+        err = scsiModeSense10(device, CONTROL_MODE_PAGE, 
+                              MPAGE_CONTROL_CURRENT, 
+                              buff, sizeof(buff));
+        if (err)
+            return err;
+    } 
+    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
+    if ((offset < 0) || (buff[offset + 1] < 0xa))
+        return SIMPLE_ERR_BAD_RESP;
+
+    if (enabled)
+        enabled = 2;
+    if (enabled == (buff[offset + 2] & 2))
+        return 0;       /* GLTSD already in wanted state so nothing to do */
+
+    if (modese_len == 6)
+        err = scsiModeSense(device, CONTROL_MODE_PAGE, 
+                            MPAGE_CONTROL_CHANGEABLE, 
+                            ch_buff, sizeof(ch_buff));
+    else
+        err = scsiModeSense10(device, CONTROL_MODE_PAGE, 
+                              MPAGE_CONTROL_CHANGEABLE, 
+                              ch_buff, sizeof(ch_buff));
+    if (err)
+        return err;
+    if (0 == (ch_buff[offset + 2] & 2))
+        return SIMPLE_ERR_BAD_PARAM;  /* GLTSD bit not chageable */
+    
+    if (10 == modese_len) {
+        resp_len = (buff[0] << 8) + buff[1] + 2;
+        buff[3] &= 0xef;    /* for disks mask out DPOFUA bit */
+    } else {
+        resp_len = buff[0] + 1;
+        buff[2] &= 0xef;    /* for disks mask out DPOFUA bit */
+    }
+    sp = (buff[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
+    if (enabled)
+        buff[offset + 2] |= 0x2;    /* set GLTSD bit */
+    else
+        buff[offset + 2] &= 0xfd;   /* clear GLTSD bit */
+    if (10 == modese_len)
+        err = scsiModeSelect10(device, sp, buff, resp_len);
+    else if (6 == modese_len)
+        err = scsiModeSelect(device, sp, buff, resp_len);
+    return err;
+}
+
+/* Returns a negative value if failed to fetch Protocol specific port mode 
+   page or it was malformed. Returns transport protocol identifier when
+   value >= 0 . */
+int scsiFetchTransportProtocol(int device, int modese_len)
+{
+    int err, offset;
+    UINT8 buff[64];
+
+    memset(buff, 0, sizeof(buff));
+    if (modese_len <= 6) {
+        if ((err = scsiModeSense(device, PROTOCOL_SPECIFIC_PORT_PAGE, 
+                                 MPAGE_CONTROL_CURRENT, 
+                                 buff, sizeof(buff)))) {
+            if (SIMPLE_ERR_BAD_OPCODE == err)
+                modese_len = 10;
+            else
+                return -EINVAL;
+        } else if (0 == modese_len)
+            modese_len = 6;
+    }
+    if (10 == modese_len) {
+        err = scsiModeSense10(device, PROTOCOL_SPECIFIC_PORT_PAGE, 
+                              MPAGE_CONTROL_CURRENT, 
+                              buff, sizeof(buff));
+        if (err)
+            return -EINVAL;
+    } 
+    offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
+    if ((offset >= 0) && (buff[offset + 1] > 1)) {
+        if ((0 == (buff[offset] & 0x40)) &&       /* SPF==0 */
+            (PROTOCOL_SPECIFIC_PORT_PAGE == (buff[offset] & 0x3f))) 
+                return (buff[offset + 2] & 0xf);
+    }
+    return -EINVAL;
+}
+
+
+/* This is Linux specific code to look for the libata ATA-SCSI
+   simulator in the vendor device identification page.
+   Returns 1 if found else 0. */
+int isLinuxLibAta(unsigned char * vpd_di_buff, int len)
+{
+    int k, id_len, c_set, assoc, id_type, i_len;
+    unsigned char * ucp;
+    unsigned char * ip;
+
+    if (len < 4) {
+        /* Device identification VPD page length too short */
+        return 0;
+    }
+    len -= 4;
+    ucp = vpd_di_buff + 4;
+    for (k = 0; k < len; k += id_len, ucp += id_len) {
+        i_len = ucp[3];
+        id_len = i_len + 4;
+        if ((k + id_len) > len) {
+            /* short descriptor, badly formed */
+            return 0;
+        }
+        ip = ucp + 4;
+        c_set = (ucp[0] & 0xf);
+        assoc = ((ucp[1] >> 4) & 0x3);
+        id_type = (ucp[1] & 0xf);
+        if ((0 == id_type) && (2 == c_set) && (0 == assoc) &&
+            (0 == strncmp((const char *)ip,
+                          "Linux ATA-SCSI simulator", i_len))) {
+            return 1;
+        }
+    }
+    return 0;
+}
diff --git a/scsicmds.h b/scsicmds.h
new file mode 100644 (file)
index 0000000..11b5087
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * scsicmds.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * Additional SCSI work:
+ * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ * N.B. What was formerly known as "SMART" are now called "informational
+ * exceptions" in recent t10.org drafts (i.e. recent SCSI).
+ *
+ */
+
+
+#ifndef SCSICMDS_H_
+#define SCSICMDS_H_
+
+#define SCSICMDS_H_CVSID "$Id: scsicmds.h,v 1.57 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.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 ... */
+#ifndef TEST_UNIT_READY
+#define TEST_UNIT_READY 0x0
+#endif
+#ifndef LOG_SENSE
+#define LOG_SENSE 0x4d
+#endif
+#ifndef MODE_SENSE
+#define MODE_SENSE 0x1a
+#endif
+#ifndef MODE_SENSE_10
+#define MODE_SENSE_10 0x5a
+#endif
+#ifndef MODE_SELECT
+#define MODE_SELECT 0x15
+#endif
+#ifndef MODE_SELECT_10
+#define MODE_SELECT_10 0x55
+#endif
+#ifndef INQUIRY
+#define INQUIRY 0x12
+#endif
+#ifndef REQUEST_SENSE
+#define REQUEST_SENSE  0x03
+#endif
+#ifndef RECEIVE_DIAGNOSTIC
+#define RECEIVE_DIAGNOSTIC  0x1c
+#endif
+#ifndef SEND_DIAGNOSTIC
+#define SEND_DIAGNOSTIC  0x1d
+#endif
+#ifndef READ_DEFECT_10
+#define READ_DEFECT_10  0x37
+#endif
+
+typedef unsigned char UINT8;
+typedef char INT8;
+typedef unsigned int UINT32;
+typedef int INT32;
+
+#define DXFER_NONE        0
+#define DXFER_FROM_DEVICE 1
+#define DXFER_TO_DEVICE   2
+
+struct scsi_cmnd_io
+{
+    UINT8 * cmnd;       /* [in]: ptr to SCSI command block (cdb) */
+    size_t  cmnd_len;   /* [in]: number of bytes in SCSI command */
+    int dxfer_dir;      /* [in]: DXFER_NONE, DXFER_FROM_DEVICE, or 
+                                 DXFER_TO_DEVICE */
+    UINT8 * dxferp;     /* [in]: ptr to outgoing or incoming data buffer */
+    size_t dxfer_len;   /* [in]: bytes to be transferred to/from dxferp */
+    UINT8 * sensep;     /* [in]: ptr to sense buffer, filled when 
+                                 CHECK CONDITION status occurs */
+    size_t max_sense_len; /* [in]: max number of bytes to write to sensep */
+    unsigned timeout;   /* [in]: seconds, 0-> default timeout (60 seconds?) */
+    size_t resp_sense_len;  /* [out]: sense buffer length written */
+    UINT8 scsi_status;  /* [out]: 0->ok, 2->CHECK CONDITION, etc ... */
+    int resid;          /* [out]: Number of bytes requested to be transferred
+                                  less actual number transferred (0 if not
+                                   supported) */
+};
+
+struct scsi_sense_disect {
+    UINT8 error_code;
+    UINT8 sense_key;
+    UINT8 asc; 
+    UINT8 ascq;
+};
+
+/* Useful data from Informational Exception Control mode page (0x1c) */
+#define SCSI_IECMP_RAW_LEN 64
+struct scsi_iec_mode_page {
+    UINT8 requestedCurrent;
+    UINT8 gotCurrent;
+    UINT8 requestedChangeable;
+    UINT8 gotChangeable;
+    UINT8 modese_len;   /* 0 (don't know), 6 or 10 */
+    UINT8 raw_curr[SCSI_IECMP_RAW_LEN];
+    UINT8 raw_chg[SCSI_IECMP_RAW_LEN];
+};
+
+/* Carrier for Error counter log pages (e.g. read, write, verify ...) */
+struct scsiErrorCounter {
+    UINT8 gotPC[7];
+    UINT8 gotExtraPC;
+    uint64_t counter[8];
+};
+
+/* Carrier for Non-medium error log page */
+struct scsiNonMediumError {
+    UINT8 gotPC0;
+    UINT8 gotExtraPC;
+    uint64_t counterPC0;
+    UINT8 gotTFE_H;
+    uint64_t counterTFE_H; /* Track following errors [Hitachi] */
+    UINT8 gotPE_H;
+    uint64_t counterPE_H;  /* Positioning errors [Hitachi] */
+};
+
+/* SCSI Peripheral types (of interest) */
+#define SCSI_PT_DIRECT_ACCESS           0x0
+#define SCSI_PT_SEQUENTIAL_ACCESS       0x1
+#define SCSI_PT_CDROM                   0x5
+#define SCSI_PT_MEDIUM_CHANGER          0x8
+#define SCSI_PT_ENCLOSURE               0xd
+
+/* ANSI SCSI-3 Log Pages retrieved by LOG SENSE. */
+#define SUPPORTED_LPAGES                        0x00
+#define BUFFER_OVERRUN_LPAGE                    0x01
+#define WRITE_ERROR_COUNTER_LPAGE               0x02
+#define READ_ERROR_COUNTER_LPAGE                0x03
+#define READ_REVERSE_ERROR_COUNTER_LPAGE        0x04
+#define VERIFY_ERROR_COUNTER_LPAGE              0x05
+#define NON_MEDIUM_ERROR_LPAGE                  0x06
+#define LAST_N_ERROR_LPAGE                      0x07
+#define FORMAT_STATUS_LPAGE                     0x08
+#define TEMPERATURE_LPAGE                       0x0d
+#define STARTSTOP_CYCLE_COUNTER_LPAGE           0x0e
+#define APPLICATION_CLIENT_LPAGE                0x0f
+#define SELFTEST_RESULTS_LPAGE                  0x10
+#define IE_LPAGE                                0x2f
+
+/* Seagate vendor specific log pages. */
+#define SEAGATE_CACHE_LPAGE                     0x37
+#define SEAGATE_FACTORY_LPAGE                   0x3e
+
+/* Log page response lengths */
+#define LOG_RESP_SELF_TEST_LEN 0x194
+
+/* See the SSC-2 document at www.t10.org . Earler note: From IBM 
+Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */
+#define TAPE_ALERTS_LPAGE                        0x2e
+
+/* ANSI SCSI-3 Mode Pages */
+#define VENDOR_UNIQUE_PAGE                       0x00
+#define READ_WRITE_ERROR_RECOVERY_PAGE           0x01
+#define DISCONNECT_RECONNECT_PAGE                0x02
+#define FORMAT_DEVICE_PAGE                       0x03
+#define RIGID_DISK_DRIVE_GEOMETRY_PAGE           0x04
+#define FLEXIBLE_DISK_PAGE                       0x05
+#define VERIFY_ERROR_RECOVERY_PAGE               0x07
+#define CACHING_PAGE                             0x08
+#define PERIPHERAL_DEVICE_PAGE                   0x09
+#define XOR_CONTROL_MODE_PAGE                    0x10
+#define CONTROL_MODE_PAGE                        0x0a
+#define MEDIUM_TYPES_SUPPORTED_PAGE              0x0b
+#define NOTCH_PAGE                               0x0c
+#define CD_DEVICE_PAGE                           0x0d
+#define CD_AUDIO_CONTROL_PAGE                    0x0e
+#define DATA_COMPRESSION_PAGE                    0x0f
+#define ENCLOSURE_SERVICES_MANAGEMENT_PAGE       0x14
+#define PROTOCOL_SPECIFIC_LUN_PAGE               0x18
+#define PROTOCOL_SPECIFIC_PORT_PAGE              0x19
+#define POWER_CONDITION_PAGE                     0x1a
+#define INFORMATIONAL_EXCEPTIONS_CONTROL_PAGE    0x1c
+#define FAULT_FAILURE_REPORTING_PAGE             0x1c
+
+#define ALL_MODE_PAGES                           0x3f
+
+/* Mode page control field */
+#define MPAGE_CONTROL_CURRENT               0
+#define MPAGE_CONTROL_CHANGEABLE            1
+#define MPAGE_CONTROL_DEFAULT               2
+#define MPAGE_CONTROL_SAVED                 3
+
+/* defines for useful SCSI Status codes */
+#define SCSI_STATUS_CHECK_CONDITION     0x2
+
+/* defines for useful Sense Key codes */
+#define SCSI_SK_NOT_READY               0x2
+#define SCSI_SK_MEDIUM_ERROR            0x3
+#define SCSI_SK_HARDWARE_ERROR          0x4
+#define SCSI_SK_ILLEGAL_REQUEST         0x5
+#define SCSI_SK_UNIT_ATTENTION          0x6
+
+/* defines for useful Additional Sense Codes (ASCs) */
+#define SCSI_ASC_NOT_READY              0x4     /* more info in ASCQ code */
+#define SCSI_ASC_NO_MEDIUM              0x3a    /* more info in ASCQ code */
+#define SCSI_ASC_UNKNOWN_OPCODE         0x20
+#define SCSI_ASC_UNKNOWN_FIELD          0x24
+#define SCSI_ASC_UNKNOWN_PARAM          0x26
+#define SCSI_ASC_WARNING                0xb
+#define SCSI_ASC_IMPENDING_FAILURE      0x5d
+
+/* Simplified error code (negative values as per errno) */
+#define SIMPLE_NO_ERROR                 0
+#define SIMPLE_ERR_NOT_READY            1
+#define SIMPLE_ERR_BAD_OPCODE           2
+#define SIMPLE_ERR_BAD_FIELD            3       /* in cbd */
+#define SIMPLE_ERR_BAD_PARAM            4       /* in data */
+#define SIMPLE_ERR_BAD_RESP             5       /* response fails sanity */
+#define SIMPLE_ERR_NO_MEDIUM            6       /* no medium present */
+#define SIMPLE_ERR_BECOMING_READY       7       /* device will be ready soon */
+#define SIMPLE_ERR_TRY_AGAIN            8       /* some warning, try again */
+#define SIMPLE_ERR_MEDIUM_HARDWARE      9       /* medium or hardware error */
+
+
+/* defines for functioncode parameter in SENDDIAGNOSTIC function */
+#define SCSI_DIAG_NO_SELF_TEST          0x00
+#define SCSI_DIAG_DEF_SELF_TEST         0xff
+#define SCSI_DIAG_BG_SHORT_SELF_TEST    0x01
+#define SCSI_DIAG_BG_EXTENDED_SELF_TEST 0x02
+#define SCSI_DIAG_FG_SHORT_SELF_TEST    0x05
+#define SCSI_DIAG_FG_EXTENDED_SELF_TEST 0x06
+#define SCSI_DIAG_ABORT_SELF_TEST       0x04
+
+
+/* SCSI command timeout values (units are seconds) */
+#define SCSI_TIMEOUT_DEFAULT    6               /* 6 seconds should be ample */
+#define SCSI_TIMEOUT_SELF_TEST  (5 * 60 * 60)   /* allow max 5 hours for */
+                                            /* extended foreground self test */
+
+
+
+#define LOGPAGEHDRSIZE  4
+
+void scsi_do_sense_disect(const struct scsi_cmnd_io * in,
+                          struct scsi_sense_disect * out);
+
+const char * scsiErrString(int scsiErr);
+
+/* STANDARD SCSI Commands  */
+int scsiTestUnitReady(int device);
+
+int scsiStdInquiry(int device, UINT8 *pBuf, int bufLen);
+
+int scsiInquiryVpd(int device, int vpd_page, UINT8 *pBuf, int bufLen);
+
+int scsiLogSense(int device, int pagenum, UINT8 *pBuf, int bufLen,
+                 int known_resp_len);
+
+int scsiModeSense(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen);
+
+int scsiModeSelect(int device, int sp, UINT8 *pBuf, int bufLen);
+
+int scsiModeSense10(int device, int pagenum, int pc, UINT8 *pBuf, int bufLen);
+
+int scsiModeSelect10(int 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 scsiSendDiagnostic(int device, int functioncode, UINT8 *pBuf, int bufLen);
+
+int scsiReceiveDiagnostic(int device, int pcv, int pagenum, UINT8 *pBuf,
+                      int bufLen);
+
+int scsiReadDefect10(int 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,
+                UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp);
+
+int scsiFetchIECmpage(int 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,
+                            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 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);
+
+/* 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 scsiSmartIBMOfflineTest(int 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);
+
+const char * scsiTapeAlertsTapeDevice(unsigned short code);
+const char * scsiTapeAlertsChangerDevice(unsigned short code);
+
+const char * scsi_get_opcode_name(UINT8 opcode);
+void dStrHex(const char* str, int len, int no_ascii);
+
+/* SCSI command transmission interface function declaration. Its
+ * definition is target OS specific (see os_<OS>.c file).
+ * Returns 0 if SCSI command successfully launched and response
+ * received. Even when 0 is returned the caller should check
+ * scsi_cmnd_io::scsi_status for SCSI defined errors and warnings
+ * (e.g. CHECK CONDITION). If the SCSI command could not be issued
+ * (e.g. device not present or 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);
+
+
+/* This is Linux specific and will be generalized later. */
+int isLinuxLibAta(unsigned char * vpd_di_buff, int len);
+
+#endif
+
diff --git a/scsiprint.c b/scsiprint.c
new file mode 100644 (file)
index 0000000..0b7c70b
--- /dev/null
@@ -0,0 +1,1233 @@
+/*
+ * scsiprint.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * Additional SCSI work:
+ * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "config.h"
+#include "int64.h"
+#include "extern.h"
+#include "scsicmds.h"
+#include "scsiprint.h"
+#include "smartctl.h"
+#include "utility.h"
+
+#define GBUF_SIZE 65535
+
+const char* scsiprint_c_cvsid="$Id: scsiprint.c,v 1.107 2006/04/12 16:18:57 ballen4705 Exp $"
+CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
+
+// control block which points to external global control variables
+extern smartmonctrl *con;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+UINT8 gBuf[GBUF_SIZE];
+#define LOG_RESP_LEN 252
+#define LOG_RESP_LONG_LEN 8192
+#define LOG_RESP_TAPE_ALERT_LEN 0x144
+
+/* Log pages supported */
+static int gSmartLPage = 0;     /* Informational Exceptions log page */
+static int gTempLPage = 0;
+static int gSelfTestLPage = 0;
+static int gStartStopLPage = 0;
+static int gReadECounterLPage = 0;
+static int gWriteECounterLPage = 0;
+static int gVerifyECounterLPage = 0;
+static int gNonMediumELPage = 0;
+static int gLastNErrorLPage = 0;
+static int gTapeAlertsLPage = 0;
+static int gSeagateCacheLPage = 0;
+static int gSeagateFactoryLPage = 0;
+
+/* Mode pages supported */
+static int gIecMPage = 1;     /* N.B. assume it until we know otherwise */
+
+/* Remember last successful mode sense/select command */
+static int modese_len = 0;
+
+// Compares failure type to policy in effect, and either exits or
+// simply returns to the calling routine.
+extern void failuretest(int type, int returnvalue);
+
+static void scsiGetSupportedLogPages(int device)
+{
+    int i, err;
+
+    if ((err = scsiLogSense(device, SUPPORTED_LPAGES, gBuf, 
+                            LOG_RESP_LEN, 0))) {
+        if (con->reportscsiioctl > 0)
+            pout("Log Sense for supported pages failed [%s]\n", 
+                 scsiErrString(err)); 
+        return;
+    } 
+
+    for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
+        switch (gBuf[i])
+        {
+            case READ_ERROR_COUNTER_LPAGE:
+                gReadECounterLPage = 1;
+                break;
+            case WRITE_ERROR_COUNTER_LPAGE:
+                gWriteECounterLPage = 1;
+                break;
+            case VERIFY_ERROR_COUNTER_LPAGE:
+                gVerifyECounterLPage = 1;
+                break;
+            case LAST_N_ERROR_LPAGE:
+                gLastNErrorLPage = 1;
+                break;
+            case NON_MEDIUM_ERROR_LPAGE:
+                gNonMediumELPage = 1;
+                break;
+            case TEMPERATURE_LPAGE:
+                gTempLPage = 1;
+                break;
+            case STARTSTOP_CYCLE_COUNTER_LPAGE:
+                gStartStopLPage = 1;
+                break;
+            case SELFTEST_RESULTS_LPAGE:
+                gSelfTestLPage = 1;
+                break;
+            case IE_LPAGE:
+                gSmartLPage = 1;
+                break;
+            case TAPE_ALERTS_LPAGE:
+                gTapeAlertsLPage = 1;
+                break;
+            case SEAGATE_CACHE_LPAGE:
+                gSeagateCacheLPage = 1;
+                break;
+            case SEAGATE_FACTORY_LPAGE:
+                gSeagateFactoryLPage = 1;
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+/* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
+   (or at least something to report). */
+static int scsiGetSmartData(int device, int attribs)
+{
+    UINT8 asc;
+    UINT8 ascq;
+    UINT8 currenttemp = 0;
+    UINT8 triptemp = 0;
+    const char * cp;
+    int err = 0;
+
+    PRINT_ON(con);
+    if (scsiCheckIE(device, gSmartLPage, gTempLPage, &asc, &ascq,
+                    &currenttemp, &triptemp)) {
+        /* error message already announced */
+        PRINT_OFF(con);
+        return -1;
+    }
+    PRINT_OFF(con);
+    cp = scsiGetIEString(asc, ascq);
+    if (cp) {
+        err = -2;
+        PRINT_ON(con);
+        pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq); 
+        PRINT_OFF(con);
+    } else if (gIecMPage)
+        pout("SMART Health Status: OK\n");
+
+    if (attribs && !gTempLPage) {
+        if (currenttemp || triptemp)
+            pout("\n");
+        if (currenttemp) {
+            if (255 != currenttemp)
+                pout("Current Drive Temperature:     %d C\n", currenttemp);
+            else
+                pout("Current Drive Temperature:     <not available>\n");
+        }
+        if (triptemp)
+            pout("Drive Trip Temperature:        %d C\n", triptemp);
+    }
+    return err;
+}
+
+
+// Returns number of logged errors or zero if none or -1 if fetching
+// TapeAlerts fails
+static char *severities = "CWI";
+
+static int scsiGetTapeAlertsData(int device, int peripheral_type)
+{
+    unsigned short pagelength;
+    unsigned short parametercode;
+    int i, err;
+    char *s;
+    const char *ts;
+    int failures = 0;
+
+    PRINT_ON(con);
+    if ((err = scsiLogSense(device, TAPE_ALERTS_LPAGE, gBuf, 
+                        LOG_RESP_TAPE_ALERT_LEN, LOG_RESP_TAPE_ALERT_LEN))) {
+        pout("scsiGetTapesAlertData Failed [%s]\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return -1;
+    }
+    if (gBuf[0] != 0x2e) {
+        pout("TapeAlerts Log Sense Failed\n");
+        PRINT_OFF(con);
+        return -1;
+    }
+    pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3];
+
+    for (s=severities; *s; s++) {
+        for (i = 4; i < pagelength; i += 5) {
+            parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1];
+
+            if (gBuf[i + 4]) {
+                ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ?
+                    scsiTapeAlertsChangerDevice(parametercode) :
+                    scsiTapeAlertsTapeDevice(parametercode);
+                if (*ts == *s) {
+                    if (!failures)
+                        pout("TapeAlert Errors (C=Critical, W=Warning, I=Informational):\n");
+                    pout("[0x%02x] %s\n", parametercode, ts);
+                    failures += 1; 
+                }
+            }
+        }
+    }
+    PRINT_OFF(con);
+
+    if (! failures)
+        pout("TapeAlert: OK\n");
+
+    return failures;
+}
+
+static void scsiGetStartStopData(int device)
+{
+    UINT32 currentStartStop;
+    UINT32 recommendedStartStop; 
+    int err, len, k;
+    char str[6];
+
+    if ((err = scsiLogSense(device, STARTSTOP_CYCLE_COUNTER_LPAGE, gBuf,
+                            LOG_RESP_LEN, 0))) {
+        PRINT_ON(con);
+        pout("scsiGetStartStopData Failed [%s]\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return;
+    }
+    if (gBuf[0] != STARTSTOP_CYCLE_COUNTER_LPAGE) {
+        PRINT_ON(con);
+        pout("StartStop Log Sense Failed, page mismatch\n");
+        PRINT_OFF(con);
+        return;
+    }
+    len = ((gBuf[2] << 8) | gBuf[3]) + 4;
+    if (len > 13) {
+        for (k = 0; k < 2; ++k)
+            str[k] = gBuf[12 + k];
+        str[k] = '\0';
+        pout("Manufactured in week %s of year ", str);
+        for (k = 0; k < 4; ++k)
+            str[k] = gBuf[8 + k];
+        str[k] = '\0';
+        pout("%s\n", str);
+    }
+    if (len > 39) {
+        recommendedStartStop = (gBuf[28] << 24) | (gBuf[29] << 16) |
+                               (gBuf[30] << 8) | gBuf[31];
+        currentStartStop = (gBuf[36] << 24) | (gBuf[37] << 16) |
+                           (gBuf[38] << 8) | gBuf[39];
+        pout("Current start stop count:      %u times\n", currentStartStop);
+       if (0xffffffff != recommendedStartStop)
+            pout("Recommended maximum start stop count:  %u times\n", 
+                 recommendedStartStop);
+    }
+} 
+
+static void scsiPrintGrownDefectListLen(int device)
+{
+    int err, dl_format, dl_len, div;
+
+    memset(gBuf, 0, 4);
+    if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */,
+                                4 /* bytes from index */, gBuf, 4))) {
+        if (con->reportscsiioctl > 0) {
+            PRINT_ON(con);
+            pout("Read defect list (10) Failed: %s\n", scsiErrString(err));
+            PRINT_OFF(con);
+        }
+        return;
+    }
+    if (0x8 != (gBuf[1] & 0x18)) {
+        PRINT_ON(con);
+        pout("Read defect list: asked for grown list but didn't get it\n");
+        PRINT_OFF(con);
+        return;
+    }
+    div = 0;
+    dl_format = (gBuf[1] & 0x7);
+    switch (dl_format) {
+        case 0:     /* short block */
+            div = 4;
+            break;
+        case 3:     /* long block */
+        case 4:     /* bytes from index */
+        case 5:     /* physical sector */
+            div = 8;
+            break;
+        default:
+            PRINT_ON(con);
+            pout("defect list format %d unknown\n", dl_format);
+            PRINT_OFF(con);
+            break;
+    }
+    dl_len = (gBuf[2] << 8) + gBuf[3];
+    if (0 == dl_len)
+        pout("Elements in grown defect list: 0\n");
+    else {
+        if (0 == div)
+            pout("Grown defect list length=%d bytes [unknown "
+                 "number of elements]\n", dl_len);
+        else
+            pout("Elements in grown defect list: %d\n", dl_len / div);
+    }
+}
+
+static void scsiPrintSeagateCacheLPage(int device)
+{
+    int k, j, num, pl, pc, err, len;
+    unsigned char * ucp;
+    unsigned char * xp;
+    uint64_t ull;
+
+    if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, gBuf,
+                            LOG_RESP_LEN, 0))) {
+        PRINT_ON(con);
+        pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return;
+    }
+    if (gBuf[0] != SEAGATE_CACHE_LPAGE) {
+        PRINT_ON(con);
+        pout("Seagate Cache Log Sense Failed, page mismatch\n");
+        PRINT_OFF(con);
+        return;
+    }
+    len = ((gBuf[2] << 8) | gBuf[3]) + 4;
+    num = len - 4;
+    ucp = &gBuf[0] + 4;
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        switch (pc) {
+        case 0: case 1: case 2: case 3: case 4:
+            break;
+        default: 
+            if (con->reportscsiioctl > 0) {
+                PRINT_ON(con);
+                pout("Vendor (Seagate) cache lpage has unexpected parameter"
+                     ", skip\n");
+                PRINT_OFF(con);
+            }
+            return;
+        }
+        num -= pl;
+        ucp += pl;
+    }
+    pout("Vendor (Seagate) cache information\n");
+    num = len - 4;
+    ucp = &gBuf[0] + 4;
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        switch (pc) {
+        case 0: pout("  Blocks sent to initiator"); break;
+        case 1: pout("  Blocks received from initiator"); break;
+        case 2: pout("  Blocks read from cache and sent to initiator"); break;
+        case 3: pout("  Number of read and write commands whose size "
+                       "<= segment size"); break;
+        case 4: pout("  Number of read and write commands whose size "
+                       "> segment size"); break;
+        default: pout("  Unknown Seagate parameter code [0x%x]", pc); break;
+        }
+        k = pl - 4;
+        xp = ucp + 4;
+        if (k > (int)sizeof(ull)) {
+            xp += (k - (int)sizeof(ull));
+            k = (int)sizeof(ull);
+        }
+        ull = 0;
+        for (j = 0; j < k; ++j) {
+            if (j > 0)
+                ull <<= 8;
+            ull |= xp[j];
+        }
+        pout(" = %"PRIu64"\n", ull);
+        num -= pl;
+        ucp += pl;
+    }
+}
+
+static void scsiPrintSeagateFactoryLPage(int device)
+{
+    int k, j, num, pl, pc, len, err, good, bad;
+    unsigned char * ucp;
+    unsigned char * xp;
+    uint64_t ull;
+
+    if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, gBuf,
+                            LOG_RESP_LEN, 0))) {
+        PRINT_ON(con);
+        pout("scsiPrintSeagateFactoryLPage Failed [%s]\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return;
+    }
+    if (gBuf[0] != SEAGATE_FACTORY_LPAGE) {
+        PRINT_ON(con);
+        pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n");
+        PRINT_OFF(con);
+        return;
+    }
+    len = ((gBuf[2] << 8) | gBuf[3]) + 4;
+    num = len - 4;
+    ucp = &gBuf[0] + 4;
+    good = 0;
+    bad = 0;
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        switch (pc) {
+        case 0: case 8:
+            ++good;
+            break;
+        default: 
+            ++bad;
+            break;
+        }
+        num -= pl;
+        ucp += pl;
+    }
+    if ((good < 2) || (bad > 4)) {  /* heuristic */
+        if (con->reportscsiioctl > 0) {
+            PRINT_ON(con);
+            pout("\nVendor (Seagate/Hitachi) factory lpage has too many "
+                 "unexpected parameters, skip\n");
+            PRINT_OFF(con);
+        }
+        return;
+    }
+    pout("Vendor (Seagate/Hitachi) factory information\n");
+    num = len - 4;
+    ucp = &gBuf[0] + 4;
+    while (num > 3) {
+        pc = (ucp[0] << 8) | ucp[1];
+        pl = ucp[3] + 4;
+        good = 0;
+        switch (pc) {
+        case 0: pout("  number of hours powered up");
+            good = 1;
+            break;
+        case 8: pout("  number of minutes until next internal SMART test");
+            good = 1;
+            break;
+        default:
+            if (con->reportscsiioctl > 0) {
+                PRINT_ON(con);
+                pout("Vendor (Seagate/Hitachi) factory lpage: "
+                     "unknown parameter code [0x%x]\n", pc);
+                PRINT_OFF(con);
+            }
+            break;
+        }
+        if (good) {
+            k = pl - 4;
+            xp = ucp + 4;
+            if (k > (int)sizeof(ull)) {
+                xp += (k - (int)sizeof(ull));
+                k = (int)sizeof(ull);
+            }
+            ull = 0;
+            for (j = 0; j < k; ++j) {
+                if (j > 0)
+                    ull <<= 8;
+                ull |= xp[j];
+            }
+            if (0 == pc)
+                pout(" = %.2f\n", uint64_to_double(ull) / 60.0 );
+            else
+                pout(" = %"PRIu64"\n", ull);
+        }
+        num -= pl;
+        ucp += pl;
+    }
+}
+
+static void scsiPrintErrorCounterLog(int device)
+{
+    struct scsiErrorCounter errCounterArr[3];
+    struct scsiErrorCounter * ecp;
+    struct scsiNonMediumError nme;
+    int found[3] = {0, 0, 0};
+    const char * pageNames[3] = {"read:   ", "write:  ", "verify: "};
+    int k;
+    double processed_gb;
+
+    if (gReadECounterLPage && (0 == scsiLogSense(device,
+                READ_ERROR_COUNTER_LPAGE, gBuf, LOG_RESP_LEN, 0))) {
+        scsiDecodeErrCounterPage(gBuf, &errCounterArr[0]);
+        found[0] = 1;
+    }
+    if (gWriteECounterLPage && (0 == scsiLogSense(device,
+                WRITE_ERROR_COUNTER_LPAGE, gBuf, LOG_RESP_LEN, 0))) {
+        scsiDecodeErrCounterPage(gBuf, &errCounterArr[1]);
+        found[1] = 1;
+    }
+    if (gVerifyECounterLPage && (0 == scsiLogSense(device,
+                VERIFY_ERROR_COUNTER_LPAGE, gBuf, LOG_RESP_LEN, 0))) {
+        scsiDecodeErrCounterPage(gBuf, &errCounterArr[2]);
+        ecp = &errCounterArr[2];
+        for (k = 0; k < 7; ++k) {
+            if (ecp->gotPC[k] && ecp->counter[k]) {
+                found[2] = 1;
+                break;
+            }
+        }
+    }
+    if (found[0] || found[1] || found[2]) {
+        pout("\nError counter log:\n");
+        pout("           Errors Corrected by           Total   "
+             "Correction     Gigabytes    Total\n");
+        pout("               ECC          rereads/    errors   "
+             "algorithm      processed    uncorrected\n");
+        pout("           fast | delayed   rewrites  corrected  "
+             "invocations   [10^9 bytes]  errors\n");
+        for (k = 0; k < 3; ++k) {
+            if (! found[k])
+                continue;
+            ecp = &errCounterArr[k];
+            pout("%s%8"PRIu64" %8"PRIu64"  %8"PRIu64"  %8"PRIu64"   %8"PRIu64, 
+                 pageNames[k], ecp->counter[0], ecp->counter[1], 
+                 ecp->counter[2], ecp->counter[3], ecp->counter[4]);
+            processed_gb = uint64_to_double(ecp->counter[5]) / 1000000000.0;
+            pout("   %12.3f    %8"PRIu64"\n", processed_gb, ecp->counter[6]);
+        }
+    }
+    else 
+        pout("\nError Counter logging not supported\n");
+    if (gNonMediumELPage && (0 == scsiLogSense(device,
+                NON_MEDIUM_ERROR_LPAGE, gBuf, LOG_RESP_LEN, 0))) {
+        scsiDecodeNonMediumErrPage(gBuf, &nme);
+        if (nme.gotPC0)
+            pout("\nNon-medium error count: %8"PRIu64"\n", nme.counterPC0);
+        if (nme.gotTFE_H)
+            pout("Track following error count [Hitachi]: %8"PRIu64"\n",
+                 nme.counterTFE_H);
+        if (nme.gotPE_H)
+            pout("Positioning error count [Hitachi]: %8"PRIu64"\n",
+                 nme.counterPE_H);
+    }
+    if (gLastNErrorLPage && (0 == scsiLogSense(device,
+                LAST_N_ERROR_LPAGE, gBuf, LOG_RESP_LONG_LEN, 0))) {
+        unsigned char * ucp;
+        int num, k, pc, pl;
+
+        num = (gBuf[2] << 8) + gBuf[3] + 4;
+        num = (num < LOG_RESP_LONG_LEN) ? num : LOG_RESP_LONG_LEN;
+        ucp = gBuf + 4;
+        num -= 4;
+        if (num < 4)
+            pout("\nNo error events logged\n");
+        else {
+            pout("\nLast n error events log page\n");
+            for (k = num; k > 0; k -= pl, ucp += pl) {
+                if (k < 3) {
+                    pout("  <<short Last n error events log page>>\n");
+                    break;
+                }
+                pl = ucp[3] + 4;
+                pc = (ucp[0] << 8) + ucp[1];
+                if (pl > 4) {
+                    if ((ucp[2] & 0x1) && (ucp[2] & 0x2)) {
+                        pout("  Error event %d:\n", pc);
+                        pout("    [binary]:\n");
+                        dStrHex((const char *)ucp + 4, pl - 4, 1);
+                    } else if (ucp[2] & 0x1) {
+                        pout("  Error event %d:\n", pc);
+                        pout("    %.*s\n", pl - 4, (const char *)(ucp + 4));
+                    } else {
+                        if (con->reportscsiioctl > 0) {
+                            pout("  Error event %d:\n", pc);
+                            pout("    [data counter??]:\n");
+                            dStrHex((const char *)ucp + 4, pl - 4, 1);
+                       }
+                    }
+                }
+            }
+        }
+    }
+}
+
+static const char * self_test_code[] = {
+        "Default         ", 
+        "Background short", 
+        "Background long ", 
+        "Reserved(3)     ",
+        "Abort background", 
+        "Foreground short", 
+        "Foreground long ",
+        "Reserved(7)     "
+};
+
+static const char * self_test_result[] = {
+        "Completed                ",
+        "Interrupted ('-X' switch)",
+        "Interrupted (bus reset ?)",
+        "Unknown error, incomplete",
+        "Completed, segment failed",
+        "Failed in first segment  ",
+        "Failed in second segment ",
+        "Failed in segment -->    ",
+        "Reserved(8)              ", 
+        "Reserved(9)              ", 
+        "Reserved(10)             ", 
+        "Reserved(11)             ", 
+        "Reserved(12)             ", 
+        "Reserved(13)             ", 
+        "Reserved(14)             ",
+        "Self test in progress ..."
+};
+
+// See SCSI Primary Commands - 3 (SPC-3) rev 23 (draft) section 7.2.10 .
+// Returns 0 if ok else FAIL* bitmask. Note that if any of the most recent
+// 20 self tests fail (result code 3 to 7 inclusive) then FAILLOG and/or
+// FAILSMART is returned.
+static int scsiPrintSelfTest(int device)
+{
+    int num, k, n, res, err, durationSec;
+    int noheader = 1;
+    int retval = 0;
+    UINT8 * ucp;
+    uint64_t ull=0;
+
+    if ((err = scsiLogSense(device, SELFTEST_RESULTS_LPAGE, gBuf, 
+                            LOG_RESP_SELF_TEST_LEN, 0))) {
+        PRINT_ON(con);
+        pout("scsiPrintSelfTest Failed [%s]\n", scsiErrString(err));
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    if (gBuf[0] != SELFTEST_RESULTS_LPAGE) {
+        PRINT_ON(con);
+        pout("Self-test Log Sense Failed, page mismatch\n");
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    // compute page length
+    num = (gBuf[2] << 8) + gBuf[3];
+    // Log sense page length 0x190 bytes
+    if (num != 0x190) {
+        PRINT_ON(con);
+        pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num);
+        PRINT_OFF(con);
+        return FAILSMART;
+    }
+    // loop through the twenty possible entries
+    for (k = 0, ucp = gBuf + 4; k < 20; ++k, ucp += 20 ) {
+        int i;
+
+        // timestamp in power-on hours (or zero if test in progress)
+        n = (ucp[6] << 8) | ucp[7];
+
+        // The spec says "all 20 bytes will be zero if no test" but
+        // DG has found otherwise.  So this is a heuristic.
+        if ((0 == n) && (0 == ucp[4]))
+            break;
+
+        // only print header if needed
+        if (noheader) {
+            pout("\nSMART Self-test log\n");
+            pout("Num  Test              Status                 segment  "
+                   "LifeTime  LBA_first_err [SK ASC ASQ]\n");
+            pout("     Description                              number   "
+                   "(hours)\n");
+            noheader=0;
+        }
+
+        // print parameter code (test number) & self-test code text
+        pout("#%2d  %s", (ucp[0] << 8) | ucp[1], 
+            self_test_code[(ucp[4] >> 5) & 0x7]);
+
+        // check the self-test result nibble, using the self-test results
+        // field table from T10/1416-D (SPC-3) Rev. 23, section 7.2.10:
+        switch ((res = ucp[4] & 0xf)) {
+        case 0x3:
+            // an unknown error occurred while the device server
+            // was processing the self-test and the device server
+            // was unable to complete the self-test
+            retval|=FAILSMART;
+            break;
+        case 0x4:
+            // the self-test completed with a failure in a test
+            // segment, and the test segment that failed is not
+            // known
+            retval|=FAILLOG;
+            break;
+        case 0x5:
+            // the first segment of the self-test failed
+            retval|=FAILLOG;
+            break;
+        case 0x6:
+            // the second segment of the self-test failed
+            retval|=FAILLOG;
+            break;
+        case 0x7:
+            // another segment of the self-test failed and which
+            // test is indicated by the contents of the SELF-TEST
+            // NUMBER field
+            retval|=FAILLOG;
+            break;
+        default:
+            break;
+        }
+        pout("  %s", self_test_result[res]);
+
+        // self-test number identifies test that failed and consists
+        // of either the number of the segment that failed during
+        // the test, or the number of the test that failed and the
+        // number of the segment in which the test was run, using a
+        // vendor-specific method of putting both numbers into a
+        // single byte.
+        if (ucp[5])
+            pout(" %3d",  (int)ucp[5]);
+        else
+            pout("   -");
+
+        // print time that the self-test was completed
+        if (n==0 && res==0xf)
+        // self-test in progress
+            pout("     NOW");
+        else   
+            pout("   %5d", n);
+          
+        // construct 8-byte integer address of first failure
+        for (i = 0; i < 8; i++) {
+            ull <<= 8;
+            ull |= ucp[i+8];
+        }
+        // print Address of First Failure, if sensible
+        if ((~(uint64_t)0 != ull) && (res > 0) && (res < 0xf)) {
+            char buff[32];
+
+            // was hex but change to decimal to conform with ATA
+            snprintf(buff, sizeof(buff), "%"PRIu64, ull);
+            // snprintf(buff, sizeof(buff), "0x%"PRIx64, ull);
+            pout("%18s", buff);
+        } else
+            pout("                 -");
+
+        // if sense key nonzero, then print it, along with
+        // additional sense code and additional sense code qualifier
+        if (ucp[16] & 0xf)
+            pout(" [0x%x 0x%x 0x%x]\n", ucp[16] & 0xf, ucp[17], ucp[18]);
+        else
+            pout(" [-   -    -]\n");
+    }
+
+    // if header never printed, then there was no output
+    if (noheader)
+        pout("No self-tests have been logged\n");
+    else
+        pout("\n");
+    if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec, 
+                        modese_len)) && (durationSec > 0)) {
+        pout("Long (extended) Self Test duration: %d seconds "
+             "[%.1f minutes]\n", durationSec, durationSec / 60.0);
+    }
+    return retval;
+}
+
+static const char * peripheral_dt_arr[] = {
+        "disk",
+        "tape",
+        "printer",
+        "processor",
+        "optical disk(4)",
+        "CD/DVD",
+        "scanner",
+        "optical disk(7)",
+        "medium changer",
+        "communications",
+        "graphics(10)",
+        "graphics(11)",
+        "storage array",
+        "enclosure",
+        "simplified disk",
+        "optical card reader"
+};
+
+static const char * transport_proto_arr[] = {
+        "Fibre channel (FCP-2)",
+        "Parallel SCSI (SPI-4)",
+        "SSA",
+        "IEEE 1394 (SBP-2)",
+        "RDMA (SRP)",
+        "iSCSI",
+        "SAS",
+        "ADT",
+        "0x8",
+        "0x9",
+        "0xa",
+        "0xb",
+        "0xc",
+        "0xd",
+        "0xe",
+        "0xf"
+};
+
+/* Returns 0 on success, 1 on general error and 2 for early, clean exit */
+static int scsiGetDriveInfo(int device, UINT8 * peripheral_type, int all)
+{
+    char manufacturer[9];
+    char product[17];
+    char revision[5];
+    char timedatetz[DATEANDEPOCHLEN];
+    struct scsi_iec_mode_page iec;
+    int err, iec_err, len, req_len, avail_len, val;
+    int is_tape = 0;
+    int peri_dt = 0;
+    int returnval=0;
+        
+    memset(gBuf, 0, 96);
+    req_len = 36;
+    if ((err = scsiStdInquiry(device, gBuf, req_len))) {
+        PRINT_ON(con);
+        pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
+        pout("Retrying with a 64 byte Standard Inquiry\n");
+        PRINT_OFF(con);
+        /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
+        req_len = 64;
+        if ((err = scsiStdInquiry(device, gBuf, req_len))) {
+            PRINT_ON(con);
+            pout("Standard Inquiry (64 bytes) failed [%s]\n",
+                 scsiErrString(err));
+            PRINT_OFF(con);
+            return 1;
+        }
+    }
+    avail_len = gBuf[4] + 5;
+    len = (avail_len < req_len) ? avail_len : req_len;
+    peri_dt = gBuf[0] & 0x1f;
+    if (peripheral_type)
+        *peripheral_type = peri_dt;
+    if (! all)
+        return 0;
+
+    if (len < 36) {
+        PRINT_ON(con);
+        pout("Short INQUIRY response, skip product id\n");
+        PRINT_OFF(con);
+        return 1;
+    }
+    memset(manufacturer, 0, sizeof(manufacturer));
+    strncpy(manufacturer, (char *)&gBuf[8], 8);
+    memset(product, 0, sizeof(product));
+    strncpy(product, (char *)&gBuf[16], 16);
+        
+    memset(revision, 0, sizeof(revision));
+    strncpy(revision, (char *)&gBuf[32], 4);
+    pout("Device: %s %s Version: %s\n", manufacturer, product, revision);
+
+    if (0 == strncmp(manufacturer, "3ware", 5) || 0 == strncmp(manufacturer, "AMCC", 4) ) {
+        pout("please try adding '-d 3ware,N'\n");
+        pout("you may also need to change device to /dev/twaN or /dev/tweN\n");
+        return 2;
+    } else if ((len >= 42) &&
+              (0 == strncmp((const char *)(gBuf + 36), "MVSATA", 6))) {
+        pout("please try '-d marvell'\n");
+        return 2;
+    } else if ((avail_len >= 96) && (0 == strncmp(manufacturer, "ATA", 3))) {
+        /* <<<< This is Linux specific code to detect SATA disks using a
+                SCSI-ATA command translation layer. This may be generalized
+                later when the t10.org SAT project matures. >>>> */
+        req_len = 96;
+        memset(gBuf, 0, req_len);
+        if ((err = scsiInquiryVpd(device, 0x83, gBuf, req_len))) {
+            PRINT_ON(con);
+            pout("Inquiry for VPD page 0x83 [device id] failed [%s]\n",
+                  scsiErrString(err));
+            PRINT_OFF(con);
+            return 1;
+        }
+        avail_len = ((gBuf[2] << 8) + gBuf[3]) + 4;
+        len = (avail_len < req_len) ? avail_len : req_len;
+        if (isLinuxLibAta(gBuf, len)) {
+            pout("\nIn Linux, SATA disks accessed via libata are "
+                 "only supported by smartmontools\n"
+                 "for kernel versions 2.6.15 and above. Try "
+                 "an additional '-d ata' argument.\n");
+            return 2;
+        }
+    }
+
+    /* Do this here to try and detect badly conforming devices (some USB
+       keys) that will lock up on a InquiryVpd or log sense or ... */
+    if ((iec_err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        if (SIMPLE_ERR_BAD_RESP == iec_err) {
+            pout(">> Terminate command early due to bad response to IEC "
+                 "mode page\n");
+            PRINT_OFF(con);
+            gIecMPage = 0;
+            return 1;
+        }
+    } else
+        modese_len = iec.modese_len;
+
+    if (0 == (err = scsiInquiryVpd(device, 0x80, gBuf, 64))) {
+        /* should use VPD page 0x83 and fall back to this page (0x80)
+         * if 0x83 not supported. NAA requires a lot of decoding code */
+        len = gBuf[3];
+        gBuf[4 + len] = '\0';
+        pout("Serial number: %s\n", &gBuf[4]);
+    }
+    else if (con->reportscsiioctl > 0) {
+        PRINT_ON(con);
+        if (SIMPLE_ERR_BAD_RESP == err)
+            pout("Vital Product Data (VPD) bit ignored in INQUIRY\n");
+        else
+            pout("Vital Product Data (VPD) INQUIRY failed [%d]\n", err);
+        PRINT_OFF(con);
+    }
+
+    // print SCSI peripheral device type
+    if (peri_dt < (int)(sizeof(peripheral_dt_arr) / 
+                        sizeof(peripheral_dt_arr[0])))
+        pout("Device type: %s\n", peripheral_dt_arr[peri_dt]);
+    else
+        pout("Device type: <%d>\n", peri_dt);
+
+    // See if transport protocol is known
+    val = scsiFetchTransportProtocol(device, modese_len);
+    if ((val >= 0) && (val <= 0xf))
+        pout("Transport protocol: %s\n", transport_proto_arr[val]);
+
+    // print current time and date and timezone
+    dateandtimezone(timedatetz);
+    pout("Local Time is: %s\n", timedatetz);
+
+    if ((SCSI_PT_SEQUENTIAL_ACCESS == *peripheral_type) ||
+        (SCSI_PT_MEDIUM_CHANGER == *peripheral_type))
+        is_tape = 1;
+    // See if unit accepts SCSI commmands from us
+    if ((err = scsiTestUnitReady(device))) {
+        if (SIMPLE_ERR_NOT_READY == err) {
+            PRINT_ON(con);
+            if (!is_tape)
+                pout("device is NOT READY (e.g. spun down, busy)\n");
+            else
+                pout("device is NOT READY (e.g. no tape)\n");
+            PRINT_OFF(con);
+         } else if (SIMPLE_ERR_NO_MEDIUM == err) {
+            PRINT_ON(con);
+            pout("NO MEDIUM present on device\n");
+            PRINT_OFF(con);
+         } else if (SIMPLE_ERR_BECOMING_READY == err) {
+            PRINT_ON(con);
+            pout("device becoming ready (wait)\n");
+            PRINT_OFF(con);
+        } else {
+            PRINT_ON(con);
+            pout("device Test Unit Ready  [%s]\n", scsiErrString(err));
+            PRINT_OFF(con);
+        }
+        failuretest(MANDATORY_CMD, returnval|=FAILID);
+    }
+   
+    if (iec_err) {
+        if (!is_tape) {
+            PRINT_ON(con);
+            pout("Device does not support SMART");
+            if (con->reportscsiioctl > 0)
+                pout(" [%s]\n", scsiErrString(iec_err));
+            else
+                pout("\n");
+            PRINT_OFF(con);
+        }
+        gIecMPage = 0;
+        return 0;
+    }
+
+    if (!is_tape)
+        pout("Device supports SMART and is %s\n",
+             (scsi_IsExceptionControlEnabled(&iec)) ? "Enabled" : "Disabled");
+    pout("%s\n", (scsi_IsWarningEnabled(&iec)) ? 
+                  "Temperature Warning Enabled" :
+                  "Temperature Warning Disabled or Not Supported");
+    return 0;
+}
+
+static int scsiSmartEnable(int device)
+{
+    struct scsi_iec_mode_page iec;
+    int err;
+
+    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        PRINT_ON(con);
+        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
+             scsiErrString(err));
+        PRINT_OFF(con);
+        return 1;
+    } else
+        modese_len = iec.modese_len;
+
+    if ((err = scsiSetExceptionControlAndWarning(device, 1, &iec))) {
+        PRINT_ON(con);
+        pout("unable to enable Exception control and warning [%s]\n",
+             scsiErrString(err));
+        PRINT_OFF(con);
+        return 1;
+    }
+    /* Need to refetch 'iec' since could be modified by previous call */
+    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
+             scsiErrString(err));
+        return 1;
+    } else
+        modese_len = iec.modese_len;
+
+    pout("Informational Exceptions (SMART) %s\n",
+         scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
+    pout("Temperature warning %s\n",
+         scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
+    return 0;
+}
+        
+static int scsiSmartDisable(int device)
+{
+    struct scsi_iec_mode_page iec;
+    int err;
+
+    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        PRINT_ON(con);
+        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
+             scsiErrString(err));
+        PRINT_OFF(con);
+        return 1;
+    } else
+        modese_len = iec.modese_len;
+
+    if ((err = scsiSetExceptionControlAndWarning(device, 0, &iec))) {
+        PRINT_ON(con);
+        pout("unable to disable Exception control and warning [%s]\n",
+             scsiErrString(err));
+        PRINT_OFF(con);
+        return 1;
+    }
+    /* Need to refetch 'iec' since could be modified by previous call */
+    if ((err = scsiFetchIECmpage(device, &iec, modese_len))) {
+        pout("unable to fetch IEC (SMART) mode page [%s]\n", 
+             scsiErrString(err));
+        return 1;
+    } else
+        modese_len = iec.modese_len;
+
+    pout("Informational Exceptions (SMART) %s\n",
+         scsi_IsExceptionControlEnabled(&iec) ? "enabled" : "disabled");
+    pout("Temperature warning %s\n",
+         scsi_IsWarningEnabled(&iec) ? "enabled" : "disabled");
+    return 0;
+}
+
+static void scsiPrintTemp(int device)
+{
+    UINT8 temp = 0;
+    UINT8 trip = 0;
+
+    if (scsiGetTemp(device, &temp, &trip))
+        return;
+  
+    if (temp) {
+        if (255 != temp)
+            pout("Current Drive Temperature:     %d C\n", temp);
+        else
+            pout("Current Drive Temperature:     <not available>\n");
+    }
+    if (trip)
+        pout("Drive Trip Temperature:        %d C\n", trip);
+}
+
+/* Main entry point used by smartctl command. Return 0 for success */
+int scsiPrintMain(int fd)
+{
+    int checkedSupportedLogPages = 0;
+    UINT8 peripheral_type = 0;
+    int returnval = 0;
+    int res, durationSec;
+
+    res = scsiGetDriveInfo(fd, &peripheral_type, con->driveinfo);
+    if (res) {
+        if (2 == res)
+            return 0;
+        else
+            failuretest(MANDATORY_CMD, returnval |= FAILID);
+    }
+
+    if (con->smartenable) {
+        if (scsiSmartEnable(fd))
+            failuretest(MANDATORY_CMD, returnval |= FAILSMART);
+    }
+
+    if (con->smartdisable) {
+        if (scsiSmartDisable(fd))
+            failuretest(MANDATORY_CMD,returnval |= FAILSMART);
+    }
+    
+    if (con->smartautosaveenable) {
+      if (scsiSetControlGLTSD(fd, 0, modese_len)) {
+        pout("Enable autosave (clear GLTSD bit) failed\n");
+        failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
+      }
+    }
+    
+    if (con->smartautosavedisable) {
+      if (scsiSetControlGLTSD(fd, 1, modese_len)) {
+        pout("Disable autosave (set GLTSD bit) failed\n");
+        failuretest(OPTIONAL_CMD,returnval |= FAILSMART);
+      }
+    }
+    
+    if (con->checksmart) {
+        scsiGetSupportedLogPages(fd);
+        checkedSupportedLogPages = 1;
+        if ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
+            (SCSI_PT_MEDIUM_CHANGER == peripheral_type)) { /* tape device */
+            if (gTapeAlertsLPage) {
+                if (con->driveinfo)
+                    pout("TapeAlert Supported\n");
+                if (-1 == scsiGetTapeAlertsData(fd, peripheral_type))
+                    failuretest(OPTIONAL_CMD, returnval |= FAILSMART);
+            }
+            else
+                pout("TapeAlert Not Supported\n");
+        } else { /* disk, cd/dvd, enclosure, etc */
+            if ((res = scsiGetSmartData(fd, con->smartvendorattrib))) {
+                if (-2 == res)
+                    returnval |= FAILSTATUS;
+                else
+                    returnval |= FAILSMART;
+            }
+        }
+    }   
+    if (con->smartvendorattrib) {
+        if (! checkedSupportedLogPages)
+            scsiGetSupportedLogPages(fd);
+        if (gTempLPage) {
+            if (con->checksmart)
+                pout("\n");
+            scsiPrintTemp(fd);         
+        }
+        if (gStartStopLPage)
+            scsiGetStartStopData(fd);
+        if (SCSI_PT_DIRECT_ACCESS == peripheral_type) {
+            scsiPrintGrownDefectListLen(fd);
+            if (gSeagateCacheLPage)
+                scsiPrintSeagateCacheLPage(fd);
+            if (gSeagateFactoryLPage)
+                scsiPrintSeagateFactoryLPage(fd);
+        }
+    }
+    if (con->smarterrorlog) {
+        if (! checkedSupportedLogPages)
+            scsiGetSupportedLogPages(fd);
+        scsiPrintErrorCounterLog(fd);
+        if (1 == scsiFetchControlGLTSD(fd, modese_len, 1))
+            pout("\n[GLTSD (Global Logging Target Save Disable) set. "
+                 "Enable Save with '-S on']\n");
+    }
+    if (con->smartselftestlog) {
+        if (! checkedSupportedLogPages)
+            scsiGetSupportedLogPages(fd);
+        res = 0;
+        if (gSelfTestLPage)
+            res = scsiPrintSelfTest(fd);
+        else {
+            pout("Device does not support Self Test logging\n");
+            failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
+        }
+        if (0 != res)
+            failuretest(OPTIONAL_CMD, returnval|=res);
+    }
+    if (con->smartexeoffimmediate) {
+        if (scsiSmartDefaultSelfTest(fd))
+            return returnval | FAILSMART;
+        pout("Default Self Test Successful\n");
+    }
+    if (con->smartshortcapselftest) {
+        if (scsiSmartShortCapSelfTest(fd))
+            return returnval | FAILSMART;
+        pout("Short Foreground Self Test Successful\n");
+    }
+    if (con->smartshortselftest ) { 
+        if (scsiSmartShortSelfTest(fd))
+            return returnval | FAILSMART;
+        pout("Short Background Self Test has begun\n");
+        pout("Use smartctl -X to abort test\n");
+    }
+    if (con->smartextendselftest) {
+        if (scsiSmartExtendSelfTest(fd))
+            return returnval | FAILSMART;
+        pout("Extended Background Self Test has begun\n");
+        if ((0 == scsiFetchExtendedSelfTestTime(fd, &durationSec, 
+                        modese_len)) && (durationSec > 0)) {
+            time_t t = time(NULL);
+
+            t += durationSec;
+            pout("Please wait %d minutes for test to complete.\n", 
+                 durationSec / 60);
+            pout("Estimated completion time: %s\n", ctime(&t));
+        }
+        pout("Use smartctl -X to abort test\n");        
+    }
+    if (con->smartextendcapselftest) {
+        if (scsiSmartExtendCapSelfTest(fd))
+            return returnval | FAILSMART;
+        pout("Extended Foreground Self Test Successful\n");
+    }
+    if (con->smartselftestabort) {
+        if (scsiSmartSelfTestAbort(fd))
+            return returnval | FAILSMART;
+        pout("Self Test returned without error\n");
+    }           
+    return returnval;
+}
diff --git a/scsiprint.h b/scsiprint.h
new file mode 100644 (file)
index 0000000..429223e
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * scsiprint.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * Additional SCSI work:
+ * Copyright (C) 2003-6 Douglas Gilbert <dougg@torque.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+
+/* scsismart version number */
+#ifndef SCSI_PRINT_H_
+#define SCSI_PRINT_H_
+
+#define SCSIPRINT_H_CVSID "$Id: scsiprint.h,v 1.20 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+int scsiPrintMain(int fd);
+
+#endif
diff --git a/smartctl.8.in b/smartctl.8.in
new file mode 100644 (file)
index 0000000..d1eabab
--- /dev/null
@@ -0,0 +1,1290 @@
+.ig
+ Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+
+ $Id: smartctl.8.in,v 1.78 2006/04/12 15:45:38 ballen4705 Exp $
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2, or (at your option) any later
+ version.
+ You should have received a copy of the GNU General Public License (for
+ example COPYING); if not, write to the Free Software Foundation, Inc., 675
+ Mass Ave, Cambridge, MA 02139, USA.
+
+ This code was originally developed as a Senior Thesis by Michael Cornwell
+ at the Concurrent Systems Laboratory (now part of the Storage Systems
+ Research Center), Jack Baskin School of Engineering, University of
+ California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+
+..
+.TH SMARTCTL 8 CURRENT_CVS_DATE CURRENT_CVS_VERSION CURRENT_CVS_DATE
+.SH NAME
+\fBsmartctl\fP \- Control and Monitor Utility for SMART Disks
+
+.SH SYNOPSIS
+.B smartctl [options] device
+
+.SH FULL PATH
+.B /usr/local/sbin/smartctl
+
+.SH PACKAGE VERSION
+CURRENT_CVS_VERSION released CURRENT_CVS_DATE at CURRENT_CVS_TIME
+
+.SH DESCRIPTION
+\fBsmartctl\fP controls the Self\-Monitoring, Analysis and Reporting
+Technology (SMART) system built into many ATA\-3 and later ATA, IDE and
+SCSI\-3 hard drives. The purpose of SMART is to monitor the reliability
+of the hard drive and predict drive failures, and to carry out
+different types of drive self\-tests.  This version of \fBsmartctl\fP
+is compatible with ATA/ATAPI\-7 and earlier standards (see REFERENCES
+below)
+
+\fBsmartctl\fP is a command line utility designed to perform SMART
+tasks such as printing the SMART self\-test and error logs, enabling
+and disabling SMART automatic testing, and initiating device
+self\-tests. Note: if the user issues a SMART command that is
+(apparently) not implemented by the device, \fBsmartctl\fP will print
+a warning message but issue the command anyway (see the \fB\-T,
+\-\-tolerance\fP option below).  This should not cause problems: on
+most devices, unimplemented SMART commands issued to a drive are
+ignored and/or return an error.
+
+\fBsmartctl\fP also provides support for polling TapeAlert messages
+from SCSI tape drives and changers.
+
+The user must specify the device to be controlled or interrogated as
+the final argument to \fBsmartctl\fP.  Device paths are as follows:
+.IP \fBLINUX\fP: 9
+Use the forms \fB"/dev/hd[a\-t]"\fP for IDE/ATA
+devices, and \fB"/dev/sd[a\-z]"\fP for SCSI devices. For
+SCSI Tape Drives and Changers with TapeAlert support use the devices
+\fB"/dev/nst*"\fP and \fB"/dev/sg*"\fP. 
+For SATA disks accessed with libata, use \fB"/dev/sd[a\-z]"\fP
+and append \fB"\-d ata"\fP. For disks behind 3ware controllers
+you may need \fB"/dev/sd[a\-z]"\fP or \fB"/dev/twe[0\-9]"\fP
+or \fB"/dev/twa[0\-9]"\fP: see details below.
+More general paths (such as devfs ones) may also be specified.
+.IP \fBDARWIN\fP: 9
+Use the forms \fB/dev/disk[0\-9]\fP or equivalently \fBdisk[0\-9]\fP or equivalently
+\fB/dev/rdisk[0\-9]\fP.  Long forms are also available: please use \'\-h\' to see some
+examples. Note that there is currently no Darwin SCSI support.
+.IP \fBFREEBSD\fP: 9
+Use the forms \fB"/dev/ad[0\-9]+"\fP for IDE/ATA
+devices and \fB"/dev/da[0\-9]+"\fP for SCSI devices.
+.IP \fBNETBSD/OPENBSD\fP: 9
+Use the form \fB"/dev/wd[0\-9]+c"\fP for IDE/ATA
+devices.  For SCSI disk and tape devices, use the device names
+\fB"/dev/sd[0\-9]+c"\fP and \fB"/dev/st[0\-9]+c"\fP respectively.  
+Be sure to specify the correct "whole disk" partition letter for 
+your architecture.
+.IP \fBSOLARIS\fP: 9
+Use the forms \fB"/dev/rdsk/c?t?d?s?"\fP for IDE/ATA and SCSI disk
+devices, and \fB"/dev/rmt/*"\fP for SCSI tape devices.
+.IP \fBWINDOWS\fP: 9
+Use the forms \fB"/dev/hd[a\-j]"\fP for IDE/ATA devices
+"\\\\.\\PhysicalDrive[0\-9]" on WinNT4/2000/XP,
+\fB"/dev/hd[a\-d]"\fP for standard IDE/ATA devices on Win95/98/98SE/ME,
+and \fB"/dev/scsi[0\-9][0\-f]"\fP for SCSI devices on ASPI adapter 0\-9, ID 0\-15.
+The prefix \fB"/dev/"\fP is optional.
+.IP \fBCYGWIN\fP: 9
+See "WINDOWS" above.
+.IP \fBOS/2,eComStation\fP: 9
+Use the form \fB"/dev/hd[a\-z]"\fP for IDE/ATA devices.
+.PP
+Based on the device path, \fBsmartctl\fP will guess the device type
+(ATA or SCSI).  If necessary, the \'\-d\' option can be used to over\-ride
+this guess
+
+Note that the printed output of \fBsmartctl\fP displays most numerical
+values in base 10 (decimal), but some values are displayed in base 16
+(hexidecimal).  To distinguish them, the base 16 values are always
+displayed with a leading \fB"0x"\fP, for example: "0xff". This man
+page follows the same convention.
+
+.PP
+.SH OPTIONS
+.PP
+The options are grouped below into several categories.  \fBsmartctl\fP
+will execute the corresponding commands in the order: INFORMATION,
+ENABLE/DISABLE, DISPLAY DATA, RUN/ABORT TESTS.
+
+SCSI devices only accept the options \fB\-h, \-V, \-i, \-a, \-A, \-d,
+\-s, \-S,\-H, \-t, \-C, \-l selftest, \-l error, \-r,\fP and
+\fB\-X\fP.  TapeAlert devices only accept the options \fB\-h, \-V,
+\-i, \-a, \-A, \-d, \-s, \-S, \-t, \-l selftest, \-l error, \-r,\fP
+and \fB\-H\fP.
+
+Long options  are  not  supported  on  all  systems.   Use
+.B \'smartctl \-h\'
+to see the available options.
+
+.TP
+.B SHOW INFORMATION OPTIONS:
+.TP
+.B \-h, \-\-help, \-\-usage
+Prints a usage message to STDOUT and exits.
+.TP
+.B \-V, \-\-version, \-\-copyright, \-\-license
+Prints version, copyright, license, home page and CVS\-id information
+for your copy of \fBsmartctl\fP to STDOUT and then exits.  Please
+include this information if you are reporting bugs or problems.
+.TP
+.B \-i, \-\-info
+Prints the device model number, serial number, firmware version, and
+ATA Standard version/revision information.  Says if the device
+supports SMART, and if so, whether SMART support is currently enabled
+or disabled.  If the device supports Logical Block Address mode (LBA
+mode) print current user drive capacity in bytes. (If drive is has a
+user protected area reserved, or is "clipped", this may be smaller
+than the potential maximum drive capacity.)  Indicates if the drive is
+in the smartmontools database (see \'\-v\' options below).  If so, the
+drive model family may also be printed.
+.TP
+.B \-a, \-\-all
+Prints all SMART information about the disk, or TapeAlert information
+about the tape drive or changer.  For ATA devices this is equivalent
+to
+.nf
+\'\-H \-i \-c \-A \-l error \-l selftest -l selective\'
+.fi
+and for SCSI, this is equivalent to
+.nf
+\'\-H \-i \-A \-l error \-l selftest\'.
+.fi
+Note that for ATA disks this does \fBnot\fP enable the \'\-l
+directory\' option.
+
+.TP
+.B RUN\-TIME BEHAVIOR OPTIONS:
+.TP
+.B \-q TYPE, \-\-quietmode=TYPE
+Specifies that \fBsmartctl\fP should run in one of the two quiet modes
+described here.  The valid arguments to this option are:
+
+.I errorsonly
+\- only print: For the \'\-l error\' option, if nonzero, the number
+of errors recorded in the SMART error log and the power\-on time when
+they occurred; For the \'\-l selftest\' option, errors recorded in the device
+self\-test log; For the \'\-H\' option, SMART "disk failing" status or device
+Attributes (pre\-failure or usage) which failed either now or in the
+past; For the \'\-A\' option, device Attributes (pre\-failure or usage)
+which failed either now or in the past.
+
+.I silent
+\- print no output.  The only way to learn about what was found is to
+use the exit status of \fBsmartctl\fP (see RETURN VALUES below).
+.TP
+.B \-d TYPE, \-\-device=TYPE
+Specifies the type of the device.  The valid arguments to this option
+are \fIata\fP, \fIscsi\fP, \fImarvell\fP, and \fI3ware,N\fP. If this option is not
+used then \fBsmartctl\fP will attempt to guess the device type from
+the device name.
+
+Under Linux, to look at SATA disks behind Marvell SATA controllers
+(using Marvell's \'linuxIAL\' driver rather than libata driver) use \'\-d marvell\'. Such
+controllers show up as Marvell Technology Group Ltd. SATA I or II controllers
+using lspci, or using lspci -n show a vendor ID 0x11ab and a device ID of
+either 0x5040, 0x5041, 0x5080, 0x5081, 0x6041 or 0x6081. The \'linuxIAL\' driver
+seems not (yet?) available in the Linux kernel source tree, but should be available
+from system vendors (ftp://ftp.aslab.com/ is known to provide a patch with the driver).
+
+To look at ATA disks behind 3ware SCSI RAID controllers, use syntax
+such as:
+.nf
+\fBsmartctl \-a \-d 3ware,2 /dev/sda\fP
+.fi
+.nf
+\fBsmartctl \-a \-d 3ware,0 /dev/twe0\fP
+.fi
+.nf
+\fBsmartctl \-a \-d 3ware,1 /dev/twa0\fP
+.fi
+where in the argument \fI3ware,N\fP, the integer N is the disk number
+(3ware \'port\') within the 3ware ATA RAID controller.  The allowed
+values of N are from 0 to 15 inclusive.  The first two forms, which
+refer to devices /dev/sda-z and /dev/twe0-15, may be used with 3ware
+series 6000, 7000, and 8000 series controllers that use the 3x-xxxx
+driver.  \fBNote that the /dev/sda-z form is deprecated\fP starting
+with the Linux 2.6 kernel series and may not be supported by the Linux
+kernel in the near future. The final form, which refers to devices
+/dev/twa0-15, must be used with 3ware 9000 series controllers, which
+use the 3w-9xxx driver.
+
+Note that if the special character device nodes /dev/twa? and
+/dev/twe? do not exist, or exist with the incorrect major or minor
+numbers, smartctl will recreate them on the fly.  Typically /dev/twa0
+refers to the first 9000-series controller, /dev/twa1 refers to the
+second 9000 series controller, and so on. Likewise /dev/twe0 refers to
+the first 6/7/8000-series controller, /dev/twa1 refers to the second
+6/7/8000 series controller, and so on.
+
+Note that for the 6/7/8000 controllers, \fBany\fP of the physical
+disks can be queried or examined using \fBany\fP of the 3ware's SCSI
+logical device /dev/sd?  entries.  Thus, if logical device /dev/sda is
+made up of two physical disks (3ware ports zero and one) and logical
+device /dev/sdb is made up of two other physical disks (3ware ports
+two and three) then you can examine the SMART data on \fBany\fP of the
+four physical disks using \fBeither\fP SCSI device /dev/sda \fBor\fP
+/dev/sdb.  If you need to know which logical SCSI device a particular
+physical disk (3ware port) is associated with, use the dmesg or SYSLOG
+output to show which SCSI ID corresponds to a particular 3ware unit,
+and then use the 3ware CLI or 3dm tool to determine which ports
+(physical disks) correspond to particular 3ware units.
+
+If the value of N corresponds to a port that does \fBnot\fP exist on
+the 3ware controller, or to a port that does not physically have a
+disk attached to it, the behavior of \fBsmartctl\fP depends upon the
+specific controller model, firmware, Linux kernel and platform.  In
+some cases you will get a warning message that the device does not
+exist. In other cases you will be presented with \'void\' data for a
+non\-existent device.
+
+Note that if the /dev/sd? addressing form is used, then older 3w\-xxxx
+drivers do not pass the "Enable Autosave"
+(\'\fB\-S on\fP\') and "Enable Automatic Offline" (\'\fB\-o on\fP\')
+commands to the disk, and produce these types of harmless syslog error
+messages instead: "\fB3w\-xxxx: tw_ioctl(): Passthru size (123392) too
+big\fP". This can be fixed by upgrading to version 1.02.00.037 or
+later of the 3w\-xxxx driver, or by applying a patch to older
+versions. See \fBhttp://smartmontools.sourceforge.net/\fP for
+instructions.  Alternatively, use the character device /dev/twe0-15 interface.
+
+The selective self\-test functions (\'\-t select,A\-B\') are only supported
+using the character device interface /dev/twa0\-15 and /dev/twe0\-15.
+The necessary WRITE LOG commands can not be passed through the SCSI
+interface.
+
+.B 3ware controllers are currently ONLY supported under Linux and FreeBSD.
+
+.TP
+.B \-T TYPE, \-\-tolerance=TYPE
+Specifies how tolerant \fBsmartctl\fP should be of ATA and SMART command
+failures. 
+
+The behavior of \fBsmartctl\fP depends upon whether the command is
+"\fBoptional\fP" or "\fBmandatory\fP". Here "\fBmandatory\fP" means
+"required by the ATA/ATAPI\-5 Specification if the device implements
+the SMART command set" and "\fBoptional\fP" means "not required by the
+ATA/ATAPI\-5 Specification even if the device implements the SMART
+command set."  The "\fBmandatory\fP" ATA and SMART commands are: (1)
+ATA IDENTIFY DEVICE, (2) SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE, (3)
+SMART ENABLE/DISABLE, and (4) SMART RETURN STATUS.
+
+The valid arguments to this option are:
+
+.I normal
+\- exit on failure of any \fBmandatory\fP SMART command, and ignore
+all failures of \fBoptional\fP SMART commands.  This is the default.
+Note that on some devices, issuing unimplemented optional SMART
+commands doesn\'t cause an error.  This can result in misleading
+\fBsmartctl\fP messages such as "Feature X not implemented", followed
+shortly by "Feature X: enabled".  In most such cases, contrary to the
+final message, Feature X is \fBnot\fP enabled.
+
+.I conservative
+\- exit on failure of any \fBoptional\fP SMART command.
+
+.I permissive
+\- ignore failure(s) of \fBmandatory\fP SMART commands.  This option
+may be given more than once.  Each additional use of this option will
+cause one more additional failure to be ignored.  Note that the use of
+this option can lead to messages like "Feature X not implemented",
+followed shortly by "Error: unable to enable Feature X".  In a few
+such cases, contrary to the final message, Feature X \fBis\fP enabled.
+
+.I verypermissive
+\- equivalent to giving a large number of \'\-T permissive\' options:
+ignore failures of \fBany number\fP of \fBmandatory\fP SMART commands.
+Please see the note above.
+
+.TP
+.B \-b TYPE, \-\-badsum=TYPE
+Specifies the action \fBsmartctl\fP should take if a checksum error is
+detected in the: (1) Device Identity Structure, (2) SMART Self\-Test
+Log Structure, (3) SMART Attribute Value Structure, (4) SMART
+Attribute Threshold Structure, or (5) ATA Error Log Structure.
+
+The valid arguments to this option are:
+
+.I warn
+\- report the incorrect checksum but carry on in spite of it.  This is the
+default.
+
+.I exit
+\- exit \fBsmartctl\fP.
+
+.I ignore
+\- continue silently without issuing a warning.
+
+.TP
+.B \-r TYPE, \-\-report=TYPE
+Intended primarily to help \fBsmartmontools\fP developers understand
+the behavior of \fBsmartmontools\fP on non\-conforming or poorly
+conforming hardware.  This option reports details of \fBsmartctl\fP
+transactions with the device.  The option can be used multiple times.
+When used just once, it shows a record of the ioctl() transactions
+with the device.  When used more than once, the detail of these
+ioctl() transactions are reported in greater detail.  The valid
+arguments to this option are:
+
+.I ioctl
+\- report all ioctl() transactions.
+
+.I ataioctl
+\- report only ioctl() transactions with ATA devices.
+
+.I scsiioctl
+\- report only ioctl() transactions with SCSI devices. Invoking this once
+shows the SCSI commands in hex and the corresponding status. Invoking
+it a second time adds a hex listing of the first 64 bytes of data send to, 
+or received from the device.
+
+Any argument may include a positive integer to specify the level of detail
+that should be reported.  The argument should be followed by a comma then
+the integer with no spaces.  For example, 
+.I ataioctl,2
+The default
+level is 1, so \'\-r ataioctl,1\' and \'\-r ataioctl\' are equivalent.
+
+.TP
+.B SMART FEATURE ENABLE/DISABLE COMMANDS:
+.IP
+.B Note: 
+if multiple options are used to both enable and disable a
+feature, then 
+.B both
+the enable and disable commands will be issued.  The enable command
+will always be issued
+.B before
+the corresponding disable command.
+.TP
+.B \-s VALUE, \-\-smart=VALUE 
+Enables or disables SMART on device.  The valid arguments to
+this option are \fIon\fP and \fIoff\fP.  Note that the command \'\-s on\'
+(perhaps used with with the \'\-o on\' and \'\-S on\' options) should be placed
+in a start\-up script for your machine, for example in rc.local or rc.sysinit.
+In principle the SMART feature settings are preserved over
+power\-cycling, but it doesn\'t hurt to be sure. It is not necessary (or
+useful) to enable SMART to see the TapeAlert messages.
+.TP
+.B \-o VALUE, \-\-offlineauto=VALUE
+Enables or disables SMART automatic offline test, which scans the drive
+every four hours for disk defects. This command can be given during normal
+system operation.  The valid arguments to this option are \fIon\fP
+and \fIoff\fP.
+
+Note that the SMART automatic offline test command is listed as
+"Obsolete" in every version of the ATA and ATA/ATAPI Specifications.
+It was originally part of the SFF\-8035i Revision 2.0 specification,
+but was never part of any ATA specification.  However it is
+implemented and used by many vendors. [Good documentation can be found
+in IBM\'s Official Published Disk Specifications.  For example the IBM
+Travelstar 40GNX Hard Disk Drive Specifications (Revision 1.1, 22
+April 2002, Publication # 1541, Document S07N\-7715\-02) page 164. You
+can also read the SFF\-8035i Specification \-\- see REFERENCES below.]
+You can tell if automatic offline testing is supported by seeing if
+this command enables and disables it, as indicated by the \'Auto
+Offline Data Collection\' part of the SMART capabilities report
+(displayed with \'\-c\').
+
+SMART provides \fBthree\fP basic categories of testing.  The
+\fBfirst\fP category, called "online" testing, has no effect on the
+performance of the device.  It is turned on by the \'\-s on\' option.
+
+The \fBsecond\fP category of testing is called "offline" testing. This
+type of test can, in principle, degrade the device performance.  The
+\'\-o on\' option causes this offline testing to be carried out,
+automatically, on a regular scheduled basis.  Normally, the disk will
+suspend offline testing while disk accesses are taking place, and then
+automatically resume it when the disk would otherwise be idle, so in
+practice it has little effect.  Note that a one\-time offline test can
+also be carried out immediately upon receipt of a user command.  See
+the \'\-t offline\' option below, which causes a one\-time offline test
+to be carried out immediately.
+
+The choice (made by the SFF\-8035i and ATA specification authors) of
+the word \fItesting\fP for these first two categories is unfortunate,
+and often leads to confusion.  In fact these first two categories of
+online and offline testing could have been more accurately described
+as online and offline \fBdata collection\fP.
+
+The results of this automatic or immediate offline testing (data
+collection) are reflected in the values of the SMART Attributes.
+Thus, if problems or errors are detected, the values of these
+Attributes will go below their failure thresholds; some types of
+errors may also appear in the SMART error log. These are visible with
+the \'\-A\' and \'\-l error\' options respectively.
+
+Some SMART attribute values are updated only during off\-line data
+collection activities; the rest are updated during normal operation of
+the device or during both normal operation and off\-line testing.  The
+Attribute value table produced by the \'\-A\' option indicates this in
+the UPDATED column.  Attributes of the first type are labeled
+"Offline" and Attributes of the second type are labeled "Always".
+
+The \fBthird\fP category of testing (and the \fIonly\fP category for
+which the word \'testing\' is really an appropriate choice) is "self"
+testing.  This third type of test is only performed (immediately) when
+a command to run it is issued.  The \'\-t\' and \'\-X\' options can be
+used to carry out and abort such self\-tests; please see below for
+further details.
+
+Any errors detected in the self testing will be shown in the
+SMART self\-test log, which can be examined using the \'\-l selftest\'
+option.
+
+\fBNote:\fP in this manual page, the word \fB"Test"\fP is used in
+connection with the second category just described, e.g. for the
+"offline" testing.  The words \fB"Self\-test"\fP are used in
+connection with the third category.
+.TP
+.B \-S VALUE, \-\-saveauto=VALUE
+Enables or disables SMART autosave of device vendor\-specific
+Attributes. The valid arguments to this option are \fIon\fP
+and \fIoff\fP.  Note that this feature is preserved across disk power
+cycles, so you should only need to issue it once.
+
+For SCSI devices this toggles the value of the Global Logging Target
+Save Disabled (GLTSD) bit in the Control Mode Page. Some disk
+manufacturers set this bit by default. This prevents error counters,
+power\-up hours and other useful data from being placed in non\-volatile
+storage, so these values may be reset to zero the next time the device
+is power\-cycled.  If the GLTSD bit is set then \'smartctl \-a\' will
+issue a warning. Use \fIon\fP to clear the GLTSD bit and thus enable
+saving counters to non\-volatile storage. For extreme streaming\-video
+type applications you might consider using \fIoff\fP to set the GLTSD
+bit.
+
+.TP
+.B SMART READ AND DISPLAY DATA OPTIONS:
+.TP
+.B \-H, \-\-health
+Check: Ask the device to report its SMART health status or pending
+TapeAlert messages.  SMART status is based on
+information that it has gathered from online and offline
+tests, which were used to determine/update its
+SMART vendor\-specific Attribute values. TapeAlert status is obtained
+by reading the TapeAlert log page.
+
+If the device reports failing health status, this means
+.B either
+that the device has already failed, 
+.B or 
+that it is predicting its own failure within the next 24 hours.  If
+this happens, use the \'\-a\' option to get more information, and
+.B get your data off the disk and someplace safe as soon as you can.
+.TP
+.B \-c, \-\-capabilities
+Prints only the generic SMART capabilities.  These show
+what SMART features are implemented and how the device will
+respond to some of the different SMART commands.  For example it
+shows if the device logs errors, if it supports offline surface
+scanning, and so on.  If the device can carry out self\-tests, this
+option also shows the estimated time required to run those tests.
+
+Note that the time required to run the Self\-tests (listed in minutes)
+are fixed.  However the time required to run the Immediate Offline
+Test (listed in seconds) is variable.  This means that if you issue a
+command to perform an Immediate Offline test with the \'\-t offline\' option,
+then the time may jump to a larger value and then count down as the
+Immediate Offline Test is carried out.  Please see REFERENCES below
+for further information about the the flags and capabilities described
+by this option.
+.TP
+.B \-A, \-\-attributes
+Prints only the vendor specific SMART Attributes.  The Attributes are
+numbered from 1 to 253 and have specific names and ID numbers. For
+example Attribute 12 is "power cycle count": how many times has the
+disk been powered up.
+
+Each Attribute has a "Raw" value, printed under the heading
+"RAW_VALUE", and a "Normalized" value printed under the heading
+"VALUE".  [Note: \fBsmartctl\fP prints these values in base\-10.]  In
+the example just given, the "Raw Value" for Attribute 12 would be the
+actual number of times that the disk has been power\-cycled, for
+example 365 if the disk has been turned on once per day for exactly
+one year.  Each vendor uses their own algorithm to convert this "Raw"
+value to a "Normalized" value in the range from 1 to 254.  Please keep
+in mind that \fBsmartctl\fP only reports the different Attribute
+types, values, and thresholds as read from the device.  It does
+\fBnot\fP carry out the conversion between "Raw" and "Normalized"
+values: this is done by the disk\'s firmware.
+
+The conversion from Raw value to a quantity with physical units is
+not specified by the SMART standard. In most cases, the values printed
+by \fBsmartctl\fP are sensible.  For example the temperature Attribute
+generally has its raw value equal to the temperature in Celsius.
+However in some cases vendors use unusual conventions.  For example
+the Hitachi disk on my laptop reports its power\-on hours in minutes,
+not hours. Some IBM disks track three temperatures rather than one, in
+their raw values.  And so on.
+
+Each Attribute also has a Threshold value (whose range is 0 to 255)
+which is printed under the heading "THRESH".  If the Normalized value
+is \fBless than or equal to\fP the Threshold value, then the Attribute
+is said to have failed.  If the Attribute is a pre\-failure Attribute,
+then disk failure is imminent.
+
+Each Attribute also has a "Worst" value shown under the heading
+"WORST".  This is the smallest (closest to failure) value that the
+disk has recorded at any time during its lifetime when SMART was
+enabled.  [Note however that some vendors firmware may actually
+\fBincrease\fP the "Worst" value for some "rate\-type" Attributes.]
+
+The Attribute table printed out by \fBsmartctl\fP also shows the
+"TYPE" of the Attribute. Attributes are one of two possible types:
+Pre\-failure or Old age.  Pre\-failure Attributes are ones which, if
+less than or equal to their threshold values, indicate pending disk
+failure.  Old age, or usage Attributes, are ones which indicate
+end\-of\-product life from old\-age or normal aging and wearout, if
+the Attribute value is less than or equal to the threshold.  \fBPlease
+note\fP: the fact that an Attribute is of type 'Pre\-fail' does
+\fBnot\fP mean that your disk is about to fail!  It only has this
+meaning if the Attribute\'s current Normalized value is less than or
+equal to the threshold value.
+
+If the Attribute\'s current Normalized value is less than or equal to
+the threshold value, then the "WHEN_FAILED" column will display
+"FAILING_NOW". If not, but the worst recorded value is less than or
+equal to the threshold value, then this column will display
+"In_the_past".  If the "WHEN_FAILED" column has no entry (indicated by
+a dash: \'\-\') then this Attribute is OK now (not failing) and has
+also never failed in the past.
+
+The table column labeled "UPDATED" shows if the SMART Attribute values
+are updated during both normal operation and off\-line testing, or
+only during offline testing.  The former are labeled "Always" and the
+latter are labeled "Offline".
+
+So to summarize: the Raw Attribute values are the ones that might have
+a real physical interpretation, such as "Temperature Celsius",
+"Hours", or "Start\-Stop Cycles".  Each manufacturer converts these,
+using their detailed knowledge of the disk\'s operations and failure
+modes, to Normalized Attribute values in the range 1\-254.  The
+current and worst (lowest measured) of these Normalized Attribute
+values are stored on the disk, along with a Threshold value that the
+manufacturer has determined will indicate that the disk is going to
+fail, or that it has exceeded its design age or aging limit.
+\fBsmartctl\fP does \fBnot\fP calculate any of the Attribute values,
+thresholds, or types, it merely reports them from the SMART data on
+the device.
+
+Note that starting with ATA/ATAPI\-4, revision 4, the meaning of these
+Attribute fields has been made entirely vendor\-specific.  However most
+ATA/ATAPI\-5 disks seem to respect their meaning, so we have retained
+the option of printing the Attribute values.
+
+For SCSI devices the "attributes" are obtained from the temperature
+and start-stop cycle counter log pages. Certain vendor specific
+attributes are listed if recognised. The attributes are output in a
+relatively free format (compared with ATA disk attributes).
+.TP
+.B \-l TYPE, \-\-log=TYPE
+Prints either the SMART Error Log, the SMART Self\-Test Log, the SMART
+Selective Self\-Test Log [ATA only], or the Log Directory [ATA only].
+The valid arguments to this option are:
+
+.I error
+\- prints only the SMART error log.  SMART disks maintain a log of the
+most recent five non\-trivial errors. For each of these errors, the
+disk power\-on lifetime at which the error occurred is recorded, as is
+the device status (idle, standby, etc) at the time of the error.  For
+some common types of errors, the Error Register (ER) and Status
+Register (SR) values are decoded and printed as text. The meanings of these
+are:
+.nf
+   \fBABRT\fP:  Command \fBAB\fPo\fBRT\fPed
+   \fBAMNF\fP:  \fBA\fPddress \fBM\fPark \fBN\fPot \fBF\fPound
+   \fBCCTO\fP:  \fBC\fPommand \fBC\fPompletion \fBT\fPimed \fBO\fPut
+   \fBEOM\fP:   \fBE\fPnd \fBO\fPf \fBM\fPedia
+   \fBICRC\fP:  \fBI\fPnterface \fBC\fPyclic \fBR\fPedundancy \fBC\fPode (CRC) error
+   \fBIDNF\fP:  \fBID\fPentity \fBN\fPot \fBF\fPound
+   \fBILI\fP:   (packet command\-set specific)
+   \fBMC\fP:    \fBM\fPedia \fBC\fPhanged
+   \fBMCR\fP:   \fBM\fPedia \fBC\fPhange \fBR\fPequest
+   \fBNM\fP:    \fBN\fPo \fBM\fPedia
+   \fBobs\fP:   \fBobs\fPolete
+   \fBTK0NF\fP: \fBT\fPrac\fBK 0 N\fPot \fBF\fPound
+   \fBUNC\fP:   \fBUNC\fPorrectable Error in Data
+   \fBWP\fP:    Media is \fBW\fPrite \fBP\fProtected
+.fi
+In addition, up to the last five commands that preceded the error are
+listed, along with a timestamp measured from the start of the
+corresponding power cycle. This is displayed in the form
+Dd+HH:MM:SS.msec where D is the number of days, HH is hours, MM is
+minutes, SS is seconds and msec is milliseconds.  [Note: this time
+stamp wraps after 2^32 milliseconds, or 49 days 17 hours 2 minutes and
+47.296 seconds.]  The key ATA disk registers are also recorded in the
+log.  The final column of the error log is a text\-string description
+of the ATA command defined by the Command Register (CR) and Feature
+Register (FR) values.  Commands that are obsolete in the most current
+(ATA\-7) spec are listed like this: \fBREAD LONG (w/ retry) [OBS\-4]\fP,
+indicating that the command became obsolete with or in the ATA\-4
+specification.  Similarly, the notation \fB[RET\-\fP\fIN\fP\fB]\fP is
+used to indicate that a command was retired in the ATA\-\fIN\fP
+specification.  Some commands are not defined in any version of the
+ATA specification but are in common use nonetheless; these are marked
+\fB[NS]\fP, meaning non\-standard.
+
+The ATA Specification (ATA\-5 Revision 1c, Section 8.41.6.8.2) says:
+\fB"Error log structures shall include UNC errors, IDNF errors for
+which the address requested was valid, servo errors, write fault
+errors, etc.  Error log data structures shall not include errors
+attributed to the receipt of faulty commands such as command codes not
+implemented by the device or requests with invalid parameters or
+invalid addresses."\fP The definitions of these terms are:
+.br
+\fBUNC\fP (\fBUNC\fPorrectable): data is uncorrectable.  This refers
+to data which has been read from the disk, but for which the Error
+Checking and Correction (ECC) codes are inconsistent.  In effect, this
+means that the data can not be read.
+.br
+\fBIDNF\fP (\fBID N\fPot \fBF\fPound): user\-accessible address could
+not be found. For READ LOG type commands, \fBIDNF\fP can also indicate
+that a device data log structure checksum was incorrect.
+
+If the command that caused the error was a READ or WRITE command, then
+the Logical Block Address (LBA) at which the error occurred will be
+printed in base 10 and base 16.  The LBA is a linear address, which
+counts 512\-byte sectors on the disk, starting from zero.  (Because of
+the limitations of the SMART error log, if the LBA is greater than
+0xfffffff, then either no error log entry will be made, or the error
+log entry will have an incorrect LBA. This may happen for drives with
+a capacity greater than 128 GiB or 137 GB.) On Linux systems the
+smartmontools web page has instructions about how to convert the LBA
+address to the name of the disk file containing the erroneous disk
+sector.
+
+Please note that some manufacturers \fBignore\fP the ATA
+specifications, and make entries in the error log if the device
+receives a command which is not implemented or is not valid.
+
+.I error [SCSI]
+\- prints the error counter log pages for reads, write and verifies.
+The verify row is only output if it has an element other than zero.
+
+.I selftest
+\- prints the SMART self\-test log.  The disk maintains a self\-test log
+showing the results of the self tests, which can be run using the
+\'\-t\' option described below.  For each of the most recent
+twenty\-one self\-tests, the log shows the type of test (short or
+extended, off\-line or captive) and the final status of the test.  If
+the test did not complete successfully, then the percentage of the
+test remaining is shown.  The time at which the test took place,
+measured in hours of disk lifetime, is also printed.  If any errors
+were detected, the Logical Block Address (LBA) of the first error is
+printed in decimal notation. On Linux systems the smartmontools
+web page has instructions about how to convert this LBA address to the
+name of the disk file containing the erroneous block.
+
+.I selftest [SCSI]
+\- the self\-test log for a SCSI device has a slightly different format
+than for an ATA device.  For each of the most recent twenty
+self\-tests, it shows the type of test and the status (final or in
+progress) of the test. SCSI standards use the terms "foreground" and
+"background" (rather than ATA\'s corresponding "captive" and
+"off\-line") and "short" and "long" (rather than ATA\'s corresponding
+"short" and "extended") to describe the type of the test.  The printed
+segment number is only relevant when a test fails in the third or
+later test segment.  It identifies the test that failed and consists
+of either the number of the segment that failed during the test, or
+the number of the test that failed and the number of the segment in
+which the test was run, using a vendor\-specific method of putting both
+numbers into a single byte.  The Logical Block Address (LBA) of the
+first error is printed in hexadecimal notation.  On Linux systems the
+smartmontools web page has instructions about how to convert this LBA
+address to the name of the disk file containing the erroneous block.
+If provided, the SCSI Sense Key (SK), Additional Sense Code (ASC) and
+Additional Sense Code Qualifier (ASQ) are also printed. The self tests
+can be run using the \'\-t\' option described below (using the ATA
+test terminology).
+
+.I selective [ATA]
+\- Some ATA\-7 disks (example: Maxtor) also maintain a selective
+self\-test log.  Please see the \'\-t select\' option below for a
+description of selective self\-tests.  The selective self\-test log
+shows the start/end Logical Block Addresses (LBA) of each of the five
+test spans, and their current test status.  If the span is being
+tested or the remainder of the disk is being read\-scanned, the
+current 65536\-sector block of LBAs being tested is also displayed.
+The selective self\-test log also shows if a read\-scan of the
+remainder of the disk will be carried out after the selective
+self\-test has completed (see \'\-t afterselect\' option) and the time
+delay before restarting this read\-scan if it is interrupted (see
+\'\-t pending\' option). This is a new smartmontools feature; please
+report unusual or incorrect behavior to the smartmontools\-support
+mailing list.
+
+.I directory
+\- if the device supports the General Purpose Logging feature set
+(ATA\-6 and ATA\-7 only) then this prints the Log Directory (the log at
+address 0).  The Log Directory shows what logs are available and their
+length in sectors (512 bytes).  The contents of the logs at address 1
+[Summary SMART error log] and at address 6 [SMART self\-test log] may
+be printed using the previously\-described
+.I error
+and
+.I selftest
+arguments to this option. [Please note: this is a new, experimental
+feature.  We would like to add support for printing the contents of
+extended and comprehensive SMART self\-test and error logs.  If your
+disk supports these, and you would like to assist, please contact the
+\fBsmartmontools\fP developers.]
+
+.TP
+.B \-v N,OPTION, \-\-vendorattribute=N,OPTION
+Sets a vendor\-specific display OPTION for Attribute N.  This option
+may be used multiple times. Valid arguments to this option are:
+
+.I help
+\- Prints (to STDOUT) a list of all valid arguments to this option,
+then exits.
+
+.I 9,minutes
+\- Raw Attribute number 9 is power\-on time in minutes.  Its raw value
+will be displayed in the form "Xh+Ym".  Here X is hours, and Y is
+minutes in the range 0\-59 inclusive.  Y is always printed with two
+digits, for example "06" or "31" or "00".
+
+.I 9,seconds
+\- Raw Attribute number 9 is power\-on time in seconds.  Its raw value
+will be displayed in the form "Xh+Ym+Zs".  Here X is hours, Y is
+minutes in the range 0\-59 inclusive, and Z is seconds in the range
+0\-59 inclusive.  Y and Z are always printed with two digits, for
+example "06" or "31" or "00".
+
+.I 9,halfminutes
+\- Raw Attribute number 9 is power\-on time, measured in units of 30
+seconds.  This format is used by some Samsung disks.  Its raw value
+will be displayed in the form "Xh+Ym".  Here X is hours, and Y is
+minutes in the range 0\-59 inclusive.  Y is always printed with two
+digits, for example "06" or "31" or "00".
+
+.I 9,temp
+\- Raw Attribute number 9 is the disk temperature in Celsius.
+
+.I 192,emergencyretractcyclect
+\- Raw Attribute number 192 is the Emergency Retract Cycle Count.
+
+.I 193,loadunload
+\- Raw Attribute number 193 contains two values. The first is the
+number of load cycles.  The second is the number of unload cycles.
+The difference between these two values is the number of times that
+the drive was unexpectedly powered off (also called an emergency
+unload). As a rule of thumb, the mechanical stress created by one
+emergency unload is equivalent to that created by one hundred normal
+unloads.
+
+.I 194,10xCelsius
+\- Raw Attribute number 194 is ten times the disk temperature in
+Celsius.  This is used by some Samsung disks (example: model SV1204H
+with RK100\-13 firmware).
+
+.I 194,unknown
+\- Raw Attribute number 194 is NOT the disk temperature, and its
+interpretation is unknown. This is primarily useful for the \-P
+(presets) option.
+
+.I 198,offlinescanuncsectorct
+\- Raw Attribute number 198 is the Offline Scan UNC Sector Count.
+
+.I 200,writeerrorcount
+\- Raw Attribute number 200 is the Write Error Count.
+
+.I 201,detectedtacount
+\- Raw Attribute number 201 is the Detected TA Count.
+
+.I 220,temp
+\- Raw Attribute number 220 is the disk temperature in Celsius.
+
+Note: a table of hard drive models, listing which Attribute
+corresponds to temperature, can be found at:
+\fBhttp://www.guzu.net/linux/hddtemp.db\fP
+
+.I N,raw8
+\- Print the Raw value of Attribute N as six 8\-bit unsigned base\-10
+integers.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw8\' prints Raw values for ALL Attributes in this
+form.  The form (for example) \'123,raw8\' only prints the Raw value for
+Attribute 123 in this form.
+
+.I N,raw16
+\- Print the Raw value of Attribute N as three 16\-bit unsigned base\-10
+integers.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw16\' prints Raw values for ALL Attributes in this
+form.  The form (for example) \'123,raw16\' only prints the Raw value for
+Attribute 123 in this form.
+
+.I N,raw48
+\- Print the Raw value of Attribute N as a 48\-bit unsigned base\-10
+integer.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw48\' prints Raw values for ALL Attributes in
+this form.  The form (for example) \'123,raw48\' only prints the Raw
+value for Attribute 123 in this form.
+
+.TP
+.B \-F TYPE, \-\-firmwarebug=TYPE
+Modifies the behavior of \fBsmartctl\fP to compensate for some known
+and understood device firmware bug.  The arguments to this option are
+exclusive, so that only the final option given is used.  The valid
+values are:
+
+.I none
+\- Assume that the device firmware obeys the ATA specifications.  This
+is the default, unless the device has presets for \'\-F\' in the
+device database (see note below).
+
+.I samsung
+\- In some Samsung disks (example: model SV4012H Firmware Version:
+RM100\-08) some of the two\- and four\-byte quantities in the SMART data
+structures are byte\-swapped (relative to the ATA specification).
+Enabling this option tells \fBsmartctl\fP to evaluate these quantities
+in byte\-reversed order.  Some signs that your disk needs this option
+are (1) no self\-test log printed, even though you have run self\-tests;
+(2) very large numbers of ATA errors reported in the ATA error log;
+(3) strange and impossible values for the ATA error log timestamps.
+
+.I samsung2
+\- In more recent Samsung disks (firmware revisions ending in "\-23")
+the number of ATA errors reported is byte swapped.  Enabling this
+option tells \fBsmartctl\fP to evaluate this quantity in
+byte\-reversed order. An indication that your Samsung disk needs this
+option is that the self-test log is printed correctly, but there are a
+very large number of errors in the SMART error log.  This is because
+the error count is byte swapped.  Thus a disk with five errors
+(0x0005) will appear to have 20480 errors (0x5000).
+
+Note that an explicit \'\-F\' option on the command line will
+over\-ride any preset values for \'\-F\' (see the \'\-P\' option
+below).
+
+.TP
+.B \-P TYPE, \-\-presets=TYPE
+Specifies whether \fBsmartctl\fP should use any preset options that
+are available for this drive. By default, if the drive is recognized
+in the \fBsmartmontools\fP database, then the presets are used.
+
+\fBsmartctl\fP can automatically set appropriate options for known
+drives.  For example, the Maxtor 4D080H4 uses Attribute 9 to stores
+power\-on time in minutes whereas most drives use that Attribute to
+store the power\-on time in hours.  The command\-line option \'\-v
+9,minutes\' ensures that \fBsmartctl\fP correctly interprets Attribute
+9 in this case, but that option is preset for the Maxtor 4D080H4 and
+so need not be specified by the user on the \fBsmartctl\fP command
+line.
+
+The argument
+.I show
+will show any preset options for your drive and the argument
+.I showall
+will show all known drives in the \fBsmartmontools\fP database, along
+with their preset options.  If there are no presets for your drive and
+you think there should be (for example, a \-v or \-F option is needed
+to get \fBsmartctl\fP to display correct values) then please contact
+the \fBsmartmontools\fP developers so that this information can be
+added to the \fBsmartmontools\fP database.  Contact information is at the
+end of this man page.
+
+The valid arguments to this option are:
+
+.I use
+\- if a drive is recognized, then use the stored presets for it.  This
+is the default. Note that presets will NOT over\-ride additional
+Attribute interpretation (\'\-v N,something\') command\-line options or
+explicit \'\-F\' command\-line options..
+
+.I ignore
+\- do not use presets.
+
+.I show
+\- show if the drive is recognized in the database, and if so, its
+presets, then exit.
+
+.I showall
+\- list all recognized drives, and the presets that are set for them,
+then exit.
+
+The \'\-P showall\' option takes up to two optional arguments to
+match a specific drive type and firmware version. The command:
+.nf
+  smartctl \-P showall
+.fi
+lists all entries, the command:
+.nf
+  smartctl \-P showall \'MODEL\'
+.fi
+lists all entries matching MODEL, and the command:
+.nf
+  smartctl \-P showall \'MODEL\' \'FIRMWARE\'
+.fi
+lists all entries for this MODEL and a specific FIRMWARE version.
+
+.TP
+.B SMART RUN/ABORT OFFLINE TEST AND SELF\-TEST OPTIONS:
+.TP
+.B \-t TEST, \-\-test=TEST
+Executes TEST immediately.  The \'\-C\' option can be used in
+conjunction with this option to run the short or long (and also for
+ATA devices, selective or conveyance) self\-tests in captive mode
+(known as "foreground mode" for SCSI devices).  Note that only one
+test type can be run at a time, so only one test type should be
+specified per command line.  Note also that if a computer is shutdown
+or power cycled during a self\-test, no harm should result.  The
+self\-test will either be aborted or will resume automatically.
+
+The valid arguments to this option are:  
+
+.I offline
+\- runs SMART Immediate Offline Test.  This immediately
+starts the test described above.  This command can be given during
+normal system operation.  The effects of this test are visible only in
+that it updates the SMART Attribute values, and if errors are
+found they will appear in the SMART error log, visible with the \'\-l error\'
+option. [In the case of SCSI devices runs the default self test in
+foreground. No entry is placed in the self test log.]
+
+If the \'\-c\' option to \fBsmartctl\fP shows that the device has the
+"Suspend Offline collection upon new command" capability then you can
+track the progress of the Immediate Offline test using the \'\-c\'
+option to \fBsmartctl\fP.  If the \'\-c\' option show that the device
+has the "Abort Offline collection upon new command" capability then
+most commands will abort the Immediate Offline Test, so you should not
+try to track the progress of the test with \'\-c\', as it will abort
+the test.
+
+.I short
+\- runs SMART Short Self Test (usually under ten minutes).
+[Note: in the case of SCSI devices,
+this command option runs the "Background short" self\-test.]
+This command can be given during normal system operation (unless run in
+captive mode \- see the \'\-C\' option below).  This is a
+test in a different category than the immediate or automatic offline
+tests.  The "Self" tests check the electrical and mechanical
+performance as well as the read performance of the disk.  Their
+results are reported in the Self Test Error Log, readable with
+the \'\-l selftest\' option.  Note that on some disks the progress of the
+self\-test can be monitored by watching this log during the self\-test; with other disks
+use the \'\-c\' option to monitor progress.
+
+.I long
+\- runs SMART Extended Self Test (tens of minutes).
+[Note: in the case of SCSI devices,
+this command option runs the "Background long" self\-test.]
+This is a
+longer and more thorough version of the Short Self Test described
+above.  Note that this command can be given during normal
+system operation (unless run in captive mode \- see the \'\-C\' option below).
+
+.I conveyance
+\- [ATA ONLY] runs a SMART Conveyance Self Test (minutes).  This
+self\-test routine is intended to identify damage incurred during
+transporting of the device. This self\-test routine should take on the
+order of minutes to complete.  Note that this command can be given
+during normal system operation (unless run in captive mode \- see the
+\'\-C\' option below).
+
+.I select,N\-M
+\- [ATA ONLY] [NEW EXPERIMENTAL SMARTCTL FEATURE] runs a SMART
+Selective Self Test, to test a \fBrange\fP of disk Logical Block
+Addresses (LBAs), rather than the entire disk.  Each range of LBAs
+that is checked is called a "span" and is specified by a starting LBA
+(N) and an ending LBA (M) with N less than or equal to M.  For example
+the command:
+.nf
+  smartctl \-t select,10\-20 /dev/hda
+.fi
+runs a self test on one span consisting of LBAs ten to twenty
+(inclusive). The \'\-t\' option can be given up to five times, to test
+up to five spans.  For example the command:
+.nf
+  smartctl \-t select,0\-100 \-t select,1000\-2000 /dev/hda
+.fi
+runs a self test on two spans.  The first span consists of 101 LBAs
+and the second span consists of 1001 LBAs.  Note that the spans can
+overlap partially or completely, for example:
+.nf
+  smartctl \-t select,0\-10 \-t select,5\-15 \-t select,10\-20 /dev/hda
+.fi
+The results of the selective self\-test can be obtained (both during
+and after the test) by printing the SMART self\-test log, using the
+\'\-l selftest\' option to smartctl.
+
+Selective self tests are particularly useful as disk capacities
+increase: an extended self test (smartctl \-t long) can take several
+hours.  Selective self\-tests are helpful if (based on SYSLOG error
+messages, previous failed self\-tests, or SMART error log entries) you
+suspect that a disk is having problems at a particular range of
+Logical Block Addresses (LBAs).
+
+Selective self\-tests can be run during normal system operation (unless
+done in captive mode \- see the \'\-C\' option below).
+
+[Note: this new experimental smartmontools feature is currently only
+available under Linux.  The Linux kernel must be compiled with the
+configuration option CONFIG_IDE_TASKFILE_IO enabled.  Please report
+unusual or incorrect behavior to the smartmontools\-support mailing
+list.]
+
+.I afterselect,on
+\- [ATA ONLY] perform an offline read scan after a Selective Self\-test
+has completed. This option must be used together with one or more of
+the \fIselect,N\-M\fP options above. If the LBAs that have been
+specified in the Selective self\-test pass the test with no errors
+found, then read scan the \fBremainder\fP of the disk.  If the device
+is powered\-cycled while this read scan is in progress, the read scan
+will be automatically resumed after a time specified by the pending
+timer (see below).  The value of this option is preserved between
+selective self\-tests.
+
+.I afterselect,off
+\- [ATA ONLY] do not read scan the remainder of the disk after a
+Selective self\-test has completed.  This option must be use together
+with one or more of the \fIselect,N\-M\fP options above.  The value of this
+option is preserved between selective self\-tests.
+
+.I pending,N 
+\- [ATA ONLY] set the pending offline read scan timer to N minutes.
+Here N is an integer in the range from 0 to 65535 inclusive.  If the
+device is powered off during a read scan after a Selective self\-test,
+then resume the test automatically N minutes after power\-up.  This
+option must be use together with one or more of the \fIselect,N\-M\fP
+options above. The value of this option is preserved between selective
+self\-tests.
+
+.TP
+.B \-C, \-\-captive
+Runs self\-tests in captive mode.  This has no effect with \'\-t
+offline\' or if the \'\-t\' option is not used. [Note: in the case of
+SCSI devices, this command option runs the self\-test in "Foreground"
+mode.]
+
+\fBWARNING: Tests run in captive mode may busy out the drive for the
+length of the test.  Only run captive tests on drives without any
+mounted partitions!\fP
+
+.TP
+.B \-X, \-\-abort
+Aborts non\-captive SMART Self Tests.  Note that this
+command will abort the Offline Immediate Test routine only if your
+disk has the "Abort Offline collection upon new command" capability.
+.PP
+.SH EXAMPLES
+.nf
+.B smartctl \-a /dev/hda
+.fi
+Print all SMART information for drive /dev/hda (Primary Master).
+.PP
+.nf
+.B smartctl \-s off /dev/hdd
+.fi
+Disable SMART on drive /dev/hdd (Secondary Slave).
+.PP
+.nf
+.B smartctl \-\-smart=on \-\-offlineauto=on \-\-saveauto=on /dev/hda
+.fi
+Enable SMART on drive /dev/hda, enable automatic offline
+testing every four hours, and enable autosaving of
+SMART Attributes.  This is a good start\-up line for your system\'s
+init files.  You can issue this command on a running system.
+.PP
+.nf
+.B smartctl \-t long /dev/hdc
+.fi
+Begin an extended self\-test of drive /dev/hdc.  You can issue this
+command on a running system.  The results can be seen in the self\-test
+log visible with the \'\-l selftest\' option after it has completed.
+.PP
+.nf
+.B smartctl \-s on \-t offline /dev/hda
+.fi
+Enable SMART on the disk, and begin an immediate offline test of
+drive /dev/hda.  You can issue this command on a running system.  The
+results are only used to update the SMART Attributes, visible
+with the \'\-A\' option.  If any device errors occur, they are logged to
+the SMART error log, which can be seen with the \'\-l error\' option.
+.PP
+.nf
+.B smartctl \-A \-v 9,minutes /dev/hda
+.fi
+Shows the vendor Attributes, when the disk stores its power\-on time
+internally in minutes rather than hours.
+.PP
+.nf
+.B smartctl \-q errorsonly \-H \-l selftest /dev/hda
+.fi
+Produces output only if the device returns failing SMART status,
+or if some of the logged self\-tests ended with errors.
+.PP
+.nf
+.B smartctl \-q silent \-a /dev/hda
+.fi
+Examine all SMART data for device /dev/hda, but produce no
+printed output.  You must use the exit status (the
+.B $?
+shell variable) to learn if any Attributes are out of bound, if the
+SMART status is failing, if there are errors recorded in the
+self\-test log, or if there are errors recorded in the disk error log.
+.PP
+.nf
+.B smartctl \-a \-d 3ware,0 /dev/sda
+.fi
+Examine all SMART data for the first ATA disk connected to a 3ware
+RAID controller card.
+.PP
+.nf
+.B smartctl \-a \-d 3ware,0 /dev/twe0
+.fi
+Examine all SMART data for the first ATA disk connected to a 3ware
+RAID 6000/7000/8000 controller card.
+.PP
+.nf
+.B smartctl \-a \-d 3ware,0 /dev/twa0
+.fi
+Examine all SMART data for the first ATA disk connected to a 3ware
+RAID 9000 controller card.
+.PP
+.nf
+.B smartctl \-t short \-d 3ware,3 /dev/sdb
+.fi
+Start a short self\-test on the fourth ATA disk connected to the 3ware RAID
+controller card which is the second SCSI device /dev/sdb.
+.nf
+.B smartctl \-t select,10\-100 \-t select,30\-300 \-t afterselect,on \-t pending,45 /dev/hda
+.fi
+Run a selective self\-test on LBAs 10 to 100 and 30 to 300.  After the
+these LBAs have been tested, read\-scan the remainder of the disk.  If the disk is
+power\-cycled during the read\-scan, resume the scan 45 minutes after power to the
+device is restored.
+.PP
+.SH RETURN VALUES
+The return values of \fBsmartctl\fP are defined by a bitmask.  If all
+is well with the disk, the return value (exit status) of
+\fBsmartctl\fP is 0 (all bits turned off).  If a problem occurs, or an
+error, potential error, or fault is detected, then a non\-zero status
+is returned.  In this case, the eight different bits in the return
+value have the following meanings for ATA disks; some of these values
+may also be returned for SCSI disks.
+.TP
+.B Bit 0:
+Command line did not parse.
+.TP
+.B Bit 1:
+Device open failed, or device did not return an IDENTIFY DEVICE structure. 
+.TP
+.B Bit 2:
+Some SMART command to the disk failed, or there was a checksum error
+in a SMART data structure (see \'\-b\' option above).
+.TP
+.B Bit 3:
+SMART status check returned "DISK FAILING".
+.TP
+.B Bit 4:
+SMART status check returned "DISK OK" but we found prefail Attributes <= threshold.
+.TP
+.B Bit 5:
+SMART status check returned "DISK OK" but we found that some (usage
+or prefail) Attributes have been <= threshold at some time in the
+past. 
+.TP
+.B Bit 6:
+The device error log contains records of errors.
+.TP
+.B Bit 7:
+The device self\-test log contains records of errors.
+
+To test within the shell for whether or not the different bits are
+turned on or off, you can use the following type of construction (this
+is bash syntax):
+.nf
+.B smartstat=$(($? & 8))
+.fi
+This looks at only at bit 3 of the exit status
+.B $?
+(since 8=2^3).  The shell variable
+$smartstat will be nonzero if SMART status check returned "disk
+failing" and zero otherwise.
+
+.PP
+.SH NOTES
+The TapeAlert log page flags are cleared for the initiator when the
+page is read. This means that each alert condition is reported only
+once by \fBsmartctl\fP for each initiator for each activation of the
+condition.
+
+.PP
+.SH AUTHOR
+\fBBruce Allen\fP smartmontools\-support@lists.sourceforge.net
+.fi
+University of Wisconsin \- Milwaukee Physics Department
+.PP
+.SH CONTRIBUTORS
+The following have made large contributions to smartmontools:
+.nf
+\fBCasper Dik\fP (Solaris SCSI interface)
+\fBChristian Franke\fP (Windows interface and Cygwin package)
+\fBDouglas Gilbert\fP (SCSI subsystem)
+\fBGuido Guenther\fP (Autoconf/Automake packaging)
+\fBGeoffrey Keating\fP (Darwin ATA interface)
+\fBEduard Martinescu\fP (FreeBSD interface)
+\fBFr\*'ed\*'eric L. W. Meunier\fP (Web site and Mailing list)
+\fBKeiji Sawada\fP (Solaris ATA interface)
+\fBSergey Svishchev\fP (NetBSD interface)
+\fBDavid Snyder and Sergey Svishchev\fP (OpenBSD interface)
+\fBPhil Williams\fP (User interface and drive database)
+\fBYuri Dario\fP (OS/2, eComStation interface)
+.fi
+Many other individuals have made smaller contributions and corrections.
+
+.PP
+.SH CREDITS
+.fi
+This code was derived from the smartsuite package, written by Michael
+Cornwell, and from the previous UCSC smartsuite package.  It extends
+these to cover ATA\-5 disks.  This code was originally developed as a
+Senior Thesis by Michael Cornwell at the Concurrent Systems Laboratory
+(now part of the Storage Systems Research Center), Jack Baskin School
+of Engineering, University of California, Santa
+Cruz. \fBhttp://ssrc.soe.ucsc.edu/\fP .
+.SH
+HOME PAGE FOR SMARTMONTOOLS: 
+.fi
+Please see the following web site for updates, further documentation, bug
+reports and patches: \fBhttp://smartmontools.sourceforge.net/\fP
+
+.SH
+SEE ALSO:
+\fBsmartd\fP(8), \fBbadblocks\fP(8), \fBide\-smart\fP(8).
+.SH
+REFERENCES FOR SMART
+.fi
+An introductory article about smartmontools is \fIMonitoring Hard
+Disks with SMART\fP, by Bruce Allen, Linux Journal, January 2004,
+pages 74-77. This is \fBhttp://www.linuxjournal.com/article.php?sid=6983\fP
+online.
+
+If you would like to understand better how SMART works, and what it
+does, a good place to start is with Sections 4.8 and 6.54 of the first
+volume of the \'AT Attachment with Packet Interface-7\' (ATA/ATAPI-7)
+specification.  This documents the SMART functionality which the
+\fBsmartmontools\fP utilities provide access to.  You can find
+Revision 4b of this document at
+\fBhttp://www.t13.org/docs2004/d1532v1r4b-ATA-ATAPI-7.pdf\fP .
+Earlier and later versions of this Specification are available from
+the T13 web site \fBhttp://www.t13.org/\fP .
+
+.fi
+The functioning of SMART was originally defined by the SFF\-8035i
+revision 2 and the SFF\-8055i revision 1.4 specifications.  These are
+publications of the Small Form Factors (SFF) Committee.  Links to
+these documents may be found in the References section of the
+\fBsmartmontools\fP home page at
+\fBhttp://smartmontools.sourceforge.net/\fP .
+
+.SH
+CVS ID OF THIS PAGE:
+$Id: smartctl.8.in,v 1.78 2006/04/12 15:45:38 ballen4705 Exp $
+.\" Local Variables:            
+.\" mode: nroff         
+.\" End:
diff --git a/smartctl.c b/smartctl.c
new file mode 100644 (file)
index 0000000..6c0886f
--- /dev/null
@@ -0,0 +1,896 @@
+/*
+ * smartctl.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "config.h"
+#ifdef HAVE_GETOPT_LONG
+#include <getopt.h>
+#endif
+#if defined(__FreeBSD_version) && (__FreeBSD_version < 500000)
+#include <unistd.h>
+#endif
+
+#include "int64.h"
+#include "atacmds.h"
+#include "ataprint.h"
+#include "extern.h"
+#include "knowndrives.h"
+#include "scsicmds.h"
+#include "scsiprint.h"
+#include "smartctl.h"
+#include "utility.h"
+
+#ifdef NEED_SOLARIS_ATA_CODE
+extern const char *os_solaris_ata_s_cvsid;
+#endif
+extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid;
+const char* smartctl_c_cvsid="$Id: smartctl.c,v 1.143 2006/04/12 14:54:28 ballen4705 Exp $"
+ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
+
+// This is a block containing all the "control variables".  We declare
+// this globally in this file, and externally in other files.
+smartmonctrl *con=NULL;
+
+// to hold onto exit code for atexit routine
+extern int exitstatus;
+
+// Track memory use
+extern int64_t bytes;
+
+void printslogan(){
+#ifdef HAVE_GET_OS_VERSION_STR
+  const char * ver = get_os_version_str();
+#else
+  const char * ver = SMARTMONTOOLS_BUILD_HOST;
+#endif
+  pout("smartctl version %s [%s] Copyright (C) 2002-6 Bruce Allen\n", PACKAGE_VERSION, ver);
+  pout("Home page is " PACKAGE_HOMEPAGE "\n\n");
+  return;
+}
+
+void PrintOneCVS(const char *a_cvs_id){
+  char out[CVSMAXLEN];
+  printone(out,a_cvs_id);
+  pout("%s",out);
+  return;
+}
+
+void printcopy(){
+  char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]";
+
+  pout("smartctl comes with ABSOLUTELY NO WARRANTY. This\n");
+  pout("is free software, and you are welcome to redistribute it\n");
+  pout("under the terms of the GNU General Public License Version 2.\n");
+  pout("See http://www.gnu.org for further details.\n\n");
+  pout("CVS version IDs of files used to build this code are:\n");
+  PrintOneCVS(atacmdnames_c_cvsid);
+  PrintOneCVS(atacmds_c_cvsid);
+  PrintOneCVS(ataprint_c_cvsid);
+  PrintOneCVS(knowndrives_c_cvsid);
+  PrintOneCVS(os_XXXX_c_cvsid);
+#ifdef NEED_SOLARIS_ATA_CODE
+  PrintOneCVS(os_solaris_ata_s_cvsid);
+#endif
+  PrintOneCVS(scsicmds_c_cvsid);
+  PrintOneCVS(scsiprint_c_cvsid);
+  PrintOneCVS(smartctl_c_cvsid);
+  PrintOneCVS(utility_c_cvsid);
+  pout("\nsmartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n");
+  pout("smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n");
+  pout("smartmontools build configured: " SMARTMONTOOLS_CONFIGURE_DATE "\n");
+  pout("smartctl compile dated " __DATE__ " at "__TIME__ "\n");
+  pout("smartmontools configure arguments: %s\n", configargs);
+  return;
+}
+
+void UsageSummary(){
+  pout("\nUse smartctl -h to get a usage summary\n\n");
+  return;
+}
+
+/*  void prints help information for command syntax */
+void Usage (void){
+  printf("Usage: smartctl [options] device\n\n");
+  printf("============================================ SHOW INFORMATION OPTIONS =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+"  -h, --help, --usage\n"
+"         Display this help and exit\n\n"
+"  -V, --version, --copyright, --license\n"
+"         Print license, copyright, and version information and exit\n\n"
+"  -i, --info                                                       \n"
+"         Show identity information for device\n\n"
+"  -a, --all                                                        \n"
+"         Show all SMART information for device\n\n"
+  );
+#else
+  printf(
+"  -h        Display this help and exit\n"
+"  -V        Print license, copyright, and version information\n"
+"  -i        Show identity information for device\n"
+"  -a        Show all SMART information for device\n\n"
+  );
+#endif
+  printf("================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+"  -q TYPE, --quietmode=TYPE                                           (ATA)\n"
+"         Set smartctl quiet mode to one of: errorsonly, silent\n\n"
+"  -d TYPE, --device=TYPE\n"
+"         Specify device type to one of: ata, scsi, marvell, 3ware,N\n\n"
+"  -T TYPE, --tolerance=TYPE                                           (ATA)\n"
+"         Tolerance: normal, conservative, permissive, verypermissive\n\n"
+"  -b TYPE, --badsum=TYPE                                              (ATA)\n"
+"         Set action on bad checksum to one of: warn, exit, ignore\n\n"
+"  -r TYPE, --report=TYPE\n"
+"         Report transactions (see man page)\n\n"
+  );
+#else
+  printf(
+"  -q TYPE   Set smartctl quiet mode to one of: errorsonly, silent     (ATA)\n"
+"  -d TYPE   Specify device type to one of: ata, scsi, 3ware,N\n"
+"  -T TYPE   Tolerance: normal, conservative,permissive,verypermissive (ATA\n"
+"  -b TYPE   Set action on bad checksum to one of: warn, exit, ignore  (ATA)\n"
+"  -r TYPE   Report transactions (see man page)\n\n"
+  );
+#endif
+  printf("============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+"  -s VALUE, --smart=VALUE\n"
+"        Enable/disable SMART on device (on/off)\n\n"
+"  -o VALUE, --offlineauto=VALUE                                       (ATA)\n"
+"        Enable/disable automatic offline testing on device (on/off)\n\n"
+"  -S VALUE, --saveauto=VALUE                                          (ATA)\n"
+"        Enable/disable Attribute autosave on device (on/off)\n\n"
+  );
+#else
+  printf(
+"  -s VALUE  Enable/disable SMART on device (on/off)\n"
+"  -o VALUE  Enable/disable device automatic offline testing (on/off)  (ATA)\n"
+"  -S VALUE  Enable/disable device Attribute autosave (on/off)         (ATA)\n\n"
+  );
+#endif
+  printf("======================================= READ AND DISPLAY DATA OPTIONS =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+"  -H, --health\n"
+"        Show device SMART health status\n\n"
+"  -c, --capabilities                                                  (ATA)\n"
+"        Show device SMART capabilities\n\n"
+"  -A, --attributes                                                         \n"
+"        Show device SMART vendor-specific Attributes and values\n\n"
+"  -l TYPE, --log=TYPE\n"
+"        Show device log. TYPE: error, selftest, selective, directory\n\n"
+"  -v N,OPTION , --vendorattribute=N,OPTION                            (ATA)\n"
+"        Set display OPTION for vendor Attribute N (see man page)\n\n"
+"  -F TYPE, --firmwarebug=TYPE                                         (ATA)\n"
+"        Use firmware bug workaround: none, samsung, samsung2\n\n"
+"  -P TYPE, --presets=TYPE                                             (ATA)\n"
+"        Drive-specific presets: use, ignore, show, showall\n\n"
+  );
+#else
+  printf(
+"  -H        Show device SMART health status\n"
+"  -c        Show device SMART capabilities                             (ATA)\n"
+"  -A        Show device SMART vendor-specific Attributes and values    (ATA)\n"
+"  -l TYPE   Show device log. TYPE: error,selftest,selective,directory\n"
+"  -v N,OPT  Set display OPTion for vendor Attribute N (see man page)   (ATA)\n"
+"  -F TYPE   Use firmware bug workaround: none, samsung, samsung2       (ATA)\n"
+"  -P TYPE   Drive-specific presets: use, ignore, show, showall         (ATA)\n\n"
+  );
+#endif
+  printf("============================================ DEVICE SELF-TEST OPTIONS =====\n\n");
+#ifdef HAVE_GETOPT_LONG
+  printf(
+"  -t TEST, --test=TEST\n"
+"        Run test.  TEST is: offline short long conveyance select,M-N pending,N afterselect,on afterselect,off\n\n"
+"  -C, --captive\n"
+"        Do test in captive mode (along with -t)\n\n"
+"  -X, --abort\n"
+"        Abort any non-captive test on device\n\n"
+);
+#else
+  printf(
+"  -t TEST   Run test.  TEST is: offline short long conveyance select,M-N pending,N afterselect,on afterselect,off\n"
+"  -C        Do test in captive mode (along with -t)\n"
+"  -X        Abort any non-captive test\n\n"
+  );
+#endif
+  print_smartctl_examples();
+  return;
+}
+
+/* Returns a pointer to a static string containing a formatted list of the valid
+   arguments to the option opt or NULL on failure. Note 'v' case different */
+const char *getvalidarglist(char opt) {
+  switch (opt) {
+  case 'q':
+    return "errorsonly, silent";
+  case 'd':
+    return "ata, scsi, marvell, 3ware,N";
+  case 'T':
+    return "normal, conservative, permissive, verypermissive";
+  case 'b':
+    return "warn, exit, ignore";
+  case 'r':
+    return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
+  case 's':
+  case 'o':
+  case 'S':
+    return "on, off";
+  case 'l':
+    return "error, selftest, selective, directory";
+  case 'P':
+    return "use, ignore, show, showall";
+  case 't':
+    return "offline, short, long, conveyance, select,M-N, pending,N, afterselect,on, afterselect,off";
+  case 'F':
+    return "none, samsung, samsung2";
+  case 'v':
+  default:
+    return NULL;
+  }
+}
+
+/* Prints the message "=======> VALID ARGUMENTS ARE: <LIST> \n", where
+   <LIST> is the list of valid arguments for option opt. */
+void printvalidarglistmessage(char opt) {
+  char *s;
+  
+  if (opt=='v')
+    s=create_vendor_attribute_arg_list();
+  else
+    s=(char *)getvalidarglist(opt);
+  
+  if (!s) {
+    pout("Error whilst constructing argument list for option %c", opt);
+    return;
+  }
+  if (opt=='v'){
+    pout("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n", s);
+    free(s);
+  }
+  else {
+  // getvalidarglist() might produce a multiline or single line string.  We
+  // need to figure out which to get the formatting right.
+    char separator = strchr(s, '\n') ? '\n' : ' ';
+    pout("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, (char *)s, separator);
+  }
+
+  return;
+}
+
+/*      Takes command options and sets features to be run */    
+void ParseOpts (int argc, char** argv){
+  int optchar;
+  int badarg;
+  int captive;
+  unsigned char *charp;
+  extern char *optarg;
+  extern int optopt, optind, opterr;
+  char extraerror[256];
+  // Please update getvalidarglist() if you edit shortopts
+  const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iav:P:t:CXF:";
+#ifdef HAVE_GETOPT_LONG
+  char *arg;
+  // Please update getvalidarglist() if you edit longopts
+  struct option longopts[] = {
+    { "help",            no_argument,       0, 'h' },
+    { "usage",           no_argument,       0, 'h' },
+    { "version",         no_argument,       0, 'V' },
+    { "copyright",       no_argument,       0, 'V' },
+    { "license",         no_argument,       0, 'V' },
+    { "quietmode",       required_argument, 0, 'q' },
+    { "device",          required_argument, 0, 'd' },
+    { "tolerance",       required_argument, 0, 'T' },
+    { "badsum",          required_argument, 0, 'b' },
+    { "report",          required_argument, 0, 'r' },
+    { "smart",           required_argument, 0, 's' },
+    { "offlineauto",     required_argument, 0, 'o' },
+    { "saveauto",        required_argument, 0, 'S' },
+    { "health",          no_argument,       0, 'H' },
+    { "capabilities",    no_argument,       0, 'c' },
+    { "attributes",      no_argument,       0, 'A' },
+    { "log",             required_argument, 0, 'l' },
+    { "info",            no_argument,       0, 'i' },
+    { "all",             no_argument,       0, 'a' },
+    { "vendorattribute", required_argument, 0, 'v' },
+    { "presets",         required_argument, 0, 'P' },
+    { "test",            required_argument, 0, 't' },
+    { "captive",         no_argument,       0, 'C' },
+    { "abort",           no_argument,       0, 'X' },
+    { "firmwarebug",     required_argument, 0, 'F' },
+    { 0,                 0,                 0, 0   }
+  };
+#endif
+  
+  memset(extraerror, 0, sizeof(extraerror));
+  memset(con,0,sizeof(*con));
+  con->testcase=-1;
+  opterr=optopt=0;
+  badarg = captive = FALSE;
+  
+  // This miserable construction is needed to get emacs to do proper indenting. Sorry!
+  while (-1 != (optchar = 
+#ifdef HAVE_GETOPT_LONG
+                getopt_long(argc, argv, shortopts, longopts, NULL)
+#else
+                getopt(argc, argv, shortopts)
+#endif
+                )){
+    switch (optchar){
+    case 'V':
+      con->dont_print=FALSE;
+      printslogan();
+      printcopy();
+      exit(0);
+      break;
+    case 'q':
+      if (!strcmp(optarg,"errorsonly")) {
+        con->printing_switchable     = TRUE;
+        con->dont_print = FALSE;
+      } else if (!strcmp(optarg,"silent")) {
+        con->printing_switchable     = FALSE;
+        con->dont_print = TRUE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'd':
+      if (!strcmp(optarg,"ata")) {
+       con->controller_type = CONTROLLER_ATA;
+        con->controller_port = 0;
+      } else if (!strcmp(optarg,"scsi")) {
+       con->controller_type = CONTROLLER_SCSI;
+        con->controller_port = 0;
+      } else if (!strcmp(optarg,"marvell")) {
+       con->controller_type = CONTROLLER_MARVELL_SATA;
+        con->controller_port = 0;
+      } else {
+        // look for RAID-type device
+        int i;
+        char *s;
+        
+        // make a copy of the string to mess with
+        if (!(s = strdup(optarg))) {
+          con->dont_print = FALSE;
+          pout("No memory for argument of -d. Exiting...\n");
+          exit(FAILCMD);
+        } else if (strncmp(s,"3ware,",6)) {
+          badarg = TRUE;
+        } else if (split_report_arg2(s, &i)) {
+          sprintf(extraerror, "Option -d 3ware,N requires N to be a non-negative integer\n");
+          badarg = TRUE;
+        } else if (i<0 || i>15) {
+          sprintf(extraerror, "Option -d 3ware,N (N=%d) must have 0 <= N <= 15\n", i);
+          badarg = TRUE;
+        } else {
+         // NOTE: controller_port == disk number + 1
+         con->controller_type = CONTROLLER_3WARE;
+          con->controller_port = i+1;
+        }
+        free(s);
+      }         
+      break;
+    case 'T':
+      if (!strcmp(optarg,"normal")) {
+        con->conservative = FALSE;
+        con->permissive   = 0;
+      } else if (!strcmp(optarg,"conservative")) {
+        con->conservative = TRUE;
+      } else if (!strcmp(optarg,"permissive")) {
+        if (con->permissive<0xff)
+          con->permissive++;
+      } else if (!strcmp(optarg,"verypermissive")) {
+        con->permissive=0xff;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'b':
+      if (!strcmp(optarg,"warn")) {
+        con->checksumfail   = FALSE;
+        con->checksumignore = FALSE;
+      } else if (!strcmp(optarg,"exit")) {
+        con->checksumfail   = TRUE;
+        con->checksumignore = FALSE;
+      } else if (!strcmp(optarg,"ignore")) {
+        con->checksumignore = TRUE;
+        con->checksumfail   = FALSE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'r':
+      {
+        int i;
+        char *s;
+
+        // split_report_arg() may modify its first argument string, so use a
+        // copy of optarg in case we want optarg for an error message.
+        if (!(s = strdup(optarg))) {
+          con->dont_print = FALSE;
+          pout("Can't allocate memory to copy argument to -r option"
+               " - exiting\n");
+          EXIT(FAILCMD);
+        }
+        if (split_report_arg(s, &i)) {
+          badarg = TRUE;
+        } else if (!strcmp(s,"ioctl")) {
+          con->reportataioctl  = con->reportscsiioctl = i;
+        } else if (!strcmp(s,"ataioctl")) {
+          con->reportataioctl = i;
+        } else if (!strcmp(s,"scsiioctl")) {
+          con->reportscsiioctl = i;
+        } else {
+          badarg = TRUE;
+        }
+        free(s);
+      }
+      break;
+    case 's':
+      if (!strcmp(optarg,"on")) {
+        con->smartenable  = TRUE;
+        con->smartdisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartdisable = TRUE;
+        con->smartenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'o':
+      if (!strcmp(optarg,"on")) {
+        con->smartautoofflineenable  = TRUE;
+        con->smartautoofflinedisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartautoofflinedisable = TRUE;
+        con->smartautoofflineenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'S':
+      if (!strcmp(optarg,"on")) {
+        con->smartautosaveenable  = TRUE;
+        con->smartautosavedisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartautosavedisable = TRUE;
+        con->smartautosaveenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'H':
+      con->checksmart = TRUE;           
+      break;
+    case 'F':
+      if (!strcmp(optarg,"none")) {
+        con->fixfirmwarebug = FIX_NONE;
+      } else if (!strcmp(optarg,"samsung")) {
+        con->fixfirmwarebug = FIX_SAMSUNG;
+      } else if (!strcmp(optarg,"samsung2")) {
+        con->fixfirmwarebug = FIX_SAMSUNG2;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'c':
+      con->generalsmartvalues = TRUE;
+      break;
+    case 'A':
+      con->smartvendorattrib = TRUE;
+      break;
+    case 'l':
+      if (!strcmp(optarg,"error")) {
+        con->smarterrorlog = TRUE;
+      } else if (!strcmp(optarg,"selftest")) {
+        con->smartselftestlog = TRUE;
+      } else if (!strcmp(optarg, "selective")) {
+       con->selectivetestlog = TRUE;
+      } else if (!strcmp(optarg,"directory")) {
+        con->smartlogdirectory = TRUE;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'i':
+      con->driveinfo = TRUE;
+      break;            
+    case 'a':
+      con->driveinfo          = TRUE;
+      con->checksmart         = TRUE;
+      con->generalsmartvalues = TRUE;
+      con->smartvendorattrib  = TRUE;
+      con->smarterrorlog      = TRUE;
+      con->smartselftestlog   = TRUE;
+      con->selectivetestlog   = TRUE;
+      break;
+    case 'v':
+      // parse vendor-specific definitions of attributes
+      if (!strcmp(optarg,"help")) {
+        char *s;
+        con->dont_print=FALSE;
+        printslogan();
+        if (!(s = create_vendor_attribute_arg_list())) {
+          pout("Insufficient memory to construct argument list\n");
+          EXIT(FAILCMD);
+        }
+        pout("The valid arguments to -v are:\n\thelp\n%s\n", s);
+        free(s);
+        EXIT(0);
+      }
+      charp=con->attributedefs;
+      if (!charp){
+        pout("Fatal internal error in ParseOpts()\n");
+        EXIT(FAILCMD);
+      }
+      if (parse_attribute_def(optarg, &charp))
+        badarg = TRUE;
+      break;    
+    case 'P':
+      if (!strcmp(optarg, "use")) {
+        con->ignorepresets = FALSE;
+      } else if (!strcmp(optarg, "ignore")) {
+        con->ignorepresets = TRUE;
+      } else if (!strcmp(optarg, "show")) {
+        con->showpresets = TRUE;
+      } else if (!strcmp(optarg, "showall")) {
+        if (optind < argc) { // -P showall MODEL [FIRMWARE]
+          int cnt = showmatchingpresets(argv[optind], (optind+1<argc ? argv[optind+1] : NULL));
+          EXIT(cnt); // report #matches
+        }
+        if (showallpresets())
+          EXIT(FAILCMD); // report regexp syntax error
+        EXIT(0);
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 't':
+      if (!strcmp(optarg,"offline")) {
+        con->smartexeoffimmediate = TRUE;
+        con->testcase             = OFFLINE_FULL_SCAN;
+      } else if (!strcmp(optarg,"short")) {
+        con->smartshortselftest = TRUE;
+        con->testcase           = SHORT_SELF_TEST;
+      } else if (!strcmp(optarg,"long")) {
+        con->smartextendselftest = TRUE;
+        con->testcase            = EXTEND_SELF_TEST;
+      } else if (!strcmp(optarg,"conveyance")) {
+        con->smartconveyanceselftest = TRUE;
+        con->testcase            = CONVEYANCE_SELF_TEST;
+      } else if (!strcmp(optarg,"afterselect,on")) {
+       // scan remainder of disk after doing selected segments
+       con->scanafterselect=2;
+      } else if (!strcmp(optarg,"afterselect,off")) {
+       // don't scan remainder of disk after doing selected segments
+       con->scanafterselect=1;
+      } else if (!strncmp(optarg,"pending,",strlen("pending,"))) {
+       // parse number of minutes that test should be pending
+       int i;
+       char *tailptr=NULL;
+       errno=0;
+       i=(int)strtol(optarg+strlen("pending,"), &tailptr, 10);
+       if (errno || *tailptr != '\0') {
+         sprintf(extraerror, "Option -t pending,N requires N to be a non-negative integer\n");
+         badarg = TRUE;
+       } else if (i<0 || i>65535) {
+         sprintf(extraerror, "Option -t pending,N (N=%d) must have 0 <= N <= 65535\n", i);
+         badarg = TRUE;
+       } else {
+         con->pendingtime=i+1;
+       }
+      } else if (!strncmp(optarg,"select",strlen("select"))) {
+       // parse range of LBAs to test
+       uint64_t start, stop;
+
+        if (split_selective_arg(optarg, &start, &stop)) {
+         sprintf(extraerror, "Option -t select,M-N must have non-negative integer M and N\n");
+          badarg = TRUE;
+        } else {
+          if (con->smartselectivenumspans >= 5 || start > stop) {
+            if (start > stop) {
+              sprintf(extraerror, "ERROR: Start LBA (%"PRIu64") > ending LBA (%"PRId64") in argument \"%s\"\n",
+                start, stop, optarg);
+            } else {
+              sprintf(extraerror,"ERROR: No more than five selective self-test spans may be"
+                " defined\n");
+            }
+           badarg = TRUE;
+          }
+          con->smartselectivespan[con->smartselectivenumspans][0] = start;
+          con->smartselectivespan[con->smartselectivenumspans][1] = stop;
+          con->smartselectivenumspans++;
+          con->testcase            = SELECTIVE_SELF_TEST;
+        }
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'C':
+      captive = TRUE;
+      break;
+    case 'X':
+      con->smartselftestabort = TRUE;
+      con->testcase           = ABORT_SELF_TEST;
+      break;
+    case 'h':
+      con->dont_print=FALSE;
+      printslogan();
+      Usage();
+      EXIT(0);  
+      break;
+    case '?':
+    default:
+      con->dont_print=FALSE;
+      printslogan();
+#ifdef HAVE_GETOPT_LONG
+      // Point arg to the argument in which this option was found.
+      arg = argv[optind-1];
+      // Check whether the option is a long option that doesn't map to -h.
+      if (arg[1] == '-' && optchar != 'h') {
+        // Iff optopt holds a valid option then argument must be missing.
+        if (optopt && (strchr(shortopts, optopt) != NULL)) {
+          pout("=======> ARGUMENT REQUIRED FOR OPTION: %s\n", arg+2);
+          printvalidarglistmessage(optopt);
+        } else
+          pout("=======> UNRECOGNIZED OPTION: %s\n",arg+2);
+       if (extraerror[0])
+         pout("=======> %s", extraerror);
+        UsageSummary();
+        EXIT(FAILCMD);
+      }
+#endif
+      if (optopt) {
+        // Iff optopt holds a valid option then argument must be
+        // missing.  Note (BA) this logic seems to fail using Solaris
+        // getopt!
+        if (strchr(shortopts, optopt) != NULL) {
+          pout("=======> ARGUMENT REQUIRED FOR OPTION: %c\n", optopt);
+          printvalidarglistmessage(optopt);
+        } else
+          pout("=======> UNRECOGNIZED OPTION: %c\n",optopt);
+       if (extraerror[0])
+         pout("=======> %s", extraerror);
+        UsageSummary();
+        EXIT(FAILCMD);
+      }
+      Usage();
+      EXIT(0);  
+    } // closes switch statement to process command-line options
+    
+    // Check to see if option had an unrecognized or incorrect argument.
+    if (badarg) {
+      printslogan();
+      // It would be nice to print the actual option name given by the user
+      // here, but we just print the short form.  Please fix this if you know
+      // a clean way to do it.
+      pout("=======> INVALID ARGUMENT TO -%c: %s\n", optchar, optarg);
+      printvalidarglistmessage(optchar);
+      if (extraerror[0])
+       pout("=======> %s", extraerror);
+      UsageSummary();
+      EXIT(FAILCMD);
+    }
+  }
+  // At this point we have processed all command-line options.  If the
+  // print output is switchable, then start with the print output
+  // turned off
+  if (con->printing_switchable)
+    con->dont_print=TRUE;
+
+  // error message if user has asked for more than one test
+  if (1<(con->smartexeoffimmediate+con->smartshortselftest+con->smartextendselftest+
+         con->smartshortcapselftest+con->smartextendcapselftest+con->smartselftestabort + (con->smartselectivenumspans>0?1:0))){
+    con->dont_print=FALSE;
+    printslogan();
+    pout("\nERROR: smartctl can only run a single test type (or abort) at a time.\n");
+    UsageSummary();
+    EXIT(FAILCMD);
+  }
+
+  // error message if user has set selective self-test options without
+  // asking for a selective self-test
+  if ((con->pendingtime || con->scanafterselect) && !con->smartselectivenumspans){
+    con->dont_print=FALSE;
+    printslogan();
+    if (con->pendingtime)
+      pout("\nERROR: smartctl -t pending,N must be used with -t select,N-M.\n");
+    else
+      pout("\nERROR: smartctl -t afterselect,(on|off) must be used with -t select,N-M.\n");
+    UsageSummary();
+    EXIT(FAILCMD);
+  }
+
+  // If captive option was used, change test type if appropriate.
+  if (captive && con->smartshortselftest) {
+    con->smartshortselftest    = FALSE;
+    con->smartshortcapselftest = TRUE;
+    con->testcase              = SHORT_CAPTIVE_SELF_TEST;
+  } else if (captive && con->smartextendselftest) {
+    con->smartextendselftest    = FALSE;
+    con->smartextendcapselftest = TRUE;
+    con->testcase               = EXTEND_CAPTIVE_SELF_TEST;
+  }
+  else if (captive && con->smartconveyanceselftest) {
+    con->smartconveyanceselftest    = FALSE;
+    con->smartconveyancecapselftest = TRUE;
+    con->testcase                   = CONVEYANCE_CAPTIVE_SELF_TEST;
+  }
+  else if (captive && con->smartselectiveselftest) {
+    con->smartselectiveselftest    = FALSE;
+    con->smartselectivecapselftest = TRUE;
+    con->testcase                  = SELECTIVE_CAPTIVE_SELF_TEST;
+  }
+  // From here on, normal operations...
+  printslogan();
+  
+  // Warn if the user has provided no device name
+  if (argc-optind<1){
+    pout("ERROR: smartctl requires a device name as the final command-line argument.\n\n");
+    UsageSummary();
+    EXIT(FAILCMD);
+  }
+  
+  // Warn if the user has provided more than one device name
+  if (argc-optind>1){
+    int i;
+    pout("ERROR: smartctl takes ONE device name as the final command-line argument.\n");
+    pout("You have provided %d device names:\n",argc-optind);
+    for (i=0; i<argc-optind; i++)
+      pout("%s\n",argv[optind+i]);
+    UsageSummary();
+    EXIT(FAILCMD);
+  }  
+}
+
+// Printing function (controlled by global con->dont_print) 
+// [From GLIBC Manual: Since the prototype doesn't specify types for
+// optional arguments, in a call to a variadic function the default
+// argument promotions are performed on the optional argument
+// values. This means the objects of type char or short int (whether
+// signed or not) are promoted to either int or unsigned int, as
+// appropriate.]
+void pout(char *fmt, ...){
+  va_list ap;
+  
+  // initialize variable argument list 
+  va_start(ap,fmt);
+  if (con->dont_print){
+    va_end(ap);
+    return;
+  }
+
+  // print out
+  vprintf(fmt,ap);
+  va_end(ap);
+  fflush(stdout);
+  return;
+}
+
+// This function is used by utility.c to report LOG_CRIT errors.
+// The smartctl version prints to stdout instead of syslog().
+void PrintOut(int priority, char *fmt, ...) {
+  va_list ap;
+
+  // avoid warning message about unused variable from gcc -W: just
+  // change value of local copy.
+  priority=0;
+
+  va_start(ap,fmt);
+  vprintf(fmt,ap);
+  va_end(ap);
+  return;
+}
+
+
+/* Main Program */
+int main (int argc, char **argv){
+  int fd,retval=0;
+  char *device;
+  smartmonctrl control;
+  char *mode=NULL;
+
+  // define control block for external functions
+  con=&control;
+
+  // Part input arguments
+  ParseOpts(argc,argv);
+
+  device = argv[argc-1];
+
+  // If use has specified 3ware controller, determine which interface 
+  if (con->controller_type == CONTROLLER_3WARE) {
+    con->controller_type=guess_device_type(device);
+    if (con->controller_type!=CONTROLLER_3WARE_9000_CHAR && con->controller_type!=CONTROLLER_3WARE_678K_CHAR)
+      con->controller_type = CONTROLLER_3WARE_678K;
+  }
+
+  if (con->controller_type == CONTROLLER_UNKNOWN)
+    con->controller_type=guess_device_type(device);
+  
+  if (con->controller_type == CONTROLLER_UNKNOWN) {
+    pout("Smartctl: please specify device type with the -d option.\n");
+    UsageSummary();
+    return FAILCMD;
+  }
+  
+  // set up mode for open() call.  SCSI case is:
+  switch (con->controller_type) {
+  case CONTROLLER_SCSI:
+    mode="SCSI";
+    break;
+  case CONTROLLER_3WARE_9000_CHAR:
+    mode="ATA_3WARE_9000";
+    break;
+  case CONTROLLER_3WARE_678K_CHAR:
+    mode="ATA_3WARE_678K";
+    break;
+  default:
+    mode="ATA";
+    break;
+  }
+  
+  // open device - SCSI devices are opened (O_RDWR | O_NONBLOCK) so the
+  // scsi generic device can be used (needs write permission for MODE 
+  // SELECT command) plus O_NONBLOCK to stop open hanging if media not
+  // present (e.g. with st).  Opening is retried O_RDONLY if read-only
+  // media prevents opening O_RDWR (it cannot happen for scsi generic
+  // devices, but it can for the others).
+  fd = deviceopen(device, mode);
+  if (fd<0) {
+    char errmsg[256];
+    snprintf(errmsg,256,"Smartctl open device: %s failed",argv[argc-1]);
+    errmsg[255]='\0';
+    syserror(errmsg);
+    return FAILDEV;
+  }
+
+  // now call appropriate ATA or SCSI routine
+  switch (con->controller_type) {
+  case CONTROLLER_UNKNOWN:
+    // we should never fall into this branch!
+    pout("Smartctl: please specify device type with the -d option.\n");
+    UsageSummary();
+    retval = FAILCMD;
+    break;
+  case CONTROLLER_SCSI:
+    retval = scsiPrintMain(fd);
+    break;
+  default:
+    retval = ataPrintMain(fd);
+    break;
+  }
+  
+  return retval;
+}
diff --git a/smartctl.h b/smartctl.h
new file mode 100644 (file)
index 0000000..cba0616
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * smartctl.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+#ifndef SMARTCTL_H_
+#define SMARTCTL_H_
+
+#define SMARTCTL_H_CVSID "$Id: smartctl.h,v 1.23 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+/* Boolean Values */
+#define TRUE 0x01
+#define FALSE 0x00
+
+// Return codes (bitmask)
+
+// command line did not parse, or internal error occured in smartctl
+#define FAILCMD   (0x01<<0)
+
+// device open failed
+#define FAILDEV   (0x01<<1)
+
+// read device identity (ATA only) failed
+#define FAILID    (0x01<<1)
+
+// smart command failed, or ATA identify device structure missing information
+#define FAILSMART (0x01<<2)
+
+// SMART STATUS returned FAILURE
+#define FAILSTATUS (0x01<<3)
+
+// Attributes found <= threshold with prefail=1
+#define FAILATTR (0x01<<4)
+
+// SMART STATUS returned GOOD but age attributes failed or prefail
+// attributes have failed in the past
+#define FAILAGE (0x01<<5)
+
+// Device had Errors in the error log
+#define FAILERR (0x01<<6)
+
+// Device had Errors in the self-test log
+#define FAILLOG (0x01<<7)
+
+// Classes of SMART commands.  Here 'mandatory' means "Required by the
+// ATA/ATAPI-5 Specification if the device implements the S.M.A.R.T.
+// command set."  The 'mandatory' S.M.A.R.T.  commands are: (1)
+// Enable/Disable Attribute Autosave, (2) Enable/Disable S.M.A.R.T.,
+// and (3) S.M.A.R.T. Return Status.  All others are optional.
+#define OPTIONAL_CMD 1
+#define MANDATORY_CMD 2
+
+void print_smartctl_examples();
+
+#endif
diff --git a/smartd.8.in b/smartd.8.in
new file mode 100644 (file)
index 0000000..16b426a
--- /dev/null
@@ -0,0 +1,1844 @@
+.ig
+Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+$Id: smartd.8.in,v 1.100 2006/04/12 13:55:44 ballen4705 Exp $
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+You should have received a copy of the GNU General Public License (for
+example COPYING); if not, write to the Free Software Foundation, Inc.,
+675 Mass Ave, Cambridge, MA 02139, USA.
+This code was originally developed as a Senior Thesis by Michael
+Cornwell at the Concurrent Systems Laboratory (now part of the Storage
+Systems Research Center), Jack Baskin School of Engineering,
+University of California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+..
+.TH SMARTD 8 CURRENT_CVS_DATE CURRENT_CVS_VERSION CURRENT_CVS_DATE
+.SH NAME
+\fBsmartd\fP \- SMART Disk Monitoring Daemon
+
+.SH SYNOPSIS
+.B smartd [options]
+
+.SH FULL PATH
+.B /usr/local/sbin/smartd
+
+.SH PACKAGE VERSION
+CURRENT_CVS_VERSION released CURRENT_CVS_DATE at CURRENT_CVS_TIME
+
+.SH DESCRIPTION
+\fBsmartd\fP is a daemon that monitors the Self-Monitoring, Analysis
+and Reporting Technology (SMART) system built into many ATA-3 and
+later ATA, IDE and SCSI-3 hard drives. The purpose of SMART is to
+monitor the reliability of the hard drive and predict drive failures,
+and to carry out different types of drive self-tests.  This version of
+\fBsmartd\fP is compatible with ATA/ATAPI-7 and earlier standards (see
+\fBREFERENCES\fP below).
+
+\fBsmartd\fP will attempt to enable SMART monitoring on ATA devices
+(equivalent to \fBsmartctl -s on\fP) and polls these and SCSI devices
+every 30 minutes (configurable), logging SMART errors and changes of
+SMART Attributes via the SYSLOG interface.  The default location for
+these SYSLOG notifications and warnings is \fB/var/log/messages\fP.
+To change this default location, please see the \fB\'-l\'\fP
+command-line option described below.
+
+In addition to logging to a file, \fBsmartd\fP can also be configured
+to send email warnings if problems are detected.  Depending upon the
+type of problem, you may want to run self\-tests on the disk, back up
+the disk, replace the disk, or use a manufacturer\'s utility to force
+reallocation of bad or unreadable disk sectors.  If disk problems are
+detected, please see the \fBsmartctl\fP manual page and the
+\fBsmartmontools\fP web page/FAQ for further guidance.
+
+If you send a \fBUSR1\fP signal to \fBsmartd\fP it will immediately
+check the status of the disks, and then return to polling the disks
+every 30 minutes. See the \fB\'\-i\'\fP option below for additional
+details.
+
+\fBsmartd\fP can be configured at start-up using the configuration
+file \fB/usr/local/etc/smartd.conf\fP (Windows: \fB./smartd.conf\fP).
+If the configuration file is subsequently modified, \fBsmartd\fP
+can be told to re-read the configuration file by sending it a
+\fBHUP\fP signal, for example with the command:
+.fi
+\fBkillall -HUP smartd\fP.
+.fi
+(Windows: See NOTES below.)
+
+On startup, if \fBsmartd\fP finds a syntax error in the configuration
+file, it will print an error message and then exit. However if
+\fBsmartd\fP is already running, then is told with a \fBHUP\fP signal
+to re-read the configuration file, and then find a syntax error in
+this file, it will print an error message and then continue, ignoring
+the contents of the (faulty) configuration file, as if the \fBHUP\fP
+signal had never been received.
+
+When \fBsmartd\fP is running in debug mode, the \fBINT\fP signal
+(normally generated from a shell with CONTROL\-C) is treated in the
+same way as a \fBHUP\fP signal: it makes \fBsmartd\fP reload its
+configuration file. To exit \fBsmartd\fP use CONTROL-\e
+(Cygwin: 2x CONTROL\-C, Windows: CONTROL\-Break).
+
+On startup, in the absence of the configuration file
+\fB/usr/local/etc/smartd.conf\fP, the \fBsmartd\fP daemon first scans for all
+devices that support SMART.  The scanning is done as follows:
+.IP \fBLINUX:\fP 9
+Examine all entries \fB"/dev/hd[a-t]"\fP for IDE/ATA
+devices, and \fB"/dev/sd[a-z]"\fP for SCSI devices.
+.IP \fBFREEBSD:\fP 9
+Examine all entries \fB"/dev/ad[0-9]+"\fP for IDE/ATA
+devices and \fB"/dev/da[0-9]+"\fP for SCSI devices.
+.IP \fBNETBSD/OPENBSD:\fP 9
+Authoritative list of disk devices is obtained from sysctl 
+\'hw.disknames\'.
+.IP \fBSOLARIS:\fP 9
+Examine all entries \fB"/dev/rdsk/c?t?d?s?"\fP for IDE/ATA and SCSI disk
+devices, and entries \fB"/dev/rmt/*"\fP for SCSI tape devices.
+.IP \fBDARWIN:\fP 9
+The IOService plane is scanned for ATA block storage devices.
+.IP \fBWINDOWS:\fP 9
+Examine all entries \fB"/dev/hd[a-j]"\fP ("\\\\.\\PhysicalDrive[0-9]")
+for IDE/ATA devices on WinNT4/2000/XP, \fB"/dev/hd[a-d]"\fP
+(bitmask from "\\\\.\\SMARTVSD") for IDE/ATA devices on Win95/98/98SE/ME,
+and \fB"/dev/scsi[0-9][0-7]"\fP (ASPI adapter 0-9, ID 0-7) for SCSI
+devices on all versions of Windows.
+.IP \fBCYGWIN\fP: 9
+See "WINDOWS" above.
+.IP \fBOS/2,eComStation\fP: 9
+Use the form \fB"/dev/hd[a\-z]"\fP for IDE/ATA devices.
+.PP
+\fBsmartd\fP then monitors
+for \fIall\fP possible SMART errors (corresponding to the \fB\'\-a\'\fP
+Directive in the configuration file; see \fBCONFIGURATION FILE\fP
+below). 
+
+.SH 
+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
+
+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
+message and exit with nonzero status.  Thus, \'\-c /usr/local/etc/smartd.conf\'
+can be used to verify the existence of the default configuration file.
+
+By using \'\-\' for FILE, the configuration is read from standard
+input. This is useful for commands like:
+.nf
+.B echo /dev/hdb \-m user@home \-M test | smartd \-c \- \-q onecheck
+.fi
+to perform quick and simple checks without a configuration file.
+
+.TP
+.B \-d, \-\-debug
+Runs \fBsmartd\fP in "debug" mode. In this mode, it displays status
+information to STDOUT rather than logging it to SYSLOG and does not
+\fBfork(2)\fP into the background and detach from the controlling
+terminal.  In this mode, \fBsmartd\fP also prints more verbose
+information about what it is doing than when operating in "daemon"
+mode. In this mode, the \fBQUIT\fP signal (normally generated from a
+terminal with CONTROL\-C) makes \fBsmartd\fP reload its configuration
+file.  Please use CONTROL-\e to exit
+(Cygwin: 2x CONTROL\-C, Windows: CONTROL\-Break).
+
+Windows only: The "debug" mode can be toggled by the command
+\fBsmartd sigusr2\fP. A new console for debug output is opened when
+debug mode is enabled.
+.TP
+.B \-D, \-\-showdirectives
+Prints a list (to STDOUT) of all the possible Directives which may
+appear in the configuration file /usr/local/etc/smartd.conf, and then exits.
+These Directives are also described later in this man page. They may
+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
+\fIN\fP is a decimal integer.  The minimum allowed value is ten and
+the maximum is the largest positive integer that can be represented on
+your system (often 2^31-1).  The default is 1800 seconds.
+
+Note that the superuser can make \fBsmartd\fP check the status of the
+disks at any time by sending it the \fBSIGUSR1\fP signal, for example
+with the command:
+.nf
+.B kill -SIGUSR1 <pid>
+.fi
+where \fB<pid>\fP is the process id number of \fBsmartd\fP.  One may
+also use:
+.nf
+.B killall -USR1 smartd
+.fi
+for the same purpose.
+.fi
+(Windows: See NOTES below.)
+
+.TP
+.B \-l FACILITY, \-\-logfacility=FACILITY
+Uses syslog facility FACILITY to log the messages from \fBsmartd\fP.
+Here FACILITY is one of \fIlocal0\fP, \fIlocal1\fP, ..., \fIlocal7\fP,
+or \fIdaemon\fP [default].  If this command-line option is not used,
+then by default messages from \fBsmartd\fP are logged to the facility
+\fIdaemon\fP.
+
+If you would like to have \fBsmartd\fP messages logged somewhere other
+than the default \fB/var/log/messages\fP location, this can typically
+be accomplished with (for example) the following steps:
+.RS 7
+.IP \fB[1]\fP 4
+Modify the script that starts \fBsmartd\fP to include the \fBsmartd\fP
+command-line argument \'\-l local3\'.  This tells \fBsmartd\fP to log its
+messages to facility \fBlocal3\fP.
+.IP \fB[2]\fP 4
+Modify the \fBsyslogd\fP configuration file (typically
+\fB/etc/syslog.conf\fP) by adding a line of the form:
+.nf
+\fBlocal3.* /var/log/smartd.log\fP
+.fi
+This tells \fBsyslogd\fP to log all the messages from facility \fBlocal3\fP to
+the designated file: /var/log/smartd.log.
+.IP \fB[3]\fP 4
+Tell \fBsyslogd\fP to re-read its configuration file, typically by
+sending the \fBsyslogd\fP process a \fBSIGHUP\fP hang-up signal.
+.IP \fB[4]\fP 4
+Start (or restart) the \fBsmartd\fP daemon.
+.RE
+.\"  The following two lines are a workaround for a man2html bug.  Please leave them.
+.\" They define a non-existent option; useful because man2html can't correctly reset the margins.
+.TP
+.B \&
+For more detailed information, please refer to the man pages for
+\fBsyslog.conf\fP, \fBsyslogd\fP, and \fBsyslog\fP.  You may also want
+to modify the log rotation configuration files; see the man pages for
+\fBlogrotate\fP and examine your system\'s /etc/logrotate.conf file.
+
+Cygwin: Support for \fBsyslogd\fP as described above is available starting with Cygwin 1.5.15.
+On older releases or if no local \fBsyslogd\fP is running, the \'\-l\' option has no effect.
+In this case, all \fBsyslog\fP messages are written to Windows event log
+or to file \fBC:/CYGWIN_SYSLOG.TXT\fP if the event log is not available.
+
+Windows: Some \fBsyslog\fP functionality is implemented
+internally in \fBsmartd\fP as follows: If no \'\-l\' option
+(or \'\-l daemon\') is specified, messages are written to Windows
+event log or to file \fB./smartd.log\fP if event log is not available
+(Win9x/ME or access denied). By specifying other values of FACILITY,
+log output is redirected as follows:
+\'\-l local0\' to file \fB./smartd.log\fP,
+\'\-l local1\' to standard output (redirect with \'>\' to any file),
+\'\-l local2\' to standard error,
+\'\-l local[3-7]\': to file \fB./smartd[1-5].log\fP.
+
+When using the event log, the enclosed utility \fBsyslogevt.exe\fP
+should be registered as an event message file to avoid error
+messages from the event viewer. Use \'\fBsyslogevt -r smartd\fP\'
+to register, \'\fBsyslogevt -u smartd\fP\' to unregister and
+\'\fBsyslogevt\fP\' for more help.
+
+.TP
+.B \-p NAME, \-\-pidfile=NAME
+Writes pidfile \fINAME\fP containing the \fBsmartd\fP Process ID
+number (PID).  To avoid symlink attacks make sure the directory to
+which pidfile is written is only writable for root.  Without this
+option, or if the \-\-debug option is given, no PID file is written on
+startup.  If \fBsmartd\fP is killed with a maskable signal then the
+pidfile is removed.
+.TP
+.B \-q WHEN, \-\-quit=WHEN
+Specifies when, if ever, \fBsmartd\fP should exit.  The valid
+arguments are to this option are:
+
+.I nodev
+\- Exit if there are no devices to monitor, or if any errors are found
+at startup in the configuration file.  This is the default.
+
+.I errors
+\- Exit if there are no devices to monitor, or if any errors are found
+in the configuration file /usr/local/etc/smartd.conf at startup or whenever it
+is reloaded.
+
+.I nodevstartup
+\- Exit if there are no devices to monitor at startup.  But continue
+to run if no devices are found whenever the configuration file is
+reloaded.
+
+.I never
+\- Only exit if a fatal error occurs (no remaining system memory,
+invalid command line arguments). In this mode, even if there are no
+devices to monitor, or if the configuration file
+\fB/usr/local/etc/smartd.conf\fP has errors, \fBsmartd\fP will continue to run,
+waiting to load a configuration file listing valid devices.
+
+.I onecheck
+\- Start \fBsmartd\fP in debug mode, then register devices, then check
+device\'s SMART status once, and then exit with zero exit status if all
+of these steps worked correctly.
+
+This last option is intended for \'distribution-writers\' who want to
+create automated scripts to determine whether or not to automatically
+start up \fBsmartd\fP after installing smartmontools.  After starting
+\fBsmartd\fP with this command-line option, the distribution\'s install
+scripts should wait a reasonable length of time (say ten seconds).  If
+\fBsmartd\fP has not exited with zero status by that time, the script
+should send \fBsmartd\fP a SIGTERM or SIGKILL and assume that
+\fBsmartd\fP will not operate correctly on the host.  Conversely, if
+\fBsmartd\fP exits with zero status, then it is safe to run
+\fBsmartd\fP in normal daemon mode. If \fBsmartd\fP is unable to
+monitor any devices or encounters other problems then it will return
+with non-zero exit status.
+
+.I showtests
+\- Start \fBsmartd\fP in debug mode, then register devices, then write
+a list of future scheduled self tests to stdout, and then exit with zero
+exit status if all of these steps worked correctly.
+Device's SMART status is not checked.
+
+This option is intended to test whether the '-s REGEX' directives in
+smartd.conf will have the desired effect. The output lists the next test
+schedules, limited to 5 tests per type and device. This is followed by a
+summary of all tests of each device within the next 90 days.
+.TP
+.B \-r TYPE, \-\-report=TYPE
+Intended primarily to help
+.B smartmontools
+developers understand the behavior of
+.B smartmontools
+on non-conforming or poorly-conforming hardware.  This option reports
+details of
+\fBsmartd\fP
+transactions with the device.  The option can be used multiple times.
+When used just once, it shows a record of the ioctl() transactions
+with the device.  When used more than once, the detail of these ioctl()
+transactions are reported in greater detail.  The valid arguments to
+this option are:
+
+.I ioctl
+\- report all ioctl() transactions.
+
+.I ataioctl
+\- report only ioctl() transactions with ATA devices.
+
+.I scsiioctl
+\- report only ioctl() transactions with SCSI devices.
+
+Any argument may include a positive integer to specify the level of
+detail that should be reported.  The argument should be followed by a
+comma then the integer with no spaces.  For example, \fIataioctl,2\fP
+The default level is 1, so \'\-r ataioctl,1\' and \'\-r ataioctl\' are
+equivalent.
+
+.TP
+.B \-\-service
+Cygwin and Windows only: Enables \fBsmartd\fP to run as a Windows service.
+
+On Cygwin, this option simply prevents fork'ing into background mode to
+allow running \fBsmartd\fP as service via cygrunsrv, see NOTES below.
+
+On Windows, this option enables the buildin service support.
+The option must be specified in the service command line as the first
+argument. It should not be used from console.
+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.
+
+.SH EXAMPLES
+
+.B
+smartd
+.fi
+Runs the daemon in forked mode. This is the normal way to run
+\fBsmartd\fP.
+Entries are logged to SYSLOG (by default
+.B /var/log/messages.)
+
+.B
+smartd -d -i 30
+.fi
+Run in foreground (debug) mode, checking the disk status
+every 30 seconds.
+
+.B
+smartd -q onecheck
+.fi
+Registers devices, and checks the status of the devices exactly
+once. The exit status (the bash
+.B $?
+variable) will be zero if all went well, and nonzero if no devices
+were detected or some other problem was encountered.
+
+.fi 
+Note that \fBsmartmontools\fP provides a start-up script in
+\fB/usr/local/etc/rc.d/init.d/smartd\fP which is responsible for starting and
+stopping the daemon via the normal init interface.  Using this script,
+you can start \fBsmartd\fP by giving the command:
+.nf
+.B /usr/local/etc/rc.d/init.d/smartd start
+.fi
+and stop it by using the command:
+.nf
+.B /usr/local/etc/rc.d/init.d/smartd stop
+
+.fi
+If you want \fBsmartd\fP to start running whenever your machine is
+booted, this can be enabled by using the command:
+.nf
+.B /sbin/chkconfig --add smartd
+.fi
+and disabled using the command:
+.nf
+.B /sbin/chkconfig --del smartd
+.fi
+
+.\" DO NOT MODIFY THIS OR THE FOLLOWING TWO LINES. THIS MATERIAL
+.\" IS AUTOMATICALLY INCLUDED IN THE FILE smartd.conf.5
+.\" STARTINCLUDE
+
+.SH CONFIGURATION FILE /usr/local/etc/smartd.conf
+In the absence of a configuration file, under Linux
+\fBsmartd\fP 
+will try to open the 20 ATA devices 
+.B /dev/hd[a-t] 
+and the 26 SCSI devices
+.B /dev/sd[a-z].
+Under FreeBSD, 
+\fBsmartd\fP
+will try to open all existing ATA devices (with entries in /dev)
+.B /dev/ad[0-9]+
+and all existing SCSI devices
+.B /dev/da[0-9]+.  
+Under NetBSD/OpenBSD, 
+\fBsmartd\fP
+will try to open all existing ATA devices (with entries in /dev)
+.B /dev/wd[0-9]+c
+and all existing SCSI devices
+.B /dev/sd[0-9]+c.
+Under Solaris \fBsmartd\fP will try to open all entries \fB"/dev/rdsk/c?t?d?s?"\fP for IDE/ATA and SCSI disk
+devices, and entries \fB"/dev/rmt/*"\fP for SCSI tape devices.
+Under Windows \fBsmartd\fP will try to open all entries \fB"/dev/hd[a-j]"\fP ("\\\\.\\PhysicalDrive[0-9]")
+for IDE/ATA devices on WinNT4/2000/XP, \fB"/dev/hd[a-d]"\fP
+(bitmask from "\\\\.\\SMARTVSD") for IDE/ATA devices on Win95/98/98SE/ME,
+and \fB"/dev/scsi[0-9][0-7]"\fP (ASPI adapter 0-9, ID 0-7) for SCSI
+devices on all versions of Windows.
+Under Darwin, \fBsmartd\fP will open any ATA block storage device.
+
+This can be annoying if you have an ATA or SCSI device that hangs or
+misbehaves when receiving SMART commands.  Even if this causes no
+problems, you may be annoyed by the string of error log messages about
+block-major devices that can\'t be found, and SCSI devices that can\'t
+be opened.
+
+One can avoid this problem, and gain more control over the types of
+events monitored by
+\fBsmartd\fP,
+by using the configuration file
+.B /usr/local/etc/smartd.conf.
+This file contains a list of devices to monitor, with one device per
+line.  An example file is included with the
+.B smartmontools
+distribution. You will find this sample configuration file in
+\fB/usr/local/share/doc/smartmontools-5.1/\fP. For security, the configuration file
+should not be writable by anyone but root. The syntax of the file is as
+follows:
+.IP \(bu 4
+There should be one device listed per line, although you may have
+lines that are entirely comments or white space.
+.IP \(bu 4
+Any text following a hash sign \'#\' and up to the end of the line is
+taken to be a comment, and ignored.
+.IP \(bu 4
+Lines may be continued by using a backslash \'\e\' as the last
+non-whitespace or non-comment item on a line.
+.IP \(bu 4
+Note: a line whose first character is a hash sign \'#\' is treated as
+a white-space blank line, \fBnot\fP as a non-existent line, and will
+\fBend\fP a continuation line.
+.PP 0
+.fi
+Here is an example configuration file.  It\'s for illustrative purposes
+only; please don\'t copy it onto your system without reading to the end
+of the
+.B DIRECTIVES
+Section below!
+
+.nf
+.B ################################################
+.B # This is an example smartd startup config file
+.B # /usr/local/etc/smartd.conf for monitoring three
+.B # ATA disks, three SCSI disks, six ATA disks
+.B # behind two 3ware controllers and one SATA disk
+.B #
+.nf
+.B # First ATA disk on two different interfaces. On
+.B # the second disk, start a long self-test every
+.B # Sunday between 3 and 4 am.
+.B #
+.B \ \ /dev/hda -a -m admin@example.com,root@localhost 
+.B \ \ /dev/hdc -a -I 194 -I 5 -i 12 -s L/../../7/03
+.B #
+.nf
+.B # SCSI disks.  Send a TEST warning email to admin on
+.B # startup.
+.B #
+.B \ \ /dev/sda
+.B \ \ /dev/sdb -m admin@example.com -M test
+.B #
+.nf
+.B # Strange device.  It\'s SCSI. Start a scheduled
+.B # long self test between 5 and 6 am Monday/Thursday
+.B \ \ /dev/weird -d scsi -s L/../../(1|4)/05
+.B #
+.nf
+.B # Linux-specific: SATA disk using the libata
+.B # driver.  This requires a 2.6.15 or greater
+.B # kernel.  The device entry is SCSI but the
+.B # underlying disk understands ATA SMART commands
+.B \ \ /dev/sda -a -d ata
+.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
+.B # kernel series, /dev/sdX is deprecated in favor of
+.B # /dev/tweN.  For example replace /dev/sdc by /dev/twe0
+.B # and /dev/sdd by /dev/twe1.
+.B \ \ /dev/sdc -d 3ware,0 -a -s S/../.././00
+.B \ \ /dev/sdc -d 3ware,1 -a -s S/../.././01
+.B \ \ /dev/sdd -d 3ware,2 -a -s S/../.././02
+.B \ \ /dev/sdd -d 3ware,3 -a -s S/../.././03
+.B #
+.nf
+.B # Two ATA disks on a 3ware 9000 controller.
+.B # Start long self-tests Sundays between  midnight and 
+.B # 1am and 2-3 am
+.B \ \ /dev/twa0 -d 3ware,0 -a -s L/../../7/00
+.B \ \ /dev/twa0 -d 3ware,1 -a -s L/../../7/02
+.B #
+.nf
+.B # The following line enables monitoring of the 
+.B # ATA Error Log and the Self-Test Error Log.  
+.B # It also tracks changes in both Prefailure
+.B # and Usage Attributes, apart from Attributes
+.B # 9, 194, and 231, and shows  continued lines:
+.B #
+.B \ \ /dev/hdd\ -l\ error\ \e
+.B \ \ \ \ \ \ \ \ \ \ \ -l\ selftest\ \e
+.B \ \ \ \ \ \ \ \ \ \ \ -t\ \e\ \ \ \ \ \ # Attributes not tracked:
+.B \ \ \ \ \ \ \ \ \ \ \ -I\ 194\ \e\ \ # temperature
+.B \ \ \ \ \ \ \ \ \ \ \ -I\ 231\ \e\ \ # also temperature
+.B \ \ \ \ \ \ \ \ \ \ \ -I 9\ \ \ \ \ \ # power-on hours
+.B #
+.B ################################################
+.fi
+
+.PP 
+.SH CONFIGURATION FILE DIRECTIVES
+.PP
+
+If the first non-comment entry in the configuration file is the text
+string
+.B DEVICESCAN
+in capital letters, then
+\fBsmartd\fP
+will ignore any remaining lines in the configuration file, and will
+scan for devices.
+.B DEVICESCAN
+may optionally be followed by Directives that will apply to all
+devices that are found in the scan.  Please see below for additional
+details.
+
+.sp 2
+The following are the Directives that may appear following the device
+name or
+.B DEVICESCAN
+on any line of the
+.B /usr/local/etc/smartd.conf
+configuration file. Note that
+.B these are NOT command-line options for 
+\fBsmartd\fP.
+The Directives below may appear in any order, following the device
+name. 
+
+.B For an ATA device,
+if no Directives appear, then the device will be monitored
+as if the \'\-a\' Directive (monitor all SMART properties) had been given.
+
+.B If a SCSI disk is listed,
+it will be monitored at the maximum implemented level: roughly
+equivalent to using the \'\-H \-l selftest\' options for an ATA disk.
+So with the exception of \'\-d\', \'\-m\', \'\-l selftest\', \'\-s\', and
+\'\-M\', the Directives below are ignored for SCSI disks.  For SCSI
+disks, the \'\-m\' Directive sends a warning email if the SMART status
+indicates a disk failure or problem, if the SCSI inquiry about disk
+status fails, or if new errors appear in the self-test log.
+
+.B If a 3ware controller is used
+then the corresponding SCSI (/dev/sd?) or character device (/dev/twe?
+or /dev/twa?) must be listed, along with the \'\-d 3ware,N\' Directive
+(see below).  The individual ATA disks hosted by the 3ware controller
+appear to \fBsmartd\fP as normal ATA devices.  Hence all the ATA
+directives can be used for these disks (but see note below).
+
+.TP
+.B \-d TYPE
+Specifies the type of the device.  This Directive may be used multiple
+times for one device, but the arguments \fIata\fP, \fIscsi\fP,
+\fImarvell\fP, and \fI3ware,N\fP are mutually-exclusive. If more than
+one is given then \fBsmartd\fP will use the last one which appears.
+
+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
+character in the device name is an \'s\' or an \'h\'.  This will work for
+device names like /dev/hda or /dev/sdb, and corresponds to choosing
+\fIata\fP or \fIscsi\fP respectively. If
+\fBsmartd\fP
+can\'t guess from this sixth character, then it will simply try to
+access the device using first ATA and then SCSI ioctl()s.
+
+The valid arguments to this Directive are:
+
+.I ata
+\- the device type is ATA.  This prevents
+\fBsmartd\fP
+from issuing SCSI commands to an ATA device.
+
+.I scsi
+\- the device type is SCSI.  This prevents
+\fBsmartd\fP
+from issuing ATA commands to a SCSI device.
+
+.I marvell
+\- Under Linux, interact with SATA disks behind Marvell chip-set
+controllers (using the Marvell rather than libata driver).
+
+.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 15
+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 15 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
+such in the the configuration file.
+However when the \'\-d 3ware,N\'
+Directive is used, then the corresponding disk is addressed using
+native ATA commands which are \'passed through\' the SCSI driver. All
+ATA Directives listed in this man page may be used.  Note that while
+you may use \fBany\fP of the 3ware SCSI logical devices /dev/sd? to
+address \fBany\fP of the physical disks (3ware ports), error and log
+messages will make the most sense if you always list the 3ware SCSI
+logical device corresponding to the particular physical disks.  Please
+see the \fBsmartctl\fP man page for further details.
+
+ATA disks behind 3ware controllers may alternatively be accessed via a
+character device interface /dev/twe0-15 (3ware 6000/7000/8000
+controllers) and /dev/twa0-15 (3ware 9000 series controllers).  Note
+that the 9000 series controllers may \fBonly\fP be accessed using the
+character device interface /dev/twa0-15 and not the SCSI device
+interface /dev/sd?.  Please see the \fBsmartctl\fP man page for
+further details.
+
+Note that older 3w-xxxx drivers do not pass the \'Enable Autosave\'
+(\fB-S on\fP) and \'Enable Automatic Offline\' (\fB-o on\fP) commands
+to the disk, if the SCSI interface is used, and produce these types of
+harmless syslog error messages instead: \fB\'3w-xxxx: tw_ioctl():
+Passthru size (123392) too big\'\fP. This can be fixed by upgrading to
+version 1.02.00.037 or later of the 3w-xxxx driver, or by applying a
+patch to older versions.  See
+\fBhttp://smartmontools.sourceforge.net/\fP for instructions.
+Alternatively use the character device interfaces /dev/twe0-15 (3ware
+6/7/8000 series controllers) or /dev/twa0-15 (3ware 9000 series
+controllers).
+
+
+.B 3ware controllers are currently ONLY supported under Linux.
+
+.I removable
+\- the device or its media is removable.  This indicates to
+\fBsmartd\fP
+that it should continue (instead of exiting, which is the default
+behavior) if the device does not appear to be present when
+\fBsmartd\fP is started.  This Directive may be used in conjunction
+with the other \'\-d\' Directives.
+
+.TP
+.B \-n POWERMODE[,q]
+This \'nocheck\' Directive is used to prevent a disk from being
+spun-up when it is periodically polled by \fBsmartd\fP.
+
+ATA disks have five different power states. In order of increasing
+power consumption they are: \'OFF\', \'SLEEP\', \'STANDBY\', \'IDLE\',
+and \'ACTIVE\'.  Typically in the OFF, SLEEP, and STANDBY modes the
+disk\'s platters are not spinning. But usually, in response to SMART
+commands issued by \fBsmartd\fP, the disk platters are spun up.  So if
+this option is not used, then a disk which is in a low\-power mode may
+be spun up and put into a higher\-power mode when it is periodically
+polled by \fBsmartd\fP.
+
+Note that if the disk is in SLEEP mode when \fBsmartd\fP is started,
+then it won't respond to \fBsmartd\fP commands, and so the disk won't
+be registered as a device for \fBsmartd\fP to monitor. If a disk is in
+any other low\-power mode, then the commands issued by \fBsmartd\fP to
+register the disk will probably cause it to spin\-up.
+
+The \'\fB\-n\fP\' (nocheck) Directive specifies if \fBsmartd\fP\'s
+periodic checks should still be carried out when the device is in a
+low\-power mode.  It may be used to prevent a disk from being spun\-up
+by periodic \fBsmartd\fP polling.  The allowed values of POWERMODE
+are:
+
+.I never
+\- \fBsmartd\fP will poll (check) the device regardless of its power
+mode. This may cause a disk which is spun\-down to be spun\-up when
+\fBsmartd\fP checks it.  This is the default behavior if the '\-n'
+Directive is not given.
+
+.I sleep
+\- check the device unless it is in SLEEP mode.
+
+.I standby
+\- check the device unless it is in SLEEP or STANDBY mode.  In
+these modes most disks are not spinning, so if you want to prevent
+a laptop disk from spinning up each time that \fBsmartd\fP polls,
+this is probably what you want.
+
+.I idle
+\- check the device unless it is in SLEEP, STANDBY or IDLE mode.
+In the IDLE state, most disks are still spinning, so this is probably
+not what you want.
+
+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.
+
+.TP
+.B \-T TYPE
+Specifies how tolerant
+\fBsmartd\fP
+should be of SMART command failures.  The valid arguments to this
+Directive are:
+
+.I normal
+\- do not try to monitor the disk if a mandatory SMART command fails, but
+continue if an optional SMART command fails.  This is the default.
+
+.I permissive
+\- try to monitor the disk even if it appears to lack SMART
+capabilities.  This may be required for some old disks (prior to
+ATA\-3 revision 4) that implemented SMART before the SMART standards
+were incorporated into the ATA/ATAPI Specifications.  This may also be
+needed for some Maxtor disks which fail to comply with the ATA
+Specifications and don't properly indicate support for error\- or
+self\-test logging.
+
+[Please see the \fBsmartctl \-T\fP command-line option.]
+.TP
+.B \-o VALUE
+Enables or disables SMART Automatic Offline Testing when
+\fBsmartd\fP
+starts up and has no further effect.  The valid arguments to this
+Directive are \fIon\fP and \fIoff\fP.
+
+The delay between tests is vendor-specific, but is typically four
+hours.
+
+Note that SMART Automatic Offline Testing is \fBnot\fP part of the ATA
+Specification.  Please see the
+.B smartctl \-o
+command-line option documentation for further information about this
+feature.
+.TP
+.B \-S VALUE
+Enables or disables Attribute Autosave when \fBsmartd\fP
+starts up and has no further effect.  The valid arguments to this
+Directive are \fIon\fP and \fIoff\fP.  Also affects SCSI devices.
+[Please see the \fBsmartctl \-S\fP command-line option.]
+.TP
+.B \-H
+Check the SMART health status of the disk.  If any Prefailure
+Attributes are less than or equal to their threshold values, then disk
+failure is predicted in less than 24 hours, and a message at loglevel
+.B \'LOG_CRITICAL\'
+will be logged to syslog.  [Please see the
+.B smartctl \-H
+command-line option.]
+.TP
+.B \-l TYPE
+Reports increases in the number of errors in one of the two SMART logs.  The
+valid arguments to this Directive are:
+
+.I error
+\- report if the number of ATA errors reported in the ATA Error Log
+has increased since the last check.
+
+.I selftest
+\- report if the number of failed tests reported in the SMART
+Self-Test Log has increased since the last check, or if the timestamp
+associated with the most recent failed test has increased.  Note that
+such errors will \fBonly\fP be logged if you run self-tests on the
+disk (and it fails a test!).  Self-Tests can be run automatically by
+\fBsmartd\fP: please see the \fB\'\-s\'\fP Directive below.
+Self-Tests can also be run manually by using the \fB\'\-t\ short\'\fP
+and \fB\'\-t\ long\'\fP options of \fBsmartctl\fP and the results of
+the testing can be observed using the \fBsmartctl \'\-l\ selftest\'\fP
+command-line option.]
+
+[Please see the \fBsmartctl \-l\fP and \fB\-t\fP command-line
+options.]
+.TP
+.B \-s REGEXP
+Run Self-Tests or Offline Immediate Tests, at scheduled times.  A
+Self- or Offline Immediate Test will be run at the end of periodic
+device polling, if all 12 characters of the string \fBT/MM/DD/d/HH\fP
+match the extended regular expression \fBREGEXP\fP. Here:
+.RS 7
+.IP \fBT\fP 4
+is the type of the test.  The values that \fBsmartd\fP will try to
+match (in turn) are: \'L\' for a \fBL\fPong Self-Test, \'S\' for a
+\fBS\fPhort Self-Test, \'C\' for a \fBC\fPonveyance Self-Test (ATA
+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.
+.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
+use a single decimal digit or the match will always fail!
+.IP \fBDD\fP 4
+is the day of the month, expressed with two decimal digits. The
+range is from 01 to 31 inclusive.  Do \fBnot\fP
+use a single decimal digit or the match will always fail!
+.IP \fBd\fP 4
+is the day of the week, expressed with one decimal digit.  The
+range is from 1 (Monday) to 7 (Sunday) inclusive.
+.IP \fBHH\fP 4
+is the hour of the day, written with two decimal digits, and given in
+hours after midnight.  The range is 00 (midnight to just before 1am)
+to 23 (11pm to just before midnight) inclusive.  Do \fBnot\fP use a
+single decimal digit or the match will always fail!
+.RE
+.\"  The following two lines are a workaround for a man2html bug.  Please leave them.
+.\" They define a non-existent option; useful because man2html can't correctly reset the margins.
+.TP
+.B \&
+Some examples follow.  In reading these, keep in mind that in extended
+regular expressions a dot \fB\'.\'\fP matches any single character, and
+a parenthetical expression such as \fB\'(A|B|C)\'\fP denotes any one of the three possibilities \fBA\fP,
+\fBB\fP, or \fBC\fP.
+
+To schedule a short Self-Test between 2-3am every morning, use:
+.nf
+\fB \-s S/../.././02\fP
+.fi
+To schedule a long Self-Test between 4-5am every Sunday morning, use:
+.nf
+\fB \-s L/../../7/04\fP
+.fi
+To schedule a long Self-Test between 10-11pm on the first and
+fifteenth day of each month, use:
+.nf
+\fB \-s L/../(01|15)/./22\fP
+.fi
+To schedule an Offline Immediate test after every midnight, 6am,
+noon,and 6pm, plus a Short Self-Test daily at 1-2am and a Long
+Self-Test every Saturday at 3-4am, use:
+.nf
+\fB \-s (O/../.././(00|06|12|18)|S/../.././01|L/../../6/03)\fP
+.fi
+
+Scheduled tests are run immediately following the regularly-scheduled
+device polling, if the current local date, time, and test type, match
+\fBREGEXP\fP.  By default the regularly-scheduled device polling
+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.
+
+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
+already running, then this running self test will \fBnot\fP be
+interrupted to begin another test.
+
+\fBsmartd\fP will not attempt to run \fBany\fP type of test if another
+test was already started or run in the same hour.
+
+Each time a test is run, \fBsmartd\fP will log an entry to SYSLOG.
+You can use these or the '-q showtests' command-line option to verify
+that you constructed \fBREGEXP\fP correctly.  The matching order
+(\fBL\fP before \fBS\fP before \fBC\fP before \fBO\fP) ensures that
+if multiple test types are all scheduled for the same hour, the
+longer test type has precedence.  This is usually the desired behavior.
+
+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
+issue harmless informational warning messages if it detects characters
+in \fBREGEXP\fP that appear to indicate that you have made this
+mistake.
+
+.TP
+.B \-m ADD
+Send a warning email to the email address \fBADD\fP if the \'\-H\',
+\'\-l\', \'\-f\', \'\-C\', or \'\-O\' Directives detect a failure or a
+new error, or if a SMART command to the disk fails. This Directive
+only works in conjunction with these other Directives (or with the
+equivalent default \'\-a\' Directive).
+
+To prevent your email in-box from getting filled up with warning
+messages, by default only a single warning will be sent for each of
+the enabled alert types, \'\-H\', \'\-l\', \'\-f\', \'\-C\', or
+\'\-O\' even if more than one failure or error is detected or if the
+failure or error persists.  [This behavior can be modified; see the
+\'\-M\' Directive below.]
+
+To send email to more than one user, please use the following "comma
+separated" form for the address: \fBuser1@add1,user2@add2,...,userN@addN\fP
+(with no spaces).
+
+To test that email is being sent correctly, use the \'\-M test\'
+Directive described below to send one test email message on
+\fBsmartd\fP
+startup.
+
+By default, email is sent using the system 
+.B mail
+command.  In order that
+\fBsmartd\fP
+find the mail command (normally /bin/mail) an executable named
+.B \'mail\'
+must be in the path of the shell or environment from which
+\fBsmartd\fP
+was started.  If you wish to specify an explicit path to the mail
+executable (for example /usr/local/bin/mail) or a custom script to
+run, please use the \'\-M exec\' Directive below.
+
+Note that by default under Solaris, in the previous paragraph,
+\'\fBmailx\fP\' and \'\fB/bin/mailx\fP\' are used, since Solaris
+\'/bin/mail\' does not accept a \'\-s\' (Subject) command-line
+argument.
+
+On Windows, the \'\fBBlat\fP\' mailer
+(\fBhttp://blat.sourceforge.net/\fP) is used by default.
+This mailer uses a different command line syntax, see
+\'\-M exec\' below.
+
+Note also that there is a special argument
+.B <nomailer>
+which can be given to the \'\-m\' Directive in conjunction with the \'\-M
+exec\' Directive. Please see below for an explanation of its effect.
+
+If the mailer or the shell running it produces any STDERR/STDOUT
+output, then a snippet of that output will be copied to SYSLOG.  The
+remainder of the output is discarded. If problems are encountered in
+sending mail, this should help you to understand and fix them.  If
+you have mail problems, we recommend running \fBsmartd\fP in debug
+mode with the \'-d\' flag, using the \'-M test\' Directive described
+below.
+
+The following extension is available on Windows:
+By specifying \'\fBmsgbox\fP\' as a mail address, a warning
+"email" is displayed as a message box on the screen.
+Using both \'\fBmsgbox\fP\' and regular mail addresses is possible,
+if \'\fBmsgbox\fP\' is the first word in the comma separated list.
+With \'\fBsysmsgbox\fP\', a system modal (always on top) message box
+is used. If running as a service, a service notification message box
+(always shown on current visible desktop) is used.
+
+.TP
+.B \-M TYPE
+These Directives modify the behavior of the
+\fBsmartd\fP
+email warnings enabled with the \'\-m\' email Directive described above.
+These \'\-M\' Directives only work in conjunction with the \'\-m\'
+Directive and can not be used without it.
+
+Multiple \-M Directives may be given.  If more than one of the
+following three \-M Directives are given (example: \-M once \-M daily)
+then the final one (in the example, \-M daily) is used.
+
+The valid arguments to the \-M Directive are (one of the following
+three):
+
+.I once
+\- send only one warning email for each type of disk problem detected.  This
+is the default.
+
+.I daily
+\- send additional warning reminder emails, once per day, for each type
+of disk problem detected.
+
+.I diminishing
+\- send additional warning reminder emails, after a one-day interval,
+then a two-day interval, then a four-day interval, and so on for each
+type of disk problem detected. Each interval is twice as long as the
+previous interval.
+
+In addition, one may add zero or more of the following Directives:
+
+.I test
+\- send a single test email
+immediately upon
+\fBsmartd\fP
+startup.  This allows one to verify that email is delivered correctly.
+
+.I exec PATH
+\- run the executable PATH instead of the default mail command, when
+\fBsmartd\fP
+needs to send email.  PATH must point to an executable binary file or
+script.
+
+By setting PATH to point to a customized script, you can make
+\fBsmartd\fP perform useful tricks when a disk problem is detected
+(beeping the console, shutting down the machine, broadcasting warnings
+to all logged-in users, etc.)  But please be careful. \fBsmartd\fP
+will \fBblock\fP until the executable PATH returns, so if your
+executable hangs, then \fBsmartd\fP will also hang. Some sample
+scripts are included in
+/usr/local/share/doc/smartmontools-5.1/examplescripts/.
+
+The return status of the executable is recorded by \fBsmartd\fP in
+SYSLOG. The executable is not expected to write to STDOUT or
+STDERR.  If it does, then this is interpreted as indicating that
+something is going wrong with your executable, and a fragment of this
+output is logged to SYSLOG to help you to understand the problem.
+Normally, if you wish to leave some record behind, the executable
+should send mail or write to a file or device.
+
+Before running the executable, \fBsmartd\fP sets a number of
+environment variables.  These environment variables may be used to
+control the executable\'s behavior.  The environment variables
+exported by \fBsmartd\fP are:
+.RS 7
+.IP \fBSMARTD_MAILER\fP 4
+is set to the argument of \-M exec, if present or else to \'mail\'
+(examples: /bin/mail, mail).
+.IP \fBSMARTD_DEVICE\fP 4
+is set to the device path (examples: /dev/hda, /dev/sdb).
+.IP \fBSMARTD_DEVICETYPE\fP 4
+is set to the device type (possible values: ata, scsi, 3ware,N). Here
+N=0,...,15 denotes the ATA disk behind a 3ware RAID controller.
+.IP \fBSMARTD_DEVICESTRING\fP 4
+is set to the device description.  For SMARTD_DEVICETYPE of ata or
+scsi, this is the same as SMARTD_DEVICE.  For 3ware RAID controllers,
+the form used is \'/dev/sdc [3ware_disk_01]\'. In this case the device
+string contains a space and is NOT quoted.  So to use
+$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:
+.nf
+.fi
+\fIEmailTest\fP: this is an email test message.
+.nf
+.fi
+\fIHealth\fP: the SMART health status indicates imminent failure.
+.nf
+.fi
+\fIUsage\fP: a usage Attribute has failed.
+.nf
+.fi
+\fISelfTest\fP: the number of self-test failures has increased.
+.nf
+.fi
+\fIErrorCount\fP: the number of errors in the ATA error log has increased.
+.nf
+.fi
+\fICurrentPendingSector\fP: one of more disk sectors could not be
+read and are marked to be reallocated (replaced with spare sectors).
+.nf
+.fi
+\fIOfflineUncorrectableSector\fP: during off\-line testing, or self\-testing,
+one or more disk sectors could not be read.
+.nf
+.fi
+\fIFailedHealthCheck\fP: the SMART health status command failed.
+.nf
+.fi
+\fIFailedReadSmartData\fP: the command to read SMART Attribute data failed.
+.nf
+.fi
+\fIFailedReadSmartErrorLog\fP: the command to read the SMART error log failed.
+.nf
+.fi
+\fIFailedReadSmartSelfTestLog\fP: the command to read the SMART self-test log failed.
+.nf
+.fi
+\fIFailedOpenDevice\fP: the open() command to the device failed.
+.IP \fBSMARTD_ADDRESS\fP 4
+is determined by the address argument ADD of the \'\-m\' Directive.
+If ADD is \fB<nomailer>\fP, then \fBSMARTD_ADDRESS\fP is not set.
+Otherwise, it is set to the comma-separated-list of email addresses
+given by the argument ADD, with the commas replaced by spaces
+(example:admin@example.com root).  If more than one email address is
+given, then this string will contain space characters and is NOT
+quoted, so to use it in a bash script you may want to enclose it in
+double quotes.
+.IP \fBSMARTD_MESSAGE\fP 4
+is set to the one sentence summary warning email message string from
+\fBsmartd\fP. 
+This message string contains space characters and is NOT quoted. So to
+use $SMARTD_MESSAGE in a bash script you should probably enclose it in
+double quotes.
+.IP \fBSMARTD_FULLMESSAGE\fP 4
+is set to the contents of the entire email warning message string from
+\fBsmartd\fP. 
+This message string contains space and return characters and is NOT quoted. So to
+use $SMARTD_FULLMESSAGE in a bash script you should probably enclose it in
+double quotes.
+.IP \fBSMARTD_TFIRST\fP 4
+is a text string giving the time and date at which the first problem
+of this type was reported. This text string contains space characters
+and no newlines, and is NOT quoted. For example:
+.nf
+.fi
+Sun Feb  9 14:58:19 2003 CST
+.IP \fBSMARTD_TFIRSTEPOCH\fP 4
+is an integer, which is the unix epoch (number of seconds since Jan 1,
+1970) for \fBSMARTD_TFIRST\fP.
+.RE
+.\"  The following two lines are a workaround for a man2html bug.  Please leave them.
+.\" They define a non-existent option; useful because man2html can't correctly reset the margins.
+.TP
+.B \&
+The shell which is used to run PATH is system-dependent. For vanilla
+Linux/glibc it\'s bash. For other systems, the man page for
+\fBpopen\fP(3) should say what shell is used.
+
+If the \'\-m ADD\' Directive is given with a normal address argument,
+then the executable pointed to by PATH will be run in a shell with
+STDIN receiving the body of the email message, and with the same
+command-line arguments:
+.nf
+-s "$SMARTD_SUBJECT" $SMARTD_ADDRESS
+.fi
+that would normally be provided to \'mail\'.  Examples include:
+.nf
+.B -m user@home -M exec /bin/mail
+.B -m admin@work -M exec /usr/local/bin/mailto
+.B -m root -M exec /Example_1/bash/script/below
+.fi
+
+Note that on Windows, the syntax of the \'\fBBlat\fP\' mailer is
+used:
+.nf
+- -q -subject "$SMARTD_SUBJECT" -to "$SMARTD_ADDRESS"
+.fi
+
+If the \'\-m ADD\' Directive is given with the special address argument
+.B <nomailer>
+then the executable pointed to by PATH is run in a shell with
+.B no
+STDIN and
+.B no
+command-line arguments, for example:
+.nf
+.B -m <nomailer> -M exec /Example_2/bash/script/below
+.fi
+If the executable produces any STDERR/STDOUT output, then \fBsmartd\fP
+assumes that something is going wrong, and a snippet of that output
+will be copied to SYSLOG.  The remainder of the output is then
+discarded.
+
+Some EXAMPLES of scripts that can be used with the \'\-M exec\'
+Directive are given below. Some sample scripts are also included in
+/usr/local/share/doc/smartmontools-5.1/examplescripts/.
+
+.TP
+.B \-f
+Check for \'failure\' of any Usage Attributes.  If these Attributes are
+less than or equal to the threshold, it does NOT indicate imminent
+disk failure.  It "indicates an advisory condition where the usage or
+age of the device has exceeded its intended design life period."
+[Please see the \fBsmartctl \-A\fP command-line option.]
+.TP
+.B \-p
+Report anytime that a Prefail Attribute has changed
+its value since the last check, 30 minutes ago. [Please see the
+.B smartctl \-A
+command-line option.]
+.TP
+.B \-u
+Report anytime that a Usage Attribute has changed its value
+since the last check, 30 minutes ago. [Please see the
+.B smartctl \-A
+command-line option.]
+.TP
+.B \-t
+Equivalent to turning on the two previous flags \'\-p\' and \'\-u\'.
+Tracks changes in \fIall\fP device Attributes (both Prefailure and
+Usage). [Please see the \fBsmartctl\fP \-A command-line option.]
+.TP
+.B \-i ID
+Ignore device Attribute number \fBID\fP when checking for failure of
+Usage Attributes.  \fBID\fP must be a decimal integer in the range
+from 1 to 255.  This Directive modifies the behavior of the \'\-f\'
+Directive and has no effect without it.
+
+This is useful, for example, if you have a very old disk and don\'t
+want to keep getting messages about the hours-on-lifetime Attribute
+(usually Attribute 9) failing.  This Directive may appear multiple
+times for a single device, if you want to ignore multiple Attributes.
+.TP
+.B \-I ID
+Ignore device Attribute \fBID\fP when tracking changes in the
+Attribute values.  \fBID\fP must be a decimal integer in the range
+from 1 to 255.  This Directive modifies the behavior of the \'\-p\',
+\'\-u\', and \'\-t\' tracking Directives and has no effect without one
+of them.
+
+This is useful, for example, if one of the device Attributes is the disk
+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
+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
+the behavior of the \'\-p\', \'\-u\', and \'\-t\' tracking Directives
+and has no effect without one of them.  This Directive may be given
+multiple times.
+
+A common use of this Directive is to track the device Temperature
+(often ID=194 or 231).
+
+.TP
+.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
+integer in the range from 1 to 255.  This Directive modifies the
+behavior of the \'\-p\', \'\-u\', and \'\-t\' tracking Directives and
+has no effect without one of them.  This Directive may be given
+multiple times.
+
+If this Directive is given, it automatically implies the \'\-r\'
+Directive for the same Attribute, so that the Raw value of the
+Attribute is reported.
+
+A common use of this Directive is to track the device Temperature
+(often ID=194 or 231).  It is also useful for understanding how
+different types of system behavior affects the values of certain
+Attributes.
+
+.TP
+.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
+\fBID\fP is 0 to 255 inclusive.  To turn off this reporting, use
+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).
+
+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
+the read failed because the data on it has been corrupted and has
+inconsistent Error Checking and Correction (ECC) codes.  This is
+important to know, because it means that there is some unreadable data
+on the disk.  The problem of figuring out what file this data belongs
+to is operating system and file system specific.  You can typically
+force the sector to reallocate by writing to it (translation: make the
+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
+[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
+\fBID\fP is 0 to 255 inclusive.  To turn off this reporting, use
+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).
+
+
+An offline uncorrectable sector is a disk sector which was not
+readable during an off\-line scan or a self\-test.  This is important
+to know, because if you have data stored in this disk sector, and you
+need to read it, the read will fail.  Please see the previous \'\-C\'
+option for more details.
+
+.TP
+.B \-F TYPE
+[ATA only] Modifies the behavior of \fBsmartd\fP to compensate for
+some known and understood device firmware bug.  The arguments to this
+Directive are exclusive, so that only the final Directive given is
+used.  The valid values are:
+
+.I none
+\- Assume that the device firmware obeys the ATA specifications.  This is
+the default, unless the device has presets for \'\-F\' in the device
+database.
+
+.I samsung
+\- In some Samsung disks (example: model SV4012H Firmware Version:
+RM100-08) some of the two- and four-byte quantities in the SMART data
+structures are byte-swapped (relative to the ATA specification).
+Enabling this option tells \fBsmartd\fP to evaluate these quantities
+in byte-reversed order.  Some signs that your disk needs this option
+are (1) no self-test log printed, even though you have run self-tests;
+(2) very large numbers of ATA errors reported in the ATA error log;
+(3) strange and impossible values for the ATA error log timestamps.
+
+.I samsung2
+\- In more recent Samsung disks (firmware revisions ending in "\-23") the
+number of ATA errors reported is byte swapped.  Enabling this option
+tells \fBsmartd\fP to evaluate this quantity in byte-reversed order.
+
+Note that an explicit \'\-F\' Directive will over-ride any preset
+values for \'\-F\' (see the \'\-P\' option below).
+
+
+[Please see the \fBsmartctl \-F\fP command-line option.]
+
+.TP
+.B \-v N,OPTION
+Modifies the labeling for Attribute N, for disks which use
+non-standard Attribute definitions.  This is useful in connection with
+the Attribute tracking/reporting Directives.
+
+This Directive may appear multiple times. Valid arguments to this
+Directive are:
+
+.I 9,minutes
+\- Raw Attribute number 9 is power-on time in minutes.  Its raw value
+will be displayed in the form \'Xh+Ym\'.  Here X is hours, and Y is
+minutes in the range 0-59 inclusive.  Y is always printed with two
+digits, for example \'06\' or \'31\' or \'00\'.
+
+.I 9,seconds
+\- Raw Attribute number 9 is power-on time in seconds.  Its raw value
+will be displayed in the form \'Xh+Ym+Zs\'.  Here X is hours, Y is
+minutes in the range 0-59 inclusive, and Z is seconds in the range
+0-59 inclusive.  Y and Z are always printed with two digits, for
+example \'06\' or \'31\' or \'00\'.
+
+.I 9,halfminutes
+\- Raw Attribute number 9 is power-on time, measured in units of 30
+seconds.  This format is used by some Samsung disks.  Its raw value
+will be displayed in the form \'Xh+Ym\'.  Here X is hours, and Y is
+minutes in the range 0-59 inclusive.  Y is always printed with two
+digits, for example \'06\' or \'31\' or \'00\'.
+
+.I 9,temp
+\- Raw Attribute number 9 is the disk temperature in Celsius.
+
+.I 192,emergencyretractcyclect
+\- Raw Attribute number 192 is the Emergency Retract Cycle Count.
+
+.I 193,loadunload
+\- Raw Attribute number 193 contains two values. The first is the
+number of load cycles.  The second is the number of unload cycles.
+The difference between these two values is the number of times that
+the drive was unexpectedly powered off (also called an emergency
+unload). As a rule of thumb, the mechanical stress created by one
+emergency unload is equivalent to that created by one hundred normal
+unloads.
+
+.I 194,10xCelsius
+\- Raw Attribute number 194 is ten times the disk temperature in
+Celsius.  This is used by some Samsung disks (example: model SV1204H
+with RK100-13 firmware).
+
+.I 194,unknown
+\- Raw Attribute number 194 is NOT the disk temperature, and its
+interpretation is unknown. This is primarily useful for the -P
+(presets) Directive.
+
+.I 198,offlinescanuncsectorct
+\- Raw Attribute number 198 is the Offline Scan UNC Sector Count.
+
+.I 200,writeerrorcount
+\- Raw Attribute number 200 is the Write Error Count.
+
+.I 201,detectedtacount
+\- Raw Attribute number 201 is the Detected TA Count.
+
+.I 220,temp
+\- Raw Attribute number 220 is the disk temperature in Celsius.
+
+Note: a table of hard drive models, listing which Attribute
+corresponds to temperature, can be found at:
+\fBhttp://www.guzu.net/linux/hddtemp.db\fP
+
+.I N,raw8
+\- Print the Raw value of Attribute N as six 8-bit unsigned base-10
+integers.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw8\' prints Raw values for ALL Attributes in this
+form.  The form (for example) \'123,raw8\' only prints the Raw value for
+Attribute 123 in this form.
+
+.I N,raw16
+\- Print the Raw value of Attribute N as three 16-bit unsigned base-10
+integers.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw16\' prints Raw values for ALL Attributes in this
+form.  The form (for example) \'123,raw16\' only prints the Raw value for
+Attribute 123 in this form.
+
+.I N,raw48
+\- Print the Raw value of Attribute N as a 48-bit unsigned base-10
+integer.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw48\' prints Raw values for ALL Attributes in
+this form.  The form (for example) \'123,raw48\' only prints the Raw
+value for Attribute 123 in this form.
+
+.TP
+.B \-P TYPE
+Specifies whether
+\fBsmartd\fP
+should use any preset options that are available for this drive.  The
+valid arguments to this Directive are:
+
+.I use
+\- use any presets that are available for this drive.  This is the default.
+
+.I ignore
+\- do not use any presets for this drive.
+
+.I show
+\- show the presets listed for this drive in the database.
+
+.I showall
+\- show the presets that are available for all drives and then exit.
+
+[Please see the
+.B smartctl \-P
+command-line option.]
+
+.TP
+.B \-a
+Equivalent to turning on all of the following Directives: 
+.B \'\-H\' 
+to check the SMART health status,
+.B \'\-f\' 
+to report failures of Usage (rather than Prefail) Attributes,
+.B \'\-t\' 
+to track changes in both Prefailure and Usage Attributes,
+.B \'\-l\ selftest\' 
+to report increases in the number of Self-Test Log errors,
+.B \'\-l\ error\' 
+to report increases in the number of ATA errors,
+.B \'\-C 197\'
+to report nonzero values of the current pending sector count, and
+.B \'\-U 198\'
+to report nonzero values of the offline pending sector count.
+
+Note that \-a is the default for ATA devices.  If none of these other
+Directives is given, then \-a is assumed.
+
+.TP
+.B #
+Comment: ignore the remainder of the line.
+.TP
+.B \e
+Continuation character: if this is the last non-white or non-comment
+character on a line, then the following line is a continuation of the current
+one.
+.PP
+If you are not sure which Directives to use, I suggest experimenting
+for a few minutes with
+.B smartctl
+to see what SMART functionality your disk(s) support(s).  If you do
+not like voluminous syslog messages, a good choice of
+\fBsmartd\fP
+configuration file Directives might be:
+.nf
+.B \-H \-l\ selftest \-l\ error \-f.
+.fi
+If you want more frequent information, use:
+.B -a.
+
+.TP
+.B ADDITIONAL DETAILS ABOUT DEVICESCAN
+If the first non-comment entry in the configuration file is the text
+string \fBDEVICESCAN\fP in capital letters, then \fBsmartd\fP will
+ignore any remaining lines in the configuration file, and will scan
+for devices.
+
+If \fBDEVICESCAN\fP is not followed by any Directives, then smartd
+will scan for both ATA and SCSI devices, and will monitor all possible
+SMART properties of any devices that are found.
+
+\fBDEVICESCAN\fP may optionally be followed by any valid Directives,
+which will be applied to all devices that are found in the scan.  For
+example
+.nf
+.B DEVICESCAN -m root@example.com
+.fi
+will scan for all devices, and then monitor them.  It will send one
+email warning per device for any problems that are found.
+.nf
+.B  DEVICESCAN -d ata -m root@example.com
+.fi
+will do the same, but restricts the scan to ATA devices only.  
+.nf
+.B  DEVICESCAN -H -d ata -m root@example.com
+.fi
+will do the same, but only monitors the SMART health status of the
+devices, (rather than the default \-a, which monitors all SMART
+properties).
+
+.TP
+.B EXAMPLES OF SHELL SCRIPTS FOR \'\-M exec\'
+These are two examples of shell scripts that can be used with the \'\-M
+exec PATH\' Directive described previously.  The paths to these scripts
+and similar executables is the PATH argument to the \'\-M exec PATH\'
+Directive.
+
+Example 1: This script is for use with \'\-m ADDRESS -M exec PATH\'.  It appends
+the output of
+.B smartctl -a
+to the output of the smartd email warning message and sends it to ADDRESS.
+
+.nf
+\fB
+#! /bin/bash
+
+# Save the email message (STDIN) to a file:
+cat > /root/msg
+
+# Append the output of smartctl -a to the message:
+/usr/local/sbin/smartctl -a -d $SMART_DEVICETYPE $SMARTD_DEVICE >> /root/msg
+# Now email the message to the user at address ADD:
+/bin/mail -s "$SMARTD_SUBJECT" $SMARTD_ADDRESS < /root/msg
+\fP
+.fi
+
+Example 2: This script is for use with \'\-m <nomailer> \-M exec
+PATH\'. It warns all users about a disk problem, waits 30 seconds, and
+then powers down the machine.
+
+.nf
+\fB
+#! /bin/bash
+
+# Warn all users of a problem
+wall \'Problem detected with disk: \' "$SMARTD_DEVICESTRING"
+wall \'Warning message from smartd is: \' "$SMARTD_MESSAGE"
+wall \'Shutting down machine in 30 seconds... \'
+# Wait half a minute
+sleep 30
+# Power down the machine
+/sbin/shutdown -hf now
+\fP
+.fi
+
+Some example scripts are distributed with the smartmontools package,
+in /usr/local/share/doc/smartmontools-5.1/examplescripts/.
+
+Please note that these scripts typically run as root, so any files
+that they read/write should not be writable by ordinary users or
+reside in directories like /tmp that are writable by ordinary users
+and may expose your system to symlink attacks.
+
+As previously described, if the scripts write to STDOUT or STDERR,
+this is interpreted as indicating that there was an internal error
+within the script, and a snippet of STDOUT/STDERR is logged to SYSLOG.
+The remainder is flushed.
+
+.\" ENDINCLUDE
+.\" DO NOT MODIFY THIS OR PREVIOUS/NEXT LINES. THIS DEFINES THE 
+.\" END OF THE INCLUDE SECTION FOR smartd.conf.5
+
+.SH NOTES
+\fBsmartd\fP
+will make log entries at loglevel 
+.B LOG_INFO
+if the Normalized SMART Attribute values have changed, as reported using the
+.B \'\-t\', \'\-p\',
+or
+.B \'\-u\'
+Directives. For example:
+.nf
+.B \'Device: /dev/hda, SMART Attribute: 194 Temperature_Celsius changed from 94 to 93\'
+.fi
+Note that in this message, the value given is the \'Normalized\' not the \'Raw\' 
+Attribute value (the disk temperature in this case is about 22
+Celsius).  The 
+.B \'-R\'
+and 
+.B \'-r\'
+Directives modify this behavior, so that the information is printed
+with the Raw values as well, for example:
+.nf
+.B \'Device: /dev/hda, SMART Attribute: 194 Temperature_Celsius changed from 94 [Raw 22] to 93 [Raw 23]\'
+.fi
+Here the Raw values are the actual disk temperatures in Celsius.  The
+way in which the Raw values are printed, and the names under which the
+Attributes are reported, is governed by the various
+.B \'-v Num,Description\'
+Directives described previously.
+
+Please see the
+.B smartctl
+manual page for further explanation of the differences between
+Normalized and Raw Attribute values.
+
+\fBsmartd\fP
+will make log entries at loglevel
+.B LOG_CRIT
+if a SMART Attribute has failed, for example:
+.nf
+.B \'Device: /dev/hdc, Failed SMART Attribute: 5 Reallocated_Sector_Ct\'
+.fi
+ This loglevel is used for reporting enabled by the
+.B \'\-H\', \-f\', \'\-l\ selftest\',
+and
+.B \'\-l\ error\'
+Directives. Entries reporting failure of SMART Prefailure Attributes
+should not be ignored: they mean that the disk is failing.  Use the
+.B smartctl
+utility to investigate. 
+
+Under Solaris with the default \fB/etc/syslog.conf\fP configuration,
+messages below loglevel \fBLOG_NOTICE\fP will \fBnot\fP be recorded.
+Hence all \fBsmartd\fP messages with loglevel \fBLOG_INFO\fP will be
+lost.  If you want to use the existing daemon facility to log all
+messages from \fBsmartd\fP, you should change \fB/etc/syslog.conf\fP
+from:
+.nf
+       ...;daemon.notice;...        /var/adm/messages
+.fi
+to read:
+.nf
+       ...;daemon.info;...          /var/adm/messages
+.fi
+Alternatively, you can use a local facility to log messages: please
+see the \fBsmartd\fP '-l' command-line option described above.
+
+On Cygwin and Windows, the log messages are written to the event log
+or to a file. See documentation of the '-l FACILITY' option above for
+details.
+
+On Windows, the following built-in commands can be used to control
+\fBsmartd\fP, if running as a daemon:
+
+\'\fBsmartd status\fP\' \- check status
+
+\'\fBsmartd stop\fP\' \- stop smartd
+
+\'\fBsmartd reload\fP\' \- reread config file
+
+\'\fBsmartd restart\fP\' \- restart smartd
+
+\'\fBsmartd sigusr1\fP\' \- check disks now
+
+\'\fBsmartd sigusr2\fP\' \- toggle debug mode
+
+On WinNT4/2000/XP, \fBsmartd\fP can also be run as a Windows service:
+
+
+The Cygwin Version of \fBsmartd\fP can be run as a service via the
+cygrunsrv tool. The start-up script provides Cygwin-specific commands
+to install and remove the service:
+.nf
+.B /usr/local/etc/rc.d/init.d/smartd install [options]
+.B /usr/local/etc/rc.d/init.d/smartd remove
+.fi
+The service can be started and stopped by the start-up script as usual
+(see \fBEXAMPLES\fP above).
+
+
+The Windows Version of \fBsmartd\fP has buildin support for services:
+
+\'\fBsmartd install [options]\fP\' installs a service
+named "smartd" (display name "SmartD Service") using the command line
+\'/installpath/smartd.exe --service [options]\'.
+
+\'\fBsmartd remove\fP\' can later be used to remove the service entry
+from registry.
+
+Upon startup, the smartd service changes the working directory
+to its own installation path. If smartd.conf and blat.exe are stored
+in this directory, no \'-c\' option and \'-M exec\' directive is needed.
+
+The debug mode (\'-d\', \'-q onecheck\') does not work if smartd is
+running as service.
+
+The service can be controlled as usual with Windows commands \'net\'
+or \'sc\' (\'\fBnet start smartd\fP\', \'\fBnet stop smartd\fP\').
+
+Pausing the service (\'\fBnet pause smartd\fP\') sets the interval between
+disk checks (\'-i N\') to infinite.
+
+Continuing the paused service (\'\fBnet continue smartd\fP\') resets the
+interval and rereads the configuration file immediately (like \fBSIGHUP\fP):
+
+Continuing a still running service (\'\fBnet continue smartd\fP\' without
+preceding \'\fBnet pause smartd\fP\') does not reread configuration but
+checks disks immediately (like \fBSIGUSR1\fP).
+
+.SH LOG TIMESTAMP TIMEZONE
+
+When \fBsmartd\fP makes log entries, these are time-stamped.  The time
+stamps are in the computer's local time zone, which is generally set
+using either the environment variable \'\fBTZ\fP\' or using a
+time-zone file such as \fB/etc/localtime\fP.  You may wish to change
+the timezone while \fBsmartd\fP is running (for example, if you carry
+a laptop to a new time-zone and don't reboot it).  Due to a bug in the
+\fBtzset(3)\fP function of many unix standard C libraries, the
+time-zone stamps of \fBsmartd\fP might not change.  For some systems,
+\fBsmartd\fP will work around this problem \fIif\fP the time-zone is
+set using \fB/etc/localtime\fP. The work-around \fIfails\fP if the
+time-zone is set using the \'\fBTZ\fP\' variable (or a file that it
+points to).
+
+
+.SH RETURN VALUES
+The return value (exit status) of 
+\fBsmartd\fP
+can have the following values:
+.TP
+.B 0:
+Daemon startup successful, or \fBsmartd\fP was killed by a SIGTERM (or in debug mode, a SIGQUIT).
+.TP
+.B 1:
+Commandline did not parse.
+.TP
+.B 2:
+There was a syntax error in the config file.
+.TP
+.B 3:
+Forking the daemon failed.
+.TP
+.B 4:
+Couldn\'t create PID file.
+.TP
+.B 5:
+Config file does not exist (only returned in conjunction with the \'-c\' option).
+.TP
+.B 6:
+Config file exists, but cannot be read.
+.TP
+.B 8:
+\fBsmartd\fP
+ran out of memory during startup.
+.TP
+.B 9:
+A compile time constant of\fB smartd\fP was too small.  This can be caused by an
+excessive number of disks, or by lines in \fB /usr/local/etc/smartd.conf\fP that are too long.
+Please report this problem to \fB smartmontools-support@lists.sourceforge.net\fP.
+.TP
+.B 10
+An inconsistency was found in \fBsmartd\fP\'s internal data
+structures. This should never happen.  It must be due to either a
+coding or compiler bug.  \fIPlease\fP report such failures to
+smartmontools-support@lists.sourceforge.net.
+.TP
+.B 16:
+A device explicitly listed in
+.B /usr/local/etc/smartd.conf
+can\'t be monitored.
+.TP
+.B 17:
+\fBsmartd\fP
+didn\'t find any devices to monitor.
+.TP
+.B 254:
+When in daemon mode,
+\fBsmartd\fP
+received a SIGINT or SIGQUIT.  (Note that in debug mode, SIGINT has
+the same effect as SIGHUP, and makes \fBsmartd\fP reload its
+configuration file. SIGQUIT has the same effect as SIGTERM and causes
+\fBsmartd\fP to exit with zero exit status.
+.TP
+.B 132 and above
+\fBsmartd\fP
+was killed by a signal that is not explicitly listed above.  The exit
+status is then 128 plus the signal number.  For example if
+\fBsmartd\fP
+is killed by SIGKILL (signal 9) then the exit status is 137.
+
+.PP
+.SH AUTHOR
+\fBBruce Allen\fP smartmontools-support@lists.sourceforge.net
+.fi
+University of Wisconsin \- Milwaukee Physics Department
+
+.PP
+.SH CONTRIBUTORS
+The following have made large contributions to smartmontools:
+.nf
+\fBCasper Dik\fP (Solaris SCSI interface)
+\fBChristian Franke\fP (Windows interface and Cygwin package)
+\fBDouglas Gilbert\fP (SCSI subsystem)
+\fBGuido Guenther\fP (Autoconf/Automake packaging)
+\fBGeoffrey Keating\fP (Darwin ATA interface)
+\fBEduard Martinescu\fP (FreeBSD interface)
+\fBFr\*'ed\*'eric L. W. Meunier\fP (Web site and Mailing list)
+\fBKeiji Sawada\fP (Solaris ATA interface)
+\fBSergey Svishchev\fP (NetBSD interface)
+\fBDavid Snyder and Sergey Svishchev\fP (OpenBSD interface)
+\fBPhil Williams\fP (User interface and drive database)
+.fi
+Many other individuals have made smaller contributions and corrections.
+
+.PP
+.SH CREDITS
+.fi
+This code was derived from the smartsuite package, written by Michael
+Cornwell, and from the previous ucsc smartsuite package. It extends
+these to cover ATA-5 disks. This code was originally developed as a
+Senior Thesis by Michael Cornwell at the Concurrent Systems Laboratory
+(now part of the Storage Systems Research Center), Jack Baskin School
+of Engineering, University of California, Santa
+Cruz. \fBhttp://ssrc.soe.ucsc.edu/\fP .
+.SH
+HOME PAGE FOR SMARTMONTOOLS: 
+.fi
+Please see the following web site for updates, further documentation, bug
+reports and patches: \fBhttp://smartmontools.sourceforge.net/\fP
+
+.SH SEE ALSO:
+\fBsmartd.conf\fP(5), \fBsmartctl\fP(8), \fBsyslogd\fP(8),
+\fBsyslog.conf\fP(5), \fBbadblocks\fP(8), \fBide\-smart\fP(8), \fBregex\fP(7).
+
+.SH
+REFERENCES FOR SMART
+.fi
+An introductory article about smartmontools is \fIMonitoring Hard
+Disks with SMART\fP, by Bruce Allen, Linux Journal, January 2004,
+pages 74-77. This is \fBhttp://www.linuxjournal.com/article.php?sid=6983\fP
+online.
+
+If you would like to understand better how SMART works, and what it
+does, a good place to start is with Sections 4.8 and 6.54 of the first
+volume of the \'AT Attachment with Packet Interface-7\' (ATA/ATAPI-7)
+specification.  This documents the SMART functionality which the
+\fBsmartmontools\fP utilities provide access to.  You can find
+Revision 4b of this document at
+\fBhttp://www.t13.org/docs2004/d1532v1r4b-ATA-ATAPI-7.pdf\fP .
+Earlier and later versions of this Specification are available from
+the T13 web site \fBhttp://www.t13.org/\fP .
+
+.fi
+The functioning of SMART was originally defined by the SFF-8035i
+revision 2 and the SFF-8055i revision 1.4 specifications.  These are
+publications of the Small Form Factors (SFF) Committee.  Links to
+these documents may be found in the References section of the
+smartmontools home page at \fBhttp://smartmontools.sourceforge.net/#references\fP .
+
+.SH
+CVS ID OF THIS PAGE:
+$Id: smartd.8.in,v 1.100 2006/04/12 13:55:44 ballen4705 Exp $
diff --git a/smartd.c b/smartd.c
new file mode 100644 (file)
index 0000000..63242fe
--- /dev/null
+++ b/smartd.c
@@ -0,0 +1,4127 @@
+/*
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+// unconditionally included files
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>   // umask
+#ifndef _WIN32
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+#include <signal.h>
+#include <fcntl.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <limits.h>
+
+#if SCSITIMEOUT
+#include <setjmp.h>
+#endif
+
+// see which system files to conditionally include
+#include "config.h"
+
+// conditionally included files
+#ifdef HAVE_GETOPT_LONG
+#include <getopt.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+#ifdef _WIN32
+#ifdef _MSC_VER
+#pragma warning(disable:4761) // "conversion supplied"
+typedef unsigned short mode_t;
+typedef int pid_t;
+#endif
+#include <io.h> // umask()
+#include <process.h> // getpid()
+#endif // _WIN32
+
+#ifdef __CYGWIN__
+// From <windows.h>:
+// BOOL WINAPI FreeConsole(void);
+int __stdcall FreeConsole(void);
+#include <io.h> // setmode()
+#endif // __CYGWIN__
+
+// locally included files
+#include "int64.h"
+#include "atacmds.h"
+#include "ataprint.h"
+#include "extern.h"
+#include "knowndrives.h"
+#include "scsicmds.h"
+#include "smartd.h"
+#include "utility.h"
+
+#ifdef _WIN32
+#include "hostname_win32.h" // gethost/domainname()
+#define HAVE_GETHOSTNAME   1
+#define HAVE_GETDOMAINNAME 1
+// fork()/signal()/initd simulation for native Windows
+#include "daemon_win32.h" // daemon_main/detach/signal()
+#undef SIGNALFN
+#define SIGNALFN  daemon_signal
+#define strsignal daemon_strsignal
+#define sleep     daemon_sleep
+#undef EXIT // see utility.h
+#define EXIT(x)  { exitstatus = daemon_winsvc_exitcode = (x); exit((x)); }
+// SIGQUIT does not exits, CONTROL-Break signals SIGBREAK.
+#define SIGQUIT SIGBREAK
+#define SIGQUIT_KEYNAME "CONTROL-Break"
+#else  // _WIN32
+#ifdef __CYGWIN__
+// 2x CONTROL-C simulates missing SIGQUIT via keyboard
+#define SIGQUIT_KEYNAME "2x CONTROL-C"
+#else // __CYGWIN__
+#define SIGQUIT_KEYNAME "CONTROL-\\"
+#endif // __CYGWIN__
+#endif // _WIN32
+
+#if defined (__SVR4) && defined (__sun)
+int getdomainname(char *, int); /* no declaration in header files! */
+#endif
+
+#define ARGUSED(x) ((void)(x))
+
+// These are CVS identification information for *.c and *.h files
+extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *escalade_c_cvsid, 
+                  *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *utility_c_cvsid;
+
+static const char *filenameandversion="$Id: smartd.c,v 1.362 2006/04/12 16:18:57 ballen4705 Exp $";
+#ifdef NEED_SOLARIS_ATA_CODE
+extern const char *os_solaris_ata_s_cvsid;
+#endif
+#ifdef _WIN32
+extern const char *daemon_win32_c_cvsid, *hostname_win32_c_cvsid, *syslog_win32_c_cvsid;
+#endif
+const char *smartd_c_cvsid="$Id: smartd.c,v 1.362 2006/04/12 16:18:57 ballen4705 Exp $" 
+ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID
+#ifdef DAEMON_WIN32_H_CVSID
+DAEMON_WIN32_H_CVSID
+#endif
+EXTERN_H_CVSID INT64_H_CVSID
+#ifdef HOSTNAME_WIN32_H_CVSID
+HOSTNAME_WIN32_H_CVSID
+#endif
+KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SMARTD_H_CVSID
+#ifdef SYSLOG_H_CVSID
+SYSLOG_H_CVSID
+#endif
+UTILITY_H_CVSID;
+
+extern const char *reportbug;
+
+// GNU copyleft statement.  Needed for GPL purposes.
+const char *copyleftstring="smartd comes with ABSOLUTELY NO WARRANTY. This is\n"
+                           "free software, and you are welcome to redistribute it\n"
+                           "under the terms of the GNU General Public License\n"
+                           "Version 2. See http://www.gnu.org for further details.\n\n";
+
+extern unsigned char debugmode;
+
+// command-line: how long to sleep between checks
+static int checktime=CHECKTIME;
+
+// command-line: name of PID file (NULL for no pid file)
+static char* pid_file=NULL;
+
+// configuration file name
+#ifndef _WIN32
+static char* configfile = SMARTMONTOOLS_SYSCONFDIR "/" CONFIGFILENAME ;
+#else
+static char* configfile = "./" CONFIGFILENAME ;
+#endif
+// configuration file "name" if read from stdin
+static /*const*/ char * const configfile_stdin = "<stdin>";
+// allocated memory for alternate configuration file name
+static char* configfile_alt = NULL;
+
+// command-line: when should we exit?
+static int quit=0;
+
+// command-line; this is the default syslog(3) log facility to use.
+static int facility=LOG_DAEMON;
+
+#ifdef __CYGWIN__
+// command-line: running as service, so don't fork()
+static int is_service=0;
+#endif
+
+// used for control of printing, passing arguments to atacmds.c
+smartmonctrl *con=NULL;
+
+// pointers to (real or simulated) entries in configuration file, and
+// maximum space currently allocated for these entries.
+cfgfile **cfgentries=NULL;
+int cfgentries_max=0;
+
+// pointers to ATA and SCSI devices being monitored, maximum and
+// actual numbers
+cfgfile **atadevlist=NULL, **scsidevlist=NULL;
+int atadevlist_max=0, scsidevlist_max=0;
+int numdevata=0, numdevscsi=0;
+
+// track memory usage
+extern int64_t bytes;
+
+// exit status
+extern int exitstatus;
+
+// set to one if we catch a USR1 (check devices now)
+volatile int caughtsigUSR1=0;
+
+#ifdef _WIN32
+// set to one if we catch a USR2 (toggle debug mode)
+volatile int caughtsigUSR2=0;
+#endif
+
+// set to one if we catch a HUP (reload config file). In debug mode,
+// set to two, if we catch INT (also reload config file).
+volatile int caughtsigHUP=0;
+
+// set to signal value if we catch INT, QUIT, or TERM
+volatile int caughtsigEXIT=0;
+
+#if SCSITIMEOUT
+// stack environment if we time out during SCSI access (USB devices)
+jmp_buf registerscsienv;
+#endif
+
+// tranlate cfg->pending into the correct Attribute numbers
+void TranslatePending(unsigned short pending, unsigned char *current, unsigned char *offline) {
+
+  unsigned char curr = CURR_PEND(pending);
+  unsigned char off =  OFF_PEND(pending);
+
+  // look for special value of CUR_UNC_DEFAULT that means DONT
+  // monitor. 0 means DO test.
+  if (curr==CUR_UNC_DEFAULT)
+    curr=0;
+  else if (curr==0)
+    curr=CUR_UNC_DEFAULT;
+       
+  // look for special value of OFF_UNC_DEFAULT that means DONT
+  // monitor.  0 means DO TEST.
+  if (off==OFF_UNC_DEFAULT)
+    off=0;
+  else if (off==0)
+    off=OFF_UNC_DEFAULT;
+
+  *current=curr;
+  *offline=off;
+
+  return;
+}
+
+
+// free all memory associated with selftest part of configfile entry.  Return NULL
+testinfo* FreeTestData(testinfo *data){
+  
+  // make sure we have something to do.
+  if (!data)
+    return NULL;
+  
+  // free space for text pattern
+  data->regex=FreeNonZero(data->regex, -1, __LINE__, filenameandversion);
+  
+  // free compiled expression
+  regfree(&(data->cregex));
+
+  // make sure that no sign of the compiled expression is left behind
+  // (just in case, to help detect bugs if we ever try and refer to
+  // that again).
+  memset(&(data->cregex), '0', sizeof(regex_t));
+
+  // free remaining memory space
+  data=FreeNonZero(data, sizeof(testinfo), __LINE__, filenameandversion);
+
+  return NULL;
+}
+
+cfgfile **AllocateMoreSpace(cfgfile **oldarray, int *oldsize, char *listname){
+  // for now keep BLOCKSIZE small to help detect coding problems.
+  // Perhaps increase in the future.
+  const int BLOCKSIZE=8;
+  int i;
+  int old = *oldsize;
+  int new = old + BLOCKSIZE;
+  cfgfile **newptr=realloc(oldarray, new*sizeof(cfgfile *));
+  
+  // did we get more space?
+  if (newptr) {
+
+    // clear remaining entries ala calloc()
+    for (i=old; i<new; i++)
+      newptr[i]=NULL;
+    
+    bytes += BLOCKSIZE*sizeof(cfgfile *);
+    
+    *oldsize=new;
+    
+#if 0
+    PrintOut(LOG_INFO, "allocating %d slots for %s\n", BLOCKSIZE, listname);
+#endif
+
+    return newptr;
+  }
+  
+  PrintOut(LOG_CRIT, "out of memory for allocating %s list\n", listname);
+  EXIT(EXIT_NOMEM);
+}
+
+void PrintOneCVS(const char *a_cvs_id){
+  char out[CVSMAXLEN];
+  printone(out,a_cvs_id);
+  PrintOut(LOG_INFO,"%s",out);
+  return;
+}
+
+// prints CVS identity information for the executable
+void PrintCVS(void){
+  char *configargs=strlen(SMARTMONTOOLS_CONFIGURE_ARGS)?SMARTMONTOOLS_CONFIGURE_ARGS:"[no arguments given]";
+
+  PrintOut(LOG_INFO,(char *)copyleftstring);
+  PrintOut(LOG_INFO,"CVS version IDs of files used to build this code are:\n");
+  PrintOneCVS(atacmdnames_c_cvsid);
+  PrintOneCVS(atacmds_c_cvsid);
+  PrintOneCVS(ataprint_c_cvsid);
+#ifdef _WIN32
+  PrintOneCVS(daemon_win32_c_cvsid);
+#endif
+#ifdef _WIN32
+  PrintOneCVS(hostname_win32_c_cvsid);
+#endif
+  PrintOneCVS(knowndrives_c_cvsid);
+  PrintOneCVS(os_XXXX_c_cvsid);
+#ifdef NEED_SOLARIS_ATA_CODE
+  PrintOneCVS( os_solaris_ata_s_cvsid);
+#endif
+  PrintOneCVS(scsicmds_c_cvsid);
+  PrintOneCVS(smartd_c_cvsid);
+#ifdef _WIN32
+  PrintOneCVS(syslog_win32_c_cvsid);
+#endif
+  PrintOneCVS(utility_c_cvsid);
+  PrintOut(LOG_INFO, "\nsmartmontools release " PACKAGE_VERSION " dated " SMARTMONTOOLS_RELEASE_DATE " at " SMARTMONTOOLS_RELEASE_TIME "\n");
+  PrintOut(LOG_INFO, "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n");
+  PrintOut(LOG_INFO, "smartmontools build configured: " SMARTMONTOOLS_CONFIGURE_DATE "\n");
+  PrintOut(LOG_INFO, "smartd compile dated " __DATE__ " at "__TIME__ "\n");
+  PrintOut(LOG_INFO, "smartmontools configure arguments: %s\n", configargs);
+  return;
+}
+
+// Removes config file entry, freeing all memory
+void RmConfigEntry(cfgfile **anentry, int whatline){
+  
+  cfgfile *cfg;
+
+  // pointer should never be null!
+  if (!anentry){
+    PrintOut(LOG_CRIT,"Internal error in RmConfigEntry() at line %d of file %s\n%s",
+             whatline, filenameandversion, reportbug);    
+    EXIT(EXIT_BADCODE);
+  }
+  
+  // only remove entries that exist!
+  if (!(cfg=*anentry))
+    return;
+
+  // entry exists -- free all of its memory  
+  cfg->name            = FreeNonZero(cfg->name,           -1,__LINE__,filenameandversion);
+  cfg->smartthres      = FreeNonZero(cfg->smartthres,      sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion);
+  cfg->smartval        = FreeNonZero(cfg->smartval,        sizeof(struct ata_smart_values),__LINE__,filenameandversion);
+  cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion);
+  cfg->attributedefs   = FreeNonZero(cfg->attributedefs,   MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion);
+  if (cfg->mailwarn){
+    cfg->mailwarn->address         = FreeNonZero(cfg->mailwarn->address,        -1,__LINE__,filenameandversion);
+    cfg->mailwarn->emailcmdline    = FreeNonZero(cfg->mailwarn->emailcmdline,   -1,__LINE__,filenameandversion);
+    cfg->mailwarn                  = FreeNonZero(cfg->mailwarn,   sizeof(maildata),__LINE__,filenameandversion);
+  }
+  cfg->testdata        = FreeTestData(cfg->testdata);
+  *anentry             = FreeNonZero(cfg,                  sizeof(cfgfile),__LINE__,filenameandversion);
+
+  return;
+}
+
+// deallocates all memory associated with cfgentries list
+void RmAllConfigEntries(){
+  int i;
+
+  for (i=0; i<cfgentries_max; i++)
+    RmConfigEntry(cfgentries+i, __LINE__);
+
+  cfgentries=FreeNonZero(cfgentries, sizeof(cfgfile *)*cfgentries_max, __LINE__, filenameandversion);
+  cfgentries_max=0;
+
+  return;
+}
+
+// deallocates all memory associated with ATA/SCSI device lists
+void RmAllDevEntries(){
+  int i;
+  
+  for (i=0; i<atadevlist_max; i++)
+    RmConfigEntry(atadevlist+i, __LINE__);
+
+  atadevlist=FreeNonZero(atadevlist, sizeof(cfgfile *)*atadevlist_max, __LINE__, filenameandversion);
+  atadevlist_max=0;
+
+  for (i=0; i<scsidevlist_max; i++)
+    RmConfigEntry(scsidevlist+i, __LINE__);
+  
+  scsidevlist=FreeNonZero(scsidevlist, sizeof(cfgfile *)*scsidevlist_max, __LINE__, filenameandversion);
+  scsidevlist_max=0;
+
+  return;
+}
+
+// remove the PID file
+void RemovePidFile(){
+  if (pid_file) {
+    if ( -1==unlink(pid_file) )
+      PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n", 
+               pid_file, strerror(errno));
+    pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion);
+  }
+  return;
+}
+
+
+//  Note if we catch a SIGUSR1
+void USR1handler(int sig){
+  if (SIGUSR1==sig)
+    caughtsigUSR1=1;
+  return;
+}
+
+#ifdef _WIN32
+//  Note if we catch a SIGUSR2
+void USR2handler(int sig){
+  if (SIGUSR2==sig)
+    caughtsigUSR2=1;
+  return;
+}
+#endif
+
+// Note if we catch a HUP (or INT in debug mode)
+void HUPhandler(int sig){
+  if (sig==SIGHUP)
+    caughtsigHUP=1;
+  else
+    caughtsigHUP=2;
+  return;
+}
+
+// signal handler for TERM, QUIT, and INT (if not in debug mode)
+void sighandler(int sig){
+  if (!caughtsigEXIT)
+    caughtsigEXIT=sig;
+  return;
+}
+
+
+// signal handler that prints Goodbye message and removes pidfile
+void Goodbye(void){
+  
+  // clean up memory -- useful for debugging
+  RmAllConfigEntries();
+  RmAllDevEntries();
+
+  // delete PID file, if one was created
+  RemovePidFile();
+
+  // remove alternate configfile name
+  configfile_alt=FreeNonZero(configfile_alt, -1,__LINE__,filenameandversion);
+
+  // useful for debugging -- have we managed memory correctly?
+  if (debugmode || (bytes && exitstatus!=EXIT_NOMEM))
+    PrintOut(LOG_INFO, "Memory still allocated for devices at exit is %" PRId64 " bytes.\n", bytes);
+
+  // if we are exiting because of a code bug, tell user
+  if (exitstatus==EXIT_BADCODE || (bytes && exitstatus!=EXIT_NOMEM))
+        PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n");
+
+  if (exitstatus==0 && bytes)
+    exitstatus=EXIT_BADCODE;
+
+  // and this should be the final output from smartd before it exits
+  PrintOut(exitstatus?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", exitstatus);
+
+  return;
+}
+
+#define ENVLENGTH 1024
+
+// a replacement for setenv() which is not available on all platforms.
+// Note that the string passed to putenv must not be freed or made
+// invalid, since a pointer to it is kept by putenv(). This means that
+// it must either be a static buffer or allocated off the heap. The
+// string can be freed if the environment variable is redefined or
+// deleted via another call to putenv(). So we keep these on the stack
+// as long as the popen() call is underway.
+int exportenv(char* stackspace, const char *name, const char *value){
+  snprintf(stackspace,ENVLENGTH, "%s=%s", name, value);
+  return putenv(stackspace);
+}
+
+char* dnsdomain(const char* hostname) {
+  char *p = NULL;
+#ifdef HAVE_GETHOSTBYNAME
+  struct hostent *hp;
+  
+  if ((hp = gethostbyname(hostname))) {
+    // Does this work if gethostbyname() returns an IPv6 name in
+    // colon/dot notation?  [BA]
+    if ((p = strchr(hp->h_name, '.')))
+      p++; // skip "."
+  }
+#else
+  ARGUSED(hostname);
+#endif
+  return p;
+}
+
+#define EBUFLEN 1024
+
+// If either address or executable path is non-null then send and log
+// a warning email, or execute executable
+void MailWarning(cfgfile *cfg, int which, char *fmt, ...){
+  char command[2048], message[256], hostname[256], domainname[256], additional[256],fullmessage[1024];
+  char original[256], further[256], nisdomain[256], subject[256],dates[DATEANDEPOCHLEN];
+  char environ_strings[11][ENVLENGTH];
+  time_t epoch;
+  va_list ap;
+  const int day=24*3600;
+  int days=0;
+  char *whichfail[]={
+    "EmailTest",                  // 0
+    "Health",                     // 1
+    "Usage",                      // 2
+    "SelfTest",                   // 3
+    "ErrorCount",                 // 4
+    "FailedHealthCheck",          // 5
+    "FailedReadSmartData",        // 6
+    "FailedReadSmartErrorLog",    // 7
+    "FailedReadSmartSelfTestLog", // 8
+    "FailedOpenDevice",           // 9
+    "CurrentPendingSector",       // 10
+    "OfflineUncorrectableSector" //  11
+  };
+  
+  char *address, *executable;
+  mailinfo *mail;
+  maildata* data=cfg->mailwarn;
+#ifndef _WIN32
+  FILE *pfp=NULL;
+#else
+  char stdinbuf[1024]; int boxmsgoffs, boxtype;
+#endif
+  char *newadd=NULL, *newwarn=NULL;
+  const char *unknown="[Unknown]";
+
+  // See if user wants us to send mail
+  if(!data)
+    return;
+  
+  address=data->address;
+  executable=data->emailcmdline;
+  
+  if (!address && !executable)
+    return;
+  
+  // which type of mail are we sending?
+  mail=(data->maillog)+which;
+  
+  // checks for sanity
+  if (data->emailfreq<1 || data->emailfreq>3) {
+    PrintOut(LOG_CRIT,"internal error in MailWarning(): cfg->mailwarn->emailfreq=%d\n",data->emailfreq);
+    return;
+  }
+  if (which<0 || which>=SMARTD_NMAIL || sizeof(whichfail)!=SMARTD_NMAIL*sizeof(char *)) {
+    PrintOut(LOG_CRIT,"Contact " PACKAGE_BUGREPORT "; internal error in MailWarning(): which=%d, size=%d\n",
+             which, (int)sizeof(whichfail));
+    return;
+  }
+  
+  // Return if a single warning mail has been sent.
+  if ((data->emailfreq==1) && mail->logged)
+    return;
+
+  // Return if this is an email test and one has already been sent.
+  if (which == 0 && mail->logged)
+    return;
+  
+  // To decide if to send mail, we need to know what time it is.
+  epoch=time(NULL);
+
+  // Return if less than one day has gone by
+  if (data->emailfreq==2 && mail->logged && epoch<(mail->lastsent+day))
+    return;
+
+  // Return if less than 2^(logged-1) days have gone by
+  if (data->emailfreq==3 && mail->logged){
+    days=0x01<<(mail->logged-1);
+    days*=day;
+    if  (epoch<(mail->lastsent+days))
+      return;
+  }
+
+  // record the time of this mail message, and the first mail message
+  if (!mail->logged)
+    mail->firstsent=epoch;
+  mail->lastsent=epoch;
+  
+  // get system host & domain names (not null terminated if length=MAX) 
+#ifdef HAVE_GETHOSTNAME
+  if (gethostname(hostname, 256))
+    strcpy(hostname, unknown);
+  else {
+    char *p=NULL;
+    hostname[255]='\0';
+    p = dnsdomain(hostname);
+    if (p && *p) {
+      strncpy(domainname, p, 255);
+      domainname[255]='\0';
+    } else
+      strcpy(domainname, unknown);
+  }
+#else
+  strcpy(hostname, unknown);
+  strcpy(domainname, unknown);
+#endif
+  
+#ifdef HAVE_GETDOMAINNAME
+  if (getdomainname(nisdomain, 256))
+    strcpy(nisdomain, unknown);
+  else
+    nisdomain[255]='\0';
+#else
+  strcpy(nisdomain, unknown);
+#endif
+  
+  // print warning string into message
+  va_start(ap, fmt);
+  vsnprintf(message, 256, fmt, ap);
+  va_end(ap);
+
+  // appropriate message about further information
+  additional[0]=original[0]=further[0]='\0';
+  if (which) {
+    sprintf(further,"You can also use the smartctl utility for further investigation.\n");
+
+    switch (data->emailfreq){
+    case 1:
+      sprintf(additional,"No additional email messages about this problem will be sent.\n");
+      break;
+    case 2:
+      sprintf(additional,"Another email message will be sent in 24 hours if the problem persists.\n");
+      break;
+    case 3:
+      sprintf(additional,"Another email message will be sent in %d days if the problem persists\n",
+              (0x01)<<mail->logged);
+      break;
+    }
+    if (data->emailfreq>1 && mail->logged){
+      dateandtimezoneepoch(dates, mail->firstsent);
+      sprintf(original,"The original email about this issue was sent at %s\n", dates);
+    }
+  }
+  
+  snprintf(subject, 256,"SMART error (%s) detected on host: %s", whichfail[which], hostname);
+
+  // If the user has set cfg->emailcmdline, use that as mailer, else "mail" or "mailx".
+  if (!executable)
+#ifdef DEFAULT_MAILER
+    executable = DEFAULT_MAILER ;
+#else
+#ifndef _WIN32
+    executable = "mail";
+#else
+    executable = "blat"; // http://blat.sourceforge.net/
+#endif
+#endif
+
+  // make a private copy of address with commas replaced by spaces
+  // to separate recipients
+  if (address) {
+    address=CustomStrDup(data->address, 1, __LINE__, filenameandversion);
+#ifndef _WIN32 // blat mailer needs comma
+    {
+      char *comma=address;
+      while ((comma=strchr(comma, ',')))
+        *comma=' ';
+    }
+#endif
+  }
+
+  // Export information in environment variables that will be useful
+  // for user scripts
+  exportenv(environ_strings[0], "SMARTD_MAILER", executable);
+  exportenv(environ_strings[1], "SMARTD_MESSAGE", message);
+  exportenv(environ_strings[2], "SMARTD_SUBJECT", subject);
+  dateandtimezoneepoch(dates, mail->firstsent);
+  exportenv(environ_strings[3], "SMARTD_TFIRST", dates);
+  snprintf(dates, DATEANDEPOCHLEN,"%d", (int)mail->firstsent);
+  exportenv(environ_strings[4], "SMARTD_TFIRSTEPOCH", dates);
+  exportenv(environ_strings[5], "SMARTD_FAILTYPE", whichfail[which]);
+  if (address)
+    exportenv(environ_strings[6], "SMARTD_ADDRESS", address);
+  exportenv(environ_strings[7], "SMARTD_DEVICESTRING", cfg->name);
+
+  switch (cfg->controller_type) {
+  case CONTROLLER_3WARE_678K:
+  case CONTROLLER_3WARE_9000_CHAR:
+  case CONTROLLER_3WARE_678K_CHAR:
+    {
+      char *s,devicetype[16];
+      sprintf(devicetype, "3ware,%d", cfg->controller_port-1);
+      exportenv(environ_strings[8], "SMARTD_DEVICETYPE", devicetype);
+      if ((s=strchr(cfg->name, ' ')))
+       *s='\0';
+      exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
+      if (s)
+       *s=' ';
+    }
+    break;
+  case CONTROLLER_ATA:
+    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "ata");
+    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
+    break;
+  case CONTROLLER_MARVELL_SATA:
+    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "marvell");
+    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
+    break;
+  case CONTROLLER_SCSI:
+    exportenv(environ_strings[8], "SMARTD_DEVICETYPE", "scsi");
+    exportenv(environ_strings[9], "SMARTD_DEVICE", cfg->name);
+  }
+
+  snprintf(fullmessage, 1024,
+             "This email was generated by the smartd daemon running on:\n\n"
+             "   host name: %s\n"
+             "  DNS domain: %s\n"
+             "  NIS domain: %s\n\n"
+             "The following warning/error was logged by the smartd daemon:\n\n"
+             "%s\n\n"
+             "For details see host's SYSLOG (default: /var/log/messages).\n\n"
+             "%s%s%s",
+            hostname, domainname, nisdomain, message, further, original, additional);
+  exportenv(environ_strings[10], "SMARTD_FULLMESSAGE", fullmessage);
+
+  // now construct a command to send this as EMAIL
+#ifndef _WIN32
+  if (address)
+    snprintf(command, 2048, 
+             "$SMARTD_MAILER -s '%s' %s 2>&1 << \"ENDMAIL\"\n"
+            "%sENDMAIL\n", subject, address, fullmessage);
+  else
+    snprintf(command, 2048, "%s 2>&1", executable);
+  
+  // tell SYSLOG what we are about to do...
+  newadd=address?address:"<nomailer>";
+  newwarn=which?"Warning via":"Test of";
+
+  PrintOut(LOG_INFO,"%s %s to %s ...\n",
+           which?"Sending warning via":"Executing test of", executable, newadd);
+  
+  // issue the command to send mail or to run the user's executable
+  errno=0;
+  if (!(pfp=popen(command, "r")))
+    // failed to popen() mail process
+    PrintOut(LOG_CRIT,"%s %s to %s: failed (fork or pipe failed, or no memory) %s\n", 
+            newwarn,  executable, newadd, errno?strerror(errno):"");
+  else {
+    // pipe suceeded!
+    int len, status;
+    char buffer[EBUFLEN];
+
+    // if unexpected output on stdout/stderr, null terminate, print, and flush
+    if ((len=fread(buffer, 1, EBUFLEN, pfp))) {
+      int count=0;
+      int newlen = len<EBUFLEN ? len : EBUFLEN-1;
+      buffer[newlen]='\0';
+      PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%s%d bytes) to STDOUT/STDERR: \n%s\n", 
+              newwarn, executable, newadd, len!=newlen?"here truncated to ":"", newlen, buffer);
+      
+      // flush pipe if needed
+      while (fread(buffer, 1, EBUFLEN, pfp) && count<EBUFLEN)
+       count++;
+
+      // tell user that pipe was flushed, or that something is really wrong
+      if (count && count<EBUFLEN)
+       PrintOut(LOG_CRIT,"%s %s to %s: flushed remaining STDOUT/STDERR\n", 
+                newwarn, executable, newadd);
+      else if (count)
+       PrintOut(LOG_CRIT,"%s %s to %s: more than 1 MB STDOUT/STDERR flushed, breaking pipe\n", 
+                newwarn, executable, newadd);
+    }
+    
+    // if something went wrong with mail process, print warning
+    errno=0;
+    if (-1==(status=pclose(pfp)))
+      PrintOut(LOG_CRIT,"%s %s to %s: pclose(3) failed %s\n", newwarn, executable, newadd,
+              errno?strerror(errno):"");
+    else {
+      // mail process apparently succeeded. Check and report exit status
+      int status8;
+
+      if (WIFEXITED(status)) {
+       // exited 'normally' (but perhaps with nonzero status)
+       status8=WEXITSTATUS(status);
+       
+       if (status8>128)  
+         PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d) perhaps caught signal %d [%s]\n", 
+                  newwarn, executable, newadd, status, status8, status8-128, strsignal(status8-128));
+       else if (status8)  
+         PrintOut(LOG_CRIT,"%s %s to %s: failed (32-bit/8-bit exit status: %d/%d)\n", 
+                  newwarn, executable, newadd, status, status8);
+       else
+         PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
+      }
+      
+      if (WIFSIGNALED(status))
+       PrintOut(LOG_INFO,"%s %s to %s: exited because of uncaught signal %d [%s]\n", 
+                newwarn, executable, newadd, WTERMSIG(status), strsignal(WTERMSIG(status)));
+      
+      // this branch is probably not possible. If subprocess is
+      // stopped then pclose() should not return.
+      if (WIFSTOPPED(status)) 
+       PrintOut(LOG_CRIT,"%s %s to %s: process STOPPED because it caught signal %d [%s]\n",
+                newwarn, executable, newadd, WSTOPSIG(status), strsignal(WSTOPSIG(status)));
+      
+    }
+  }
+  
+#else // _WIN32
+
+  // No "here-documents" on Windows, so must use separate commandline and stdin
+  command[0] = stdinbuf[0] = 0;
+  boxtype = -1; boxmsgoffs = 0;
+  newadd = "<nomailer>";
+  if (address) {
+    // address "[sys]msgbox ..." => show warning (also) as [system modal ]messagebox
+    int addroffs = (!strncmp(address, "sys", 3) ? 3 : 0);
+    if (!strncmp(address+addroffs, "msgbox", 6) && (!address[addroffs+6] || address[addroffs+6] == ',')) {
+      boxtype = (addroffs > 0 ? 1 : 0);
+      addroffs += 6;
+      if (address[addroffs])
+        addroffs++;
+    }
+    else
+      addroffs = 0;
+
+    if (address[addroffs]) {
+      // Use "blat" parameter syntax (TODO: configure via -M for other mailers)
+      snprintf(command, sizeof(command),
+               "%s - -q -subject \"%s\" -to \"%s\"",
+               executable, subject, address+addroffs);
+      newadd = address+addroffs;
+    }
+    // Message for mail [0...] and messagebox [boxmsgoffs...]
+    snprintf(stdinbuf, sizeof(stdinbuf),
+             "This email was generated by the smartd daemon running on:\n\n"
+             "   host name: %s\n"
+             "  DNS domain: %s\n"
+//           "  NIS domain: %s\n"
+             "\n%n"
+             "The following warning/error was logged by the smartd daemon:\n\n"
+             "%s\n\n"
+             "For details see the event log or log file of smartd.\n\n"
+             "%s%s%s"
+             "\n",
+             hostname, /*domainname, */ nisdomain, &boxmsgoffs, message, further, original, additional);
+  }
+  else
+    snprintf(command, sizeof(command), "%s", executable);
+
+  newwarn=which?"Warning via":"Test of";
+  if (boxtype >= 0) {
+    // show message box
+    daemon_messagebox(boxtype, subject, stdinbuf+boxmsgoffs);
+    PrintOut(LOG_INFO,"%s message box\n", newwarn);
+  }
+  if (command[0]) {
+    char stdoutbuf[800]; // < buffer in syslog_win32::vsyslog()
+    int rc;
+    // run command
+    PrintOut(LOG_INFO,"%s %s to %s ...\n",
+             (which?"Sending warning via":"Executing test of"), executable, newadd);
+    rc = daemon_spawn(command, stdinbuf, strlen(stdinbuf), stdoutbuf, sizeof(stdoutbuf));
+    if (rc >= 0 && stdoutbuf[0])
+      PrintOut(LOG_CRIT,"%s %s to %s produced unexpected output (%d bytes) to STDOUT/STDERR:\n%s\n",
+        newwarn, executable, newadd, strlen(stdoutbuf), stdoutbuf);
+    if (rc != 0)
+      PrintOut(LOG_CRIT,"%s %s to %s: failed, exit status %d\n",
+        newwarn, executable, newadd, rc);
+    else
+      PrintOut(LOG_INFO,"%s %s to %s: successful\n", newwarn, executable, newadd);
+  }
+
+#endif // _WIN32
+
+  // increment mail sent counter
+  mail->logged++;
+  
+  // free copy of address (without commas)
+  address=FreeNonZero(address, -1, __LINE__, filenameandversion);
+
+  return;
+}
+
+// Printing function for watching ataprint commands, or losing them
+// [From GLIBC Manual: Since the prototype doesn't specify types for
+// optional arguments, in a call to a variadic function the default
+// argument promotions are performed on the optional argument
+// values. This means the objects of type char or short int (whether
+// signed or not) are promoted to either int or unsigned int, as
+// appropriate.]
+void pout(char *fmt, ...){
+  va_list ap;
+
+  // get the correct time in syslog()
+  FixGlibcTimeZoneBug();
+  // initialize variable argument list 
+  va_start(ap,fmt);
+  // in debug==1 mode we will print the output from the ataprint.o functions!
+  if (debugmode && debugmode!=2)
+#ifdef _WIN32
+   if (facility == LOG_LOCAL1) // logging to stdout
+    vfprintf(stderr,fmt,ap);
+   else   
+#endif
+    vprintf(fmt,ap);
+  // in debug==2 mode we print output from knowndrives.o functions
+  else if (debugmode==2 || con->reportataioctl || con->reportscsiioctl || con->controller_port) {
+    openlog("smartd", LOG_PID, facility);
+    vsyslog(LOG_INFO, fmt, ap);
+    closelog();
+  }
+  va_end(ap);
+  fflush(NULL);
+  return;
+}
+
+// This function prints either to stdout or to the syslog as needed.
+// This function is also used by utility.c to report LOG_CRIT errors.
+void PrintOut(int priority,char *fmt, ...){
+  va_list ap;
+  
+  // get the correct time in syslog()
+  FixGlibcTimeZoneBug();
+  // initialize variable argument list 
+  va_start(ap,fmt);
+  if (debugmode) 
+#ifdef _WIN32
+   if (facility == LOG_LOCAL1) // logging to stdout
+    vfprintf(stderr,fmt,ap);
+   else   
+#endif
+    vprintf(fmt,ap);
+  else {
+    openlog("smartd", LOG_PID, facility);
+    vsyslog(priority,fmt,ap);
+    closelog();
+  }
+  va_end(ap);
+  return;
+}
+
+// Forks new process, closes ALL file descriptors, redirects stdin,
+// stdout, and stderr.  Not quite daemon().  See
+// http://www.iar.unlp.edu.ar/~fede/revistas/lj/Magazines/LJ47/2335.html
+// for a good description of why we do things this way.
+void DaemonInit(){
+#ifndef _WIN32
+  pid_t pid;
+  int i;  
+
+  // flush all buffered streams.  Else we might get two copies of open
+  // streams since both parent and child get copies of the buffers.
+  fflush(NULL);
+  
+  if ((pid=fork()) < 0) {
+    // unable to fork!
+    PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
+    EXIT(EXIT_STARTUP);
+  }
+  else if (pid)
+    // we are the parent process -- exit cleanly
+    EXIT(0);
+  
+  // from here on, we are the child process.
+  setsid();
+
+  // Fork one more time to avoid any possibility of having terminals
+  if ((pid=fork()) < 0) {
+    // unable to fork!
+    PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
+    EXIT(EXIT_STARTUP);
+  }
+  else if (pid)
+    // we are the parent process -- exit cleanly
+    EXIT(0);
+
+  // Now we are the child's child...
+
+  // close any open file descriptors
+  for (i=getdtablesize();i>=0;--i)
+    close(i);
+  
+#ifdef __CYGWIN__
+  // Cygwin's setsid() does not detach the process from Windows console
+  FreeConsole();
+#endif // __CYGWIN__
+
+  // redirect any IO attempts to /dev/null for stdin
+  i=open("/dev/null",O_RDWR);
+  // stdout
+  dup(i);
+  // stderr
+  dup(i);
+  umask(0);
+  chdir("/");
+  
+  PrintOut(LOG_INFO, "smartd has fork()ed into background mode. New PID=%d.\n", (int)getpid());
+
+#else // _WIN32
+
+  // No fork() on native Win32
+  // Detach this process from console
+  fflush(NULL);
+  if (daemon_detach("smartd")) {
+    PrintOut(LOG_CRIT,"smartd unable to detach from console!\n");
+    EXIT(EXIT_STARTUP);
+  }
+  // stdin/out/err now closed if not redirected
+
+#endif // _WIN32
+  return;
+}
+
+// create a PID file containing the current process id
+void WritePidFile() {
+  if (pid_file) {
+    int error = 0;
+    pid_t pid = getpid();
+    mode_t old_umask;
+    FILE* fp; 
+
+#ifndef __CYGWIN__
+    old_umask = umask(0077); // rwx------
+#else
+    // Cygwin: smartd service runs on system account, ensure PID file can be read by admins
+    old_umask = umask(0033); // rwxr--r--
+#endif
+    fp = fopen(pid_file, "w");
+    umask(old_umask);
+    if (fp == NULL) {
+      error = 1;
+    } else if (fprintf(fp, "%d\n", (int)pid) <= 0) {
+      error = 1;
+    } else if (fclose(fp) != 0) {
+      error = 1;
+    }
+    if (error) {
+      PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file);
+      EXIT(EXIT_PID);
+    }
+    PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file, (int)pid);
+  }
+  return;
+}
+
+// Prints header identifying version of code and home
+void PrintHead(){
+#ifdef HAVE_GET_OS_VERSION_STR
+  const char * ver = get_os_version_str();
+#else
+  const char * ver = SMARTMONTOOLS_BUILD_HOST;
+#endif
+  PrintOut(LOG_INFO,"smartd version %s [%s] Copyright (C) 2002-6 Bruce Allen\n", PACKAGE_VERSION, ver);
+  PrintOut(LOG_INFO,"Home page is " PACKAGE_HOMEPAGE "\n\n");
+  return;
+}
+
+// prints help info for configuration file Directives
+void Directives() {
+  PrintOut(LOG_INFO,
+           "Configuration file (%s) Directives (after device name):\n"
+           "  -d TYPE Set the device type: ata, scsi, marvell, removable, 3ware,N\n"
+           "  -T TYPE Set the tolerance to one of: normal, permissive\n"
+           "  -o VAL  Enable/disable automatic offline tests (on/off)\n"
+           "  -S VAL  Enable/disable attribute autosave (on/off)\n"
+           "  -n MODE No check if: never[,q], sleep[,q], standby[,q], idle[,q]\n"
+           "  -H      Monitor SMART Health Status, report if failed\n"
+           "  -s REG  Do Self-Test at time(s) given by regular expression REG\n"
+           "  -l TYPE Monitor SMART log.  Type is one of: error, selftest\n"
+           "  -f      Monitor 'Usage' Attributes, report failures\n"
+           "  -m ADD  Send email warning to address ADD\n"
+           "  -M TYPE Modify email warning behavior (see man page)\n"
+           "  -p      Report changes in 'Prefailure' Attributes\n"
+           "  -u      Report changes in 'Usage' Attributes\n"
+           "  -t      Equivalent to -p and -u Directives\n"
+           "  -r ID   Also report Raw values of Attribute ID with -p, -u or -t\n"
+           "  -R ID   Track changes in Attribute ID Raw value with -p, -u or -t\n"
+           "  -i ID   Ignore Attribute ID for -f Directive\n"
+           "  -I ID   Ignore Attribute ID for -p, -u or -t Directive\n"
+          "  -C ID   Monitor Current Pending Sectors in Attribute ID\n"
+          "  -U ID   Monitor Offline Uncorrectable Sectors in Attribute ID\n"
+           "  -v N,ST Modifies labeling of Attribute N (see man page)  \n"
+           "  -P TYPE Drive-specific presets: use, ignore, show, showall\n"
+           "  -a      Default: -H -f -t -l error -l selftest -C 197 -U 198\n"
+           "  -F TYPE Firmware bug workaround: none, samsung, samsung2\n"
+           "   #      Comment: text after a hash sign is ignored\n"
+           "   \\      Line continuation character\n"
+           "Attribute ID is a decimal integer 1 <= ID <= 255\n"
+          "Use ID = 0 to turn off -C and/or -U Directives\n"
+           "Example: /dev/hda -a\n", 
+           configfile);
+  return;
+}
+
+/* Returns a pointer to a static string containing a formatted list of the valid
+   arguments to the option opt or NULL on failure. */
+const char *GetValidArgList(char opt) {
+  switch (opt) {
+  case 'c':
+    return "<FILE_NAME>, -";
+  case 's':
+    return "valid_regular_expression";
+  case 'l':
+    return "daemon, local0, local1, local2, local3, local4, local5, local6, local7";
+  case 'q':
+    return "nodev, errors, nodevstartup, never, onecheck, showtests";
+  case 'r':
+    return "ioctl[,N], ataioctl[,N], scsiioctl[,N]";
+  case 'p':
+    return "<FILE_NAME>";
+  case 'i':
+    return "<INTEGER_SECONDS>";
+  default:
+    return NULL;
+  }
+}
+
+/* prints help information for command syntax */
+void Usage (void){
+  PrintOut(LOG_INFO,"Usage: smartd [options]\n\n");
+#ifdef HAVE_GETOPT_LONG
+  PrintOut(LOG_INFO,"  -c NAME|-, --configfile=NAME|-\n");
+  PrintOut(LOG_INFO,"        Read configuration file NAME or stdin [default is %s]\n\n", configfile);
+  PrintOut(LOG_INFO,"  -d, --debug\n");
+  PrintOut(LOG_INFO,"        Start smartd in debug mode\n\n");
+  PrintOut(LOG_INFO,"  -D, --showdirectives\n");
+  PrintOut(LOG_INFO,"        Print the configuration file Directives and exit\n\n");
+  PrintOut(LOG_INFO,"  -h, --help, --usage\n");
+  PrintOut(LOG_INFO,"        Display this help and exit\n\n");
+  PrintOut(LOG_INFO,"  -i N, --interval=N\n");
+  PrintOut(LOG_INFO,"        Set interval between disk checks to N seconds, where N >= 10\n\n");
+  PrintOut(LOG_INFO,"  -l local[0-7], --logfacility=local[0-7]\n");
+#ifndef _WIN32
+  PrintOut(LOG_INFO,"        Use syslog facility local0 - local7 or daemon [default]\n\n");
+#else
+  PrintOut(LOG_INFO,"        Log to \"./smartd.log\", stdout, stderr [default is event log]\n\n");
+#endif
+  PrintOut(LOG_INFO,"  -p NAME, --pidfile=NAME\n");
+  PrintOut(LOG_INFO,"        Write PID file NAME\n\n");
+  PrintOut(LOG_INFO,"  -q WHEN, --quit=WHEN\n");
+  PrintOut(LOG_INFO,"        Quit on one of: %s\n\n", GetValidArgList('q'));
+  PrintOut(LOG_INFO,"  -r, --report=TYPE\n");
+  PrintOut(LOG_INFO,"        Report transactions for one of: %s\n\n", GetValidArgList('r'));
+#if defined(_WIN32) || defined(__CYGWIN__)
+  PrintOut(LOG_INFO,"  --service\n");
+  PrintOut(LOG_INFO,"        Running as windows service (see man page), install with:\n");
+#ifdef _WIN32
+  PrintOut(LOG_INFO,"          smartd install [options]\n");
+  PrintOut(LOG_INFO,"        Remove service with:\n");
+  PrintOut(LOG_INFO,"          smartd remove\n\n");
+#else
+  PrintOut(LOG_INFO,"          /etc/rc.d/init.d/smartd install [options]\n");
+  PrintOut(LOG_INFO,"        Remove service with:\n");
+  PrintOut(LOG_INFO,"          /etc/rc.d/init.d/smartd remove\n\n");
+#endif
+#endif // _WIN32 || __CYGWIN__
+  PrintOut(LOG_INFO,"  -V, --version, --license, --copyright\n");
+  PrintOut(LOG_INFO,"        Print License, Copyright, and version information\n");
+#else
+  PrintOut(LOG_INFO,"  -c NAME|-  Read configuration file NAME or stdin [default is %s]\n", configfile);
+  PrintOut(LOG_INFO,"  -d         Start smartd in debug mode\n");
+  PrintOut(LOG_INFO,"  -D         Print the configuration file Directives and exit\n");
+  PrintOut(LOG_INFO,"  -h         Display this help and exit\n");
+  PrintOut(LOG_INFO,"  -i N       Set interval between disk checks to N seconds, where N >= 10\n");
+  PrintOut(LOG_INFO,"  -l local?  Use syslog facility local0 - local7, or daemon\n");
+  PrintOut(LOG_INFO,"  -p NAME    Write PID file NAME\n");
+  PrintOut(LOG_INFO,"  -q WHEN    Quit on one of: %s\n", GetValidArgList('q'));
+  PrintOut(LOG_INFO,"  -r TYPE    Report transactions for one of: %s\n", GetValidArgList('r'));
+  PrintOut(LOG_INFO,"  -V         Print License, Copyright, and version information\n");
+#endif
+}
+
+// returns negative if problem, else fd>=0
+static int OpenDevice(char *device, char *mode, int scanning) {
+  int fd;
+  char *s=device;
+  
+  // If there is an ASCII "space" character in the device name,
+  // terminate string there.  This is for 3ware devices only.
+  if ((s=strchr(device,' ')))
+    *s='\0';
+
+  // open the device
+  fd = deviceopen(device, mode);
+
+  // if we removed a space, put it back in please
+  if (s)
+    *s=' ';
+
+  // if we failed to open the device, complain!
+  if (fd < 0) {
+
+    // For linux+devfs, a nonexistent device gives a strange error
+    // message.  This makes the error message a bit more sensible.
+    // If no debug and scanning - don't print errors
+    if (debugmode || !scanning) {
+      if (errno==ENOENT || errno==ENOTDIR)
+       errno=ENODEV;
+      
+      PrintOut(LOG_INFO,"Device: %s, %s, open() failed\n",
+              device, strerror(errno));
+    }
+    return -1;
+  }
+  // device opened sucessfully
+  return fd;
+}
+
+int CloseDevice(int fd, char *name){
+  if (deviceclose(fd)){
+    PrintOut(LOG_INFO,"Device: %s, %s, close(%d) failed\n", name, strerror(errno), fd);
+    return 1;
+  }
+  // device sucessfully closed
+  return 0;
+}
+
+// returns <0 on failure
+int ATAErrorCount(int fd, char *name){
+  struct ata_smart_errorlog log;
+  
+  if (-1==ataReadErrorLog(fd,&log)){
+    PrintOut(LOG_INFO,"Device: %s, Read SMART Error Log Failed\n",name);
+    return -1;
+  }
+  
+  // return current number of ATA errors
+  return log.error_log_pointer?log.ata_error_count:0;
+}
+
+// returns <0 if problem.  Otherwise, bottom 8 bits are the self test
+// error count, and top bits are the power-on hours of the last error.
+int SelfTestErrorCount(int fd, char *name){
+  struct ata_smart_selftestlog log;
+
+  if (-1==ataReadSelfTestLog(fd,&log)){
+    PrintOut(LOG_INFO,"Device: %s, Read SMART Self Test Log Failed\n",name);
+    return -1;
+  }
+  
+  // return current number of self-test errors
+  return ataPrintSmartSelfTestlog(&log,0);
+}
+
+// scan to see what ata devices there are, and if they support SMART
+int ATADeviceScan(cfgfile *cfg, int scanning){
+  int fd, supported=0;
+  struct ata_identify_device drive;
+  char *name=cfg->name;
+  int retainsmartdata=0;
+  int retid;
+  char *mode;
+  
+  // should we try to register this as an ATA device?
+  switch (cfg->controller_type) {
+  case CONTROLLER_ATA:
+  case CONTROLLER_3WARE_678K:
+  case CONTROLLER_MARVELL_SATA:
+  case CONTROLLER_UNKNOWN:
+    mode="ATA";
+    break;
+  case CONTROLLER_3WARE_678K_CHAR:
+    mode="ATA_3WARE_678K";
+    break;
+  case CONTROLLER_3WARE_9000_CHAR:
+    mode="ATA_3WARE_9000";
+    break;
+  default:
+    // not a recognized ATA or SATA device.  We should never enter
+    // this branch.
+    return 1;
+  }
+  
+  // open the device
+  if ((fd=OpenDevice(name, mode, scanning))<0)
+    // device open failed
+    return 1;
+  PrintOut(LOG_INFO,"Device: %s, opened\n", name);
+  
+  // pass user settings on to low-level ATA commands
+  con->controller_port=cfg->controller_port;
+  con->controller_type=cfg->controller_type;
+  con->fixfirmwarebug = cfg->fixfirmwarebug;
+  
+  // Get drive identity structure
+  if ((retid=ataReadHDIdentity (fd,&drive))){
+    if (retid<0)
+      // Unable to read Identity structure
+      PrintOut(LOG_INFO,"Device: %s, not ATA, no IDENTIFY DEVICE Structure\n",name);
+    else
+      PrintOut(LOG_INFO,"Device: %s, packet devices [this device %s] not SMART capable\n",
+               name, packetdevicetype(retid-1));
+    CloseDevice(fd, name);
+    return 2; 
+  }
+
+  // Show if device in database, and use preset vendor attribute
+  // options unless user has requested otherwise.
+  if (cfg->ignorepresets)
+    PrintOut(LOG_INFO, "Device: %s, smartd database not searched (Directive: -P ignore).\n", name);
+  else {
+    // do whatever applypresets decides to do. Will allocate memory if
+    // cfg->attributedefs is needed.
+    if (applypresets(&drive, &cfg->attributedefs, con)<0)
+      PrintOut(LOG_INFO, "Device: %s, not found in smartd database.\n", name);
+    else
+      PrintOut(LOG_INFO, "Device: %s, found in smartd database.\n", name);
+    
+    // then save the correct state of the flag (applypresets may have changed it)
+    cfg->fixfirmwarebug = con->fixfirmwarebug;
+  }
+  
+  // If requested, show which presets would be used for this drive
+  if (cfg->showpresets) {
+    int savedebugmode=debugmode;
+    PrintOut(LOG_INFO, "Device %s: presets are:\n", name);
+    if (!debugmode)
+      debugmode=2;
+    showpresets(&drive);
+    debugmode=savedebugmode;
+  }
+
+  // see if drive supports SMART
+  supported=ataSmartSupport(&drive);
+  if (supported!=1) {
+    if (supported==0)
+      // drive does NOT support SMART
+      PrintOut(LOG_INFO,"Device: %s, lacks SMART capability\n",name);
+    else
+      // can't tell if drive supports SMART
+      PrintOut(LOG_INFO,"Device: %s, ATA IDENTIFY DEVICE words 82-83 don't specify if SMART capable.\n",name);
+  
+    // should we proceed anyway?
+    if (cfg->permissive){
+      PrintOut(LOG_INFO,"Device: %s, proceeding since '-T permissive' Directive given.\n",name);
+    }
+    else {
+      PrintOut(LOG_INFO,"Device: %s, to proceed anyway, use '-T permissive' Directive.\n",name);
+      CloseDevice(fd, name);
+      return 2;
+    }
+  }
+  
+  if (ataEnableSmart(fd)){
+    // Enable SMART command has failed
+    PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name);
+    CloseDevice(fd, name);
+    return 2; 
+  }
+  
+  // disable device attribute autosave...
+  if (cfg->autosave==1){
+    if (ataDisableAutoSave(fd))
+      PrintOut(LOG_INFO,"Device: %s, could not disable SMART Attribute Autosave.\n",name);
+    else
+      PrintOut(LOG_INFO,"Device: %s, disabled SMART Attribute Autosave.\n",name);
+  }
+
+  // or enable device attribute autosave
+  if (cfg->autosave==2){
+    if (ataEnableAutoSave(fd))
+      PrintOut(LOG_INFO,"Device: %s, could not enable SMART Attribute Autosave.\n",name);
+    else
+      PrintOut(LOG_INFO,"Device: %s, enabled SMART Attribute Autosave.\n",name);
+  }
+
+  // capability check: SMART status
+  if (cfg->smartcheck && ataSmartStatus2(fd)==-1){
+    PrintOut(LOG_INFO,"Device: %s, not capable of SMART Health Status check\n",name);
+    cfg->smartcheck=0;
+  }
+  
+  // capability check: Read smart values and thresholds.  Note that
+  // smart values are ALSO needed even if we ONLY want to know if the
+  // device is self-test log or error-log capable!  After ATA-5, this
+  // information was ALSO reproduced in the IDENTIFY DEVICE response,
+  // but sadly not for ATA-5.  Sigh.
+
+  // do we need to retain SMART data after returning from this routine?
+  retainsmartdata=cfg->usagefailed || cfg->prefail || cfg->usage;
+  
+  // do we need to get SMART data?
+  if (retainsmartdata || cfg->autoofflinetest || cfg->selftest || cfg->errorlog || cfg->pending!=DONT_MONITOR_UNC) {
+
+    unsigned char currentpending, offlinepending;
+
+    cfg->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values));
+    cfg->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt));
+    
+    if (!cfg->smartval || !cfg->smartthres){
+      PrintOut(LOG_CRIT,"Not enough memory to obtain SMART data\n");
+      EXIT(EXIT_NOMEM);
+    }
+    
+    if (ataReadSmartValues(fd,cfg->smartval) ||
+        ataReadSmartThresholds (fd,cfg->smartthres)){
+      PrintOut(LOG_INFO,"Device: %s, Read SMART Values and/or Thresholds Failed\n",name);
+      retainsmartdata=cfg->usagefailed=cfg->prefail=cfg->usage=0;
+      cfg->pending=DONT_MONITOR_UNC;
+    }
+    
+    // see if the necessary Attribute is there to monitor offline or
+    // current pending sectors
+    TranslatePending(cfg->pending, &currentpending, &offlinepending);
+    
+    if (currentpending && ATAReturnAttributeRawValue(currentpending, cfg->smartval)<0) {
+      PrintOut(LOG_INFO,"Device: %s, can't monitor Current Pending Sector count - no Attribute %d\n",
+              name, (int)currentpending);
+      cfg->pending &= 0xff00;
+      cfg->pending |= CUR_UNC_DEFAULT;
+    }
+    
+    if (offlinepending && ATAReturnAttributeRawValue(offlinepending, cfg->smartval)<0) {
+      PrintOut(LOG_INFO,"Device: %s, can't monitor Offline Uncorrectable Sector count  - no Attribute %d\n",
+              name, (int)offlinepending);
+      cfg->pending &= 0x00ff;
+      cfg->pending |= OFF_UNC_DEFAULT<<8;
+    }
+  }
+  
+  // enable/disable automatic on-line testing
+  if (cfg->autoofflinetest){
+    // is this an enable or disable request?
+    char *what=(cfg->autoofflinetest==1)?"disable":"enable";
+    if (!cfg->smartval)
+      PrintOut(LOG_INFO,"Device: %s, could not %s SMART Automatic Offline Testing.\n",name, what);
+    else {
+      // if command appears unsupported, issue a warning...
+      if (!isSupportAutomaticTimer(cfg->smartval))
+        PrintOut(LOG_INFO,"Device: %s, SMART Automatic Offline Testing unsupported...\n",name);
+      // ... but then try anyway
+      if ((cfg->autoofflinetest==1)?ataDisableAutoOffline(fd):ataEnableAutoOffline(fd))
+        PrintOut(LOG_INFO,"Device: %s, %s SMART Automatic Offline Testing failed.\n", name, what);
+      else
+        PrintOut(LOG_INFO,"Device: %s, %sd SMART Automatic Offline Testing.\n", name, what);
+    }
+  }
+  
+  // capability check: self-test-log
+  if (cfg->selftest){
+    int retval;
+    
+    // start with service disabled, and re-enable it if all works OK
+    cfg->selftest=0;
+    cfg->selflogcount=0;
+    cfg->selfloghour=0;
+
+    if (!cfg->smartval)
+      PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log (SMART READ DATA failed); disabling -l selftest\n", name);
+    else if (!cfg->permissive && !isSmartTestLogCapable(cfg->smartval, &drive))
+      PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Self-Test log; disabling -l selftest (override with -T permissive Directive)\n", name);
+    else if ((retval=SelfTestErrorCount(fd, name))<0)
+      PrintOut(LOG_INFO, "Device: %s, no SMART Self-Test log; remove -l selftest Directive from smartd.conf\n", name);
+    else {
+      cfg->selftest=1;
+      cfg->selflogcount=SELFTEST_ERRORCOUNT(retval);
+      cfg->selfloghour =SELFTEST_ERRORHOURS(retval);
+    }
+  }
+  
+  // capability check: ATA error log
+  if (cfg->errorlog){
+    int val;
+
+    // start with service disabled, and re-enable it if all works OK
+    cfg->errorlog=0;
+    cfg->ataerrorcount=0;
+
+    if (!cfg->smartval)
+      PrintOut(LOG_INFO, "Device: %s, no SMART Error log (SMART READ DATA failed); disabling -l error\n", name);
+    else if (!cfg->permissive && !isSmartErrorLogCapable(cfg->smartval, &drive))
+      PrintOut(LOG_INFO, "Device: %s, appears to lack SMART Error log; disabling -l error (override with -T permissive Directive)\n", name);
+    else if ((val=ATAErrorCount(fd, name))<0)
+      PrintOut(LOG_INFO, "Device: %s, no SMART Error log; remove -l error Directive from smartd.conf\n", name);
+    else {
+        cfg->errorlog=1;
+        cfg->ataerrorcount=val;
+    }
+  }
+  
+  // If we don't need to save SMART data, get rid of it now
+  if (!retainsmartdata) {
+    if (cfg->smartval) {
+      cfg->smartval=CheckFree(cfg->smartval, __LINE__,filenameandversion);
+      bytes-=sizeof(struct ata_smart_values);
+    }
+    if (cfg->smartthres) {
+      cfg->smartthres=CheckFree(cfg->smartthres, __LINE__,filenameandversion);
+      bytes-=sizeof(struct ata_smart_thresholds_pvt);
+    }
+  }
+
+  // capabilities check -- does it support powermode?
+  if (cfg->powermode) {
+    int powermode=ataCheckPowerMode(fd);
+    
+    if (-1 == powermode) {
+      PrintOut(LOG_CRIT, "Device: %s, no ATA CHECK POWER STATUS support, ignoring -n Directive\n", name);
+      cfg->powermode=0;
+    } 
+    else if (powermode!=0 && powermode!=0x80 && powermode!=0xff) {
+      PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
+              name, powermode);
+      cfg->powermode=0;
+    }
+  }
+
+  // If no tests available or selected, return
+  if (!(cfg->errorlog || cfg->selftest || cfg->smartcheck || 
+        cfg->usagefailed || cfg->prefail || cfg->usage)) {
+    CloseDevice(fd, name);
+    return 3;
+  }
+  
+  // Do we still have entries available?
+  while (numdevata>=atadevlist_max)
+    atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device");
+  
+  // register device
+  PrintOut(LOG_INFO,"Device: %s, is SMART capable. Adding to \"monitor\" list.\n",name);
+  
+    // record number of device, type of device, increment device count
+  if (cfg->controller_type == CONTROLLER_UNKNOWN)
+    cfg->controller_type=CONTROLLER_ATA;
+  
+  // close file descriptor
+  CloseDevice(fd, name);
+  return 0;
+}
+
+// Returns 1 if device recognised as one we do not want to treat as a general
+// SCSI device. Also returns 1 if INQUIRY fails (all "SCSI" devices should
+// respond to INQUIRY). Otherwise returns 0 (i.e. normal SCSI device).
+static int SCSIFilterKnown(int fd, char * device)
+{
+  char req_buff[256];
+  char di_buff[256];
+  int req_len, avail_len, len;
+
+  memset(req_buff, 0, 96);
+  req_len = 36;
+  if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) {
+    /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
+    /* watch this spot ... other devices could lock up here */
+    req_len = 64;
+    if (scsiStdInquiry(fd, (unsigned char *)req_buff, req_len)) {
+      PrintOut(LOG_INFO, "Device: %s, failed on INQUIRY; skip device\n", device);
+      // device doesn't like INQUIRY commands
+      return 1;
+    }
+  }
+  avail_len = req_buff[4] + 5;
+  len = (avail_len < req_len) ? avail_len : req_len;
+  if (len >= 36) {
+    if (0 == strncmp(req_buff + 8, "3ware", 5) || 0 == strncmp(req_buff + 8, "AMCC", 4) ) {
+      PrintOut(LOG_INFO, "Device %s, please try adding '-d 3ware,N'\n", device);
+      PrintOut(LOG_INFO, "Device %s, you may need to replace %s with /dev/twaN or /dev/tweN\n", device, device);
+      return 1;
+    } else if ((len >= 42) && (0 == strncmp(req_buff + 36, "MVSATA", 6))) {
+      PrintOut(LOG_INFO, "Device %s, please try '-d marvell'\n", device);
+      return 1;
+    } else if ((avail_len >= 96) && (0 == strncmp(req_buff + 8, "ATA", 3))) {
+      /* <<<< This is Linux specific code to detect SATA disks using a
+              SCSI-ATA command translation layer. This may be generalized
+              later when the t10.org SAT project matures. >>>> */
+      req_len = 96;
+      memset(di_buff, 0, req_len);
+      if (scsiInquiryVpd(fd, 0x83, (unsigned char *)di_buff, req_len)) {
+        return 0;    // guess it is normal device
+      }
+      avail_len = ((di_buff[2] << 8) + di_buff[3]) + 4;
+      len = (avail_len < req_len) ? avail_len : req_len;
+      if (isLinuxLibAta((unsigned char *)di_buff, len)) {
+        PrintOut(LOG_INFO, "Device %s: SATA disks accessed via libata are "
+                 "supported by Linux kernel versions 2.6.15-rc1 and above.\n"
+                 "Try adding '-d ata' to the smartd.conf config file line.\n", device);
+        return 1;
+      }
+    }
+  }
+  return 0;
+}
+
+// on success, return 0. On failure, return >0.  Never return <0,
+// please.
+static int SCSIDeviceScan(cfgfile *cfg, int scanning) {
+  int k, fd, err; 
+  char *device = cfg->name;
+  struct scsi_iec_mode_page iec;
+  UINT8  tBuf[64];
+  
+  // should we try to register this as a SCSI device?
+  switch (cfg->controller_type) {
+  case CONTROLLER_SCSI:
+  case CONTROLLER_UNKNOWN:
+    break;
+  default:
+    return 1;
+  }
+  
+  // open the device
+  if ((fd = OpenDevice(device, "SCSI", scanning)) < 0)
+    return 1;
+  PrintOut(LOG_INFO,"Device: %s, opened\n", device);
+
+  // early skip if device known and needs to be handled by some other
+  // device type (e.g. '-d 3ware,<n>')
+  if (SCSIFilterKnown(fd, device)) {
+    CloseDevice(fd, device);
+    return 2; 
+  }
+    
+  // check that device is ready for commands. IE stores its stuff on
+  // the media.
+  if ((err = scsiTestUnitReady(fd))) {
+    if (SIMPLE_ERR_NOT_READY == err)
+      PrintOut(LOG_INFO, "Device: %s, NOT READY (e.g. spun down); skip device\n", device);
+    else if (SIMPLE_ERR_NO_MEDIUM == err)
+      PrintOut(LOG_INFO, "Device: %s, NO MEDIUM present; skip device\n", device);
+    else if (SIMPLE_ERR_BECOMING_READY == err)
+      PrintOut(LOG_INFO, "Device: %s, BECOMING (but not yet) READY; skip device\n", device);
+    else
+      PrintOut(LOG_CRIT, "Device: %s, failed Test Unit Ready [err=%d]\n", device, err);
+    CloseDevice(fd, device);
+    return 2; 
+  }
+  
+  // Badly-conforming USB storage devices may fail this check.
+  // The response to the following IE mode page fetch (current and
+  // changeable values) is carefully examined. It has been found
+  // that various USB devices that malform the response will lock up
+  // if asked for a log page (e.g. temperature) so it is best to
+  // bail out now.
+  if (!(err = scsiFetchIECmpage(fd, &iec, cfg->modese_len)))
+    cfg->modese_len = iec.modese_len;
+  else if (SIMPLE_ERR_BAD_FIELD == err)
+    ;  /* continue since it is reasonable not to support IE mpage */
+  else { /* any other error (including malformed response) unreasonable */
+    PrintOut(LOG_INFO, 
+             "Device: %s, Bad IEC (SMART) mode page, err=%d, skip device\n", 
+             device, err);
+    CloseDevice(fd, device);
+    return 3;
+  }
+  
+  // N.B. The following is passive (i.e. it doesn't attempt to turn on
+  // smart if it is off). This may change to be the same as the ATA side.
+  if (!scsi_IsExceptionControlEnabled(&iec)) {
+    PrintOut(LOG_INFO, "Device: %s, IE (SMART) not enabled, skip device\n"
+                      "Try 'smartctl -s on %s' to turn on SMART features\n", 
+                        device, device);
+    CloseDevice(fd, device);
+    return 3;
+  }
+  
+  // Device exists, and does SMART.  Add to list (allocating more space if needed)
+  while (numdevscsi >= scsidevlist_max)
+    scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device");
+  
+  // Flag that certain log pages are supported (information may be
+  // available from other sources).
+  if (0 == scsiLogSense(fd, SUPPORTED_LPAGES, tBuf, sizeof(tBuf), 0)) {
+    for (k = 4; k < tBuf[3] + LOGPAGEHDRSIZE; ++k) {
+      switch (tBuf[k]) { 
+      case TEMPERATURE_LPAGE:
+        cfg->TempPageSupported = 1;
+        break;
+      case IE_LPAGE:
+        cfg->SmartPageSupported = 1;
+        break;
+      default:
+        break;
+      }
+    }   
+  }
+  
+  // record type of device
+  cfg->controller_type = CONTROLLER_SCSI;
+  
+  // get rid of allocated memory only needed for ATA devices.  These
+  // might have been allocated if the user specified Ignore options or
+  // other ATA-only Attribute-specific options on the DEVICESCAN line.
+  cfg->monitorattflags = FreeNonZero(cfg->monitorattflags, NMONITOR*32,__LINE__,filenameandversion);
+  cfg->attributedefs   = FreeNonZero(cfg->attributedefs,   MAX_ATTRIBUTE_NUM,__LINE__,filenameandversion);
+  cfg->smartval        = FreeNonZero(cfg->smartval,        sizeof(struct ata_smart_values),__LINE__,filenameandversion);
+  cfg->smartthres      = FreeNonZero(cfg->smartthres,      sizeof(struct ata_smart_thresholds_pvt),__LINE__,filenameandversion);
+  
+  // Check if scsiCheckIE() is going to work
+  {
+    UINT8 asc = 0;
+    UINT8 ascq = 0;
+    UINT8 currenttemp = 0;
+    UINT8 triptemp = 0;
+    
+    if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
+                    &asc, &ascq, &currenttemp, &triptemp)) {
+      PrintOut(LOG_INFO, "Device: %s, unexpectedly failed to read SMART values\n", device);
+      cfg->SuppressReport = 1;
+    }
+  }
+  
+  // capability check: self-test-log
+  if (cfg->selftest){
+    int retval=scsiCountFailedSelfTests(fd, 0);
+    if (retval<0) {
+      // no self-test log, turn off monitoring
+      PrintOut(LOG_INFO, "Device: %s, does not support SMART Self-Test Log.\n", device);
+      cfg->selftest=0;
+      cfg->selflogcount=0;
+      cfg->selfloghour=0;
+    }
+    else {
+      // register starting values to watch for changes
+      cfg->selflogcount=SELFTEST_ERRORCOUNT(retval);
+      cfg->selfloghour =SELFTEST_ERRORHOURS(retval);
+    }
+  }
+  
+  // disable autosave (set GLTSD bit)
+  if (cfg->autosave==1){
+    if (scsiSetControlGLTSD(fd, 1, cfg->modese_len))
+      PrintOut(LOG_INFO,"Device: %s, could not disable autosave (set GLTSD bit).\n",device);
+    else
+      PrintOut(LOG_INFO,"Device: %s, disabled autosave (set GLTSD bit).\n",device);
+  }
+
+  // or enable autosave (clear GLTSD bit)
+  if (cfg->autosave==2){
+    if (scsiSetControlGLTSD(fd, 0, cfg->modese_len))
+      PrintOut(LOG_INFO,"Device: %s, could not enable autosave (clear GLTSD bit).\n",device);
+    else
+      PrintOut(LOG_INFO,"Device: %s, enabled autosave (cleared GLTSD bit).\n",device);
+  }
+  
+  // tell user we are registering device
+  PrintOut(LOG_INFO, "Device: %s, is SMART capable. Adding to \"monitor\" list.\n", device);
+  
+  // close file descriptor
+  CloseDevice(fd, device);
+  return 0;
+}
+
+// We compare old and new values of the n'th attribute.  Note that n
+// is NOT the attribute ID number.. If (Normalized & Raw) equal,
+// then return 0, else nonzero.
+int  ATACompareValues(changedattribute_t *delta,
+                            struct ata_smart_values *new,
+                            struct ata_smart_values *old,
+                            struct ata_smart_thresholds_pvt *thresholds,
+                            int n, char *name){
+  struct ata_smart_attribute *now,*was;
+  struct ata_smart_threshold_entry *thre;
+  unsigned char oldval,newval;
+  int sameraw;
+
+  // check that attribute number in range, and no null pointers
+  if (n<0 || n>=NUMBER_ATA_SMART_ATTRIBUTES || !new || !old || !thresholds)
+    return 0;
+  
+  // pointers to disk's values and vendor's thresholds
+  now=new->vendor_attributes+n;
+  was=old->vendor_attributes+n;
+  thre=thresholds->thres_entries+n;
+
+  // consider only valid attributes
+  if (!now->id || !was->id || !thre->id)
+    return 0;
+  
+  
+  // issue warning if they don't have the same ID in all structures:
+  if ( (now->id != was->id) || (now->id != thre->id) ){
+    PrintOut(LOG_INFO,"Device: %s, same Attribute has different ID numbers: %d = %d = %d\n",
+             name, (int)now->id, (int)was->id, (int)thre->id);
+    return 0;
+  }
+
+  // new and old values of Normalized Attributes
+  newval=now->current;
+  oldval=was->current;
+
+  // See if the RAW values are unchanged (ie, the same)
+  if (memcmp(now->raw, was->raw, 6))
+    sameraw=0;
+  else
+    sameraw=1;
+  
+  // if any values out of the allowed range, or if the values haven't
+  // changed, return 0
+  if (!newval || !oldval || newval>0xfe || oldval>0xfe || (oldval==newval && sameraw))
+    return 0;
+  
+  // values have changed.  Construct output and return
+  delta->newval=newval;
+  delta->oldval=oldval;
+  delta->id=now->id;
+  delta->prefail=ATTRIBUTE_FLAGS_PREFAILURE(now->flags);
+  delta->sameraw=sameraw;
+
+  return 1;
+}
+
+// This looks to see if the corresponding bit of the 32 bytes is set.
+// This wastes a few bytes of storage but eliminates all searching and
+// sorting functions! Entry is ZERO <==> the attribute ON. Calling
+// with set=0 tells you if the attribute is being tracked or not.
+// Calling with set=1 turns the attribute OFF.
+int IsAttributeOff(unsigned char attr, unsigned char **datap, int set, int which, int whatline){
+  unsigned char *data;
+  int loc=attr>>3;
+  int bit=attr & 0x07;
+  unsigned char mask=0x01<<bit;
+
+  if (which>=NMONITOR || which < 0){
+    PrintOut(LOG_CRIT, "Internal error in IsAttributeOff() at line %d of file %s (which=%d)\n%s",
+             whatline, filenameandversion, which, reportbug);
+    EXIT(EXIT_BADCODE);
+  }
+
+  if (*datap == NULL){
+    // NULL data implies Attributes are ON...
+    if (!set)
+      return 0;
+    
+    // we are writing
+    if (!(*datap=(unsigned char *)Calloc(NMONITOR*32, 1))){
+      PrintOut(LOG_CRIT,"No memory to create monattflags\n");
+      EXIT(EXIT_NOMEM);
+    }
+  }
+  
+  // pointer to the 256 bits that we need
+  data=*datap+which*32;
+
+  // attribute zero is always OFF
+  if (!attr)
+    return 1;
+
+  if (!set)
+    return (data[loc] & mask);
+  
+  data[loc]|=mask;
+
+  // return value when setting has no sense
+  return 0;
+}
+
+// If the self-test log has got more self-test errors (or more recent
+// self-test errors) recorded, then notify user.
+void CheckSelfTestLogs(cfgfile *cfg, int new){
+  char *name=cfg->name;
+
+  if (new<0)
+    // command failed
+    MailWarning(cfg, 8, "Device: %s, Read SMART Self-Test Log Failed", name);
+  else {      
+    // old and new error counts
+    int oldc=cfg->selflogcount;
+    int newc=SELFTEST_ERRORCOUNT(new);
+    
+    // old and new error timestamps in hours
+    int oldh=cfg->selfloghour;
+    int newh=SELFTEST_ERRORHOURS(new);
+    
+    if (oldc<newc) {
+      // increase in error count
+      PrintOut(LOG_CRIT, "Device: %s, Self-Test Log error count increased from %d to %d\n",
+               name, oldc, newc);
+      MailWarning(cfg, 3, "Device: %s, Self-Test Log error count increased from %d to %d",
+                   name, oldc, newc);
+    } else if (oldh!=newh) {
+      // more recent error
+      // a 'more recent' error might actually be a smaller hour number,
+      // if the hour number has wrapped.
+      // There's still a bug here.  You might just happen to run a new test
+      // exactly 32768 hours after the previous failure, and have run exactly
+      // 20 tests between the two, in which case smartd will miss the
+      // new failure.
+      PrintOut(LOG_CRIT, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
+               name, newh);
+      MailWarning(cfg, 3, "Device: %s, new Self-Test Log error at hour timestamp %d\n",
+                   name, newh);
+    }
+    
+    // Needed since self-test error count may DECREASE.  Hour might
+    // also have changed.
+    cfg->selflogcount= newc;
+    cfg->selfloghour = newh;
+  }
+  return;
+}
+
+// returns 1 if time to do test of type testtype, 0 if not time to do
+// test, < 0 if error
+int DoTestNow(cfgfile *cfg, char testtype, time_t testtime) {
+  // start by finding out the time:
+  struct tm *timenow;
+  time_t epochnow;
+  char matchpattern[16];
+  regmatch_t substring;
+  int weekday, length;
+  unsigned short hours;
+  testinfo *dat=cfg->testdata;
+
+  // check that self-testing has been requested
+  if (!dat)
+    return 0;
+  
+  // since we are about to call localtime(), be sure glibc is informed
+  // of any timezone changes we make.
+  if (!testtime)
+    FixGlibcTimeZoneBug();
+  
+  // construct pattern containing the month, day of month, day of
+  // week, and hour
+  epochnow = (!testtime ? time(NULL) : testtime);
+  timenow=localtime(&epochnow);
+  
+  // tm_wday is 0 (Sunday) to 6 (Saturday).  We use 1 (Monday) to 7
+  // (Sunday).
+  weekday=timenow->tm_wday?timenow->tm_wday:7;
+  sprintf(matchpattern, "%c/%02d/%02d/%1d/%02d", testtype, timenow->tm_mon+1, 
+          timenow->tm_mday, weekday, timenow->tm_hour);
+  
+  // if no match, we are done
+  if (regexec(&(dat->cregex), matchpattern, 1, &substring, 0))
+    return 0;
+  
+  // must match the ENTIRE type/date/time string
+  length=strlen(matchpattern);
+  if (substring.rm_so!=0 || substring.rm_eo!=length)
+    return 0;
+  
+  // never do a second test in the same hour as another test (the % 7 ensures
+  // that the RHS will never be greater than 65535 and so will always fit into
+  // an unsigned short)
+  hours=1+timenow->tm_hour+24*(timenow->tm_yday+366*(timenow->tm_year % 7));
+  if (hours==dat->hour) {
+    if (!testtime && testtype!=dat->testtype)
+      PrintOut(LOG_INFO, "Device: %s, did test of type %c in current hour, skipping test of type %c\n",
+              cfg->name, dat->testtype, testtype);
+    return 0;
+  }
+  
+  // save time and type of the current test; we are ready to do a test
+  dat->hour=hours;
+  dat->testtype=testtype;
+  return 1;
+}
+
+// Print a list of future tests.
+void PrintTestSchedule(cfgfile **atadevices, cfgfile **scsidevices){
+  int i, t;
+  cfgfile * cfg;
+  char datenow[DATEANDEPOCHLEN], date[DATEANDEPOCHLEN];
+  time_t now; long seconds;
+  int numdev = numdevata+numdevscsi;
+  typedef int cnt_t[4];
+  cnt_t * testcnts; // testcnts[numdev][4]
+  if (numdev <= 0)
+    return;
+  testcnts = calloc(numdev, sizeof(testcnts[0]));
+  if (!testcnts)
+    return;
+
+  PrintOut(LOG_INFO, "\nNext scheduled self tests (at most 5 of each type per device):\n");
+
+  // FixGlibcTimeZoneBug(); // done in PrintOut()
+  now=time(NULL);
+  dateandtimezoneepoch(datenow, now);
+  for (seconds=0; seconds<3600L*24*90; seconds+=checktime) {
+    // Check for each device whether a test will be run
+    time_t testtime = now + seconds;
+    for (i=0; i<numdev; i++) {
+      cfg = (i<numdevata? atadevices[i] : scsidevices[i-numdevata]);
+      for (t=0; t<(i<numdevata?4:2); t++) {
+        char testtype = "LSCO"[t];
+        if (DoTestNow(cfg, testtype, testtime)) {
+          // Report at most 5 tests of each type
+          if (++testcnts[i][t] <= 5) {
+            dateandtimezoneepoch(date, testtime);
+            PrintOut(LOG_INFO, "Device: %s, will do test %d of type %c at %s\n", cfg->name,
+              testcnts[i][t], testtype, date);
+          }
+        }
+      }
+    }
+  }
+
+  // Report totals
+  dateandtimezoneepoch(date, now+seconds);
+  PrintOut(LOG_INFO, "\nTotals [%s - %s]:\n", datenow, date);
+  for (i=0; i<numdev; i++) {
+    cfg = (i<numdevata? atadevices[i] : scsidevices[i-numdevata]);
+    for (t=0; t<(i<numdevata?4:2); t++) {
+      PrintOut(LOG_INFO, "Device: %s, will do %3d test%s of type %c\n", cfg->name, testcnts[i][t],
+        (testcnts[i][t]==1?"":"s"), "LSCO"[t]);
+    }
+  }
+
+  free(testcnts);
+}
+
+// Return zero on success, nonzero on failure. Perform offline (background)
+// short or long (extended) self test on given scsi device.
+int DoSCSISelfTest(int fd, cfgfile *cfg, char testtype) {
+  int retval = 0;
+  char *testname = NULL;
+  char *name = cfg->name;
+  int inProgress;
+
+  if (scsiSelfTestInProgress(fd, &inProgress)) {
+    PrintOut(LOG_CRIT, "Device: %s, does not support Self-Tests\n", name);
+    cfg->testdata->not_cap_short=cfg->testdata->not_cap_long=1;
+    return 1;
+  }
+
+  if (1 == inProgress) {
+    PrintOut(LOG_INFO, "Device: %s, skip since Self-Test already in "
+             "progress.\n", name);
+    return 1;
+  }
+
+  switch (testtype) {
+  case 'S':
+    testname = "Short Self";
+    retval = scsiSmartShortSelfTest(fd);
+    break;
+  case 'L':
+    testname = "Long Self";
+    retval = scsiSmartExtendSelfTest(fd);
+    break;
+  }
+  // If we can't do the test, exit
+  if (NULL == testname) {
+    PrintOut(LOG_CRIT, "Device: %s, not capable of %c Self-Test\n", name, 
+             testtype);
+    return 1;
+  }
+  if (retval) {
+    if ((SIMPLE_ERR_BAD_OPCODE == retval) || 
+        (SIMPLE_ERR_BAD_FIELD == retval)) {
+      PrintOut(LOG_CRIT, "Device: %s, not capable of %s-Test\n", name, 
+               testname);
+      if ('L'==testtype)
+        cfg->testdata->not_cap_long=1;
+      else
+        cfg->testdata->not_cap_short=1;
+     
+      return 1;
+    }
+    PrintOut(LOG_CRIT, "Device: %s, execute %s-Test failed (err: %d)\n", name, 
+             testname, retval);
+    return 1;
+  }
+  
+  PrintOut(LOG_INFO, "Device: %s, starting scheduled %s-Test.\n", name, testname);
+  
+  return 0;
+}
+
+// Do an offline immediate or self-test.  Return zero on success,
+// nonzero on failure.
+int DoATASelfTest(int fd, cfgfile *cfg, char testtype) {
+  
+  struct ata_smart_values data;
+  char *testname=NULL;
+  int retval, dotest=-1;
+  char *name=cfg->name;
+  
+  // Read current smart data and check status/capability
+  if (ataReadSmartValues(fd, &data) || !(data.offline_data_collection_capability)) {
+    PrintOut(LOG_CRIT, "Device: %s, not capable of Offline or Self-Testing.\n", name);
+    return 1;
+  }
+  
+  // Check for capability to do the test
+  switch (testtype) {
+  case 'O':
+    testname="Offline Immediate ";
+    if (isSupportExecuteOfflineImmediate(&data))
+      dotest=OFFLINE_FULL_SCAN;
+    else
+      cfg->testdata->not_cap_offline=1;
+    break;
+  case 'C':
+    testname="Conveyance Self-";
+    if (isSupportConveyanceSelfTest(&data))
+      dotest=CONVEYANCE_SELF_TEST;
+    else
+      cfg->testdata->not_cap_conveyance=1;
+    break;
+  case 'S':
+    testname="Short Self-";
+    if (isSupportSelfTest(&data))
+      dotest=SHORT_SELF_TEST;
+    else
+      cfg->testdata->not_cap_short=1;
+    break;
+  case 'L':
+    testname="Long Self-";
+    if (isSupportSelfTest(&data))
+      dotest=EXTEND_SELF_TEST;
+    else
+      cfg->testdata->not_cap_long=1;
+    break;
+  }
+  
+  // If we can't do the test, exit
+  if (dotest<0) {
+    PrintOut(LOG_CRIT, "Device: %s, not capable of %sTest\n", name, testname);
+    return 1;
+  }
+  
+  // If currently running a self-test, do not interrupt it to start another.
+  if (15==(data.self_test_exec_status >> 4)) {
+    PrintOut(LOG_INFO, "Device: %s, skip scheduled %sTest; %1d0%% remaining of current Self-Test.\n",
+             name, testname, (int)(data.self_test_exec_status & 0x0f));
+    return 1;
+  }
+
+  // else execute the test, and return status
+  if ((retval=smartcommandhandler(fd, IMMEDIATE_OFFLINE, dotest, NULL)))
+    PrintOut(LOG_CRIT, "Device: %s, execute %sTest failed.\n", name, testname);
+  else
+    PrintOut(LOG_INFO, "Device: %s, starting scheduled %sTest.\n", name, testname);
+  
+  return retval;
+}
+
+
+int ATACheckDevice(cfgfile *cfg){
+  int fd,i;
+  char *name=cfg->name;
+  char *mode="ATA";
+  
+  // fix firmware bug if requested
+  con->fixfirmwarebug=cfg->fixfirmwarebug;
+  con->controller_port=cfg->controller_port;
+  con->controller_type=cfg->controller_type;
+
+  // If user has asked, test the email warning system
+  if (cfg->mailwarn && cfg->mailwarn->emailtest)
+    MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name);
+
+  if (cfg->controller_type == CONTROLLER_3WARE_9000_CHAR)
+    mode="ATA_3WARE_9000";
+  
+  if (cfg->controller_type == CONTROLLER_3WARE_678K_CHAR)
+    mode="ATA_3WARE_678K";
+
+  // if we can't open device, fail gracefully rather than hard --
+  // perhaps the next time around we'll be able to open it.  ATAPI
+  // cd/dvd devices will hang awaiting media if O_NONBLOCK is not
+  // given (see linux cdrom driver).
+  if ((fd=OpenDevice(name, mode, 0))<0){
+    MailWarning(cfg, 9, "Device: %s, unable to open device", name);
+    return 1;
+  }
+
+  // user may have requested (with the -n Directive) to leave the disk
+  // alone if it is in idle or sleeping mode.  In this case check the
+  // power mode and exit without check if needed
+  if (cfg->powermode){
+    int dontcheck=0, powermode=ataCheckPowerMode(fd);
+    char *mode=NULL;
+    if (powermode >= 0) {
+      int powermode2 = ataCheckPowerMode(fd);
+      if (powermode2 > powermode)
+        PrintOut(LOG_INFO, "Device: %s, CHECK POWER STATUS spins up disk (0x%02x -> 0x%02x)\n", name, powermode, powermode2);
+      powermode = powermode2;
+    }
+        
+    switch (powermode){
+    case -1:
+      // SLEEP
+      mode="SLEEP";
+      if (cfg->powermode>=1)
+       dontcheck=1;
+      break;
+    case 0:
+      // STANDBY
+      mode="STANDBY";
+      if (cfg->powermode>=2)
+       dontcheck=1;
+      break;
+    case 0x80:
+      // IDLE
+      mode="IDLE";
+      if (cfg->powermode>=3)
+       dontcheck=1;
+      break;
+    case 0xff:
+      // ACTIVE/IDLE
+      break;
+    default:
+      // UNKNOWN
+      PrintOut(LOG_CRIT, "Device: %s, CHECK POWER STATUS returned %d, not ATA compliant, ignoring -n Directive\n",
+              name, powermode);
+      cfg->powermode=0;
+      break;
+    }
+
+    // if we are going to skip a check, return now
+    if (dontcheck){
+      CloseDevice(fd, name);
+      if (!cfg->powerquiet) // to avoid waking up system disk
+        PrintOut(LOG_INFO, "Device: %s, is in %s mode, skipping checks\n", name, mode);
+      return 0;
+    }    
+  }
+
+  // check smart status
+  if (cfg->smartcheck){
+    int status=ataSmartStatus2(fd);
+    if (status==-1){
+      PrintOut(LOG_INFO,"Device: %s, not capable of SMART self-check\n",name);
+      MailWarning(cfg, 5, "Device: %s, not capable of SMART self-check", name);
+    }
+    else if (status==1){
+      PrintOut(LOG_CRIT, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!\n", name);
+      MailWarning(cfg, 1, "Device: %s, FAILED SMART self-check. BACK UP DATA NOW!", name);
+    }
+  }
+  
+  // Check everything that depends upon SMART Data (eg, Attribute values)
+  if (cfg->usagefailed || cfg->prefail || cfg->usage || cfg->pending!=DONT_MONITOR_UNC){
+    struct ata_smart_values     curval;
+    struct ata_smart_thresholds_pvt *thresh=cfg->smartthres;
+    
+    // Read current attribute values. *drive contains old values and thresholds
+    if (ataReadSmartValues(fd,&curval)){
+      PrintOut(LOG_CRIT, "Device: %s, failed to read SMART Attribute Data\n", name);
+      MailWarning(cfg, 6, "Device: %s, failed to read SMART Attribute Data", name);
+    }
+    else {
+      // look for current or offline pending sectors
+      if (cfg->pending != DONT_MONITOR_UNC) {
+       int64_t rawval;
+       unsigned char currentpending, offlinepending;
+       
+       TranslatePending(cfg->pending, &currentpending, &offlinepending);
+       
+       if (currentpending && (rawval=ATAReturnAttributeRawValue(currentpending, &curval))>0) {
+         // Unreadable pending sectors!!
+         PrintOut(LOG_CRIT,   "Device: %s, %"PRId64" Currently unreadable (pending) sectors\n", name, rawval);
+         MailWarning(cfg, 10, "Device: %s, %"PRId64" Currently unreadable (pending) sectors", name, rawval);
+       }
+       
+       if (offlinepending && (rawval=ATAReturnAttributeRawValue(offlinepending, &curval))>0) {
+         // Unreadable offline sectors!!
+         PrintOut(LOG_CRIT,   "Device: %s, %"PRId64" Offline uncorrectable sectors\n", name, rawval);
+         MailWarning(cfg, 11, "Device: %s, %"PRId64" Offline uncorrectable sectors", name, rawval);
+       }
+      }
+
+      if (cfg->usagefailed || cfg->prefail || cfg->usage) {
+
+       // look for failed usage attributes, or track usage or prefail attributes
+       for (i=0; i<NUMBER_ATA_SMART_ATTRIBUTES; i++){
+         int att;
+         changedattribute_t delta;
+         
+         // This block looks for usage attributes that have failed.
+         // Prefail attributes that have failed are returned with a
+         // positive sign. No failure returns 0. Usage attributes<0.
+         if (cfg->usagefailed && ((att=ataCheckAttribute(&curval, thresh, i))<0)){
+           
+           // are we ignoring failures of this attribute?
+           att *= -1;
+           if (!IsAttributeOff(att, &cfg->monitorattflags, 0, MONITOR_FAILUSE, __LINE__)){
+             char attname[64], *loc=attname;
+             
+             // get attribute name & skip white space
+             ataPrintSmartAttribName(loc, att, cfg->attributedefs);
+             while (*loc && *loc==' ') loc++;
+             
+             // warning message
+             PrintOut(LOG_CRIT, "Device: %s, Failed SMART usage Attribute: %s.\n", name, loc);
+             MailWarning(cfg, 2, "Device: %s, Failed SMART usage Attribute: %s.", name, loc);
+           }
+         }
+         
+         // This block tracks usage or prefailure attributes to see if
+         // they are changing.  It also looks for changes in RAW values
+         // if this has been requested by user.
+         if ((cfg->usage || cfg->prefail) && ATACompareValues(&delta, &curval, cfg->smartval, thresh, i, name)){
+           unsigned char id=delta.id;
+           
+           // if the only change is the raw value, and we're not
+           // tracking raw value, then continue loop over attributes
+           if (!delta.sameraw && delta.newval==delta.oldval && !IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAW, __LINE__))
+             continue;
+           
+           // are we tracking this attribute?
+           if (!IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_IGNORE, __LINE__)){
+             char newrawstring[64], oldrawstring[64], attname[64], *loc=attname;
+             
+             // get attribute name, skip spaces
+             ataPrintSmartAttribName(loc, id, cfg->attributedefs);
+             while (*loc && *loc==' ') loc++;
+             
+             // has the user asked for us to print raw values?
+             if (IsAttributeOff(id, &cfg->monitorattflags, 0, MONITOR_RAWPRINT, __LINE__)) {
+               // get raw values (as a string) and add to printout
+               char rawstring[64];
+               ataPrintSmartAttribRawValue(rawstring, curval.vendor_attributes+i, cfg->attributedefs);
+               sprintf(newrawstring, " [Raw %s]", rawstring);
+               ataPrintSmartAttribRawValue(rawstring, cfg->smartval->vendor_attributes+i, cfg->attributedefs);
+               sprintf(oldrawstring, " [Raw %s]", rawstring);
+             }
+             else
+               newrawstring[0]=oldrawstring[0]='\0';
+             
+             // prefailure attribute
+             if (cfg->prefail && delta.prefail)
+               PrintOut(LOG_INFO, "Device: %s, SMART Prefailure Attribute: %s changed from %d%s to %d%s\n",
+                        name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
+             
+             // usage attribute
+             if (cfg->usage && !delta.prefail)
+               PrintOut(LOG_INFO, "Device: %s, SMART Usage Attribute: %s changed from %d%s to %d%s\n",
+                        name, loc, delta.oldval, oldrawstring, delta.newval, newrawstring);
+           }
+         } // endof block tracking usage or prefailure
+       } // end of loop over attributes
+       
+       // Save the new values into *drive for the next time around
+       *(cfg->smartval)=curval;
+      }
+    }
+  }
+  
+  // check if number of selftest errors has increased (note: may also DECREASE)
+  if (cfg->selftest)
+    CheckSelfTestLogs(cfg, SelfTestErrorCount(fd, name));
+  
+  // check if number of ATA errors has increased
+  if (cfg->errorlog){
+
+    int new,old=cfg->ataerrorcount;
+
+    // new number of errors
+    new=ATAErrorCount(fd, name);
+
+    // did command fail?
+    if (new<0)
+      // lack of PrintOut here is INTENTIONAL
+      MailWarning(cfg, 7, "Device: %s, Read SMART Error Log Failed", name);
+
+    // has error count increased?
+    if (new>old){
+      PrintOut(LOG_CRIT, "Device: %s, ATA error count increased from %d to %d\n",
+               name, old, new);
+      MailWarning(cfg, 4, "Device: %s, ATA error count increased from %d to %d",
+                   name, old, new);
+    }
+    
+    // this last line is probably not needed, count always increases
+    if (new>=0)
+      cfg->ataerrorcount=new;
+  }
+  
+  // if the user has asked, and device is capable (or we're not yet
+  // sure) carry out scheduled self-tests.
+  if (cfg->testdata) {
+    // long test
+    if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L', 0)>0)
+      DoATASelfTest(fd, cfg, 'L');    
+    // short test
+    else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S', 0)>0)
+      DoATASelfTest(fd, cfg, 'S');
+    // conveyance test
+    else if (!cfg->testdata->not_cap_conveyance && DoTestNow(cfg, 'C', 0)>0)
+      DoATASelfTest(fd, cfg, 'C');
+    // offline immediate
+    else if (!cfg->testdata->not_cap_offline && DoTestNow(cfg, 'O', 0)>0)
+      DoATASelfTest(fd, cfg, 'O');  
+  }
+  
+  // Don't leave device open -- the OS/user may want to access it
+  // before the next smartd cycle!
+  CloseDevice(fd, name);
+  return 0;
+}
+
+#define DEF_SCSI_REPORT_TEMPERATURE_DELTA 2
+static int scsi_report_temperature_delta = DEF_SCSI_REPORT_TEMPERATURE_DELTA;
+
+int SCSICheckDevice(cfgfile *cfg)
+{
+    UINT8 asc, ascq;
+    UINT8 currenttemp;
+    UINT8 triptemp;
+    int fd;
+    char *name=cfg->name;
+    const char *cp;
+
+    // If the user has asked for it, test the email warning system
+    if (cfg->mailwarn && cfg->mailwarn->emailtest)
+      MailWarning(cfg, 0, "TEST EMAIL from smartd for device: %s", name);
+
+    // if we can't open device, fail gracefully rather than hard --
+    // perhaps the next time around we'll be able to open it
+    if ((fd=OpenDevice(name, "SCSI", 0))<0) {
+      // Lack of PrintOut() here is intentional!
+      MailWarning(cfg, 9, "Device: %s, unable to open device", name);
+      return 1;
+    }
+    currenttemp = 0;
+    asc = 0;
+    ascq = 0;
+    if (! cfg->SuppressReport) {
+        if (scsiCheckIE(fd, cfg->SmartPageSupported, cfg->TempPageSupported,
+                        &asc, &ascq, &currenttemp, &triptemp)) {
+            PrintOut(LOG_INFO, "Device: %s, failed to read SMART values\n",
+                      name);
+            MailWarning(cfg, 6, "Device: %s, failed to read SMART values", name);
+            cfg->SuppressReport = 1;
+        }
+    }
+    if (asc > 0) {
+        cp = scsiGetIEString(asc, ascq);
+        if (cp) {
+            PrintOut(LOG_CRIT, "Device: %s, SMART Failure: %s\n", name, cp);
+            MailWarning(cfg, 1,"Device: %s, SMART Failure: %s", name, cp); 
+        }
+    } else if (debugmode)
+        PrintOut(LOG_INFO,"Device: %s, Acceptable asc,ascq: %d,%d\n", 
+                 name, (int)asc, (int)ascq);  
+  
+    if (currenttemp && currenttemp!=255) {
+        if (cfg->Temperature) {
+            if (abs(((int)currenttemp - (int)cfg->Temperature)) >= 
+                scsi_report_temperature_delta) {
+                PrintOut(LOG_INFO, "Device: %s, Temperature changed %d Celsius "
+                         "to %d Celsius since last report\n", name, 
+                         (int)(currenttemp - cfg->Temperature), 
+                         (int)currenttemp);
+                cfg->Temperature = currenttemp;
+            }
+        }
+        else {
+            PrintOut(LOG_INFO, "Device: %s, initial Temperature is %d "
+                     "Celsius\n", name, (int)currenttemp);
+           if (triptemp)
+                PrintOut(LOG_INFO, "    [trip Temperature is %d Celsius]\n",
+                         (int)triptemp);
+            cfg->Temperature = currenttemp;
+            cfg->Temperature = currenttemp;
+        }
+    }
+    
+    // check if number of selftest errors has increased (note: may also DECREASE)
+    if (cfg->selftest)
+      CheckSelfTestLogs(cfg, scsiCountFailedSelfTests(fd, 0));
+    
+    if (cfg->testdata) {
+      // long (extended) background test
+      if (!cfg->testdata->not_cap_long && DoTestNow(cfg, 'L', 0)>0)
+        DoSCSISelfTest(fd, cfg, 'L');
+      // short background test
+      else if (!cfg->testdata->not_cap_short && DoTestNow(cfg, 'S', 0)>0)
+        DoSCSISelfTest(fd, cfg, 'S');
+    }
+    CloseDevice(fd, name);
+    return 0;
+}
+
+// Checks the SMART status of all ATA and SCSI devices
+void CheckDevicesOnce(cfgfile **atadevices, cfgfile **scsidevices){
+  int i;
+  
+  for (i=0; i<numdevata; i++) 
+    ATACheckDevice(atadevices[i]);
+  
+  for (i=0; i<numdevscsi; i++)
+    SCSICheckDevice(scsidevices[i]);
+
+  return;
+}
+
+#if SCSITIMEOUT
+// This alarm means that a SCSI USB device was hanging
+void AlarmHandler(int signal) {
+  longjmp(registerscsienv, 1);
+}
+#endif
+
+// Does initialization right after fork to daemon mode
+void Initialize(time_t *wakeuptime){
+
+  // install goobye message and remove pidfile handler
+  atexit(Goodbye);
+  
+  // write PID file only after installing exit handler
+  if (!debugmode)
+    WritePidFile();
+  
+  // install signal handlers.  On Solaris, can't use signal() because
+  // it resets the handler to SIG_DFL after each call.  So use sigset()
+  // instead.  So SIGNALFN()==signal() or SIGNALFN()==sigset().
+  
+  // normal and abnormal exit
+  if (SIGNALFN(SIGTERM, sighandler)==SIG_IGN)
+    SIGNALFN(SIGTERM, SIG_IGN);
+  if (SIGNALFN(SIGQUIT, sighandler)==SIG_IGN)
+    SIGNALFN(SIGQUIT, SIG_IGN);
+  
+  // in debug mode, <CONTROL-C> ==> HUP
+  if (SIGNALFN(SIGINT, debugmode?HUPhandler:sighandler)==SIG_IGN)
+    SIGNALFN(SIGINT, SIG_IGN);
+  
+  // Catch HUP and USR1
+  if (SIGNALFN(SIGHUP, HUPhandler)==SIG_IGN)
+    SIGNALFN(SIGHUP, SIG_IGN);
+  if (SIGNALFN(SIGUSR1, USR1handler)==SIG_IGN)
+    SIGNALFN(SIGUSR1, SIG_IGN);
+#ifdef _WIN32
+  if (SIGNALFN(SIGUSR2, USR2handler)==SIG_IGN)
+    SIGNALFN(SIGUSR2, SIG_IGN);
+#endif
+
+  // initialize wakeup time to CURRENT time
+  *wakeuptime=time(NULL);
+  
+  return;
+}
+
+#ifdef _WIN32
+// Toggle debug mode implemented for native windows only
+// (there is no easy way to reopen tty on *nix)
+static void ToggleDebugMode()
+{
+  if (!debugmode) {
+    PrintOut(LOG_INFO,"Signal USR2 - enabling debug mode\n");
+    if (!daemon_enable_console("smartd [Debug]")) {
+      debugmode = 1;
+      daemon_signal(SIGINT, HUPhandler);
+      PrintOut(LOG_INFO,"smartd debug mode enabled, PID=%d\n", getpid());
+    }
+    else
+      PrintOut(LOG_INFO,"enable console failed\n");
+  }
+  else if (debugmode == 1) {
+    daemon_disable_console();
+    debugmode = 0;
+    daemon_signal(SIGINT, sighandler);
+    PrintOut(LOG_INFO,"Signal USR2 - debug mode disabled\n");
+  }
+  else
+    PrintOut(LOG_INFO,"Signal USR2 - debug mode %d not changed\n", debugmode);
+}
+#endif
+
+time_t dosleep(time_t wakeuptime){
+  time_t timenow=0;
+  
+  // If past wake-up-time, compute next wake-up-time
+  timenow=time(NULL);
+  while (wakeuptime<=timenow){
+    int intervals=1+(timenow-wakeuptime)/checktime;
+    wakeuptime+=intervals*checktime;
+  }
+  
+  // sleep until we catch SIGUSR1 or have completed sleeping
+  while (timenow<wakeuptime && !caughtsigUSR1 && !caughtsigHUP && !caughtsigEXIT){
+    
+    // protect user again system clock being adjusted backwards
+    if (wakeuptime>timenow+checktime){
+      PrintOut(LOG_CRIT, "System clock time adjusted to the past. Resetting next wakeup time.\n");
+      wakeuptime=timenow+checktime;
+    }
+    
+    // Exit sleep when time interval has expired or a signal is received
+    sleep(wakeuptime-timenow);
+
+#ifdef _WIN32
+    // toggle debug mode?
+    if (caughtsigUSR2) {
+      ToggleDebugMode();
+      caughtsigUSR2 = 0;
+    }
+#endif
+
+    timenow=time(NULL);
+  }
+  // if we caught a SIGUSR1 then print message and clear signal
+  if (caughtsigUSR1){
+    PrintOut(LOG_INFO,"Signal USR1 - checking devices now rather than in %d seconds.\n",
+             wakeuptime-timenow>0?(int)(wakeuptime-timenow):0);
+    caughtsigUSR1=0;
+  }
+  
+  // return adjusted wakeuptime
+  return wakeuptime;
+}
+
+// Print out a list of valid arguments for the Directive d
+void printoutvaliddirectiveargs(int priority, char d) {
+  char *s=NULL;
+
+  switch (d) {
+  case 'n':
+    PrintOut(priority, "never[,q], sleep[,q], standby[,q], idle[,q]");
+    break;
+  case 's':
+    PrintOut(priority, "valid_regular_expression");
+    break;
+  case 'd':
+    PrintOut(priority, "ata, scsi, marvell, removable, 3ware,N");
+    break;
+  case 'T':
+    PrintOut(priority, "normal, permissive");
+    break;
+  case 'o':
+  case 'S':
+    PrintOut(priority, "on, off");
+    break;
+  case 'l':
+    PrintOut(priority, "error, selftest");
+    break;
+  case 'M':
+    PrintOut(priority, "\"once\", \"daily\", \"diminishing\", \"test\", \"exec\"");
+    break;
+  case 'v':
+    if (!(s = create_vendor_attribute_arg_list())) {
+      PrintOut(LOG_CRIT,"Insufficient memory to construct argument list\n");
+      EXIT(EXIT_NOMEM);
+    }
+    PrintOut(priority, "\n%s\n", s);
+    s=CheckFree(s, __LINE__,filenameandversion);
+    break;
+  case 'P':
+    PrintOut(priority, "use, ignore, show, showall");
+    break;
+  case 'F':
+    PrintOut(priority, "none, samsung, samsung2");
+    break;
+  }
+}
+
+// exits with an error message, or returns integer value of token
+int GetInteger(char *arg, char *name, char *token, int lineno, char *configfile, int min, int max){
+  char *endptr;
+  int val;
+  
+  // check input range
+  if (min<0){
+    PrintOut(LOG_CRIT, "min =%d passed to GetInteger() must be >=0\n", min);
+    return -1;
+  }
+
+  // make sure argument is there
+  if (!arg) {
+    PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s takes integer argument from %d to %d.\n",
+             configfile, lineno, name, token, min, max);
+    return -1;
+  }
+  
+  // get argument value (base 10), check that it's integer, and in-range
+  val=strtol(arg,&endptr,10);
+  if (*endptr!='\0' || val<min || val>max )  {
+    PrintOut(LOG_CRIT,"File %s line %d (drive %s): Directive: %s has argument: %s; needs integer from %d to %d.\n",
+             configfile, lineno, name, token, arg, min, max);
+    return -1;
+  }
+
+  // all is well; return value
+  return val;
+}
+
+// This function returns 1 if it has correctly parsed one token (and
+// any arguments), else zero if no tokens remain.  It returns -1 if an
+// error was encountered.
+int ParseToken(char *token,cfgfile *cfg){
+  char sym;
+  char *name=cfg->name;
+  int lineno=cfg->lineno;
+  char *delim = " \n\t";
+  int badarg = 0;
+  int missingarg = 0;
+  char *arg = NULL;
+  int makemail=0;
+  maildata *mdat=NULL, tempmail;
+
+  // is the rest of the line a comment
+  if (*token=='#')
+    return 1;
+  
+  // is the token not recognized?
+  if (*token!='-' || strlen(token)!=2) {
+    PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
+             configfile, lineno, name, token);
+    PrintOut(LOG_CRIT, "Run smartd -D to print a list of valid Directives.\n");
+    return -1;
+  }
+  
+  // token we will be parsing:
+  sym=token[1];
+
+  // create temporary maildata structure.  This means we can postpone
+  // allocating space in the data segment until we are sure there are
+  // no errors.
+  if ('m'==sym || 'M'==sym){
+    if (!cfg->mailwarn){
+      memset(&tempmail, 0, sizeof(maildata));
+      mdat=&tempmail;
+      makemail=1;
+    }
+    else
+      mdat=cfg->mailwarn;
+  }
+
+  // parse the token and swallow its argument
+  switch (sym) {
+    int val;
+
+  case 'C':
+    // monitor current pending sector count (default 197)
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0)
+      return -1;
+    if (val==CUR_UNC_DEFAULT)
+      val=0;
+    else if (val==0)
+      val=CUR_UNC_DEFAULT;
+    // set bottom 8 bits to correct value
+    cfg->pending &= 0xff00;
+    cfg->pending |= val;
+    break;
+  case 'U':
+    // monitor offline uncorrectable sectors (default 198)
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 0, 255))<0)
+      return -1;
+    if (val==OFF_UNC_DEFAULT)
+      val=0;
+    else if (val==0)
+      val=OFF_UNC_DEFAULT;
+    // turn off top 8 bits, then set to correct value
+    cfg->pending &= 0xff;
+    cfg->pending |= (val<<8);
+    break;
+  case 'T':
+    // Set tolerance level for SMART command failures
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "normal")) {
+      // Normal mode: exit on failure of a mandatory S.M.A.R.T. command, but
+      // not on failure of an optional S.M.A.R.T. command.
+      // This is the default so we don't need to actually do anything here.
+      cfg->permissive=0;
+    } else if (!strcmp(arg, "permissive")) {
+      // Permissive mode; ignore errors from Mandatory SMART commands
+      cfg->permissive=1;
+    } else {
+      badarg = 1;
+    }
+    break;
+  case 'd':
+    // specify the device type
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "ata")) {
+      cfg->controller_port = 0;
+      cfg->controller_type = CONTROLLER_ATA;
+    } else if (!strcmp(arg, "scsi")) {
+      cfg->controller_port =0;
+      cfg->controller_type = CONTROLLER_SCSI;
+    } else if (!strcmp(arg, "marvell")) {
+      cfg->controller_port =0;
+      cfg->controller_type = CONTROLLER_MARVELL_SATA;
+    } else if (!strcmp(arg, "removable")) {
+      cfg->removable = 1;
+    } else {
+      // look 3ware,N RAID device
+      int i;
+      char *s;
+      
+      // make a copy of the string to mess with
+      if (!(s = strdup(arg))) {
+        PrintOut(LOG_CRIT,
+                 "No memory to copy argument to -d option - exiting\n");
+        EXIT(EXIT_NOMEM);
+      } else if (strncmp(s,"3ware,",6)) {
+        badarg=1;
+      } else if (split_report_arg2(s, &i)){
+        PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N requires N integer\n",
+                 configfile, lineno, name);
+        badarg=1;
+      } else if ( i<0 || i>15) {
+        PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive -d 3ware,N (N=%d) must have 0 <= N <= 15\n",
+                 configfile, lineno, name, i);
+        badarg=1;
+      } else {
+       // determine type of escalade device from name of device
+       cfg->controller_type = guess_device_type(name);
+       if (cfg->controller_type!=CONTROLLER_3WARE_9000_CHAR && cfg->controller_type!=CONTROLLER_3WARE_678K_CHAR)
+         cfg->controller_type=CONTROLLER_3WARE_678K;
+           
+        // NOTE: controller_port == disk number + 1
+        cfg->controller_port = i+1;
+      }
+      s=CheckFree(s, __LINE__,filenameandversion); 
+    }
+    break;
+  case 'F':
+    // fix firmware bug
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "none")) {
+      cfg->fixfirmwarebug = FIX_NONE;
+    } else if (!strcmp(arg, "samsung")) {
+      cfg->fixfirmwarebug = FIX_SAMSUNG;
+    } else if (!strcmp(arg, "samsung2")) {
+      cfg->fixfirmwarebug = FIX_SAMSUNG2;
+    } else {
+      badarg = 1;
+    }
+    break;
+  case 'H':
+    // check SMART status
+    cfg->smartcheck=1;
+    break;
+  case 'f':
+    // check for failure of usage attributes
+    cfg->usagefailed=1;
+    break;
+  case 't':
+    // track changes in all vendor attributes
+    cfg->prefail=1;
+    cfg->usage=1;
+    break;
+  case 'p':
+    // track changes in prefail vendor attributes
+    cfg->prefail=1;
+    break;
+  case 'u':
+    //  track changes in usage vendor attributes
+    cfg->usage=1;
+    break;
+  case 'l':
+    // track changes in SMART logs
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "selftest")) {
+      // track changes in self-test log
+      cfg->selftest=1;
+    } else if (!strcmp(arg, "error")) {
+      // track changes in ATA error log
+      cfg->errorlog=1;
+    } else {
+      badarg = 1;
+    }
+    break;
+  case 'a':
+    // monitor everything
+    cfg->smartcheck=1;
+    cfg->prefail=1;
+    cfg->usagefailed=1;
+    cfg->usage=1;
+    cfg->selftest=1;
+    cfg->errorlog=1;
+    break;
+  case 'o':
+    // automatic offline testing enable/disable
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "on")) {
+      cfg->autoofflinetest = 2;
+    } else if (!strcmp(arg, "off")) {
+      cfg->autoofflinetest = 1;
+    } else {
+      badarg = 1;
+    }
+    break;
+  case 'n':
+    // skip disk check if in idle or standby mode
+    if (!(arg = strtok(NULL, delim)))
+      missingarg = 1;
+    else if (!strcmp(arg, "never")   || !strcmp(arg, "never,q"))
+      cfg->powermode = 0;
+    else if (!strcmp(arg, "sleep")   || !strcmp(arg, "sleep,q"))
+      cfg->powermode = 1;
+    else if (!strcmp(arg, "standby") || !strcmp(arg, "standby,q"))
+      cfg->powermode = 2;
+    else if (!strcmp(arg, "idle")    || !strcmp(arg, "idle,q"))
+      cfg->powermode = 3;
+    else
+      badarg = 1;
+    cfg->powerquiet = !!strchr(arg, ',');
+    break;
+  case 'S':
+    // automatic attribute autosave enable/disable
+    if ((arg = strtok(NULL, delim)) == NULL) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "on")) {
+      cfg->autosave = 2;
+    } else if (!strcmp(arg, "off")) {
+      cfg->autosave = 1;
+    } else {
+      badarg = 1;
+    }
+    break;
+  case 's':
+    // warn user, and delete any previously given -s REGEXP Directives
+    if (cfg->testdata){
+      PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Test Directive -s %s\n",
+               configfile, lineno, name, cfg->testdata->regex);
+      cfg->testdata=FreeTestData(cfg->testdata);
+    }
+    // check for missing argument
+    if (!(arg = strtok(NULL, delim))) {
+      missingarg = 1;
+    }
+    // allocate space for structure and string
+    else if (!(cfg->testdata=(testinfo *)Calloc(1, sizeof(testinfo))) || !(cfg->testdata->regex=CustomStrDup(arg, 1, __LINE__,filenameandversion))) {
+      PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create Test Directive -s %s!\n",
+               configfile, lineno, name, arg);
+      EXIT(EXIT_NOMEM);
+    }
+    else if ((val=regcomp(&(cfg->testdata->cregex), arg, REG_EXTENDED))) {
+      char errormsg[512];
+      // not a valid regular expression!
+      regerror(val, &(cfg->testdata->cregex), errormsg, 512);
+      PrintOut(LOG_CRIT, "File %s line %d (drive %s): -s argument \"%s\" is INVALID extended regular expression. %s.\n",
+               configfile, lineno, name, arg, errormsg);
+      cfg->testdata=FreeTestData(cfg->testdata);
+      return -1;
+    }
+    // Do a bit of sanity checking and warn user if we think that
+    // their regexp is "strange". User probably confused about shell
+    // glob(3) syntax versus regular expression syntax regexp(7).
+    if ((int)strlen(arg) != (val=strspn(arg,"0123456789/.-+*|()?^$[]SLCO")))
+      PrintOut(LOG_INFO,  "File %s line %d (drive %s): warning, character %d (%c) looks odd in extended regular expression %s\n",
+               configfile, lineno, name, val+1, arg[val], arg);
+    break;
+  case 'm':
+    // send email to address that follows
+    if (!(arg = strtok(NULL,delim)))
+      missingarg = 1;
+    else {
+      if (mdat->address) {
+        PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n",
+                 configfile, lineno, name, mdat->address);
+        mdat->address=FreeNonZero(mdat->address, -1,__LINE__,filenameandversion);
+      }
+      mdat->address=CustomStrDup(arg, 1, __LINE__,filenameandversion);
+    }
+    break;
+  case 'M':
+    // email warning options
+    if (!(arg = strtok(NULL, delim)))
+      missingarg = 1;
+    else if (!strcmp(arg, "once"))
+      mdat->emailfreq = 1;
+    else if (!strcmp(arg, "daily"))
+      mdat->emailfreq = 2;
+    else if (!strcmp(arg, "diminishing"))
+      mdat->emailfreq = 3;
+    else if (!strcmp(arg, "test"))
+      mdat->emailtest = 1;
+    else if (!strcmp(arg, "exec")) {
+      // Get the next argument (the command line)
+      if (!(arg = strtok(NULL, delim))) {
+        PrintOut(LOG_CRIT, "File %s line %d (drive %s): Directive %s 'exec' argument must be followed by executable path.\n",
+                 configfile, lineno, name, token);
+        return -1;
+      }
+      // Free the last cmd line given if any, and copy new one
+      if (mdat->emailcmdline) {
+        PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous mail Directive -M exec %s\n",
+                 configfile, lineno, name, mdat->emailcmdline);
+        mdat->emailcmdline=FreeNonZero(mdat->emailcmdline, -1,__LINE__,filenameandversion);
+      }
+      mdat->emailcmdline=CustomStrDup(arg, 1, __LINE__,filenameandversion);
+    } 
+    else
+      badarg = 1;
+    break;
+  case 'i':
+    // ignore failure of usage attribute
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
+      return -1;
+    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_FAILUSE, __LINE__);
+    break;
+  case 'I':
+    // ignore attribute for tracking purposes
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
+      return -1;
+    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_IGNORE, __LINE__);
+    break;
+  case 'r':
+    // print raw value when tracking
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
+      return -1;
+    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__);
+    break;
+  case 'R':
+    // track changes in raw value (forces printing of raw value)
+    if ((val=GetInteger(arg=strtok(NULL,delim), name, token, lineno, configfile, 1, 255))<0)
+      return -1;
+    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAWPRINT, __LINE__);
+    IsAttributeOff(val, &cfg->monitorattflags, 1, MONITOR_RAW, __LINE__);
+    break;
+  case 'v':
+    // non-default vendor-specific attribute meaning
+    if (!(arg=strtok(NULL,delim))) {
+      missingarg = 1;
+    } else if (parse_attribute_def(arg, &cfg->attributedefs)){   
+      badarg = 1;
+    }
+    break;
+  case 'P':
+    // Define use of drive-specific presets.
+    if (!(arg = strtok(NULL, delim))) {
+      missingarg = 1;
+    } else if (!strcmp(arg, "use")) {
+      cfg->ignorepresets = FALSE;
+    } else if (!strcmp(arg, "ignore")) {
+      cfg->ignorepresets = TRUE;
+    } else if (!strcmp(arg, "show")) {
+      cfg->showpresets = TRUE;
+    } else if (!strcmp(arg, "showall")) {
+      showallpresets();
+    } else {
+      badarg = 1;
+    }
+    break;
+  default:
+    // Directive not recognized
+    PrintOut(LOG_CRIT,"File %s line %d (drive %s): unknown Directive: %s\n",
+             configfile, lineno, name, token);
+    Directives();
+    return -1;
+  }
+  if (missingarg) {
+    PrintOut(LOG_CRIT, "File %s line %d (drive %s): Missing argument to %s Directive\n",
+             configfile, lineno, name, token);
+  }
+  if (badarg) {
+    PrintOut(LOG_CRIT, "File %s line %d (drive %s): Invalid argument to %s Directive: %s\n",
+             configfile, lineno, name, token, arg);
+  }
+  if (missingarg || badarg) {
+    PrintOut(LOG_CRIT, "Valid arguments to %s Directive are: ", token);
+    printoutvaliddirectiveargs(LOG_CRIT, sym);
+    PrintOut(LOG_CRIT, "\n");
+    return -1;
+  }
+
+  // If this did something to fill the mail structure, and that didn't
+  // already exist, create it and copy.
+  if (makemail) {
+    if (!(cfg->mailwarn=(maildata *)Calloc(1, sizeof(maildata)))) {
+      PrintOut(LOG_INFO, "File %s line %d (drive %s): no memory to create mail warning entry!\n",
+               configfile, lineno, name);
+      EXIT(EXIT_NOMEM);
+    }
+    memcpy(cfg->mailwarn, mdat, sizeof(maildata));
+  }
+  
+  return 1;
+}
+
+// Allocate storage for a new cfgfile entry.  If original!=NULL, it's
+// a copy of the original, but with private data storage.  Else all is
+// zeroed.  Returns address, and fails if non memory available.
+
+cfgfile *CreateConfigEntry(cfgfile *original){
+  cfgfile *add;
+  
+  // allocate memory for new structure
+  if (!(add=(cfgfile *)Calloc(1,sizeof(cfgfile))))
+    goto badexit;
+  
+  // if old structure was pointed to, copy it
+  if (original)
+    memcpy(add, original, sizeof(cfgfile));
+  
+  // make private copies of data items ONLY if they are in use (non
+  // NULL)
+  add->name         = CustomStrDup(add->name,         0, __LINE__,filenameandversion);
+
+  if (add->testdata) {
+    int val;
+    if (!(add->testdata=(testinfo *)Calloc(1,sizeof(testinfo))))
+      goto badexit;
+    memcpy(add->testdata, original->testdata, sizeof(testinfo));
+    add->testdata->regex = CustomStrDup(add->testdata->regex,   1, __LINE__,filenameandversion);
+    // only POSIX-portable way to make fresh copy of compiled regex is
+    // to recompile it completely.  There is no POSIX
+    // compiled-regex-copy command.
+    if ((val=regcomp(&(add->testdata->cregex), add->testdata->regex, REG_EXTENDED))) {
+      char errormsg[512];
+      regerror(val, &(add->testdata->cregex), errormsg, 512);
+      PrintOut(LOG_CRIT, "unable to recompile regular expression %s. %s\n", add->testdata->regex, errormsg);
+      goto badexit;
+    }
+  }
+  
+  if (add->mailwarn) {
+    if (!(add->mailwarn=(maildata *)Calloc(1,sizeof(maildata))))
+      goto badexit;
+    memcpy(add->mailwarn, original->mailwarn, sizeof(maildata));
+    add->mailwarn->address      = CustomStrDup(add->mailwarn->address,      0, __LINE__,filenameandversion);
+    add->mailwarn->emailcmdline = CustomStrDup(add->mailwarn->emailcmdline, 0, __LINE__,filenameandversion);
+  }
+
+  if (add->attributedefs) {
+    if (!(add->attributedefs=(unsigned char *)Calloc(MAX_ATTRIBUTE_NUM,1)))
+      goto badexit;
+    memcpy(add->attributedefs, original->attributedefs, MAX_ATTRIBUTE_NUM);
+  }
+  
+  if (add->monitorattflags) {
+    if (!(add->monitorattflags=(unsigned char *)Calloc(NMONITOR*32, 1)))
+      goto badexit;
+    memcpy(add->monitorattflags, original->monitorattflags, NMONITOR*32);
+  }
+  
+  if (add->smartval) {
+    if (!(add->smartval=(struct ata_smart_values *)Calloc(1,sizeof(struct ata_smart_values))))
+      goto badexit;
+  }
+  
+  if (add->smartthres) {
+    if (!(add->smartthres=(struct ata_smart_thresholds_pvt *)Calloc(1,sizeof(struct ata_smart_thresholds_pvt))))
+      goto badexit;
+  }
+
+  return add;
+
+ badexit:
+  PrintOut(LOG_CRIT, "No memory to create entry from configuration file\n");
+  EXIT(EXIT_NOMEM);
+}
+
+
+
+// This is the routine that adds things to the cfgentries list. To
+// prevent memory leaks when re-reading the configuration file many
+// times, this routine MUST deallocate any memory other than that
+// pointed to within cfg-> before it returns.
+//
+// Return values are:
+//  1: parsed a normal line
+//  0: found comment or blank line
+// -1: found SCANDIRECTIVE line
+// -2: found an error
+//
+// Note: this routine modifies *line from the caller!
+int ParseConfigLine(int entry, int lineno,char *line){
+  char *token=NULL;
+  char *name=NULL;
+  char *delim = " \n\t";
+  cfgfile *cfg=NULL;
+  int devscan=0;
+
+  // get first token: device name. If a comment, skip line
+  if (!(name=strtok(line,delim)) || *name=='#') {
+    return 0;
+  }
+
+  // Have we detected the SCANDIRECTIVE directive?
+  if (!strcmp(SCANDIRECTIVE,name)){
+    devscan=1;
+    if (entry) {
+      PrintOut(LOG_INFO,"Scan Directive %s (line %d) must be the first entry in %s\n",name, lineno, configfile);
+      return -2;
+    }
+  }
+  
+  // Is there space for another entry?  If not, allocate more
+  while (entry>=cfgentries_max)
+    cfgentries=AllocateMoreSpace(cfgentries, &cfgentries_max, "configuration file device");
+
+  // We've got a legit entry, make space to store it
+  cfg=cfgentries[entry]=CreateConfigEntry(NULL);
+  cfg->name = CustomStrDup(name, 1, __LINE__,filenameandversion);
+
+  // Store line number, and by default check for both device types.
+  cfg->lineno=lineno;
+  
+  // Try and recognize if a IDE or SCSI device.  These can be
+  // overwritten by configuration file directives.
+  if (cfg->controller_type==CONTROLLER_UNKNOWN)
+    cfg->controller_type = guess_device_type(cfg->name);
+  
+  // parse tokens one at a time from the file.
+  while ((token=strtok(NULL,delim))){
+    int retval=ParseToken(token,cfg);
+    
+    if (retval==0)
+      // No tokens left:
+      break;
+    
+    if (retval>0) {
+      // Parsed token  
+#if (0)
+      PrintOut(LOG_INFO,"Parsed token %s\n",token);
+#endif
+      continue;
+    }
+    
+    if (retval<0) {
+      // error found on the line
+      return -2;
+    }
+  }
+  
+  // If we found 3ware controller, then modify device name by adding a SPACE
+  if (cfg->controller_port){
+    int len=17+strlen(cfg->name);
+    char *newname;
+    
+    if (devscan){
+      PrintOut(LOG_CRIT, "smartd: can not scan for 3ware devices (line %d of file %s)\n",
+               lineno, configfile);
+      return -2;
+    }
+    
+    if (!(newname=(char *)calloc(len,1))) {
+      PrintOut(LOG_INFO,"No memory to parse file: %s line %d, %s\n", configfile, lineno, strerror(errno));
+      EXIT(EXIT_NOMEM);
+    }
+    
+    // Make new device name by adding a space then RAID disk number
+    snprintf(newname, len, "%s [3ware_disk_%02d]", cfg->name, cfg->controller_port-1);
+    cfg->name=CheckFree(cfg->name, __LINE__,filenameandversion);
+    cfg->name=newname;
+    bytes+=16;
+  }
+
+  // If NO monitoring directives are set, then set all of them.
+  if (!(cfg->smartcheck || cfg->usagefailed || cfg->prefail  || 
+        cfg->usage      || cfg->selftest    || cfg->errorlog   )){
+    
+    PrintOut(LOG_INFO,"Drive: %s, implied '-a' Directive on line %d of file %s\n",
+             cfg->name, cfg->lineno, configfile);
+    
+    cfg->smartcheck=1;
+    cfg->usagefailed=1;
+    cfg->prefail=1;
+    cfg->usage=1;
+    cfg->selftest=1;
+    cfg->errorlog=1;
+  }
+  
+  // additional sanity check. Has user set -M options without -m?
+  if (cfg->mailwarn && !cfg->mailwarn->address && (cfg->mailwarn->emailcmdline || cfg->mailwarn->emailfreq || cfg->mailwarn->emailtest)){
+    PrintOut(LOG_CRIT,"Drive: %s, -M Directive(s) on line %d of file %s need -m ADDRESS Directive\n",
+             cfg->name, cfg->lineno, configfile);
+    return -2;
+  }
+  
+  // has the user has set <nomailer>?
+  if (cfg->mailwarn && cfg->mailwarn->address && !strcmp(cfg->mailwarn->address,"<nomailer>")){
+    // check that -M exec is also set
+    if (!cfg->mailwarn->emailcmdline){
+      PrintOut(LOG_CRIT,"Drive: %s, -m <nomailer> Directive on line %d of file %s needs -M exec Directive\n",
+               cfg->name, cfg->lineno, configfile);
+      return -2;
+    }
+    // now free memory.  From here on the sign of <nomailer> is
+    // address==NULL and cfg->emailcmdline!=NULL
+    cfg->mailwarn->address=FreeNonZero(cfg->mailwarn->address, -1,__LINE__,filenameandversion);
+  }
+
+  // set cfg->emailfreq to 1 (once) if user hasn't set it
+  if (cfg->mailwarn && !cfg->mailwarn->emailfreq)
+    cfg->mailwarn->emailfreq = 1;
+
+  entry++;
+
+  if (devscan)
+    return -1;
+  else
+    return 1;
+}
+
+// clean up utility for ParseConfigFile()
+void cleanup(FILE **fpp, int is_stdin){
+  if (*fpp){
+    // (*fpp != stdin) does not work here if stdin has been closed & reopened
+    if (!is_stdin)
+      fclose(*fpp);
+    *fpp=NULL;
+  }
+
+  return;
+}
+
+
+// Parses a configuration file.  Return values are:
+//  N=>0: found N entries
+// -1:    syntax error in config file
+// -2:    config file does not exist
+// -3:    config file exists but cannot be read
+//
+// In the case where the return value is 0, there are three
+// possiblities:
+// Empty configuration file ==> cfgentries==NULL
+// No configuration file    ==> cfgentries[0]->lineno == 0
+// SCANDIRECTIVE found      ==> cfgentries[0]->lineno != 0
+int ParseConfigFile(){
+  FILE *fp=NULL;
+  int entry=0,lineno=1,cont=0,contlineno=0;
+  char line[MAXLINELEN+2];
+  char fullline[MAXCONTLINE+1];
+
+  int is_stdin = (configfile == configfile_stdin); // pointer comparison ok here
+
+  // Open config file, if it exists and is not <stdin>
+  if (!is_stdin) {
+    fp=fopen(configfile,"r");
+    if (fp==NULL && (errno!=ENOENT || configfile_alt)) {
+      // file exists but we can't read it or it should exist due to '-c' option
+      int ret = (errno!=ENOENT ? -3 : -2);
+      PrintOut(LOG_CRIT,"%s: Unable to open configuration file %s\n",
+               strerror(errno),configfile);
+      return ret;
+    }
+  }
+  else // read from stdin ('-c -' option)
+    fp = stdin;
+  
+  // No configuration file found -- use fake one
+  if (fp==NULL) {
+    int len=strlen(SCANDIRECTIVE)+4;
+    char *fakeconfig=(char *)calloc(len,1);
+  
+    if (!fakeconfig || 
+        (len-1) != snprintf(fakeconfig, len, "%s -a", SCANDIRECTIVE) ||
+        -1 != ParseConfigLine(entry, 0, fakeconfig)
+        ) {
+      PrintOut(LOG_CRIT,"Internal error in ParseConfigFile() at line %d of file %s\n%s", 
+               __LINE__, filenameandversion, reportbug);
+      EXIT(EXIT_BADCODE);
+    }
+    fakeconfig=CheckFree(fakeconfig, __LINE__,filenameandversion);
+    return 0;
+  }
+
+#ifdef __CYGWIN__
+  setmode(fileno(fp), O_TEXT); // Allow files with \r\n
+#endif
+
+  // configuration file exists
+  PrintOut(LOG_INFO,"Opened configuration file %s\n",configfile);
+
+  // parse config file line by line
+  while (1) {
+    int len=0,scandevice;
+    char *lastslash;
+    char *comment;
+    char *code;
+
+    // make debugging simpler
+    memset(line,0,sizeof(line));
+
+    // get a line
+    code=fgets(line,MAXLINELEN+2,fp);
+    
+    // are we at the end of the file?
+    if (!code){
+      if (cont) {
+        scandevice=ParseConfigLine(entry,contlineno,fullline);
+        // See if we found a SCANDIRECTIVE directive
+        if (scandevice==-1) {
+          cleanup(&fp, is_stdin);
+          return 0;
+        }
+        // did we find a syntax error
+        if (scandevice==-2) {
+          cleanup(&fp, is_stdin);
+          return -1;
+        }
+        // the final line is part of a continuation line
+        cont=0;
+        entry+=scandevice;
+      }
+      break;
+    }
+
+    // input file line number
+    contlineno++;
+    
+    // See if line is too long
+    len=strlen(line);
+    if (len>MAXLINELEN){
+      char *warn;
+      if (line[len-1]=='\n')
+        warn="(including newline!) ";
+      else
+        warn="";
+      PrintOut(LOG_CRIT,"Error: line %d of file %s %sis more than MAXLINELEN=%d characters.\n",
+               (int)contlineno,configfile,warn,(int)MAXLINELEN);
+      cleanup(&fp, is_stdin);
+      return -1;
+    }
+
+    // Ignore anything after comment symbol
+    if ((comment=strchr(line,'#'))){
+      *comment='\0';
+      len=strlen(line);
+    }
+
+    // is the total line (made of all continuation lines) too long?
+    if (cont+len>MAXCONTLINE){
+      PrintOut(LOG_CRIT,"Error: continued line %d (actual line %d) of file %s is more than MAXCONTLINE=%d characters.\n",
+               lineno, (int)contlineno, configfile, (int)MAXCONTLINE);
+      cleanup(&fp, is_stdin);
+      return -1;
+    }
+    
+    // copy string so far into fullline, and increment length
+    strcpy(fullline+cont,line);
+    cont+=len;
+
+    // is this a continuation line.  If so, replace \ by space and look at next line
+    if ( (lastslash=strrchr(line,'\\')) && !strtok(lastslash+1," \n\t")){
+      *(fullline+(cont-len)+(lastslash-line))=' ';
+      continue;
+    }
+
+    // Not a continuation line. Parse it
+    scandevice=ParseConfigLine(entry,contlineno,fullline);
+
+    // did we find a scandevice directive?
+    if (scandevice==-1) {
+      cleanup(&fp, is_stdin);
+      return 0;
+    }
+    // did we find a syntax error
+    if (scandevice==-2) {
+      cleanup(&fp, is_stdin);
+      return -1;
+    }
+
+    entry+=scandevice;
+    lineno++;
+    cont=0;
+  }
+  cleanup(&fp, is_stdin);
+  
+  // note -- may be zero if syntax of file OK, but no valid entries!
+  return entry;
+}
+
+
+// Prints copyright, license and version information
+void PrintCopyleft(void){
+  debugmode=1;
+  PrintHead();
+  PrintCVS();
+  return;
+}
+
+/* Prints the message "=======> VALID ARGUMENTS ARE: <LIST>  <=======\n", where
+   <LIST> is the list of valid arguments for option opt. */
+void PrintValidArgs(char opt) {
+  const char *s;
+
+  PrintOut(LOG_CRIT, "=======> VALID ARGUMENTS ARE: ");
+  if (!(s = GetValidArgList(opt)))
+    PrintOut(LOG_CRIT, "Error constructing argument list for option %c", opt);
+  else
+    PrintOut(LOG_CRIT, (char *)s);
+  PrintOut(LOG_CRIT, " <=======\n");
+}
+
+// Parses input line, prints usage message and
+// version/license/copyright messages
+void ParseOpts(int argc, char **argv){
+  extern char *optarg;
+  extern int  optopt, optind, opterr;
+  int optchar;
+  int badarg;
+  char *tailptr;
+  long lchecktime;
+  // Please update GetValidArgList() if you edit shortopts
+  const char *shortopts = "c:l:q:dDi:p:r:Vh?";
+#ifdef HAVE_GETOPT_LONG
+  char *arg;
+  // Please update GetValidArgList() if you edit longopts
+  struct option longopts[] = {
+    { "configfile",     required_argument, 0, 'c' },
+    { "logfacility",    required_argument, 0, 'l' },
+    { "quit",           required_argument, 0, 'q' },
+    { "debug",          no_argument,       0, 'd' },
+    { "showdirectives", no_argument,       0, 'D' },
+    { "interval",       required_argument, 0, 'i' },
+    { "pidfile",        required_argument, 0, 'p' },
+    { "report",         required_argument, 0, 'r' },
+#if defined(_WIN32) || defined(__CYGWIN__)
+    { "service",        no_argument,       0, 'S' },
+#endif
+    { "version",        no_argument,       0, 'V' },
+    { "license",        no_argument,       0, 'V' },
+    { "copyright",      no_argument,       0, 'V' },
+    { "help",           no_argument,       0, 'h' },
+    { "usage",          no_argument,       0, 'h' },
+    { 0,                0,                 0, 0   }
+  };
+#endif
+  
+  opterr=optopt=0;
+  badarg=FALSE;
+  
+  // Parse input options.  This horrible construction is so that emacs
+  // indents properly.  Sorry.
+  while (-1 != (optchar = 
+#ifdef HAVE_GETOPT_LONG
+                getopt_long(argc, argv, shortopts, longopts, NULL)
+#else
+                getopt(argc, argv, shortopts)
+#endif
+                )) {
+    
+    switch(optchar) {
+    case 'q':
+      // when to quit
+      if (!(strcmp(optarg,"nodev"))) {
+        quit=0;
+      } else if (!(strcmp(optarg,"nodevstartup"))) {
+        quit=1;
+      } else if (!(strcmp(optarg,"never"))) {
+        quit=2;
+      } else if (!(strcmp(optarg,"onecheck"))) {
+        quit=3;
+        debugmode=1;
+      } else if (!(strcmp(optarg,"showtests"))) {
+        quit=4;
+        debugmode=1;
+      } else if (!(strcmp(optarg,"errors"))) {
+        quit=5;
+      } else {
+        badarg = TRUE;
+      }
+      break;
+    case 'l':
+      // set the log facility level
+      if (!strcmp(optarg, "daemon"))
+        facility=LOG_DAEMON;
+      else if (!strcmp(optarg, "local0"))
+        facility=LOG_LOCAL0;
+      else if (!strcmp(optarg, "local1"))
+        facility=LOG_LOCAL1;
+      else if (!strcmp(optarg, "local2"))
+        facility=LOG_LOCAL2;
+      else if (!strcmp(optarg, "local3"))
+        facility=LOG_LOCAL3;
+      else if (!strcmp(optarg, "local4"))
+        facility=LOG_LOCAL4;
+      else if (!strcmp(optarg, "local5"))
+        facility=LOG_LOCAL5;
+      else if (!strcmp(optarg, "local6"))
+        facility=LOG_LOCAL6;
+      else if (!strcmp(optarg, "local7"))
+        facility=LOG_LOCAL7;
+      else
+        badarg = TRUE;
+      break;
+    case 'd':
+      // enable debug mode
+      debugmode = TRUE;
+      break;
+    case 'D':
+      // print summary of all valid directives
+      debugmode = TRUE;
+      Directives();
+      EXIT(0);
+      break;
+    case 'i':
+      // Period (time interval) for checking
+      // strtol will set errno in the event of overflow, so we'll check it.
+      errno = 0;
+      lchecktime = strtol(optarg, &tailptr, 10);
+      if (*tailptr != '\0' || lchecktime < 10 || lchecktime > INT_MAX || errno) {
+        debugmode=1;
+        PrintHead();
+        PrintOut(LOG_CRIT, "======> INVALID INTERVAL: %s <=======\n", optarg);
+        PrintOut(LOG_CRIT, "======> INTERVAL MUST BE INTEGER BETWEEN %d AND %d <=======\n", 10, INT_MAX);
+        PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
+        EXIT(EXIT_BADCMD);
+      }
+      checktime = (int)lchecktime;
+      break;
+    case 'r':
+      // report IOCTL transactions
+      {
+        int i;
+        char *s;
+
+        // split_report_arg() may modify its first argument string, so use a
+        // copy of optarg in case we want optarg for an error message.
+        if (!(s = strdup(optarg))) {
+          PrintOut(LOG_CRIT, "No memory to process -r option - exiting\n");
+          EXIT(EXIT_NOMEM);
+        }
+        if (split_report_arg(s, &i)) {
+          badarg = TRUE;
+        } else if (i<1 || i>3) {
+          debugmode=1;
+          PrintHead();
+          PrintOut(LOG_CRIT, "======> INVALID REPORT LEVEL: %s <=======\n", optarg);
+          PrintOut(LOG_CRIT, "======> LEVEL MUST BE INTEGER BETWEEN 1 AND 3<=======\n");
+          EXIT(EXIT_BADCMD);
+        } else if (!strcmp(s,"ioctl")) {
+          con->reportataioctl  = con->reportscsiioctl = i;
+        } else if (!strcmp(s,"ataioctl")) {
+          con->reportataioctl = i;
+        } else if (!strcmp(s,"scsiioctl")) {
+          con->reportscsiioctl = i;
+        } else {
+          badarg = TRUE;
+        }
+        s=CheckFree(s, __LINE__,filenameandversion);
+      }
+      break;
+    case 'c':
+      // alternate configuration file
+      if (strcmp(optarg,"-"))
+        configfile=configfile_alt=CustomStrDup(optarg, 1, __LINE__,filenameandversion);
+      else // read from stdin
+        configfile=configfile_stdin;
+      break;
+    case 'p':
+      // output file with PID number
+      pid_file=CustomStrDup(optarg, 1, __LINE__,filenameandversion);
+      break;
+#if defined(_WIN32) || defined(__CYGWIN__)
+    case 'S':
+      // running as service
+#ifdef __CYGWIN__ // On Windows, option is already handled by daemon_main(), so ignore it
+      is_service = 1;
+#endif
+      break;
+#endif // _WIN32 || __CYGWIN__
+    case 'V':
+      // print version and CVS info
+      PrintCopyleft();
+      EXIT(0);
+      break;
+    case 'h':
+      // help: print summary of command-line options
+      debugmode=1;
+      PrintHead();
+      Usage();
+      EXIT(0);
+      break;
+    case '?':
+    default:
+      // unrecognized option
+      debugmode=1;
+      PrintHead();
+#ifdef HAVE_GETOPT_LONG
+      // Point arg to the argument in which this option was found.
+      arg = argv[optind-1];
+      // Check whether the option is a long option that doesn't map to -h.
+      if (arg[1] == '-' && optchar != 'h') {
+        // Iff optopt holds a valid option then argument must be missing.
+        if (optopt && (strchr(shortopts, optopt) != NULL)) {
+          PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n",arg+2);
+          PrintValidArgs(optopt);
+        } else {
+          PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %s <=======\n\n",arg+2);
+        }
+        PrintOut(LOG_CRIT, "\nUse smartd --help to get a usage summary\n\n");
+        EXIT(EXIT_BADCMD);
+      }
+#endif
+      if (optopt) {
+        // Iff optopt holds a valid option then argument must be missing.
+        if (strchr(shortopts, optopt) != NULL){
+          PrintOut(LOG_CRIT, "=======> ARGUMENT REQUIRED FOR OPTION: %c <=======\n",optopt);
+          PrintValidArgs(optopt);
+        } else {
+          PrintOut(LOG_CRIT, "=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt);
+        }
+        PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
+        EXIT(EXIT_BADCMD);
+      }
+      Usage();
+      EXIT(0);
+    }
+
+    // Check to see if option had an unrecognized or incorrect argument.
+    if (badarg) {
+      debugmode=1;
+      PrintHead();
+      // It would be nice to print the actual option name given by the user
+      // here, but we just print the short form.  Please fix this if you know
+      // a clean way to do it.
+      PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <======= \n", optchar, optarg);
+      PrintValidArgs(optchar);
+      PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
+      EXIT(EXIT_BADCMD);
+    }
+  }
+
+  // non-option arguments are not allowed
+  if (argc > optind) {
+    debugmode=1;
+    PrintHead();
+    PrintOut(LOG_CRIT, "=======> UNRECOGNIZED ARGUMENT: %s <=======\n\n", argv[optind]);
+    PrintOut(LOG_CRIT, "\nUse smartd -h to get a usage summary\n\n");
+    EXIT(EXIT_BADCMD);
+  }
+
+  // no pidfile in debug mode
+  if (debugmode && pid_file) {
+    debugmode=1;
+    PrintHead();
+    PrintOut(LOG_CRIT, "=======> INVALID CHOICE OF OPTIONS: -d and -p <======= \n\n");
+    PrintOut(LOG_CRIT, "Error: pid file %s not written in debug (-d) mode\n\n", pid_file);
+    pid_file=FreeNonZero(pid_file, -1,__LINE__,filenameandversion);
+    EXIT(EXIT_BADCMD);
+  }
+  
+  // print header
+  PrintHead();
+  
+  return;
+}
+
+// Function we call if no configuration file was found or if the
+// SCANDIRECTIVE Directive was found.  It makes entries for device
+// names returned by make_device_names() in os_OSNAME.c
+int MakeConfigEntries(const char *type, int start){
+  int i;
+  int num;
+  char** devlist = NULL;
+  cfgfile *first=cfgentries[0],*cfg=first;
+
+  // make list of devices
+  if ((num=make_device_names(&devlist,type))<0)
+    PrintOut(LOG_CRIT,"Problem creating device name scan list\n");
+  
+  // if no devices, or error constructing list, return
+  if (num<=0)
+    return 0;
+  
+  // loop over entries to create
+  for (i=0; i<num; i++){
+    
+    // make storage and copy for all but first entry
+    if (start+i) {
+      // allocate more storage if needed
+      while (cfgentries_max<=start+i)
+        cfgentries=AllocateMoreSpace(cfgentries, &cfgentries_max, "simulated configuration file device");
+      cfg=cfgentries[start+i]=CreateConfigEntry(first);
+    }
+
+    // ATA or SCSI?
+    if (!strcmp(type,"ATA") )
+      cfg->controller_type = CONTROLLER_ATA;
+    if (!strcmp(type,"SCSI") ) 
+      cfg->controller_type = CONTROLLER_SCSI;
+    
+    // remove device name, if it's there, and put in correct one
+    cfg->name=FreeNonZero(cfg->name, -1,__LINE__,filenameandversion);
+    // save pointer to the device name created within
+    // make_device_names
+    cfg->name=devlist[i];
+  }
+  
+  // If needed, free memory used for devlist: pointers now in
+  // cfgentries[]->names.  If num==0 we never get to this point, but
+  // that's OK.  If we realloc()d the array length in
+  // make_device_names() that was ALREADY equivalent to calling
+  // free().
+  devlist = FreeNonZero(devlist,(sizeof (char*) * num),__LINE__, filenameandversion);
+  
+  return num;
+}
+void CanNotRegister(char *name, char *type, int line, int scandirective){
+  if( !debugmode && scandirective == 1 ) { return; }
+  if (line)
+    PrintOut(scandirective?LOG_INFO:LOG_CRIT,
+             "Unable to register %s device %s at line %d of file %s\n",
+             type, name, line, configfile);
+  else
+    PrintOut(LOG_INFO,"Unable to register %s device %s\n",
+             type, name);
+  return;
+}
+
+// Returns negative value (see ParseConfigFile()) if config file
+// had errors, else number of entries which may be zero or positive. 
+// If we found no configuration file, or it contained SCANDIRECTIVE,
+// then *scanning is set to 1, else 0.
+int ReadOrMakeConfigEntries(int *scanning){
+  int entries;
+  
+  // deallocate any cfgfile data structures in memory
+  RmAllConfigEntries();
+  
+  // parse configuration file configfile (normally /etc/smartd.conf)  
+  if ((entries=ParseConfigFile())<0) {
+    // There was an error reading the configuration file.
+    RmAllConfigEntries();
+    if (entries == -1)
+      PrintOut(LOG_CRIT, "Configuration file %s has fatal syntax errors.\n", configfile);
+    return entries;
+  }
+
+  // did we find entries or scan?
+  *scanning=0;
+  
+  // no error parsing config file.
+  if (entries) {
+    // we did not find a SCANDIRECTIVE and did find valid entries
+    PrintOut(LOG_INFO, "Configuration file %s parsed.\n", configfile);
+  }
+  else if (cfgentries && cfgentries[0]) {
+    // we found a SCANDIRECTIVE or there was no configuration file so
+    // scan.  Configuration file's first entry contains all options
+    // that were set
+    cfgfile *first=cfgentries[0];
+    int doata  = !(first->controller_type==CONTROLLER_SCSI);
+    int doscsi = !(first->controller_type==CONTROLLER_ATA);
+    
+    *scanning=1;
+    
+    if (first->lineno)
+      PrintOut(LOG_INFO,"Configuration file %s was parsed, found %s, scanning devices\n", configfile, SCANDIRECTIVE);
+    else
+      PrintOut(LOG_INFO,"No configuration file %s found, scanning devices\n", configfile);
+    
+    // make config list of ATA devices to search for
+    if (doata)
+      entries+=MakeConfigEntries("ATA", entries);
+    // make config list of SCSI devices to search for
+    if (doscsi)
+      entries+=MakeConfigEntries("SCSI", entries);
+
+    // warn user if scan table found no devices
+    if (!entries) {
+      PrintOut(LOG_CRIT,"In the system's table of devices NO devices found to scan\n");
+      // get rid of fake entry with SCANDIRECTIVE as name
+      RmConfigEntry(cfgentries, __LINE__);
+    }
+  } 
+  else
+    PrintOut(LOG_CRIT,"Configuration file %s parsed but has no entries (like /dev/hda)\n",configfile);
+  
+  return entries;
+}
+
+
+// This function tries devices from cfgentries.  Each one that can be
+// registered is moved onto the [ata|scsi]devices lists and removed
+// from the cfgentries list, else it's memory is deallocated.
+void RegisterDevices(int scanning){
+  int i;
+  
+  // start by clearing lists/memory of ALL existing devices
+  RmAllDevEntries();
+  numdevata=numdevscsi=0;
+  
+  // Register entries
+  for (i=0; i<cfgentries_max ; i++){
+    
+    cfgfile *ent=cfgentries[i];
+    
+    // skip any NULL entries (holes)
+    if (!ent)
+      continue;
+    
+    // register ATA devices
+    if (ent->controller_type!=CONTROLLER_SCSI){
+      if (ATADeviceScan(ent, scanning))
+        CanNotRegister(ent->name, "ATA", ent->lineno, scanning);
+      else {
+        // move onto the list of ata devices
+        cfgentries[i]=NULL;
+        while (numdevata>=atadevlist_max)
+          atadevlist=AllocateMoreSpace(atadevlist, &atadevlist_max, "ATA device");
+        atadevlist[numdevata++]=ent;
+      }
+    }
+    
+    // then register SCSI devices
+    if (ent->controller_type==CONTROLLER_SCSI || ent->controller_type==CONTROLLER_UNKNOWN){
+      int retscsi=0;
+
+#if SCSITIMEOUT
+      struct sigaction alarmAction, defaultaction;
+
+      // Set up an alarm handler to catch USB devices that hang on
+      // SCSI scanning...
+      alarmAction.sa_handler= AlarmHandler;
+      alarmAction.sa_flags  = SA_RESTART;
+      if (sigaction(SIGALRM, &alarmAction, &defaultaction)) {
+        // if we can't set timeout, just scan device
+        PrintOut(LOG_CRIT, "Unable to initialize SCSI timeout mechanism.\n");
+        retscsi=SCSIDeviceScan(ent, scanning);
+      }
+      else {
+        // prepare return point in case of bad SCSI device
+        if (setjmp(registerscsienv))
+          // SCSI device timed out!
+          retscsi=-1;
+        else {
+        // Set alarm, make SCSI call, reset alarm
+          alarm(SCSITIMEOUT);
+          retscsi=SCSIDeviceScan(ent, scanning);
+          alarm(0);
+        }
+        if (sigaction(SIGALRM, &defaultaction, NULL)){
+          PrintOut(LOG_CRIT, "Unable to clear SCSI timeout mechanism.\n");
+        }
+      }
+#else
+      retscsi=SCSIDeviceScan(ent, scanning);
+#endif   
+
+      // Now scan SCSI device...
+      if (retscsi){
+        if (retscsi<0)
+          PrintOut(LOG_CRIT, "Device %s timed out (poorly-implemented USB device?)\n", ent->name);
+        CanNotRegister(ent->name, "SCSI", ent->lineno, scanning);
+      }
+      else {
+        // move onto the list of scsi devices
+        cfgentries[i]=NULL;
+        while (numdevscsi>=scsidevlist_max)
+          scsidevlist=AllocateMoreSpace(scsidevlist, &scsidevlist_max, "SCSI device");
+        scsidevlist[numdevscsi++]=ent;
+      }
+    }
+    
+    // if device is explictly listed and we can't register it, then
+    // exit unless the user has specified that the device is removable
+    if (cfgentries[i]  && !scanning){
+      if (ent->removable || quit==2)
+        PrintOut(LOG_INFO, "Device %s not available\n", ent->name);
+      else {
+        PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", ent->name);
+        EXIT(EXIT_BADDEV);
+      }
+    }
+    
+    // free up memory if device could not be registered
+    RmConfigEntry(cfgentries+i, __LINE__);
+  }
+  
+  return;
+}
+
+
+#ifndef _WIN32
+// Main function
+int main(int argc, char **argv)
+#else
+// Windows: internal main function started direct or by service control manager
+static int smartd_main(int argc, char **argv)
+#endif
+{
+  // external control variables for ATA disks
+  smartmonctrl control;
+
+  // is it our first pass through?
+  int firstpass=1;
+
+  // next time to wake up
+  time_t wakeuptime;
+
+  // for simplicity, null all global communications variables/lists
+  con=&control;
+  memset(con,        0,sizeof(control));
+
+  // parse input and print header and usage info if needed
+  ParseOpts(argc,argv);
+  
+  // do we mute printing from ataprint commands?
+  con->printing_switchable=0;
+  con->dont_print=debugmode?0:1;
+  
+  // don't exit on bad checksums
+  con->checksumfail=0;
+  
+  // the main loop of the code
+  while (1){
+
+    // are we exiting from a signal?
+    if (caughtsigEXIT) {
+      // are we exiting with SIGTERM?
+      int isterm=(caughtsigEXIT==SIGTERM);
+      int isquit=(caughtsigEXIT==SIGQUIT);
+      int isok=debugmode?isterm || isquit:isterm;
+      
+      PrintOut(isok?LOG_INFO:LOG_CRIT, "smartd received signal %d: %s\n",
+               caughtsigEXIT, strsignal(caughtsigEXIT));
+      
+      EXIT(isok?0:EXIT_SIGNAL);
+    }
+
+    // Should we (re)read the config file?
+    if (firstpass || caughtsigHUP){
+      int entries, scanning=0;
+
+      if (!firstpass) {
+#ifdef __CYGWIN__
+        // Workaround for missing SIGQUIT via keyboard on Cygwin
+        if (caughtsigHUP==2) {
+          // Simulate SIGQUIT if another SIGINT arrives soon
+          caughtsigHUP=0;
+          sleep(1);
+          if (caughtsigHUP==2) {
+            caughtsigEXIT=SIGQUIT;
+            continue;
+          }
+          caughtsigHUP=2;
+        }
+#endif
+        PrintOut(LOG_INFO,
+                 caughtsigHUP==1?
+                 "Signal HUP - rereading configuration file %s\n":
+                 "\a\nSignal INT - rereading configuration file %s ("SIGQUIT_KEYNAME" quits)\n\n",
+                 configfile);
+      }
+
+      // clears cfgentries, (re)reads config file, makes >=0 entries
+      entries=ReadOrMakeConfigEntries(&scanning);
+
+      if (entries>=0) {
+        // checks devices, then moves onto ata/scsi list or deallocates.
+        RegisterDevices(scanning);
+      }
+      else if (quit==2 || ((quit==0 || quit==1) && !firstpass)) {
+        // user has asked to continue on error in configuration file
+        if (!firstpass)
+          PrintOut(LOG_INFO,"Reusing previous configuration\n");
+      }
+      else {
+        // exit with configuration file error status
+        int status = (entries==-3 ? EXIT_READCONF : entries==-2 ? EXIT_NOCONF : EXIT_BADCONF);
+        EXIT(status);
+      }
+
+      // Log number of devices we are monitoring...
+      if (numdevata+numdevscsi || quit==2 || (quit==1 && !firstpass))
+        PrintOut(LOG_INFO,"Monitoring %d ATA and %d SCSI devices\n",
+                 numdevata, numdevscsi);
+      else {
+        PrintOut(LOG_INFO,"Unable to monitor any SMART enabled devices. Try debug (-d) option. Exiting...\n");
+        EXIT(EXIT_NODEV);
+      }
+
+      if (quit==4) {
+        // user has asked to print test schedule
+        PrintTestSchedule(atadevlist, scsidevlist);
+        EXIT(0);
+      }
+      
+      // reset signal
+      caughtsigHUP=0;
+    }
+
+    // check all devices once
+    CheckDevicesOnce(atadevlist, scsidevlist); 
+    
+    // user has asked us to exit after first check
+    if (quit==3) {
+      PrintOut(LOG_INFO,"Started with '-q onecheck' option. All devices sucessfully checked once.\n"
+               "smartd is exiting (exit status 0)\n");
+      EXIT(0);
+    }
+    
+    // fork into background if needed
+    if (firstpass && !debugmode) {
+#ifdef __CYGWIN__
+     if (!is_service) // don't fork() if running as service via cygrunsrv
+#endif
+      DaemonInit();
+    }
+
+    // set exit and signal handlers, write PID file, set wake-up time
+    if (firstpass){
+      Initialize(&wakeuptime);
+      firstpass=0;
+    }
+    
+    // sleep until next check time, or a signal arrives
+    wakeuptime=dosleep(wakeuptime);
+  }
+}
+
+
+#ifdef _WIN32
+// Main function for Windows
+int main(int argc, char **argv){
+  // Options for smartd windows service
+  static const daemon_winsvc_options svc_opts = {
+    "--service", // cmd_opt
+    "smartd", "SmartD Service", // servicename, displayname
+    // description
+    "Controls and monitors storage devices using the Self-Monitoring, "
+    "Analysis and Reporting Technology System (S.M.A.R.T.) "
+    "built into ATA and SCSI Hard Drives. "
+    PACKAGE_HOMEPAGE
+  };
+  // daemon_main() handles daemon and service specific commands
+  // and starts smartd_main() direct, from a new process,
+  // or via service control manager
+  return daemon_main("smartd", &svc_opts , smartd_main, argc, argv);
+}
+#endif
diff --git a/smartd.conf b/smartd.conf
new file mode 100644 (file)
index 0000000..f821683
--- /dev/null
@@ -0,0 +1,102 @@
+# Sample configuration file for smartd.  See man smartd.conf.
+
+# Home page is: http://smartmontools.sourceforge.net
+
+# $Id: smartd.conf,v 1.40 2006/04/12 14:00:12 ballen4705 Exp $
+
+# smartd will re-read the configuration file if it receives a HUP
+# signal
+
+# The file gives a list of devices to monitor using smartd, with one
+# device per line. Text after a hash (#) is ignored, and you may use
+# spaces and tabs for white space. You may use '\' to continue lines.
+
+# You can usually identify which hard disks are on your system by
+# looking in /proc/ide and in /proc/scsi.
+
+# The word DEVICESCAN will cause any remaining lines in this
+# configuration file to be ignored: it tells smartd to scan for all
+# ATA and SCSI devices.  DEVICESCAN may be followed by any of the
+# Directives listed below, which will be applied to all devices that
+# are found.  Most users should comment out DEVICESCAN and explicitly
+# list the devices that they wish to monitor.
+DEVICESCAN
+
+# First (primary) ATA/IDE hard disk.  Monitor all attributes, enable
+# automatic online data collection, automatic Attribute autosave, and
+# start a short self-test every day between 2-3am, and a long self test
+# Saturdays between 3-4am.
+#/dev/hda -a -o on -S on -s (S/../.././02|L/../../6/03)
+
+# Monitor SMART status, ATA Error Log, Self-test log, and track
+# changes in all attributes except for attribute 194
+#/dev/hdb -H -l error -l selftest -t -I 194 
+
+# Linux-specific example: monitor a SATA (Serial ATA) disk which uses
+# the libata driver.  This requires a Linux 2.6.15 or later kernel.
+# Note that the disk is addressed via a SCSI device, but the
+# underlying disk type is actually ATA
+# /dev/sda -a -d ata
+
+# A very silent check.  Only report SMART health status if it fails
+# But send an email in this case
+#/dev/hdc -H -m admin@example.com
+
+# First two SCSI disks.  This will monitor everything that smartd can
+# monitor.  Start extended self-tests Wednesdays between 6-7pm and
+# Sundays between 1-2 am
+#/dev/sda -d scsi -s L/../../3/18
+#/dev/sdb -d scsi -s L/../../7/01
+
+# Monitor 4 ATA disks connected to a 3ware 6/7/8000 controller which uses
+# the 3w-xxxx driver. Start long self-tests Sundays between 1-2, 2-3, 3-4, 
+# and 4-5 am.
+# NOTE: starting with the Linux 2.6 kernel series, the /dev/sdX interface
+# is DEPRECATED.  Use the /dev/tweN character device interface instead.
+# For example /dev/twe0, /dev/twe1, and so on.
+#/dev/sdc -d 3ware,0 -a -s L/../../7/01
+#/dev/sdc -d 3ware,1 -a -s L/../../7/02
+#/dev/sdc -d 3ware,2 -a -s L/../../7/03
+#/dev/sdc -d 3ware,3 -a -s L/../../7/04
+
+# Monitor 2 ATA disks connected to a 3ware 9000 controller which uses
+# the 3w-9xxx driver. Start long self-tests Tuesdays between 1-2 and 3-4 am
+#/dev/twa0 -d 3ware,0 -a -s L/../../2/01
+#/dev/twa0 -d 3ware,1 -a -s L/../../2/03
+
+# HERE IS A LIST OF DIRECTIVES FOR THIS CONFIGURATION FILE.
+# PLEASE SEE THE smartd.conf MAN PAGE FOR DETAILS
+#
+#   -d TYPE Set the device type: ata, scsi, marvell, removable, 3ware,N
+#   -T TYPE set the tolerance to one of: normal, permissive
+#   -o VAL  Enable/disable automatic offline tests (on/off)
+#   -S VAL  Enable/disable attribute autosave (on/off)
+#   -n MODE No check. MODE is one of: never, sleep, standby, idle
+#   -H      Monitor SMART Health Status, report if failed
+#   -l TYPE Monitor SMART log.  Type is one of: error, selftest
+#   -f      Monitor for failure of any 'Usage' Attributes
+#   -m ADD  Send warning email to ADD for -H, -l error, -l selftest, and -f
+#   -M TYPE Modify email warning behavior (see man page)
+#   -s REGE Start self-test when type/date matches regular expression (see man page)
+#   -p      Report changes in 'Prefailure' Normalized Attributes
+#   -u      Report changes in 'Usage' Normalized Attributes
+#   -t      Equivalent to -p and -u Directives
+#   -r ID   Also report Raw values of Attribute ID with -p, -u or -t
+#   -R ID   Track changes in Attribute ID Raw value with -p, -u or -t
+#   -i ID   Ignore Attribute ID for -f Directive
+#   -I ID   Ignore Attribute ID for -p, -u or -t Directive
+#   -C ID   Report if Current Pending Sector count non-zero
+#   -U ID   Report if Offline Uncorrectable count non-zero
+#   -v N,ST Modifies labeling of Attribute N (see man page)
+#   -a      Default: equivalent to -H -f -t -l error -l selftest -C 197 -U 198
+#   -F TYPE Use firmware bug workaround. Type is one of: none, samsung
+#   -P TYPE Drive-specific presets: use, ignore, show, showall
+#    #      Comment: text after a hash sign is ignored
+#    \      Line continuation character
+# Attribute ID is a decimal integer 1 <= ID <= 255
+# except for -C and -U, where ID = 0 turns them off.
+# All but -d, -m and -M Directives are only implemented for ATA devices
+#
+# If the test string DEVICESCAN is the first uncommented text
+# then smartd will scan for devices /dev/hd[a-l] and /dev/sd[a-z]
+# DEVICESCAN may be followed by any desired Directives.
diff --git a/smartd.conf.5.in b/smartd.conf.5.in
new file mode 100644 (file)
index 0000000..d8ddc71
--- /dev/null
@@ -0,0 +1,1253 @@
+.ig
+Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+
+$Id: smartd.conf.5.in,v 1.73 2006/04/12 14:03:14 ballen4705 Exp $
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+You should have received a copy of the GNU General Public License (for
+example COPYING); if not, write to the Free Software Foundation, Inc., 675
+Mass Ave, Cambridge, MA 02139, USA.
+
+This code was originally developed as a Senior Thesis by Michael Cornwell
+at the Concurrent Systems Laboratory (now part of the Storage Systems
+Research Center), Jack Baskin School of Engineering, University of
+California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+..
+.TH SMARTD.CONF 5 CURRENT_CVS_DATE CURRENT_CVS_VERSION CURRENT_CVS_DATE
+.SH NAME
+\fBsmartd.conf\fP \- SMART Disk Monitoring Daemon Configuration File\fP
+
+.SH FULL PATH
+.B /usr/local/etc/smartd.conf
+
+.SH PACKAGE VERSION
+CURRENT_CVS_VERSION released CURRENT_CVS_DATE at CURRENT_CVS_TIME
+
+.SH DESCRIPTION
+\fB/usr/local/etc/smartd.conf\fP is the configuration file for the \fBsmartd\fP
+daemon, which monitors the Self-Monitoring, Analysis and Reporting
+Technology (SMART) system built into many ATA-3 and later ATA, IDE and
+SCSI-3 hard drives.
+
+If the configuration file \fB/usr/local/etc/smartd.conf\fP is present,
+\fBsmartd\fP reads it at startup, before \fBfork\fP(2)ing into the
+background. If \fBsmartd\fP subsequently receives a \fBHUP\fP signal,
+it will then re-read the configuration file.  If \fBsmartd\fP is
+running in debug mode, then an \fBINT\fP signal will also make it
+re-read the configuration file. This signal can be generated by typing
+\fB\<CONTROL-C\>\fP in the terminal window where \fBsmartd\fP is
+running.
+
+.\" DO NOT MODIFY THIS OR THE FOLLOWING TWO LINES. WHAT FOLLOWS
+.\" IS AUTOMATICALLY INCLUDED FROM THE FILE smartd.8.in
+.\" STARTINCLUDE
+
+.SH CONFIGURATION FILE /usr/local/etc/smartd.conf
+In the absence of a configuration file, under Linux
+\fBsmartd\fP 
+will try to open the 20 ATA devices 
+.B /dev/hd[a-t] 
+and the 26 SCSI devices
+.B /dev/sd[a-z].
+Under FreeBSD, 
+\fBsmartd\fP
+will try to open all existing ATA devices (with entries in /dev)
+.B /dev/ad[0-9]+
+and all existing SCSI devices
+.B /dev/da[0-9]+.  
+Under NetBSD/OpenBSD, 
+\fBsmartd\fP
+will try to open all existing ATA devices (with entries in /dev)
+.B /dev/wd[0-9]+c
+and all existing SCSI devices
+.B /dev/sd[0-9]+c.
+Under Solaris \fBsmartd\fP will try to open all entries \fB"/dev/rdsk/c?t?d?s?"\fP for IDE/ATA and SCSI disk
+devices, and entries \fB"/dev/rmt/*"\fP for SCSI tape devices.
+Under Windows \fBsmartd\fP will try to open all entries \fB"/dev/hd[a-j]"\fP ("\\\\.\\PhysicalDrive[0-9]")
+for IDE/ATA devices on WinNT4/2000/XP, \fB"/dev/hd[a-d]"\fP
+(bitmask from "\\\\.\\SMARTVSD") for IDE/ATA devices on Win95/98/98SE/ME,
+and \fB"/dev/scsi[0-9][0-7]"\fP (ASPI adapter 0-9, ID 0-7) for SCSI
+devices on all versions of Windows.
+Under Darwin, \fBsmartd\fP will open any ATA block storage device.
+
+This can be annoying if you have an ATA or SCSI device that hangs or
+misbehaves when receiving SMART commands.  Even if this causes no
+problems, you may be annoyed by the string of error log messages about
+block-major devices that can\'t be found, and SCSI devices that can\'t
+be opened.
+
+One can avoid this problem, and gain more control over the types of
+events monitored by
+\fBsmartd\fP,
+by using the configuration file
+.B /usr/local/etc/smartd.conf.
+This file contains a list of devices to monitor, with one device per
+line.  An example file is included with the
+.B smartmontools
+distribution. You will find this sample configuration file in
+\fB/usr/local/share/doc/smartmontools-5.1/\fP. For security, the configuration file
+should not be writable by anyone but root. The syntax of the file is as
+follows:
+.IP \(bu 4
+There should be one device listed per line, although you may have
+lines that are entirely comments or white space.
+.IP \(bu 4
+Any text following a hash sign \'#\' and up to the end of the line is
+taken to be a comment, and ignored.
+.IP \(bu 4
+Lines may be continued by using a backslash \'\e\' as the last
+non-whitespace or non-comment item on a line.
+.IP \(bu 4
+Note: a line whose first character is a hash sign \'#\' is treated as
+a white-space blank line, \fBnot\fP as a non-existent line, and will
+\fBend\fP a continuation line.
+.PP 0
+.fi
+Here is an example configuration file.  It\'s for illustrative purposes
+only; please don\'t copy it onto your system without reading to the end
+of the
+.B DIRECTIVES
+Section below!
+
+.nf
+.B ################################################
+.B # This is an example smartd startup config file
+.B # /usr/local/etc/smartd.conf for monitoring three
+.B # ATA disks, three SCSI disks, six ATA disks
+.B # behind two 3ware controllers and one SATA disk
+.B #
+.nf
+.B # First ATA disk on two different interfaces. On
+.B # the second disk, start a long self-test every
+.B # Sunday between 3 and 4 am.
+.B #
+.B \ \ /dev/hda -a -m admin@example.com,root@localhost 
+.B \ \ /dev/hdc -a -I 194 -I 5 -i 12 -s L/../../7/03
+.B #
+.nf
+.B # SCSI disks.  Send a TEST warning email to admin on
+.B # startup.
+.B #
+.B \ \ /dev/sda
+.B \ \ /dev/sdb -m admin@example.com -M test
+.B #
+.nf
+.B # Strange device.  It\'s SCSI. Start a scheduled
+.B # long self test between 5 and 6 am Monday/Thursday
+.B \ \ /dev/weird -d scsi -s L/../../(1|4)/05
+.B #
+.nf
+.B # Linux-specific: SATA disk using the libata
+.B # driver.  This requires a 2.6.15 or greater
+.B # kernel.  The device entry is SCSI but the
+.B # underlying disk understands ATA SMART commands
+.B \ \ /dev/sda -a -d ata
+.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
+.B # kernel series, /dev/sdX is deprecated in favor of
+.B # /dev/tweN.  For example replace /dev/sdc by /dev/twe0
+.B # and /dev/sdd by /dev/twe1.
+.B \ \ /dev/sdc -d 3ware,0 -a -s S/../.././00
+.B \ \ /dev/sdc -d 3ware,1 -a -s S/../.././01
+.B \ \ /dev/sdd -d 3ware,2 -a -s S/../.././02
+.B \ \ /dev/sdd -d 3ware,3 -a -s S/../.././03
+.B #
+.nf
+.B # Two ATA disks on a 3ware 9000 controller.
+.B # Start long self-tests Sundays between  midnight and 
+.B # 1am and 2-3 am
+.B \ \ /dev/twa0 -d 3ware,0 -a -s L/../../7/00
+.B \ \ /dev/twa0 -d 3ware,1 -a -s L/../../7/02
+.B #
+.nf
+.B # The following line enables monitoring of the 
+.B # ATA Error Log and the Self-Test Error Log.  
+.B # It also tracks changes in both Prefailure
+.B # and Usage Attributes, apart from Attributes
+.B # 9, 194, and 231, and shows  continued lines:
+.B #
+.B \ \ /dev/hdd\ -l\ error\ \e
+.B \ \ \ \ \ \ \ \ \ \ \ -l\ selftest\ \e
+.B \ \ \ \ \ \ \ \ \ \ \ -t\ \e\ \ \ \ \ \ # Attributes not tracked:
+.B \ \ \ \ \ \ \ \ \ \ \ -I\ 194\ \e\ \ # temperature
+.B \ \ \ \ \ \ \ \ \ \ \ -I\ 231\ \e\ \ # also temperature
+.B \ \ \ \ \ \ \ \ \ \ \ -I 9\ \ \ \ \ \ # power-on hours
+.B #
+.B ################################################
+.fi
+
+.PP 
+.SH CONFIGURATION FILE DIRECTIVES
+.PP
+
+If the first non-comment entry in the configuration file is the text
+string
+.B DEVICESCAN
+in capital letters, then
+\fBsmartd\fP
+will ignore any remaining lines in the configuration file, and will
+scan for devices.
+.B DEVICESCAN
+may optionally be followed by Directives that will apply to all
+devices that are found in the scan.  Please see below for additional
+details.
+
+.sp 2
+The following are the Directives that may appear following the device
+name or
+.B DEVICESCAN
+on any line of the
+.B /usr/local/etc/smartd.conf
+configuration file. Note that
+.B these are NOT command-line options for 
+\fBsmartd\fP.
+The Directives below may appear in any order, following the device
+name. 
+
+.B For an ATA device,
+if no Directives appear, then the device will be monitored
+as if the \'\-a\' Directive (monitor all SMART properties) had been given.
+
+.B If a SCSI disk is listed,
+it will be monitored at the maximum implemented level: roughly
+equivalent to using the \'\-H \-l selftest\' options for an ATA disk.
+So with the exception of \'\-d\', \'\-m\', \'\-l selftest\', \'\-s\', and
+\'\-M\', the Directives below are ignored for SCSI disks.  For SCSI
+disks, the \'\-m\' Directive sends a warning email if the SMART status
+indicates a disk failure or problem, if the SCSI inquiry about disk
+status fails, or if new errors appear in the self-test log.
+
+.B If a 3ware controller is used
+then the corresponding SCSI (/dev/sd?) or character device (/dev/twe?
+or /dev/twa?) must be listed, along with the \'\-d 3ware,N\' Directive
+(see below).  The individual ATA disks hosted by the 3ware controller
+appear to \fBsmartd\fP as normal ATA devices.  Hence all the ATA
+directives can be used for these disks (but see note below).
+
+.TP
+.B \-d TYPE
+Specifies the type of the device.  This Directive may be used multiple
+times for one device, but the arguments \fIata\fP, \fIscsi\fP,
+\fImarvell\fP, and \fI3ware,N\fP are mutually-exclusive. If more than
+one is given then \fBsmartd\fP will use the last one which appears.
+
+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
+character in the device name is an \'s\' or an \'h\'.  This will work for
+device names like /dev/hda or /dev/sdb, and corresponds to choosing
+\fIata\fP or \fIscsi\fP respectively. If
+\fBsmartd\fP
+can\'t guess from this sixth character, then it will simply try to
+access the device using first ATA and then SCSI ioctl()s.
+
+The valid arguments to this Directive are:
+
+.I ata
+\- the device type is ATA.  This prevents
+\fBsmartd\fP
+from issuing SCSI commands to an ATA device.
+
+.I scsi
+\- the device type is SCSI.  This prevents
+\fBsmartd\fP
+from issuing ATA commands to a SCSI device.
+
+.I marvell
+\- Under Linux, interact with SATA disks behind Marvell chip-set
+controllers (using the Marvell rather than libata driver).
+
+.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 15
+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 15 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
+such in the the configuration file.
+However when the \'\-d 3ware,N\'
+Directive is used, then the corresponding disk is addressed using
+native ATA commands which are \'passed through\' the SCSI driver. All
+ATA Directives listed in this man page may be used.  Note that while
+you may use \fBany\fP of the 3ware SCSI logical devices /dev/sd? to
+address \fBany\fP of the physical disks (3ware ports), error and log
+messages will make the most sense if you always list the 3ware SCSI
+logical device corresponding to the particular physical disks.  Please
+see the \fBsmartctl\fP man page for further details.
+
+ATA disks behind 3ware controllers may alternatively be accessed via a
+character device interface /dev/twe0-15 (3ware 6000/7000/8000
+controllers) and /dev/twa0-15 (3ware 9000 series controllers).  Note
+that the 9000 series controllers may \fBonly\fP be accessed using the
+character device interface /dev/twa0-15 and not the SCSI device
+interface /dev/sd?.  Please see the \fBsmartctl\fP man page for
+further details.
+
+Note that older 3w-xxxx drivers do not pass the \'Enable Autosave\'
+(\fB-S on\fP) and \'Enable Automatic Offline\' (\fB-o on\fP) commands
+to the disk, if the SCSI interface is used, and produce these types of
+harmless syslog error messages instead: \fB\'3w-xxxx: tw_ioctl():
+Passthru size (123392) too big\'\fP. This can be fixed by upgrading to
+version 1.02.00.037 or later of the 3w-xxxx driver, or by applying a
+patch to older versions.  See
+\fBhttp://smartmontools.sourceforge.net/\fP for instructions.
+Alternatively use the character device interfaces /dev/twe0-15 (3ware
+6/7/8000 series controllers) or /dev/twa0-15 (3ware 9000 series
+controllers).
+
+
+.B 3ware controllers are currently ONLY supported under Linux.
+
+.I removable
+\- the device or its media is removable.  This indicates to
+\fBsmartd\fP
+that it should continue (instead of exiting, which is the default
+behavior) if the device does not appear to be present when
+\fBsmartd\fP is started.  This Directive may be used in conjunction
+with the other \'\-d\' Directives.
+
+.TP
+.B \-n POWERMODE[,q]
+This \'nocheck\' Directive is used to prevent a disk from being
+spun-up when it is periodically polled by \fBsmartd\fP.
+
+ATA disks have five different power states. In order of increasing
+power consumption they are: \'OFF\', \'SLEEP\', \'STANDBY\', \'IDLE\',
+and \'ACTIVE\'.  Typically in the OFF, SLEEP, and STANDBY modes the
+disk\'s platters are not spinning. But usually, in response to SMART
+commands issued by \fBsmartd\fP, the disk platters are spun up.  So if
+this option is not used, then a disk which is in a low\-power mode may
+be spun up and put into a higher\-power mode when it is periodically
+polled by \fBsmartd\fP.
+
+Note that if the disk is in SLEEP mode when \fBsmartd\fP is started,
+then it won't respond to \fBsmartd\fP commands, and so the disk won't
+be registered as a device for \fBsmartd\fP to monitor. If a disk is in
+any other low\-power mode, then the commands issued by \fBsmartd\fP to
+register the disk will probably cause it to spin\-up.
+
+The \'\fB\-n\fP\' (nocheck) Directive specifies if \fBsmartd\fP\'s
+periodic checks should still be carried out when the device is in a
+low\-power mode.  It may be used to prevent a disk from being spun\-up
+by periodic \fBsmartd\fP polling.  The allowed values of POWERMODE
+are:
+
+.I never
+\- \fBsmartd\fP will poll (check) the device regardless of its power
+mode. This may cause a disk which is spun\-down to be spun\-up when
+\fBsmartd\fP checks it.  This is the default behavior if the '\-n'
+Directive is not given.
+
+.I sleep
+\- check the device unless it is in SLEEP mode.
+
+.I standby
+\- check the device unless it is in SLEEP or STANDBY mode.  In
+these modes most disks are not spinning, so if you want to prevent
+a laptop disk from spinning up each time that \fBsmartd\fP polls,
+this is probably what you want.
+
+.I idle
+\- check the device unless it is in SLEEP, STANDBY or IDLE mode.
+In the IDLE state, most disks are still spinning, so this is probably
+not what you want.
+
+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.
+
+.TP
+.B \-T TYPE
+Specifies how tolerant
+\fBsmartd\fP
+should be of SMART command failures.  The valid arguments to this
+Directive are:
+
+.I normal
+\- do not try to monitor the disk if a mandatory SMART command fails, but
+continue if an optional SMART command fails.  This is the default.
+
+.I permissive
+\- try to monitor the disk even if it appears to lack SMART
+capabilities.  This may be required for some old disks (prior to
+ATA\-3 revision 4) that implemented SMART before the SMART standards
+were incorporated into the ATA/ATAPI Specifications.  This may also be
+needed for some Maxtor disks which fail to comply with the ATA
+Specifications and don't properly indicate support for error\- or
+self\-test logging.
+
+[Please see the \fBsmartctl \-T\fP command-line option.]
+.TP
+.B \-o VALUE
+Enables or disables SMART Automatic Offline Testing when
+\fBsmartd\fP
+starts up and has no further effect.  The valid arguments to this
+Directive are \fIon\fP and \fIoff\fP.
+
+The delay between tests is vendor-specific, but is typically four
+hours.
+
+Note that SMART Automatic Offline Testing is \fBnot\fP part of the ATA
+Specification.  Please see the
+.B smartctl \-o
+command-line option documentation for further information about this
+feature.
+.TP
+.B \-S VALUE
+Enables or disables Attribute Autosave when \fBsmartd\fP
+starts up and has no further effect.  The valid arguments to this
+Directive are \fIon\fP and \fIoff\fP.  Also affects SCSI devices.
+[Please see the \fBsmartctl \-S\fP command-line option.]
+.TP
+.B \-H
+Check the SMART health status of the disk.  If any Prefailure
+Attributes are less than or equal to their threshold values, then disk
+failure is predicted in less than 24 hours, and a message at loglevel
+.B \'LOG_CRITICAL\'
+will be logged to syslog.  [Please see the
+.B smartctl \-H
+command-line option.]
+.TP
+.B \-l TYPE
+Reports increases in the number of errors in one of the two SMART logs.  The
+valid arguments to this Directive are:
+
+.I error
+\- report if the number of ATA errors reported in the ATA Error Log
+has increased since the last check.
+
+.I selftest
+\- report if the number of failed tests reported in the SMART
+Self-Test Log has increased since the last check, or if the timestamp
+associated with the most recent failed test has increased.  Note that
+such errors will \fBonly\fP be logged if you run self-tests on the
+disk (and it fails a test!).  Self-Tests can be run automatically by
+\fBsmartd\fP: please see the \fB\'\-s\'\fP Directive below.
+Self-Tests can also be run manually by using the \fB\'\-t\ short\'\fP
+and \fB\'\-t\ long\'\fP options of \fBsmartctl\fP and the results of
+the testing can be observed using the \fBsmartctl \'\-l\ selftest\'\fP
+command-line option.]
+
+[Please see the \fBsmartctl \-l\fP and \fB\-t\fP command-line
+options.]
+.TP
+.B \-s REGEXP
+Run Self-Tests or Offline Immediate Tests, at scheduled times.  A
+Self- or Offline Immediate Test will be run at the end of periodic
+device polling, if all 12 characters of the string \fBT/MM/DD/d/HH\fP
+match the extended regular expression \fBREGEXP\fP. Here:
+.RS 7
+.IP \fBT\fP 4
+is the type of the test.  The values that \fBsmartd\fP will try to
+match (in turn) are: \'L\' for a \fBL\fPong Self-Test, \'S\' for a
+\fBS\fPhort Self-Test, \'C\' for a \fBC\fPonveyance Self-Test (ATA
+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.
+.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
+use a single decimal digit or the match will always fail!
+.IP \fBDD\fP 4
+is the day of the month, expressed with two decimal digits. The
+range is from 01 to 31 inclusive.  Do \fBnot\fP
+use a single decimal digit or the match will always fail!
+.IP \fBd\fP 4
+is the day of the week, expressed with one decimal digit.  The
+range is from 1 (Monday) to 7 (Sunday) inclusive.
+.IP \fBHH\fP 4
+is the hour of the day, written with two decimal digits, and given in
+hours after midnight.  The range is 00 (midnight to just before 1am)
+to 23 (11pm to just before midnight) inclusive.  Do \fBnot\fP use a
+single decimal digit or the match will always fail!
+.RE
+.\"  The following two lines are a workaround for a man2html bug.  Please leave them.
+.\" They define a non-existent option; useful because man2html can't correctly reset the margins.
+.TP
+.B \&
+Some examples follow.  In reading these, keep in mind that in extended
+regular expressions a dot \fB\'.\'\fP matches any single character, and
+a parenthetical expression such as \fB\'(A|B|C)\'\fP denotes any one of the three possibilities \fBA\fP,
+\fBB\fP, or \fBC\fP.
+
+To schedule a short Self-Test between 2-3am every morning, use:
+.nf
+\fB \-s S/../.././02\fP
+.fi
+To schedule a long Self-Test between 4-5am every Sunday morning, use:
+.nf
+\fB \-s L/../../7/04\fP
+.fi
+To schedule a long Self-Test between 10-11pm on the first and
+fifteenth day of each month, use:
+.nf
+\fB \-s L/../(01|15)/./22\fP
+.fi
+To schedule an Offline Immediate test after every midnight, 6am,
+noon,and 6pm, plus a Short Self-Test daily at 1-2am and a Long
+Self-Test every Saturday at 3-4am, use:
+.nf
+\fB \-s (O/../.././(00|06|12|18)|S/../.././01|L/../../6/03)\fP
+.fi
+
+Scheduled tests are run immediately following the regularly-scheduled
+device polling, if the current local date, time, and test type, match
+\fBREGEXP\fP.  By default the regularly-scheduled device polling
+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.
+
+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
+already running, then this running self test will \fBnot\fP be
+interrupted to begin another test.
+
+\fBsmartd\fP will not attempt to run \fBany\fP type of test if another
+test was already started or run in the same hour.
+
+Each time a test is run, \fBsmartd\fP will log an entry to SYSLOG.
+You can use these or the '-q showtests' command-line option to verify
+that you constructed \fBREGEXP\fP correctly.  The matching order
+(\fBL\fP before \fBS\fP before \fBC\fP before \fBO\fP) ensures that
+if multiple test types are all scheduled for the same hour, the
+longer test type has precedence.  This is usually the desired behavior.
+
+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
+issue harmless informational warning messages if it detects characters
+in \fBREGEXP\fP that appear to indicate that you have made this
+mistake.
+
+.TP
+.B \-m ADD
+Send a warning email to the email address \fBADD\fP if the \'\-H\',
+\'\-l\', \'\-f\', \'\-C\', or \'\-O\' Directives detect a failure or a
+new error, or if a SMART command to the disk fails. This Directive
+only works in conjunction with these other Directives (or with the
+equivalent default \'\-a\' Directive).
+
+To prevent your email in-box from getting filled up with warning
+messages, by default only a single warning will be sent for each of
+the enabled alert types, \'\-H\', \'\-l\', \'\-f\', \'\-C\', or
+\'\-O\' even if more than one failure or error is detected or if the
+failure or error persists.  [This behavior can be modified; see the
+\'\-M\' Directive below.]
+
+To send email to more than one user, please use the following "comma
+separated" form for the address: \fBuser1@add1,user2@add2,...,userN@addN\fP
+(with no spaces).
+
+To test that email is being sent correctly, use the \'\-M test\'
+Directive described below to send one test email message on
+\fBsmartd\fP
+startup.
+
+By default, email is sent using the system 
+.B mail
+command.  In order that
+\fBsmartd\fP
+find the mail command (normally /bin/mail) an executable named
+.B \'mail\'
+must be in the path of the shell or environment from which
+\fBsmartd\fP
+was started.  If you wish to specify an explicit path to the mail
+executable (for example /usr/local/bin/mail) or a custom script to
+run, please use the \'\-M exec\' Directive below.
+
+Note that by default under Solaris, in the previous paragraph,
+\'\fBmailx\fP\' and \'\fB/bin/mailx\fP\' are used, since Solaris
+\'/bin/mail\' does not accept a \'\-s\' (Subject) command-line
+argument.
+
+On Windows, the \'\fBBlat\fP\' mailer
+(\fBhttp://blat.sourceforge.net/\fP) is used by default.
+This mailer uses a different command line syntax, see
+\'\-M exec\' below.
+
+Note also that there is a special argument
+.B <nomailer>
+which can be given to the \'\-m\' Directive in conjunction with the \'\-M
+exec\' Directive. Please see below for an explanation of its effect.
+
+If the mailer or the shell running it produces any STDERR/STDOUT
+output, then a snippet of that output will be copied to SYSLOG.  The
+remainder of the output is discarded. If problems are encountered in
+sending mail, this should help you to understand and fix them.  If
+you have mail problems, we recommend running \fBsmartd\fP in debug
+mode with the \'-d\' flag, using the \'-M test\' Directive described
+below.
+
+The following extension is available on Windows:
+By specifying \'\fBmsgbox\fP\' as a mail address, a warning
+"email" is displayed as a message box on the screen.
+Using both \'\fBmsgbox\fP\' and regular mail addresses is possible,
+if \'\fBmsgbox\fP\' is the first word in the comma separated list.
+With \'\fBsysmsgbox\fP\', a system modal (always on top) message box
+is used. If running as a service, a service notification message box
+(always shown on current visible desktop) is used.
+
+.TP
+.B \-M TYPE
+These Directives modify the behavior of the
+\fBsmartd\fP
+email warnings enabled with the \'\-m\' email Directive described above.
+These \'\-M\' Directives only work in conjunction with the \'\-m\'
+Directive and can not be used without it.
+
+Multiple \-M Directives may be given.  If more than one of the
+following three \-M Directives are given (example: \-M once \-M daily)
+then the final one (in the example, \-M daily) is used.
+
+The valid arguments to the \-M Directive are (one of the following
+three):
+
+.I once
+\- send only one warning email for each type of disk problem detected.  This
+is the default.
+
+.I daily
+\- send additional warning reminder emails, once per day, for each type
+of disk problem detected.
+
+.I diminishing
+\- send additional warning reminder emails, after a one-day interval,
+then a two-day interval, then a four-day interval, and so on for each
+type of disk problem detected. Each interval is twice as long as the
+previous interval.
+
+In addition, one may add zero or more of the following Directives:
+
+.I test
+\- send a single test email
+immediately upon
+\fBsmartd\fP
+startup.  This allows one to verify that email is delivered correctly.
+
+.I exec PATH
+\- run the executable PATH instead of the default mail command, when
+\fBsmartd\fP
+needs to send email.  PATH must point to an executable binary file or
+script.
+
+By setting PATH to point to a customized script, you can make
+\fBsmartd\fP perform useful tricks when a disk problem is detected
+(beeping the console, shutting down the machine, broadcasting warnings
+to all logged-in users, etc.)  But please be careful. \fBsmartd\fP
+will \fBblock\fP until the executable PATH returns, so if your
+executable hangs, then \fBsmartd\fP will also hang. Some sample
+scripts are included in
+/usr/local/share/doc/smartmontools-5.1/examplescripts/.
+
+The return status of the executable is recorded by \fBsmartd\fP in
+SYSLOG. The executable is not expected to write to STDOUT or
+STDERR.  If it does, then this is interpreted as indicating that
+something is going wrong with your executable, and a fragment of this
+output is logged to SYSLOG to help you to understand the problem.
+Normally, if you wish to leave some record behind, the executable
+should send mail or write to a file or device.
+
+Before running the executable, \fBsmartd\fP sets a number of
+environment variables.  These environment variables may be used to
+control the executable\'s behavior.  The environment variables
+exported by \fBsmartd\fP are:
+.RS 7
+.IP \fBSMARTD_MAILER\fP 4
+is set to the argument of \-M exec, if present or else to \'mail\'
+(examples: /bin/mail, mail).
+.IP \fBSMARTD_DEVICE\fP 4
+is set to the device path (examples: /dev/hda, /dev/sdb).
+.IP \fBSMARTD_DEVICETYPE\fP 4
+is set to the device type (possible values: ata, scsi, 3ware,N). Here
+N=0,...,15 denotes the ATA disk behind a 3ware RAID controller.
+.IP \fBSMARTD_DEVICESTRING\fP 4
+is set to the device description.  For SMARTD_DEVICETYPE of ata or
+scsi, this is the same as SMARTD_DEVICE.  For 3ware RAID controllers,
+the form used is \'/dev/sdc [3ware_disk_01]\'. In this case the device
+string contains a space and is NOT quoted.  So to use
+$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:
+.nf
+.fi
+\fIEmailTest\fP: this is an email test message.
+.nf
+.fi
+\fIHealth\fP: the SMART health status indicates imminent failure.
+.nf
+.fi
+\fIUsage\fP: a usage Attribute has failed.
+.nf
+.fi
+\fISelfTest\fP: the number of self-test failures has increased.
+.nf
+.fi
+\fIErrorCount\fP: the number of errors in the ATA error log has increased.
+.nf
+.fi
+\fICurrentPendingSector\fP: one of more disk sectors could not be
+read and are marked to be reallocated (replaced with spare sectors).
+.nf
+.fi
+\fIOfflineUncorrectableSector\fP: during off\-line testing, or self\-testing,
+one or more disk sectors could not be read.
+.nf
+.fi
+\fIFailedHealthCheck\fP: the SMART health status command failed.
+.nf
+.fi
+\fIFailedReadSmartData\fP: the command to read SMART Attribute data failed.
+.nf
+.fi
+\fIFailedReadSmartErrorLog\fP: the command to read the SMART error log failed.
+.nf
+.fi
+\fIFailedReadSmartSelfTestLog\fP: the command to read the SMART self-test log failed.
+.nf
+.fi
+\fIFailedOpenDevice\fP: the open() command to the device failed.
+.IP \fBSMARTD_ADDRESS\fP 4
+is determined by the address argument ADD of the \'\-m\' Directive.
+If ADD is \fB<nomailer>\fP, then \fBSMARTD_ADDRESS\fP is not set.
+Otherwise, it is set to the comma-separated-list of email addresses
+given by the argument ADD, with the commas replaced by spaces
+(example:admin@example.com root).  If more than one email address is
+given, then this string will contain space characters and is NOT
+quoted, so to use it in a bash script you may want to enclose it in
+double quotes.
+.IP \fBSMARTD_MESSAGE\fP 4
+is set to the one sentence summary warning email message string from
+\fBsmartd\fP. 
+This message string contains space characters and is NOT quoted. So to
+use $SMARTD_MESSAGE in a bash script you should probably enclose it in
+double quotes.
+.IP \fBSMARTD_FULLMESSAGE\fP 4
+is set to the contents of the entire email warning message string from
+\fBsmartd\fP. 
+This message string contains space and return characters and is NOT quoted. So to
+use $SMARTD_FULLMESSAGE in a bash script you should probably enclose it in
+double quotes.
+.IP \fBSMARTD_TFIRST\fP 4
+is a text string giving the time and date at which the first problem
+of this type was reported. This text string contains space characters
+and no newlines, and is NOT quoted. For example:
+.nf
+.fi
+Sun Feb  9 14:58:19 2003 CST
+.IP \fBSMARTD_TFIRSTEPOCH\fP 4
+is an integer, which is the unix epoch (number of seconds since Jan 1,
+1970) for \fBSMARTD_TFIRST\fP.
+.RE
+.\"  The following two lines are a workaround for a man2html bug.  Please leave them.
+.\" They define a non-existent option; useful because man2html can't correctly reset the margins.
+.TP
+.B \&
+The shell which is used to run PATH is system-dependent. For vanilla
+Linux/glibc it\'s bash. For other systems, the man page for
+\fBpopen\fP(3) should say what shell is used.
+
+If the \'\-m ADD\' Directive is given with a normal address argument,
+then the executable pointed to by PATH will be run in a shell with
+STDIN receiving the body of the email message, and with the same
+command-line arguments:
+.nf
+-s "$SMARTD_SUBJECT" $SMARTD_ADDRESS
+.fi
+that would normally be provided to \'mail\'.  Examples include:
+.nf
+.B -m user@home -M exec /bin/mail
+.B -m admin@work -M exec /usr/local/bin/mailto
+.B -m root -M exec /Example_1/bash/script/below
+.fi
+
+Note that on Windows, the syntax of the \'\fBBlat\fP\' mailer is
+used:
+.nf
+- -q -subject "$SMARTD_SUBJECT" -to "$SMARTD_ADDRESS"
+.fi
+
+If the \'\-m ADD\' Directive is given with the special address argument
+.B <nomailer>
+then the executable pointed to by PATH is run in a shell with
+.B no
+STDIN and
+.B no
+command-line arguments, for example:
+.nf
+.B -m <nomailer> -M exec /Example_2/bash/script/below
+.fi
+If the executable produces any STDERR/STDOUT output, then \fBsmartd\fP
+assumes that something is going wrong, and a snippet of that output
+will be copied to SYSLOG.  The remainder of the output is then
+discarded.
+
+Some EXAMPLES of scripts that can be used with the \'\-M exec\'
+Directive are given below. Some sample scripts are also included in
+/usr/local/share/doc/smartmontools-5.1/examplescripts/.
+
+.TP
+.B \-f
+Check for \'failure\' of any Usage Attributes.  If these Attributes are
+less than or equal to the threshold, it does NOT indicate imminent
+disk failure.  It "indicates an advisory condition where the usage or
+age of the device has exceeded its intended design life period."
+[Please see the \fBsmartctl \-A\fP command-line option.]
+.TP
+.B \-p
+Report anytime that a Prefail Attribute has changed
+its value since the last check, 30 minutes ago. [Please see the
+.B smartctl \-A
+command-line option.]
+.TP
+.B \-u
+Report anytime that a Usage Attribute has changed its value
+since the last check, 30 minutes ago. [Please see the
+.B smartctl \-A
+command-line option.]
+.TP
+.B \-t
+Equivalent to turning on the two previous flags \'\-p\' and \'\-u\'.
+Tracks changes in \fIall\fP device Attributes (both Prefailure and
+Usage). [Please see the \fBsmartctl\fP \-A command-line option.]
+.TP
+.B \-i ID
+Ignore device Attribute number \fBID\fP when checking for failure of
+Usage Attributes.  \fBID\fP must be a decimal integer in the range
+from 1 to 255.  This Directive modifies the behavior of the \'\-f\'
+Directive and has no effect without it.
+
+This is useful, for example, if you have a very old disk and don\'t
+want to keep getting messages about the hours-on-lifetime Attribute
+(usually Attribute 9) failing.  This Directive may appear multiple
+times for a single device, if you want to ignore multiple Attributes.
+.TP
+.B \-I ID
+Ignore device Attribute \fBID\fP when tracking changes in the
+Attribute values.  \fBID\fP must be a decimal integer in the range
+from 1 to 255.  This Directive modifies the behavior of the \'\-p\',
+\'\-u\', and \'\-t\' tracking Directives and has no effect without one
+of them.
+
+This is useful, for example, if one of the device Attributes is the disk
+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
+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
+the behavior of the \'\-p\', \'\-u\', and \'\-t\' tracking Directives
+and has no effect without one of them.  This Directive may be given
+multiple times.
+
+A common use of this Directive is to track the device Temperature
+(often ID=194 or 231).
+
+.TP
+.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
+integer in the range from 1 to 255.  This Directive modifies the
+behavior of the \'\-p\', \'\-u\', and \'\-t\' tracking Directives and
+has no effect without one of them.  This Directive may be given
+multiple times.
+
+If this Directive is given, it automatically implies the \'\-r\'
+Directive for the same Attribute, so that the Raw value of the
+Attribute is reported.
+
+A common use of this Directive is to track the device Temperature
+(often ID=194 or 231).  It is also useful for understanding how
+different types of system behavior affects the values of certain
+Attributes.
+
+.TP
+.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
+\fBID\fP is 0 to 255 inclusive.  To turn off this reporting, use
+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).
+
+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
+the read failed because the data on it has been corrupted and has
+inconsistent Error Checking and Correction (ECC) codes.  This is
+important to know, because it means that there is some unreadable data
+on the disk.  The problem of figuring out what file this data belongs
+to is operating system and file system specific.  You can typically
+force the sector to reallocate by writing to it (translation: make the
+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
+[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
+\fBID\fP is 0 to 255 inclusive.  To turn off this reporting, use
+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).
+
+
+An offline uncorrectable sector is a disk sector which was not
+readable during an off\-line scan or a self\-test.  This is important
+to know, because if you have data stored in this disk sector, and you
+need to read it, the read will fail.  Please see the previous \'\-C\'
+option for more details.
+
+.TP
+.B \-F TYPE
+[ATA only] Modifies the behavior of \fBsmartd\fP to compensate for
+some known and understood device firmware bug.  The arguments to this
+Directive are exclusive, so that only the final Directive given is
+used.  The valid values are:
+
+.I none
+\- Assume that the device firmware obeys the ATA specifications.  This is
+the default, unless the device has presets for \'\-F\' in the device
+database.
+
+.I samsung
+\- In some Samsung disks (example: model SV4012H Firmware Version:
+RM100-08) some of the two- and four-byte quantities in the SMART data
+structures are byte-swapped (relative to the ATA specification).
+Enabling this option tells \fBsmartd\fP to evaluate these quantities
+in byte-reversed order.  Some signs that your disk needs this option
+are (1) no self-test log printed, even though you have run self-tests;
+(2) very large numbers of ATA errors reported in the ATA error log;
+(3) strange and impossible values for the ATA error log timestamps.
+
+.I samsung2
+\- In more recent Samsung disks (firmware revisions ending in "\-23") the
+number of ATA errors reported is byte swapped.  Enabling this option
+tells \fBsmartd\fP to evaluate this quantity in byte-reversed order.
+
+Note that an explicit \'\-F\' Directive will over-ride any preset
+values for \'\-F\' (see the \'\-P\' option below).
+
+
+[Please see the \fBsmartctl \-F\fP command-line option.]
+
+.TP
+.B \-v N,OPTION
+Modifies the labeling for Attribute N, for disks which use
+non-standard Attribute definitions.  This is useful in connection with
+the Attribute tracking/reporting Directives.
+
+This Directive may appear multiple times. Valid arguments to this
+Directive are:
+
+.I 9,minutes
+\- Raw Attribute number 9 is power-on time in minutes.  Its raw value
+will be displayed in the form \'Xh+Ym\'.  Here X is hours, and Y is
+minutes in the range 0-59 inclusive.  Y is always printed with two
+digits, for example \'06\' or \'31\' or \'00\'.
+
+.I 9,seconds
+\- Raw Attribute number 9 is power-on time in seconds.  Its raw value
+will be displayed in the form \'Xh+Ym+Zs\'.  Here X is hours, Y is
+minutes in the range 0-59 inclusive, and Z is seconds in the range
+0-59 inclusive.  Y and Z are always printed with two digits, for
+example \'06\' or \'31\' or \'00\'.
+
+.I 9,halfminutes
+\- Raw Attribute number 9 is power-on time, measured in units of 30
+seconds.  This format is used by some Samsung disks.  Its raw value
+will be displayed in the form \'Xh+Ym\'.  Here X is hours, and Y is
+minutes in the range 0-59 inclusive.  Y is always printed with two
+digits, for example \'06\' or \'31\' or \'00\'.
+
+.I 9,temp
+\- Raw Attribute number 9 is the disk temperature in Celsius.
+
+.I 192,emergencyretractcyclect
+\- Raw Attribute number 192 is the Emergency Retract Cycle Count.
+
+.I 193,loadunload
+\- Raw Attribute number 193 contains two values. The first is the
+number of load cycles.  The second is the number of unload cycles.
+The difference between these two values is the number of times that
+the drive was unexpectedly powered off (also called an emergency
+unload). As a rule of thumb, the mechanical stress created by one
+emergency unload is equivalent to that created by one hundred normal
+unloads.
+
+.I 194,10xCelsius
+\- Raw Attribute number 194 is ten times the disk temperature in
+Celsius.  This is used by some Samsung disks (example: model SV1204H
+with RK100-13 firmware).
+
+.I 194,unknown
+\- Raw Attribute number 194 is NOT the disk temperature, and its
+interpretation is unknown. This is primarily useful for the -P
+(presets) Directive.
+
+.I 198,offlinescanuncsectorct
+\- Raw Attribute number 198 is the Offline Scan UNC Sector Count.
+
+.I 200,writeerrorcount
+\- Raw Attribute number 200 is the Write Error Count.
+
+.I 201,detectedtacount
+\- Raw Attribute number 201 is the Detected TA Count.
+
+.I 220,temp
+\- Raw Attribute number 220 is the disk temperature in Celsius.
+
+Note: a table of hard drive models, listing which Attribute
+corresponds to temperature, can be found at:
+\fBhttp://www.guzu.net/linux/hddtemp.db\fP
+
+.I N,raw8
+\- Print the Raw value of Attribute N as six 8-bit unsigned base-10
+integers.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw8\' prints Raw values for ALL Attributes in this
+form.  The form (for example) \'123,raw8\' only prints the Raw value for
+Attribute 123 in this form.
+
+.I N,raw16
+\- Print the Raw value of Attribute N as three 16-bit unsigned base-10
+integers.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw16\' prints Raw values for ALL Attributes in this
+form.  The form (for example) \'123,raw16\' only prints the Raw value for
+Attribute 123 in this form.
+
+.I N,raw48
+\- Print the Raw value of Attribute N as a 48-bit unsigned base-10
+integer.  This may be useful for decoding the meaning of the Raw
+value.  The form \'N,raw48\' prints Raw values for ALL Attributes in
+this form.  The form (for example) \'123,raw48\' only prints the Raw
+value for Attribute 123 in this form.
+
+.TP
+.B \-P TYPE
+Specifies whether
+\fBsmartd\fP
+should use any preset options that are available for this drive.  The
+valid arguments to this Directive are:
+
+.I use
+\- use any presets that are available for this drive.  This is the default.
+
+.I ignore
+\- do not use any presets for this drive.
+
+.I show
+\- show the presets listed for this drive in the database.
+
+.I showall
+\- show the presets that are available for all drives and then exit.
+
+[Please see the
+.B smartctl \-P
+command-line option.]
+
+.TP
+.B \-a
+Equivalent to turning on all of the following Directives: 
+.B \'\-H\' 
+to check the SMART health status,
+.B \'\-f\' 
+to report failures of Usage (rather than Prefail) Attributes,
+.B \'\-t\' 
+to track changes in both Prefailure and Usage Attributes,
+.B \'\-l\ selftest\' 
+to report increases in the number of Self-Test Log errors,
+.B \'\-l\ error\' 
+to report increases in the number of ATA errors,
+.B \'\-C 197\'
+to report nonzero values of the current pending sector count, and
+.B \'\-U 198\'
+to report nonzero values of the offline pending sector count.
+
+Note that \-a is the default for ATA devices.  If none of these other
+Directives is given, then \-a is assumed.
+
+.TP
+.B #
+Comment: ignore the remainder of the line.
+.TP
+.B \e
+Continuation character: if this is the last non-white or non-comment
+character on a line, then the following line is a continuation of the current
+one.
+.PP
+If you are not sure which Directives to use, I suggest experimenting
+for a few minutes with
+.B smartctl
+to see what SMART functionality your disk(s) support(s).  If you do
+not like voluminous syslog messages, a good choice of
+\fBsmartd\fP
+configuration file Directives might be:
+.nf
+.B \-H \-l\ selftest \-l\ error \-f.
+.fi
+If you want more frequent information, use:
+.B -a.
+
+.TP
+.B ADDITIONAL DETAILS ABOUT DEVICESCAN
+If the first non-comment entry in the configuration file is the text
+string \fBDEVICESCAN\fP in capital letters, then \fBsmartd\fP will
+ignore any remaining lines in the configuration file, and will scan
+for devices.
+
+If \fBDEVICESCAN\fP is not followed by any Directives, then smartd
+will scan for both ATA and SCSI devices, and will monitor all possible
+SMART properties of any devices that are found.
+
+\fBDEVICESCAN\fP may optionally be followed by any valid Directives,
+which will be applied to all devices that are found in the scan.  For
+example
+.nf
+.B DEVICESCAN -m root@example.com
+.fi
+will scan for all devices, and then monitor them.  It will send one
+email warning per device for any problems that are found.
+.nf
+.B  DEVICESCAN -d ata -m root@example.com
+.fi
+will do the same, but restricts the scan to ATA devices only.  
+.nf
+.B  DEVICESCAN -H -d ata -m root@example.com
+.fi
+will do the same, but only monitors the SMART health status of the
+devices, (rather than the default \-a, which monitors all SMART
+properties).
+
+.TP
+.B EXAMPLES OF SHELL SCRIPTS FOR \'\-M exec\'
+These are two examples of shell scripts that can be used with the \'\-M
+exec PATH\' Directive described previously.  The paths to these scripts
+and similar executables is the PATH argument to the \'\-M exec PATH\'
+Directive.
+
+Example 1: This script is for use with \'\-m ADDRESS -M exec PATH\'.  It appends
+the output of
+.B smartctl -a
+to the output of the smartd email warning message and sends it to ADDRESS.
+
+.nf
+\fB
+#! /bin/bash
+
+# Save the email message (STDIN) to a file:
+cat > /root/msg
+
+# Append the output of smartctl -a to the message:
+/usr/local/sbin/smartctl -a -d $SMART_DEVICETYPE $SMARTD_DEVICE >> /root/msg
+# Now email the message to the user at address ADD:
+/bin/mail -s "$SMARTD_SUBJECT" $SMARTD_ADDRESS < /root/msg
+\fP
+.fi
+
+Example 2: This script is for use with \'\-m <nomailer> \-M exec
+PATH\'. It warns all users about a disk problem, waits 30 seconds, and
+then powers down the machine.
+
+.nf
+\fB
+#! /bin/bash
+
+# Warn all users of a problem
+wall \'Problem detected with disk: \' "$SMARTD_DEVICESTRING"
+wall \'Warning message from smartd is: \' "$SMARTD_MESSAGE"
+wall \'Shutting down machine in 30 seconds... \'
+# Wait half a minute
+sleep 30
+# Power down the machine
+/sbin/shutdown -hf now
+\fP
+.fi
+
+Some example scripts are distributed with the smartmontools package,
+in /usr/local/share/doc/smartmontools-5.1/examplescripts/.
+
+Please note that these scripts typically run as root, so any files
+that they read/write should not be writable by ordinary users or
+reside in directories like /tmp that are writable by ordinary users
+and may expose your system to symlink attacks.
+
+As previously described, if the scripts write to STDOUT or STDERR,
+this is interpreted as indicating that there was an internal error
+within the script, and a snippet of STDOUT/STDERR is logged to SYSLOG.
+The remainder is flushed.
+
+.\" ENDINCLUDE
+.\" DO NOT MODIFY THIS OR PREVIOUS/NEXT LINES. THIS DEFINES THE 
+.\" END OF THE INCLUDED SECTION FROM smartd.8.in
+
+.PP
+.SH AUTHOR
+\fBBruce Allen\fP smartmontools-support@lists.sourceforge.net
+.fi
+University of Wisconsin \- Milwaukee Physics Department
+
+.PP
+.SH CONTRIBUTORS
+The following have made large contributions to smartmontools:
+.nf
+\fBCasper Dik\fP (Solaris SCSI interface)
+\fBChristian Franke\fP (Windows interface and Cygwin package)
+\fBDouglas Gilbert\fP (SCSI subsystem)
+\fBGuido Guenther\fP (Autoconf/Automake packaging)
+\fBGeoffrey Keating\fP (Darwin ATA interface)
+\fBEduard Martinescu\fP (FreeBSD interface)
+\fBFr\*'ed\*'eric L. W. Meunier\fP (Web site and Mailing list)
+\fBKeiji Sawada\fP (Solaris ATA interface)
+\fBSergey Svishchev\fP (NetBSD interface)
+\fBDavid Snyder and Sergey Svishchev\fP (OpenBSD interface)
+\fBPhil Williams\fP (User interface and drive database)
+.fi
+Many other individuals have made smaller contributions and corrections.
+
+.PP
+.SH CREDITS
+.fi
+This code was derived from the smartsuite package, written by Michael
+Cornwell, and from the previous ucsc smartsuite package. It extends
+these to cover ATA-5 disks. This code was originally developed as a
+Senior Thesis by Michael Cornwell at the Concurrent Systems Laboratory
+(now part of the Storage Systems Research Center), Jack Baskin School
+of Engineering, University of California, Santa
+Cruz. \fBhttp://ssrc.soe.ucsc.edu/\fP .
+.SH
+HOME PAGE FOR SMARTMONTOOLS: 
+.fi
+Please see the following web site for updates, further documentation, bug
+reports and patches:
+.nf
+.B
+http://smartmontools.sourceforge.net/
+
+.SH
+SEE ALSO:
+\fBsmartd\fP(8), \fBsmartctl\fP(8), \fBsyslogd\fP(8),
+\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.73 2006/04/12 14:03:14 ballen4705 Exp $
diff --git a/smartd.h b/smartd.h
new file mode 100644 (file)
index 0000000..c1b5dd1
--- /dev/null
+++ b/smartd.h
@@ -0,0 +1,291 @@
+/*
+ * smartd.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+#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.76 2006/04/12 14:54:28 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 128
+
+// 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 12
+
+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_type;          // Controller type, ATA/SCSI/3Ware/(more to come)
+  unsigned char controller_port;          // 1 + (disk number in controller). 0 means controller only handles one disk.
+  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 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
+
+  // SCSI ONLY
+  unsigned char SmartPageSupported;       // has log sense IE page (0x2f)
+  unsigned char TempPageSupported;        // has log sense temperature page (0xd)
+  unsigned char Temperature;              // last recorded figure (in Celsius)
+  unsigned char SuppressReport;           // minimize nuisance reports
+  unsigned char modese_len;               // mode sense/select cmd len: 0 (don't
+                                          // know yet) 6 or 10
+  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
+
+} 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,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
+
+#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)
diff --git a/smartd.initd.in b/smartd.initd.in
new file mode 100755 (executable)
index 0000000..c267f91
--- /dev/null
@@ -0,0 +1,478 @@
+#! /bin/sh
+
+# smartmontools init file for smartd
+# Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+# $Id: smartd.initd.in,v 1.33 2006/04/12 14:54:28 ballen4705 Exp $
+
+# For RedHat and cousins:
+# chkconfig: 2345 40 40
+# description: Self Monitoring and Reporting Technology (SMART) Daemon
+# processname: smartd 
+
+# For SuSE and cousins
+### BEGIN INIT INFO
+# Provides:          smartd
+# Required-Start:    $syslog
+# X-UnitedLinux-Should-Start: $sendmail
+# Required-Stop:     $syslog
+# X-UnitedLinux-Should-Stop:
+# Default-Start:     2 3 5
+# Default-Stop:
+# Short-Description: Monitors disk and tape health via S.M.A.R.T.
+# Description:       Start S.M.A.R.T. disk and tape monitor.
+### END INIT INFO
+
+# 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/.
+
+# Uncomment the line below to pass options to smartd on startup. 
+# Note that distribution specific configuration files like
+# /etc/{default,sysconfig}/smartmontools might override these
+#smartd_opts="--interval=1800"
+
+SMARTD_BIN=/usr/local/sbin/smartd
+
+report_unsupported () {
+    echo "Currently the smartmontools package has no init script for"
+    echo "the $1 OS/distribution. If you can provide one or this"
+    echo "one works after removing some ifdefs, please contact"
+    echo "smartmontools-support@lists.sourceforge.net."
+    exit 1
+}
+
+# Red Hat or Yellow Dog or Mandrake
+if [ -f /etc/redhat-release -o -f /etc/yellowdog-release -o -f /etc/mandrake-release -o -f /etc/whitebox-release -o -f /etc/trustix-release -o -f /etc/tinysofa-release ] ; then
+    
+# Source function library
+    . /etc/rc.d/init.d/functions
+
+# Source configuration file.  This should define the shell variable smartd_opts
+    [ -r /etc/sysconfig/smartmontools ] && . /etc/sysconfig/smartmontools
+    
+    RETVAL=0
+    
+    prog=smartd
+    
+    case "$1" in
+       start)
+           echo -n $"Starting $prog: "
+           daemon $SMARTD_BIN $smartd_opts
+           touch /var/lock/subsys/smartd
+           echo
+           ;;
+       stop)
+           echo -n $"Shutting down $prog: "
+           killproc $SMARTD_BIN
+           rm -f /var/lock/subsys/smartd
+           echo
+           ;;
+       reload)
+            echo -n $"Reloading $prog daemon configuration: "
+           killproc $SMARTD_BIN -HUP
+           RETVAL=$?
+           echo
+           ;;
+       report)
+           echo -n $"Checking SMART devices now: "
+           killproc $SMARTD_BIN -USR1
+           RETVAL=$?
+           echo
+            ;;
+       restart)
+           $0 stop
+           $0 start
+           ;;
+       status)
+           status $prog
+           ;;
+       *)
+           echo $"Usage: $0 {start|stop|reload|report|restart|status}"
+           RETVAL=1
+    esac
+    
+    exit $RETVAL
+
+# Slackware
+elif [ -f /etc/slackware-version ] ; then
+    
+# Source configuration file.  This should define the shell variable smartd_opts.
+# Email smartmontools-support@lists.sourceforge.net if there is a better choice
+# of path for Slackware.
+
+    [ -r /etc/sysconfig/smartmontools ] && . /etc/sysconfig/smartmontools
+
+    case "$1" in
+       start)
+           echo -n "Starting smartd: "
+           $SMARTD_BIN $smartd_opts
+           echo
+           ;;
+       stop)
+           echo -n "Shutting down smartd: "
+           killall $SMARTD_BIN
+           echo
+           ;;
+       restart)
+           $0 stop
+           sleep 1
+           $0 start
+           ;;
+       *)
+           echo "Usage: smartd {start|stop|restart}"
+           exit 1
+    esac
+    
+    exit 0
+    
+# SuSE
+elif [ -f /etc/SuSE-release ] ; then
+    test -x $SMARTD_BIN || exit 5
+    
+    # Existence of config file is optional
+    SMARTD_CONFIG=/etc/smartd.conf
+
+# source configuration file. This should set the shell variable smartd_opts
+    [ -r /etc/default/smartmontools ] && . /etc/default/smartmontools
+
+   # Shell functions sourced from /etc/rc.status:
+   #      rc_check         check and set local and overall rc status
+   #      rc_status        check and set local and overall rc status
+   #      rc_status -v     ditto but be verbose in local rc status
+   #      rc_status -v -r  ditto and clear the local rc status
+   #      rc_failed        set local and overall rc status to failed
+   #      rc_reset         clear local rc status (overall remains)
+   #      rc_exit          exit appropriate to overall rc status
+    . /etc/rc.status
+    
+   # First reset status of this service
+    rc_reset
+    
+   # Return values acc. to LSB for all commands but status:
+   # 0 - success
+   # 1 - misc error
+   # 2 - invalid or excess args
+   # 3 - unimplemented feature (e.g. reload)
+   # 4 - insufficient privilege
+   # 5 - program not installed
+   # 6 - program not configured
+   #
+   # Note that starting an already running service, stopping
+   # or restarting a not-running service as well as the restart
+   # with force-reload (in case signalling is not supported) are
+   # considered a success.
+    case "$1" in
+       start)
+           echo -n "Starting smartd"
+            ## Start daemon with startproc(8). If this fails
+            ## the echo return value is set appropriate.
+           
+            # startproc should return 0, even if service is
+            # already running to match LSB spec.
+            startproc $SMARTD_BIN $smartd_opts
+           
+            # Remember status and be verbose
+            rc_status -v
+           ;;
+       stop)
+           echo -n "Shutting down smartd"
+            killproc -TERM $SMARTD_BIN
+           
+            # Remember status and be verbose
+            rc_status -v
+           ;;
+       restart | force-reload)
+           $0 stop
+           $0 start
+           ;;
+       reload)
+       ## Like force-reload, but if daemon does not support
+       ## signaling, do nothing (!)
+           rc_failed 3
+           rc_status -v
+           ;;
+        status)
+            echo -n "Checking for service smartd: "
+            ## Check status with checkproc(8), if process is running
+            ## checkproc will return with exit status 0.
+           
+            # Status has a slightly different for the status command:
+            # 0 - service running
+            # 1 - service dead, but /var/run/  pid  file exists
+            # 2 - service dead, but /var/lock/ lock file exists
+            # 3 - service not running
+           
+            # NOTE: checkproc returns LSB compliant status values.
+            checkproc $SMARTD_BIN
+            rc_status -v
+            ;;
+        probe)
+           ## Optional: Probe for the necessity of a reload, print out the
+           ## argument to this init script which is required for a reload.
+           ## Note: probe is not (yet) part of LSB (as of 1.2)
+
+           test $SMARTD_CONFIG -nt /var/run/smartd.pid && echo reload
+           ;;
+       *)
+           echo "Usage: $0 {start|stop|status|restart|force-reload|reload|probe}"
+           exit 1
+           ;;
+    esac
+    
+    rc_exit
+
+# Debian case
+elif [ -f /etc/debian_version ] ; then
+        PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+       SMARTDPID=/var/run/smartd.pid
+       [ -x $SMARTD_BIN ] || exit 0
+       RET=0
+
+# source configuration file
+       [ -r /etc/default/smartmontools ] && . /etc/default/smartmontools
+
+       smartd_opts="--pidfile $SMARTDPID $smartd_opts"
+       
+       case "$1" in
+       start)
+               echo -n "Starting S.M.A.R.T. daemon: smartd"
+               if start-stop-daemon --start --quiet --pidfile $SMARTDPID \
+               --exec $SMARTD_BIN -- $smartd_opts; then 
+                       echo "."
+               else
+                       echo " (failed)"
+                       RET=1
+               fi
+       ;;
+       stop)
+               echo -n "Stopping S.M.A.R.T. daemon: smartd"
+               start-stop-daemon --stop --quiet --oknodo --pidfile $SMARTDPID
+               echo "."
+       ;;
+       restart|force-reload)
+                       $0 stop
+               $0 start
+               ;;
+       *)
+               echo "Usage: /etc/init.d/smartmontools {start|stop|restart|force-reload}"
+               exit 1
+       esac
+       exit $RET
+
+elif [ -f /etc/gentoo-release ] ; then
+    report_unsupported "Gentoo"
+
+elif [ -f /etc/turbolinux-release ] ; then
+    report_unsupported "Turbolinux"
+
+elif [ -f /etc/environment.corel ] ; then
+    report_unsupported "Corel"
+
+# PLEASE ADD OTHER LINUX DISTRIBUTIONS JUST BEFORE THIS LINE, USING elif
+
+elif uname -a | grep FreeBSD > /dev/null 2>&1 ; then
+# following is replaced by port install
+    PREFIX=@@PREFIX@@
+    
+# Updated to try both the RCNG version of things from 5.x, or fallback to
+# oldfashioned rc.conf
+
+    if [ -r /etc/rc.subr ]; then 
+# This is RC-NG, pick up our values
+       . /etc/rc.subr
+        name="smartd"
+       rcvar="smartd_enable" 
+        command="$SMARTD_BIN"
+       load_rc_config $name
+    elif [ -r /etc/defaults/rc.conf ]; then
+# Not a 5.x system, try the default location for variables
+       . /etc/defaults/rc.conf
+       source_rc_confs
+    elif [ -r /etc/rc.conf ]; then
+# Worst case, fallback to system config file
+       . /etc/rc.conf
+    fi
+
+    if [ -r /etc/rc.subr ]; then 
+# Use new functionality from RC-NG
+       run_rc_command "$1"
+    else
+       PID_FILE=/var/run/smartd.pid
+       case "$1" in
+           start)
+               $SMARTD_BIN -p $PID_FILE $smartd_flags
+               echo -n " smartd"
+               ;;
+           stop)
+               kill `cat $PID_FILE`
+               echo -n " smartd"
+               ;;
+           restart)
+               $0 stop
+               sleep 1
+               $0 start
+               ;;
+           *)
+               echo "Usage: smartd {start|stop|restart}"
+               exit 1
+       esac
+       
+       exit 0
+    fi
+elif uname -a | grep SunOS > /dev/null 2>&1 ; then
+    
+# Source configuration file.  This should define the shell variable smartd_opts.
+# Email smartmontools-support@lists.sourceforge.net if there is a better choice
+# of path for Solaris
+
+    [ -r /etc/default/smartmontools ] && . /etc/default/smartmontools
+
+    PID_FILE=/var/run/smartd.pid
+    
+    case "$1" in
+       start)
+           $SMARTD_BIN -p $PID_FILE $smartd_opts
+           echo -n "smartd "
+           ;;
+       stop)
+           [ -f $PID_FILE ] && kill `cat $PID_FILE`
+           echo -n "smartd "
+           ;;
+       restart)
+           $0 stop
+           sleep 1
+           $0 start
+           ;;
+       *)
+           echo "Usage: smartd {start|stop|restart}"
+           exit 1
+    esac
+    
+    exit 0
+
+# Cygwin
+elif uname | grep -i CYGWIN > /dev/null 2>&1 ; then
+
+# The following settings may be changed by the configuration file below
+    # Service Name (must be unique)
+    smartd_svcname=smartd
+    # Service display name
+    smartd_svcdisp="CYGWIN smartd"
+    # Service description
+    smartd_svcdesc="\
+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. \
+http://smartmontools.sourceforge.net/"
+
+# Source configuration file.  This should define the shell variable smartd_opts.
+# Email smartmontools-support@lists.sourceforge.net if there is a better choice
+# of path for Cygwin
+
+    [ -r /etc/sysconfig/smartmontools ] && . /etc/sysconfig/smartmontools
+
+    PID_FILE=/var/run/smartd.pid
+    RETVAL=0
+
+    # Note: "[ -r $PID_FILE ]" is not used here. On Cygwin, this command may
+    # return success even if the file is present but cannot be read by current user.
+    # If smartd is running as service, smartd.pid is owned by local system account
+    # which is different from any user ever executing this script.
+
+    case "$1" in
+        start)
+            if cygrunsrv -L 2>/dev/null | grep "^${smartd_svcname}$" >/dev/null 2>&1; then
+                echo -n "Starting service $smartd_svcname: "
+                cygrunsrv -S "$smartd_svcname"
+            else
+                echo -n "Starting smartd as daemon: "
+                $SMARTD_BIN -p $PID_FILE $smartd_opts
+            fi
+            RETVAL=$?
+            ;;
+        stop)
+            echo -n "Shutting down smartd: "
+            pid="`cat $PID_FILE 2>/dev/null`" && kill "$pid"
+            RETVAL=$?
+            ;;
+        reload)
+            echo -n "Reloading smartd configuration: "
+            pid="`cat $PID_FILE 2>/dev/null`" && kill -HUP "$pid"
+            RETVAL=$?
+            ;;
+        report)
+            echo -n "Checking SMART devices now: "
+            pid="`cat $PID_FILE 2>/dev/null`" && kill -USR1 "$pid"
+            RETVAL=$?
+            ;;
+        restart)
+            $0 stop
+            sleep 1
+            $0 start
+            exit $?
+            ;;
+        install)
+            shift
+            [ $# -eq 0 ] || smartd_opts="$*"
+            dep=
+            if cygrunsrv -L 2>/dev/null | grep "^syslogd$" >/dev/null 2>&1; then
+                dep="-y syslogd"
+            else
+                echo "Warning: syslogd service not installed, smartd will write to windows event log.";
+            fi
+            echo "Installing service ${smartd_svcname}${smartd_opts+ with options '$smartd_opts'}:"
+            cygrunsrv -I "$smartd_svcname" -d "$smartd_svcdisp"  -f "$smartd_svcdesc" $dep \
+              -e CYGWIN="$CYGWIN" -p $SMARTD_BIN -a "--service -p ${PID_FILE}${smartd_opts+ }$smartd_opts"
+            RETVAL=$?
+            ;;
+        remove)
+            echo "Removing service $smartd_svcname:"
+            cygrunsrv -R "$smartd_svcname"
+            RETVAL=$?
+            ;;
+        status)
+            echo -n "Checking smartd status: "
+            if cygrunsrv -L 2>/dev/null | grep "^${smartd_svcname}$" >/dev/null 2>&1; then
+                if cygrunsrv -Q "$smartd_svcname" 2>/dev/null | grep "State *: Running" >/dev/null 2>&1; then
+                    echo "running as service '$smartd_svcname'."
+                elif ps -e 2>/dev/null | grep " ${SMARTD_BIN}$" >/dev/null 2>&1; then
+                    echo "installed as service '$smartd_svcname' but running as daemon."
+                else
+                    echo "installed as service '$smartd_svcname' but not running."
+                    RETVAL=1
+                fi
+            elif ps -e 2>/dev/null | grep " ${SMARTD_BIN}$" >/dev/null 2>&1; then
+                echo "running as daemon."
+            else
+                echo "not running."
+                RETVAL=1
+            fi
+            exit $RETVAL
+            ;;
+        *)
+            echo "Usage: $0 {start|stop|restart|reload|report|status}"
+            echo "       $0 {install [options]|remove}"
+            exit 1
+    esac
+
+    if [ "$RETVAL" -eq 0 ]; then echo "done"; else echo "ERROR"; fi
+    exit $RETVAL
+
+# Add other OSes HERE, using elif...
+else
+    report_unsupported "Unknown"
+fi
+
+# One should NEVER arrive here, except for a badly written case above,
+# that fails to exit.  
+echo "SOMETHING IS WRONG WITH THE SMARTD STARTUP SCRIPT"
+echo "PLEASE CONTACT smartmontools-support@lists.sourceforge.net"
+exit 1
diff --git a/smartmontools.spec b/smartmontools.spec
new file mode 100644 (file)
index 0000000..35816d2
--- /dev/null
@@ -0,0 +1,1821 @@
+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.36
+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 redhat      %(test ! -f /etc/redhat-release ; echo $?)
+%define redhat      %(test ! -f /etc/fedora-release ; echo $?)
+%define mandrake    %(test ! -f /etc/mandrake-release ; echo $?)
+%define suse        %(test ! -f /etc/SuSE-release ; echo $?)
+
+# Source code can be found at:
+# http://ftp1.sourceforge.net/smartmontools/smartmontools-%{version}-%{release}.tar.gz
+
+# CVS ID of this file is:
+# $Id: smartmontools.spec,v 1.167 2006/04/12 17:39:32 ballen4705 Exp $
+
+# Copyright (C) 2002-6 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
+# [SS] Sergey Svishchev
+# [PW] Phil Williams
+# [LW] Leon Woestenberg
+# [RZ] Richard Zybert
+
+
+%changelog
+* Wed Apr 12 2006 Bruce Allen  <smartmontools-support@lists.sourceforge.net>
+  [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
+
diff --git a/utility.c b/utility.c
new file mode 100644 (file)
index 0000000..19c9f80
--- /dev/null
+++ b/utility.c
@@ -0,0 +1,681 @@
+/*
+ * utility.c
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+// THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO
+// BOTH SCSI AND ATA DEVICES, AND THAT MAY BE USED IN SMARTD,
+// SMARTCTL, OR BOTH.
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <stdarg.h>
+#include <sys/stat.h>
+#ifdef _WIN32
+#include <mbstring.h> // _mbsinc()
+#endif
+
+#include "config.h"
+#include "int64.h"
+#include "utility.h"
+
+// Any local header files should be represented by a CVSIDX just below.
+const char* utility_c_cvsid="$Id: utility.c,v 1.61 2006/04/12 14:54:28 ballen4705 Exp $"
+CONFIG_H_CVSID INT64_H_CVSID UTILITY_H_CVSID;
+
+const char * packet_types[] = {
+        "Direct-access (disk)",
+        "Sequential-access (tape)",
+        "Printer",
+        "Processor",
+        "Write-once (optical disk)",
+        "CD/DVD",
+        "Scanner",
+        "Optical memory (optical disk)",
+        "Medium changer",
+        "Communications",
+        "Graphic arts pre-press (10)",
+        "Graphic arts pre-press (11)",
+        "Array controller",
+        "Enclosure services",
+        "Reduced block command (simplified disk)",
+        "Optical card reader/writer"
+};
+
+// Whenever exit() status is EXIT_BADCODE, please print this message
+const char *reportbug="Please report this bug to the Smartmontools developers at " PACKAGE_BUGREPORT ".\n";
+
+
+// hang on to exit code, so we can make use of more generic 'atexit()'
+// functionality and still check our exit code
+int exitstatus = 0;
+
+// command-line argument: are we running in debug mode?.
+unsigned char debugmode = 0;
+
+
+// Solaris only: Get site-default timezone. This is called from
+// UpdateTimezone() when TZ environment variable is unset at startup.
+#if defined (__SVR4) && defined (__sun)
+static const char *TIMEZONE_FILE = "/etc/TIMEZONE";
+
+static char *ReadSiteDefaultTimezone(){
+  FILE *fp;
+  char buf[512], *tz;
+  int n;
+
+  tz = NULL;
+  fp = fopen(TIMEZONE_FILE, "r");
+  if(fp == NULL) return NULL;
+  while(fgets(buf, sizeof(buf), fp)) {
+    if (strncmp(buf, "TZ=", 3))    // searches last "TZ=" line
+      continue;
+    n = strlen(buf) - 1;
+    if (buf[n] == '\n') buf[n] = 0;
+    if (tz) free(tz);
+    tz = strdup(buf);
+  }
+  fclose(fp);
+  return tz;
+}
+#endif
+
+// Make sure that this executable is aware if the user has changed the
+// time-zone since the last time we polled devices. The cannonical
+// example is a user who starts smartd on a laptop, then flies across
+// time-zones with a laptop, and then changes the timezone, WITHOUT
+// restarting smartd. This is a work-around for a bug in
+// GLIBC. Yuk. See bug number 48184 at http://bugs.debian.org and
+// thanks to Ian Redfern for posting a workaround.
+
+// Please refer to the smartd manual page, in the section labeled LOG
+// TIMESTAMP TIMEZONE.
+void FixGlibcTimeZoneBug(){
+#if __GLIBC__  
+  if (!getenv("TZ")) {
+    putenv("TZ=GMT");
+    tzset();
+    putenv("TZ");
+    tzset();
+  }
+#elif _WIN32
+  if (!getenv("TZ")) {
+    putenv("TZ=GMT");
+    tzset();
+    putenv("TZ=");  // empty value removes TZ, putenv("TZ") does nothing
+    tzset();
+  }
+#elif defined (__SVR4) && defined (__sun)
+  // In Solaris, putenv("TZ=") sets null string and invalid timezone.
+  // putenv("TZ") does nothing.  With invalid TZ, tzset() do as if
+  // TZ=GMT.  With TZ unset, /etc/TIMEZONE will be read only _once_ at
+  // first tzset() call.  Conclusion: Unlike glibc, dynamic
+  // configuration of timezone can be done only by changing actual
+  // value of TZ environment value.
+  enum tzstate { NOT_CALLED_YET, USER_TIMEZONE, TRACK_TIMEZONE };
+  static enum tzstate state = NOT_CALLED_YET;
+
+  static struct stat prev_stat;
+  static char *prev_tz;
+  struct stat curr_stat;
+  char *curr_tz;
+
+  if(state == NOT_CALLED_YET) {
+    if(getenv("TZ")) {
+      state = USER_TIMEZONE; // use supplied timezone
+    } else {
+      state = TRACK_TIMEZONE;
+      if(stat(TIMEZONE_FILE, &prev_stat)) {
+       state = USER_TIMEZONE;  // no TZ, no timezone file; use GMT forever
+      } else {
+       prev_tz = ReadSiteDefaultTimezone(); // track timezone file change
+       if(prev_tz) putenv(prev_tz);
+      }
+    }
+    tzset();
+  } else if(state == TRACK_TIMEZONE) {
+    if(stat(TIMEZONE_FILE, &curr_stat) == 0
+       && (curr_stat.st_ctime != prev_stat.st_ctime
+           || curr_stat.st_mtime != prev_stat.st_mtime)) {
+      // timezone file changed
+      curr_tz = ReadSiteDefaultTimezone();
+      if(curr_tz) {
+       putenv(curr_tz);
+       if(prev_tz) free(prev_tz);
+       prev_tz = curr_tz; prev_stat = curr_stat; 
+      }
+    }
+    tzset();
+  }
+#endif
+  // OTHER OS/LIBRARY FIXES SHOULD GO HERE, IF DESIRED.  PLEASE TRY TO
+  // KEEP THEM INDEPENDENT.
+  return;
+}
+
+#ifdef _WIN32
+// Fix strings in tzname[] to avoid long names with non-ascii characters.
+// If TZ is not set, tzset() in the MSVC runtime sets tzname[] to the
+// national language timezone names returned by GetTimezoneInformation().
+static char * fixtzname(char * dest, int destsize, const char * src)
+{
+  int i = 0, j = 0;
+  while (src[i] && j < destsize-1) {
+    int i2 = (const char *)_mbsinc((const unsigned char *)src+i) - src;
+    if (i2 > i+1)
+      i = i2; // Ignore multibyte chars
+    else {
+      if ('A' <= src[i] && src[i] <= 'Z')
+        dest[j++] = src[i]; // "Pacific Standard Time" => "PST"
+      i++;
+    }
+  }
+  if (j < 2)
+    j = 0;
+  dest[j] = 0;
+  return dest;
+}
+#endif // _WIN32
+
+// This value follows the peripheral device type value as defined in
+// SCSI Primary Commands, ANSI INCITS 301:1997.  It is also used in
+// the ATA standard for packet devices to define the device type.
+const char *packetdevicetype(int type){
+  if (type<0x10)
+    return packet_types[type];
+  
+  if (type<0x20)
+    return "Reserved";
+  
+  return "Unknown";
+}
+
+
+// Returns 1 if machine is big endian, else zero.  This is a run-time
+// rather than a compile-time function.  We could do it at
+// compile-time but in principle there are architectures that can run
+// with either byte-ordering.
+int isbigendian(){
+  short i=0x0100;
+  char *tmp=(char *)&i;
+  return *tmp;
+}
+
+// Utility function prints date and time and timezone into a character
+// buffer of length>=64.  All the fuss is needed to get the right
+// timezone info (sigh).
+void dateandtimezoneepoch(char *buffer, time_t tval){
+  struct tm *tmval;
+  char *timezonename;
+  char datebuffer[DATEANDEPOCHLEN];
+  int lenm1;
+#ifdef _WIN32
+  char tzfixbuf[6+1];
+#endif
+
+  FixGlibcTimeZoneBug();
+  
+  // Get the time structure.  We need this to determine if we are in
+  // daylight savings time or not.
+  tmval=localtime(&tval);
+  
+  // Convert to an ASCII string, put in datebuffer
+  // same as: asctime_r(tmval, datebuffer);
+  strncpy(datebuffer, asctime(tmval), DATEANDEPOCHLEN);
+  datebuffer[DATEANDEPOCHLEN-1]='\0';
+  
+  // Remove newline
+  lenm1=strlen(datebuffer)-1;
+  datebuffer[lenm1>=0?lenm1:0]='\0';
+  
+  // correct timezone name
+  if (tmval->tm_isdst==0)
+    // standard time zone
+    timezonename=tzname[0];
+  else if (tmval->tm_isdst>0)
+    // daylight savings in effect
+    timezonename=tzname[1];
+  else
+    // unable to determine if daylight savings in effect
+    timezonename="";
+
+#ifdef _WIN32
+  // Fix long non-ascii timezone names
+  if (!getenv("TZ"))
+    timezonename=fixtzname(tzfixbuf, sizeof(tzfixbuf), timezonename);
+#endif
+  
+  // Finally put the information into the buffer as needed.
+  snprintf(buffer, DATEANDEPOCHLEN, "%s %s", datebuffer, timezonename);
+  
+  return;
+}
+
+// Date and timezone gets printed into string pointed to by buffer
+void dateandtimezone(char *buffer){
+  
+  // Get the epoch (time in seconds since Jan 1 1970)
+  time_t tval=time(NULL);
+  
+  dateandtimezoneepoch(buffer, tval);
+  return;
+}
+
+// These are two utility functions for printing CVS IDs. Massagecvs()
+// returns distance that it has moved ahead in the input string
+int massagecvs(char *out, const char *cvsid){
+  char *copy,*filename,*date,*version;
+  int retVal=0;
+  const char delimiters[] = " ,$";
+
+  // make a copy on the heap, go to first token,
+  if (!(copy=strdup(cvsid)))
+    return 0;
+
+  if (!(filename=strtok(copy, delimiters)))
+    goto endmassage;
+
+  // move to first instance of "Id:"
+  while (strcmp(filename,"Id:"))
+    if (!(filename=strtok(NULL, delimiters)))
+      goto endmassage;
+  
+  // get filename, skip "v", get version and date
+  if (!(  filename=strtok(NULL, delimiters)  ) ||
+      !(           strtok(NULL, delimiters)  ) ||
+      !(   version=strtok(NULL, delimiters)  ) ||
+      !(      date=strtok(NULL, delimiters)  ) )
+    goto endmassage;
+  
+  sprintf(out,"%-16s revision: %-5s date: %-15s", filename, version, date);
+  retVal = (date-copy)+strlen(date);
+  
+ endmassage:
+  free(copy);
+  return retVal;
+}
+
+// prints a single set of CVS ids
+void printone(char *block, const char *cvsid){
+  char strings[CVSMAXLEN];
+  const char *here=cvsid;
+  int bi=0, len=strlen(cvsid)+1;
+
+  // check that the size of the output block is sufficient
+  if (len>=CVSMAXLEN) {
+    pout("CVSMAXLEN=%d must be at least %d\n",CVSMAXLEN,len+1);
+    EXIT(1);
+  }
+
+  // loop through the different strings
+  while (bi<CVSMAXLEN && (len=massagecvs(strings,here))){
+    bi+=snprintf(block+bi,CVSMAXLEN-bi,"%s %s\n",(bi==0?"Module:":"  uses:"),strings);
+    here+=len;
+  }
+  return;
+}
+
+
+// A replacement for perror() that sends output to our choice of
+// printing. If errno not set then just print message.
+void syserror(const char *message){
+  
+  if (errno) {
+    // Get the correct system error message:
+    const char *errormessage=strerror(errno);
+    
+    // Check that caller has handed a sensible string, and provide
+    // appropriate output. See perrror(3) man page to understand better.
+    if (message && *message)
+      pout("%s: %s\n",message, errormessage);
+    else
+      pout("%s\n",errormessage);
+  }
+  else if (message && *message)
+    pout("%s\n",message);
+  
+  return;
+}
+
+// Prints a warning message for a failed regular expression compilation from
+// regcomp().
+void printregexwarning(int errcode, regex_t *compiled){
+  size_t length = regerror(errcode, compiled, NULL, 0);
+  char *buffer = malloc(length);
+  if (!buffer){
+    pout("Out of memory in printregexwarning()\n");
+    return;
+  }
+  regerror(errcode, compiled, buffer, length);
+  pout("%s\n", buffer);
+  free(buffer);
+  return;
+}
+
+// POSIX extended regular expressions interpret unmatched ')' ordinary:
+// "The close-parenthesis shall be considered special in this context
+//  only if matched with a preceding open-parenthesis."
+//
+// Actual '(...)' nesting errors remain undetected on strict POSIX
+// implementations (glibc) but an error is reported on others (Cygwin).
+// 
+// The check below is rather incomplete because it does not handle
+// e.g. '\)' '[)]'.
+// But it should work for the regex subset used in drive database.
+static int check_regex_nesting(const char * pattern)
+{
+  int level = 0, i;
+  for (i = 0; pattern[i] && level >= 0; i++) {
+    switch (pattern[i]) {
+      case '(': level++; break;
+      case ')': level--; break;
+    }
+  }
+  return level;
+}
+
+// A wrapper for regcomp().  Returns zero for success, non-zero otherwise.
+int compileregex(regex_t *compiled, const char *pattern, int cflags)
+{ 
+  int errorcode;
+
+  if (   (errorcode = regcomp(compiled, pattern, cflags))
+      || check_regex_nesting(pattern) < 0                ) {
+    pout("Internal error: unable to compile regular expression \"%s\" ", pattern);
+    if (errorcode)
+      printregexwarning(errorcode, compiled);
+    else
+      pout("Unmatched ')'\n");
+    pout("Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n");
+    return 1;
+  }
+  return 0;
+}
+
+// Splits an argument to the -r option into a name part and an (optional) 
+// positive integer part.  s is a pointer to a string containing the
+// argument.  After the call, s will point to the name part and *i the
+// integer part if there is one or 1 otherwise.  Note that the string s may
+// be changed by this function.  Returns zero if successful and non-zero
+// otherwise.
+int split_report_arg(char *s, int *i)
+{
+  if ((s = strchr(s, ','))) {
+    // Looks like there's a name part and an integer part.
+    char *tailptr;
+
+    *s++ = '\0';
+    if (*s == '0' || !isdigit((int)*s))  // The integer part must be positive
+      return 1;
+    errno = 0;
+    *i = (int) strtol(s, &tailptr, 10);
+    if (errno || *tailptr != '\0')
+      return 1;
+  } else {
+    // There's no integer part.
+    *i = 1;
+  }
+
+  return 0;
+}
+
+// same as above but sets *i to -1 if missing , argument
+int split_report_arg2(char *s, int *i){
+  char *tailptr;
+  s+=6;
+
+  if (*s=='\0' || !isdigit((int)*s)) { 
+    // What's left must be integer
+    *i=-1;
+    return 1;
+  }
+
+  errno = 0;
+  *i = (int) strtol(s, &tailptr, 10);
+  if (errno || *tailptr != '\0') {
+    *i=-1;
+    return 1;
+  }
+
+  return 0;
+}
+
+#ifndef HAVE_STRTOULL
+// Replacement for missing strtoull() (Linux with libc < 6, MSVC 6.0)
+// Functionality reduced to split_selective_arg()'s requirements.
+
+static uint64_t strtoull(const char * p, char * * endp, int base)
+{
+  uint64_t result, maxres;
+  int i = 0;
+  char c = p[i++];
+  // assume base == 0
+  if (c == '0') {
+    if (p[i] == 'x' || p[i] == 'X') {
+      base = 16; i++;
+    }
+    else
+      base = 8;
+    c = p[i++];
+  }
+  else
+    base = 10;
+
+  result = 0;
+  maxres = ~(uint64_t)0 / (unsigned)base;
+  for (;;) {
+    unsigned digit;
+    if ('0' <= c && c <= '9')
+      digit = c - '0';
+    else if ('A' <= c && c <= 'Z')
+      digit = c - 'A' + 10;
+    else if ('a' <= c && c <= 'z')
+      digit = c - 'a' + 10;
+    else
+      break;
+    if (digit >= (unsigned)base)
+      break;
+    if (!(   result < maxres
+          || (result == maxres && digit <= ~(uint64_t)0 % (unsigned)base))) {
+      result = ~(uint64_t)0; errno = ERANGE; // return on overflow
+      break;
+    }
+    result = result * (unsigned)base + digit;
+    c = p[i++];
+  }
+  *endp = (char *)p + i - 1;
+  return result;
+}
+#endif // HAVE_STRTOLL
+
+// Splits an argument to the -t option that is assumed to be of the form
+// "selective,%lld-%lld" (prefixes of "0" (for octal) and "0x"/"0X" (for hex)
+// are allowed).  The first long long int is assigned to *start and the second
+// to *stop.  Returns zero if successful and non-zero otherwise.
+int split_selective_arg(char *s, uint64_t *start,
+                        uint64_t *stop)
+{
+  char *tailptr;
+
+  if (!(s = strchr(s, ',')))
+    return 1;
+  if (!isdigit((int)(*++s)))
+    return 1;
+  errno = 0;
+  // Last argument to strtoull (the base) is 0 meaning that decimal is assumed
+  // unless prefixes of "0" (for octal) or "0x"/"0X" (for hex) are used.
+  *start = strtoull(s, &tailptr, 0);
+
+  s = tailptr;
+  if (errno || *s++ != '-')
+    return 1;
+  *stop = strtoull(s, &tailptr, 0);
+  if (errno || *tailptr != '\0')
+    return 1;
+  return 0;
+}
+
+int64_t bytes = 0;
+// Helps debugging.  If the second argument is non-negative, then
+// decrement bytes by that amount.  Else decrement bytes by (one plus)
+// length of null terminated string.
+void *FreeNonZero(void *address, int size, int line, const char* file){
+  if (address) {
+    if (size<0)
+      bytes-=1+strlen(address);
+    else
+      bytes-=size;
+    return CheckFree(address, line, file);
+  }
+  return NULL;
+}
+
+// To help with memory checking.  Use when it is known that address is
+// NOT null.
+void *CheckFree(void *address, int whatline, const char* file){
+  if (address){
+    free(address);
+    return NULL;
+  }
+  
+  PrintOut(LOG_CRIT, "Internal error in CheckFree() at line %d of file %s\n%s", 
+           whatline, file, reportbug);
+  EXIT(EXIT_BADCODE);
+}
+
+// A custom version of calloc() that tracks memory use
+void *Calloc(size_t nmemb, size_t size) { 
+  void *ptr=calloc(nmemb, size);
+  
+  if (ptr)
+    bytes+=nmemb*size;
+
+  return ptr;
+}
+
+// A custom version of strdup() that keeps track of how much memory is
+// being allocated. If mustexist is set, it also throws an error if we
+// try to duplicate a NULL string.
+char *CustomStrDup(char *ptr, int mustexist, int whatline, const char* file){
+  char *tmp;
+
+  // report error if ptr is NULL and mustexist is set
+  if (ptr==NULL){
+    if (mustexist) {
+      PrintOut(LOG_CRIT, "Internal error in CustomStrDup() at line %d of file %s\n%s", 
+               whatline, file, reportbug);
+      EXIT(EXIT_BADCODE);
+    }
+    else
+      return NULL;
+  }
+
+  // make a copy of the string...
+  tmp=strdup(ptr);
+  
+  if (!tmp) {
+    PrintOut(LOG_CRIT, "No memory to duplicate string %s at line %d of file %s\n", ptr, whatline, file);
+    EXIT(EXIT_NOMEM);
+  }
+  
+  // and track memory usage
+  bytes+=1+strlen(ptr);
+  
+  return tmp;
+}
+
+// Returns nonzero if region of memory contains non-zero entries
+int nonempty(unsigned char *testarea,int n){
+  int i;
+  for (i=0;i<n;i++)
+    if (testarea[i])
+      return 1;
+  return 0;
+}
+
+
+// This routine converts an integer number of milliseconds into a test
+// string of the form Xd+Yh+Zm+Ts.msec.  The resulting text string is
+// written to the array.
+void MsecToText(unsigned int msec, char *txt){
+  int start=0;
+  unsigned int days, hours, min, sec;
+
+  days       = msec/86400000U;
+  msec      -= days*86400000U;
+
+  hours      = msec/3600000U; 
+  msec      -= hours*3600000U;
+
+  min        = msec/60000U;
+  msec      -= min*60000U;
+
+  sec        = msec/1000U;
+  msec      -= sec*1000U;
+
+  if (days) {
+    txt += sprintf(txt, "%2dd+", (int)days);
+    start=1;
+  }
+
+  sprintf(txt, "%02d:%02d:%02d.%03d", (int)hours, (int)min, (int)sec, (int)msec);  
+  return;
+}
+
+
+#ifndef HAVE_WORKING_SNPRINTF
+// Some versions of (v)snprintf() don't append null char on overflow (MSVCRT.DLL),
+// and/or return -1 on overflow (old Linux).
+// Below are sane replacements substituted by #define in utility.h.
+
+#undef vsnprintf
+#if defined(_WIN32) && defined(_MSC_VER)
+#define vsnprintf _vsnprintf
+#endif
+
+int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap)
+{
+  int i;
+  if (size <= 0)
+    return 0;
+  i = vsnprintf(buf, size, fmt, ap);
+  if (0 <= i && i < size)
+    return i;
+  buf[size-1] = 0;
+  return strlen(buf); // Note: cannot detect for overflow, not necessary here.
+}
+
+int safe_snprintf(char *buf, int size, const char *fmt, ...)
+{
+  int i; va_list ap;
+  va_start(ap, fmt);
+  i = safe_vsnprintf(buf, size, fmt, ap);
+  va_end(ap);
+  return i;
+}
+
+#endif
diff --git a/utility.h b/utility.h
new file mode 100644 (file)
index 0000000..7229c5a
--- /dev/null
+++ b/utility.h
@@ -0,0 +1,179 @@
+/*
+ * utility.h
+ *
+ * Home page of code is: http://smartmontools.sourceforge.net
+ *
+ * Copyright (C) 2002-6 Bruce Allen <smartmontools-support@lists.sourceforge.net>
+ * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * (for example COPYING); if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * This code was originally developed as a Senior Thesis by Michael Cornwell
+ * at the Concurrent Systems Laboratory (now part of the Storage Systems
+ * Research Center), Jack Baskin School of Engineering, University of
+ * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ *
+ */
+
+#ifndef UTILITY_H_
+#define UTILITY_H_
+
+#define UTILITY_H_CVSID "$Id: utility.h,v 1.43 2006/04/12 14:54:28 ballen4705 Exp $\n"
+
+#include <time.h>
+#include <sys/types.h> // for regex.h (according to POSIX)
+#include <regex.h>
+
+#ifndef HAVE_WORKING_SNPRINTF
+// Substitute by safe replacement functions
+#include <stdarg.h>
+int safe_snprintf(char *buf, int size, const char *fmt, ...);
+int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap);
+#define snprintf  safe_snprintf
+#define vsnprintf safe_vsnprintf
+#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).
+#define DATEANDEPOCHLEN 64
+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(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);
+// Function for processing -t selective... option in smartctl
+int split_selective_arg(char *s, uint64_t *start, uint64_t *stop);
+
+
+// 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);
+
+// 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);
+
+
+#define EXIT(x)  { exitstatus = (x); exit((x)); }
+
+// replacement for calloc() that tracks memory usage
+void *Calloc(size_t nmemb, size_t size);
+
+// Utility function to free memory
+void *FreeNonZero(void* address, int size, int whatline, const char* file);
+
+// A custom version of strdup() that keeps track of how much memory is
+// being allocated. If mustexist is set, it also throws an error if we
+// try to duplicate a NULL string.
+char *CustomStrDup(char *ptr, int mustexist, int whatline, const char* file);
+
+// To help with memory checking.  Use when it is known that address is
+// NOT null.
+void *CheckFree(void *address, int whatline, const char* file);
+
+// This function prints either to stdout or to the syslog as needed
+
+// [From GLIBC Manual: Since the prototype doesn't specify types for
+// optional arguments, in a call to a variadic function the default
+// argument promotions are performed on the optional argument
+// values. This means the objects of type char or short int (whether
+// signed or not) are promoted to either int or unsigned int, as
+// appropriate.]
+void PrintOut(int priority,char *fmt, ...);
+
+// run time, determine byte ordering
+int isbigendian();
+
+// This value follows the peripheral device type value as defined in
+// SCSI Primary Commands, ANSI INCITS 301:1997.  It is also used in
+// the ATA standard for packet devices to define the device type.
+const char *packetdevicetype(int type);
+
+int deviceopen(const char *pathname, char *type);
+
+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);
+#endif
+
+// returns 1 if any of the n bytes are nonzero, else zero.
+int nonempty(unsigned char *testarea,int n);
+
+// needed to fix glibc bug
+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
+
+
+// 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;}
+
+// possible values for controller_type in extern.h
+#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
+
+
+#endif