]> git.proxmox.com Git - mirror_smartmontools-debian.git/commitdiff
import smartmontools 7.0 master
authorThomas Lamprecht <t.lamprecht@proxmox.com>
Tue, 25 Jun 2019 17:11:05 +0000 (19:11 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Wed, 26 Jun 2019 09:05:43 +0000 (11:05 +0200)
Downloaded source from
https://sourceforge.net/projects/smartmontools/files/smartmontools/7.0/
and imported here to git.

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
112 files changed:
AUTHORS
ChangeLog
INSTALL
Makefile.am
Makefile.in
NEWS
README
aacraid.h
atacmdnames.cpp
atacmdnames.h
atacmds.cpp
atacmds.h
ataidentify.cpp
ataidentify.h
ataprint.cpp
ataprint.h
autogen.sh
cciss.cpp
cciss.h
config.h.in
configure
configure.ac
dev_areca.cpp
dev_areca.h
dev_ata_cmd_set.cpp
dev_ata_cmd_set.h
dev_intelliprop.cpp
dev_intelliprop.h
dev_interface.cpp
dev_interface.h
dev_legacy.cpp
dev_tunnelled.h
drivedb.h
examplescripts/Example6
examplescripts/README
freebsd_nvme_ioctl.h
getopt/bits/getopt_core.h [new file with mode: 0644]
getopt/bits/getopt_ext.h [new file with mode: 0644]
getopt/getopt.c
getopt/getopt.h
getopt/getopt1.c
getopt/getopt_int.h [new file with mode: 0644]
json.cpp [new file with mode: 0644]
json.h [new file with mode: 0644]
knowndrives.cpp
knowndrives.h
megaraid.h
nvmecmds.cpp
nvmecmds.h
nvmeprint.cpp
nvmeprint.h
os_darwin.cpp
os_darwin.h
os_freebsd.cpp
os_freebsd.h
os_generic.cpp
os_generic.h
os_linux.cpp
os_linux.h
os_netbsd.cpp
os_netbsd.h
os_openbsd.cpp
os_openbsd.h
os_os2.cpp
os_os2.h
os_qnxnto.cpp
os_qnxnto.h
os_solaris.cpp
os_solaris.h
os_win32.cpp
os_win32/daemon_win32.cpp
os_win32/daemon_win32.h
os_win32/installer.nsi
os_win32/popen.h [new file with mode: 0644]
os_win32/popen_win32.cpp [new file with mode: 0644]
os_win32/runcmd.c
os_win32/smartd_mailer.ps1
os_win32/smartd_warning.cmd
os_win32/syslog.h
os_win32/syslog_win32.cpp
os_win32/syslogevt.mc
os_win32/update-smart-drivedb.nsi
os_win32/wmiquery.cpp
os_win32/wmiquery.h
os_win32/wtssendmsg.c
regex/regcomp.c
regex/regex.c
regex/regex.h
regex/regex_internal.c
regex/regex_internal.h
regex/regexec.c
scsiata.cpp
scsicmds.cpp
scsicmds.h
scsinvme.cpp [new file with mode: 0644]
scsiprint.cpp
scsiprint.h
sg_unaligned.h [new file with mode: 0644]
smartctl.8.in
smartctl.cpp
smartctl.h
smartd.8.in
smartd.conf.5.in
smartd.cpp
smartd.cygwin.initd.in [new file with mode: 0644]
smartd.initd.in
smartd.service.in
smartd_warning.sh.in
update-smart-drivedb.8.in
update-smart-drivedb.in
utility.cpp
utility.h

diff --git a/AUTHORS b/AUTHORS
index 1b05618ac49cda23522080d9d76e2b00863925bc..81fe45e8fc30b8249547cd10831bb2520a9b7280 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,4 +1,4 @@
-$Id: AUTHORS 4403 2017-03-09 20:31:15Z chrfranke $
+$Id: AUTHORS 4844 2018-12-02 16:56:12Z chrfranke $
 
 Developers / Maintainers / Contributors:
 
@@ -25,9 +25,11 @@ Song Liu                <songliubraving@fb.com>
 Dan Lukes               <dan+smartmontools.changelog@obluda.cz>
 Kai Mäkisara            <kai.makisara@kolumbus.fi>
 Nidhi Malhotra          <nidhi.malhotra@pmcs.com>
+Harry Mallon            <hjmallon@gmail.com>
 Eduard Martinescu       <martines@rochester.rr.com>
 Frédéric L. W. Meunier  <...>
 Kimihiro Nonaka         <...>
+Gabriele Pohl           <contact@dipohl.de>
 Alex Samorukov          <samm@os2.kiev.ua>
 Keiji Sawada            <card_captor@users.sourceforge.net>
 Manfred Schwarb         <manfred99@gmx.ch>
index 3f56c98b0db87521547c456a989c0dfd0e180e94..df34cf2aa948cdc2fbcd349d3528f06a37fde019 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,895 @@
-$Id: ChangeLog 4594 2017-11-05 15:21:35Z chrfranke $
+$Id: ChangeLog 4883 2018-12-30 14:48:54Z chrfranke $
+
+2018-12-30  Christian Franke  <franke@computer.org>
+
+       smartmontools 7.0
+
+2018-12-29  Christian Franke  <franke@computer.org>
+
+       smartctl.8.in: Remove extra quote.
+
+       INSTALL: Update or remove various outdated info.
+
+2018-12-28  Christian Franke  <franke@computer.org>
+
+       configure.ac: Set drivedb.h branch to 7.0.
+       update-smart-drivedb.in: Update public key block.
+       update-smart-drivedb.8.in: Update key ID.
+
+       Create new branch RELEASE_7_0_DRIVEDB.
+       Sign drivedb.h using new key ID 721042C5.
+
+2018-12-27  Christian Franke  <franke@computer.org>
+
+       do_release: Add quotes to AC_INIT regex.
+
+       configure.ac: Update PACKAGE_HOMEPAGE.
+
+       configure.ac: Set release number to 7.0
+
+       smartctl.cpp: Set JSON format version to 1.0 (#766).
+
+       scsiprint.cpp: Omit JSON values for unavailable counters from
+       Format Status log page.  This ensures that each JSON value always
+       has the same type.
+
+       drivedb.h:
+       - SandForce Driven SSDs: Kingston E50 (#756)
+       - WDC HGST Ultrastar He10 (#959, #997, #1093, #1111)
+       - Toshiba 2.5" HDD MQ04UBF... (USB 3.0) (#937)
+       - Seagate Barracuda 7200.10: HP OEM 160GB (#1037)
+       - Seagate Constellation ES.3: HP OEM 4TB
+       - Seagate Exos 5E8 (#1058)
+       - Seagate IronWolf Pro (#1076, GH issues/10, GH issues/14)
+       - WD Blue and Green SSDs: Rename, add Green (#980, #1073)
+
+2018-12-20  Donald Pierce  <...>
+
+       drivedb.h:
+       - Dell Certified Intel S3520 Series SSDs (#1147)
+       - Dell Certified Intel S4x00/D3-S4x10 Series SSDs (#1148)
+
+2018-12-20  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - SandForce Driven SSDs: Kingston HyperX Fury (#805)
+       - Phison Driven SSDs: PNY CS2211 (#992)
+       - JMicron based SSDs: ADATA SX390 (#818),
+         KingSpec KDM-SA.51-008GMJ (#741)
+       - SiliconMotion based SSDs: KingSpec KSD, KingSpec T60,
+         Team Group L5Lite 3D (#1144), Transcend ESD400
+       - USB: Transcend ESD400 (0x2174:0x2000)
+
+       smartd.cpp: Remove unneeded '.c_str()' call.  Update a comment.
+
+       configure.ac: Use AS_HELP_STRING instead of AC_HELP_STRING
+       as suggested by autoupdate.  Add missing check for 'enableval'.
+
+2018-12-16  Christian Franke  <franke@computer.org>
+
+       smartd.8.in: Don't use empty lines before '.SH' macros.
+
+       smartd.cpp: [_WIN32] Remove check for '-m [sys]msgbox'.
+
+       nvmeprint.cpp: Don't print NSID in SMART/Health Information title
+       line.  This log is always read with broadcast NSID.
+
+2018-12-16  Giuseppe Iuculano  <iuculano@debian.org>
+
+       cciss.cpp: Fix kFreeBSD build (Debian kfreebsd.patch).
+
+       smartd.service.in: Declaring After=syslog.target is unnecessary by
+       now because syslog is socket-activated and will therefore be started
+       when needed (Debian removesyslogtarget.patch).
+
+2018-12-11  Christian Franke  <franke@computer.org>
+
+       smartd.conf.5.in: Update DEVICESCAN info and move it up to a
+       new section.  Add section header for DEFAULT SETTINGS.
+
+       smartctl.8.in, smartd.8.in, smartd.conf.5.in: Remove EXPERIMENTAL
+       notes for features added before 6.5.
+
+       os_linux.cpp: Call realpath() with full /sys/* path instead of
+       device name (GH pull/23).  This fixes detection of hpsa devices
+       (regression from r4603).
+
+2018-12-11  Harry Mallon  <hjmallon@gmail.com>
+
+       scsinvme.cpp: Fix debug message.
+
+2018-12-05  Christian Franke  <franke@computer.org>
+
+       smartctl.8.in, smartd.conf.5.in: Mark '-d sntjmicron' as
+       EXPERIMENTAL.
+
+       drivedb.h: Enable JMicron JMS583 entry, use an internal -d option.
+       scsinvme.cpp: Detect this internal -d option and ask user to test
+       '-d sntjmicron'.
+
+       scsinvme.cpp: Add missing include of config.h.
+
+2018-12-05  Harry Mallon  <hjmallon@gmail.com>
+
+       Add '-d sntjmicron[,NSID]' device type for NVMe drives behind
+       JMicron USB to NVMe bridges (JMS583).
+
+2018-12-04  Christian Franke  <franke@computer.org>
+
+       os_linux.cpp: Add '-d by-id' option to device scanning.
+       If specified, scan '/dev/disk/by-id/*' for symlinks to '/dev/sdX'
+       and remove duplicates.
+
+2018-12-02  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Samsung based SSDs: CM851 (#1109), SM863a (#1140)
+       - SiliconMotion based SSDs: Transcend 420K (GH issues/20),
+         Transcend 630 (#1038)
+       - Western Digital Gold: Re-add 8TB *2 variant
+       - USB: Buffalo HD-PNTU3 (0x0411:0x01e7), HD-LC3 (0x0411:0x027e)
+       - USB: ADATA NH13 (0x125f:0xa13a), HD710P (0x125f:0xa75a)
+       - USB: Verbatim External Hard Drive (0x18a5:0x0408) (#1107)
+
+       AUTHORS: Add Harry Mallon.
+
+2018-12-02  Harry Mallon  <hjmallon@gmail.com>
+
+       drivedb.h: USB: LaCie Rugged Mini HDD (0x059f:0x106b)
+
+       Fix many typos.
+
+       ataprint.cpp: Fix Form Factor string with bits set in reserved area
+       - Happens with APPLE SSD SD0256F
+
+2018-11-27  Christian Franke  <franke@computer.org>
+
+       os_linux.cpp: Add USB ID detection for '/dev/sgN'.
+
+       smartd_warning.sh.in: Fix typo (#1138).
+
+2018-11-27  Harry Mallon  <hjmallon@gmail.com>
+
+       autogen.sh: allow automake 1.16 and 1.16.1.
+
+2018-11-25  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Crucial/Micron BX/MX1/2/3/500, M5/600, 1100 SSDs: Micron 1100
+         alternative ID string (#1131)
+       - SandForce Driven SSDs: Comay BladeDrive E28 (#823),
+         MX-DS FUSION (#900), OCZ Deneva 2 *.C (#1119), OCZ-VERTEX3 LT
+       - Phison Driven SSDs: Kingston A400 with extra space in ID (#801)
+       - Samsung based SSDs: SM951 *HDGM variant (patch from #1113)
+       - SiliconMotion based SSDs: KingDian S400 (#1116)
+       - Western Digital Gold: 1TB, 2TB (#1035, #1047), 8TB (#1033),
+         12TB, attribute 22 "Helium_Level" (patch from #1115)
+
+2018-11-25  Cameron Costa  <cameron.costa@intel.com>
+
+       drivedb.h: Intel S4510 M.2 (#1121, #1122, #1123, #1133)
+
+2018-11-13  Christian Franke  <franke@computer.org>
+
+       os_linux.cpp: Drop device scan support for obsolete devfs.
+       Implement new version of scan_smart_devices().  This avoids
+       duplicates if multiple '-d TYPE' options are specified.
+
+       dev_interface.cpp, dev_interface.h: Add default implementation for
+       old version of scan_smart_devices().
+
+2018-11-02  Oleksii Samorukov  <samm@os2.kiev.ua>
+
+       os_darwin.cpp, os_freebsd.cpp: fix return value in error paths
+       patch provided by rikard.falkeborn (github)
+
+2018-11-02  Christian Franke  <franke@computer.org>
+
+       json.cpp: Allow UTF-8 characters in strings.
+
+       ataprint.cpp: Add JSON support for '-l defects'.
+       Add numeric values to JSON 'interface_speed' info.
+       Replace local 'le*_to_uint()' with 'sg_get_unaligned_le*()'.
+
+       ataprint.cpp, ataprint.h: Remove request to send '-l defects' output.
+       Remove 'pending_defects_info' flag.
+       smartctl.cpp, smartctl.8.in: Add '-l defects' to '-x' output.
+
+2018-10-25  Christian Franke  <franke@computer.org>
+
+       json.cpp, json.h: Add 'pretty' print option.
+       smartctl.cpp, smartctl.8.in: Add '--json=c' option to disable
+       pretty-printing.
+
+       ataprint.cpp, nvmeprint.cpp, smartctl.cpp: Use const references
+       for json::ref function parameters.
+
+       json.cpp, json.h: Clean up usage of 'int64_t' and 'long long'.
+       Use PRI?64 instead of "ll?" in printf() format strings.
+       This re-enables build on older versions of MinGW.
+
+2018-10-23  Christian Franke  <franke@computer.org>
+
+       json.cpp: Remove extra space after JSON key names.
+
+       json.cpp, json.h: Remove return of self reference from operator=().
+
+       json.cpp, json.h: Change handling of unsafe and 128-bit integers:
+       Output as string 'KEY_s' and LE byte array 'KEY_le' if range exceeded
+       or verbose mode enabled.
+       smartctl.cpp, smartctl.8.in: Add '--json=v' option.
+
+2018-10-17  Christian Franke  <franke@computer.org>
+
+       os_win32/popen_win32.cpp, os_win32/popen.h: New popen()/pclose()
+       for Windows.  Unlike MSVCRT _popen(), it does not open a new console.
+       os_win32.cpp: Remove run_cmd(), use popen() instead.
+       os_win32/daemon_win32.cpp, os_win32/daemon_win32.h: Remove
+       daemon_spawn().
+       smartd.cpp: Remove _WIN32 specific usage of daemon_spawn(),
+       use generic code with popen() also on Windows.
+       Place quotes around warning script path on Windows.
+       Makefile.am, os_win32/vc14/smart*.vcxproj*: Add new files.
+
+2018-10-17  Rick Chen  <juihsiang.chen@gmail.com>
+
+       scsiprint.cpp: Add SCSI information to JSON output as below:
+       - Drive trip temperature (#1079)
+       - Error counter log read/write/verify (#1079)
+       - Grown defect list (#1082)
+       - Percentage used endurance indicator (#1083)
+
+2018-10-14  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Crucial/Micron BX/MX1/2/3/500, M5/600, 1100 SSDs: MX500 M.2
+       - Samsung based SSDs: Samsung SM841 (#1043), PM841 (#1052),
+         Samsung 860 EVO (#1034, #1040, #1051, #1059),
+         Samsung 860 PRO (#1010, #1068, #1102, #1103, #1104),
+         Samsung Portable SSD T5 (#1050)
+       - USB: Samsung Portable SSD T5 (0x04e8:0x61f5) (#1050)
+
+       os_darwin.cpp: Add missing braces to SMART RETURN STATUS LBA register
+       setting.  Detected by g++ 7.3 -Wmisleading-indentation.
+
+2018-10-11  Christian Franke  <franke@computer.org>
+
+       os_win32.cpp: Decode Windows 10 1809 and Server 2019 build number.
+       Move "(64)" to end of version info.
+
+       os_linux.cpp: Fix '-d megaraid' open crash on missing /proc/devices.
+       There is no /proc/devices on ESXi (see #800) and WSL.
+
+2018-10-09  Christian Franke  <franke@computer.org>
+
+       smartd.cpp: Move code for '--capabilities' to separate functions.
+
+       smartd.cpp: Rework main loop.
+
+       smartctl.cpp, smartd.cpp, os_linux.cpp, os_solaris.cpp:
+       Replace all uses of EXIT() macro.  Use early return where possible,
+       use throw otherwise.
+       utility.h: Remove EXIT() macro.
+
+       utility.cpp: Detect more C++ language versions for -V option.
+
+       drivedb.h:
+       - Crucial/Micron BX/MX1/2/3/500, M5/600, 1100 SSDs: Rename,
+         BX500 (#1095)
+       - Seagate Samsung SpinPoint F4 EG (AF) (#1090)
+       - Seagate Momentus 5400.6: Add '-F xerrorlba' (#1094)
+       - USB: JMicron JM562 (0x152d:0x0562) (IDENTIFY only, see #966)
+       - USB: VIA VL715 (0x2109:0x0715) (#1098)
+
+2018-10-09  Anthony D'Atri  <anthony.datri@gmail.com>
+
+       drivedb.h: (#1096)
+       - Samsung based SSDs: Samsung PM863a (#951, #952, #961, #962, #972)
+       - Intel 730 and DC S35x0/3610/3700 Series SSDs: Dell-flavor S3500
+
+2018-10-09  Thomas Niedermeier  <tniedermeier@thomas-krenn.com>
+
+       drivedb.h: Samsung PM883 and SM883 (GH pull/19)
+
+2018-09-27  Christian Franke  <franke@computer.org>
+
+       INSTALL: Update list of default ./configure options.
+
+       utility.cpp: Add check of sg_get_unaligned_[bl]e16() and *32 to
+       check_endianness().
+
+       utility.cpp, utility.h: Optionally use C++11 'std::regex' instead of
+       POSIX regex(3).
+       configure.ac: Add option '--with-cxx11-regex'.
+
+       utility.cpp, utility.h: Simplify 'class regular_expression', remove
+       unneeded flag parameters, remove unused function.
+       atacmds.cpp, knowndrives.cpp, os_win32.cpp, smartd.cpp: Adjust usage
+       accordingly.
+
+       configure.ac, utility.cpp, utility.h: Remove replacement for missing
+       'strtoull()'.
+
+       configure.ac: Change default for '--with-nvme-devicescan' to 'yes'
+       on Linux and Windows.  Keep 'no' on FreeBSD, NetBSD and Darwin.
+
+2018-09-26  Christian Franke  <franke@computer.org>
+
+       configure.ac: Print warning if systemd(1) is present but
+       libsystemd-dev package is missing.
+
+       smartd.cpp: Notify READY=1 to systemd just before first sleep() to
+       ensure that the signal handlers are set.
+
+       smartd.cpp: Always ignore failure of ATA SMART ENABLE command if
+       '-T permissive' is specified.  Useful for testing on virtual
+       machines.
+
+2018-09-21  Christian Franke  <franke@computer.org>
+
+       configure.ac, os_linux.cpp: Remove redundant define WITH_SELINUX.
+
+       configure.ac: Check for 'libcap-ng' only on Linux.
+       Rework __USE_MINGW_ANSI_STDIO test for MinGW runtime.
+       Print 'deprecated' warning for '--without-working-snprintf'.
+
+       Add systemd(1) notify support to smartd (#1081):
+       configure.ac: Add option '--with-libsystemd'.
+       Makefile.am: Add linker flag and man page conditional.
+       smartd.cpp: If environment variable NOTIFY_SOCKET is set, use
+       sd_notify(3) to inform the service manager about state changes.
+       smartd.service.in: Set 'Type=notify'.
+       smartd.8.in: Document new functionality.
+
+2018-09-16  Christian Franke  <franke@computer.org>
+
+       atacmds.cpp: Avoid possible virtual call in dtor
+       (cppcheck 1.84: virtualCallInConstructor).
+
+       os_win32.cpp: Use unsigned int for bit shifts
+       (cppcheck 1.84: shiftTooManyBitsSigned).
+
+       Makefile.am: Set HAVE_WORKING_SNPRINTF also in VC14 config.h.
+
+       os_netbsd.cpp: Add spaces between string literals and macros for
+       C++11 (g++ -Wliteral-suffix).
+
+       ataprint.cpp: Add JSON support for '-l selective'.
+
+       drivedb.h: Update or remove links in warning messages.
+
+       drivedb.h: Crucial/Micron BX300, MX1/2/3/500, M5/600, 1100 SSDs:
+       - Rename,
+       - Crucial BX300 (GH pull/16, #963),
+       - Crucial MX300 750GB,
+       - Crucial MX500 (#977, #994, #995, #1004, #1024),
+       - Micron M500IT (#958),
+       - Micron 1100 OEM (GH pull/17),
+       - fix name of attribute 202 and 248.
+
+2018-09-12  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp: Get JSON values 'temperature.op_limit_min/max' from
+       Device Statistics.
+
+       atacmds.h, ataprint.cpp: Print ACS-4 max operating temperature
+       from SCT Status.
+
+       Makefile.am: Remove define of 'HAVE_GETOPT_LONG'.
+       os*.cpp: Remove remaining checks for 'HAVE_GETOPT_LONG'.
+
+       configure.ac: Remove check for 'uname()'.
+       os_generic.cpp, os_qnxnto.cpp: Remove function 'unsupported()'.
+
+       drivedb.h:
+       - HGST Deskstar NAS: *6040ALE614 (#935, #1089)
+       - HGST Ultrastar DC HC520 (He12) (#1086)
+
+2018-09-12  Anthony D'Atri  <anthony.datri@gmail.com>
+
+       drivedb.h: Micron 5100 Pro / 5200 SSDs (#1071)
+
+2018-09-11  Oleksii Samorukov  <samm@os2.kiev.ua>
+
+       os_freebsd.cpp: Fix build on FreeBSD 12, patch by fernape@
+
+2018-09-10  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Seagate Enterprise Capacity 3.5 HDD: V5.1 (#1087)
+       - Seagate Exos X12 HDD (#1042, #1046)
+       - Western Digital VelociRaptor (AF): WD5000BHTZ (patch from #1041)
+
+2018-09-10  David Purdy  <david.c.purdy@gmail.com>
+
+       drivedb.h: Phison Driven SSDs: Kingston A400 (#801)
+
+2018-09-02  Christian Franke  <franke@computer.org>
+
+       dev_intelliprop.h: Fix copyright info.
+
+       ataprint.cpp, nvmeprint.cpp: Change JSON value 'power_on_hours'
+       to 'power_on_time.hours'.  Add '.minutes' if available.
+       scsiprint.cpp: Add JSON values 'power_on_time.hours/minutes' from
+       Seagate factory lpage or from background scan lpage.
+
+2018-08-20  Christian Franke  <franke@computer.org>
+
+       Add missing license headers to some source files.
+
+2018-08-19  Christian Franke  <franke@computer.org>
+
+       Add SPDX-License-Identifier to all files with GPL header (#919).
+       Remove GPL headers.  Remove outdated info about smartsuite.
+
+       getopt/*, regex/*: Replace with current version from glibc 2.28
+       (2018-08-01).  Add _GETOPT/REGEX*_STANDALONE configurations.
+       Makefile.am, os_win32/vc14/smart*.vcxproj*: Set *_STANDALONE.
+       Add new files.
+
+       examplescripts/README: Update mailing list address.
+       os_solaris_ata.s: Remove old mailing list address.
+
+       os_win32/wbemcli_small.h: Remove this file.
+       The file <wbemcli.h> is usually provided by recent MinGW packages.
+       configure.ac: Remove check for <wbemcli.h>.
+       Makefile.am, os_win32/wmiquery.h, os_win32/vc14/smart*.vcxproj*:
+       Remove usage of 'wbemcli_small.h'.
+
+       ataprint.cpp, nvmeprint.cpp: Add JSON values 'power_cycle_count'
+       and 'power_on_hours'.
+       json.cpp, json.h: Add 'set_if_safe_*' member functions.
+
+2018-08-13  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp: Add JSON support for '-l devstat'.
+       Add JSON support also for old SCT Status format.
+
+2018-08-10  Christian Franke  <franke@computer.org>
+
+       smartctl.cpp, os_win32/wmiquery.h: Add missing printf() format checks.
+       This also silences -Wformat-nonliteral warnings from clang++ 5.0.
+
+       os_win32.cpp: Increase IOCTL_ATA_PASS_THROUGH timeout to 60 seconds.
+
+2018-08-10  Zhdan Bybin  <zhdan.bybin@intel.com>
+
+       drivedb.h:
+       - Intel S3520 Series SSDs (#985)
+       - Intel S4510/S4610/S4500/S4600 Series SSDs (#912, #928, #1000)
+
+2018-08-04  Christian Franke  <franke@computer.org>
+
+       Remove int64.h, use <inttypes.h> or <stdint.h> instead.
+
+       configure.ac, utility.cpp, utility.h: Add 128-bit unsigned integer
+       to string conversion.  Provides full integer precision if compiler
+       supports '__int128' (e.g. x86_64 GCC and CLang).
+       json.cpp, nvmeprint.cpp: Use these new functions.
+       Makefile.am: Adjust config-vc14 target.
+
+2018-08-02  Christian Franke  <franke@computer.org>
+
+       scsicmds.h, scsiprint.cpp: Add support for SAS host managed drives
+       (patch from #1045).
+
+2018-08-01  Christian Franke  <franke@computer.org>
+
+       dev_interface.cpp, scsiata.cpp, smartctl.8.in, smartd.conf.5.in:
+       Add option '-d scsi+TYPE' to disable SAT auto detection.
+       Useful in conjunction with TYPEs 'aacraid' and 'cciss' (#871).
+
+2018-07-31  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Phison Driven SSDs: Kingston DC400 (#933, #1011), move GOODRAM to ...
+       - Phison Driven OEM SSDs: ... here, PC Engines msata16d (#967),
+         INTENSO SATA III TOP (#1053)
+       - USB: Iomega MDHD500-U (0x059b:0x0274) (#1003)
+       - USB: Freecom (0x07ab:0xfc17) (#1049)
+       - USB: JMicron JMS539 (0x152d:0x0539/0x2801) (patch from #970)
+       - USB: JMicron (0x152d:0x0561) (#945)
+       - USB: JMicron JMS567 (0x152d:0x2567) (#948)
+       - USB: JMicron (0x152d:0x578e) (#987)
+
+       json.cpp: Add missing ';' to '--json=g' output of 128-bit values.
+
+2018-07-29  Christian Franke  <franke@computer.org>
+
+       os_win32.cpp: Decode Windows Server 1803 build number.
+       Improve search for actual CSMI port number.
+
+2018-06-21  Christian Franke  <franke@computer.org>
+
+       os_linux.cpp: Rework handling of glob() return code.
+       Don't abort device scan on missing '/dev/discs' (#1036).
+
+       os_win32.cpp: Decode Windows 10 1803 build number.
+       Silence g++ 7.3 -Wformat-truncation warning.
+
+2018-04-19  Christian Franke  <franke@computer.org>
+
+       utility.cpp, utility.h: Use array reference for buffer parameter
+       of dateandtimezoneepoch().  Remove no longer used dateandtimezone().
+
+       utility.cpp: Add check of sg_get_unaligned_[bl]e64() to
+       check_endianness().
+
+2018-04-16  Douglas Gilbert  <dgilbert@interlog.com>
+
+       switch usage of unaligned.h to sg_unaligned.h which is functionally
+       the same. sg_unaligned.h is the same header used by libsgutils which
+       is the basis of the sg3_utils, sdparm and ddpt packages available on
+       many of the same architectures as smartmontools is. This change
+       introduces a "sg_" prefix on the inline functions defined
+       sg_unaligned.h . The new header has specializations for big and little
+       endian machines that depends on the non-standard bswap_16(), bswap_32()
+       and bswap_64() calls. They are defined in the byteswap.h header which is
+       a GNU extension. According to the 'net both gcc and clang use intrinsics
+       {assembler ?} to implement these calls. If the byteswap.h header is not
+       present on the build machine, the generic implementations will be
+       used for the "unaligned" family of functions. Additionally the generic
+       implementations can be imposed with './configure --disable-fast-lebe'.
+       Developers may need to use './autogen.sh' prior to their normal build
+       sequence. Please report any problems to the author.
+
+2018-03-28  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp, nvmeprint.cpp, scsiprint.cpp:
+       Output JSON 'user_capacity' as 'blocks' and 'bytes'.
+       Handle both as unsafe ints.
+
+       smartd.cpp: Ignore remaining percentage in initial check of
+       self-test execution status.
+
+       scsiata.cpp: Fix device type info for 'usbcypress'.
+
+       os_linux.cpp: Fix device scan crash on missing /proc/devices.
+
+       update-smart-drivedb.in, update-smart-drivedb.8.in:
+       Add option '-u github'.
+
+2018-03-20  Christian Franke  <franke@computer.org>
+
+       nvmeprint.cpp: Add initial JSON support for '-i', '-H' and '-A'.
+
+       json.cpp, json.h: Add support for 64 and 128 bit unsigned
+       integers.  Add 'set_unsafe_*()' member functions to print unsigned
+       integers >= 53 bit as JSON number and string.
+
+2018-03-07  Douglas Gilbert  <dgilbert@interlog.com>
+
+       smartd.cpp:
+       - continue to use READ CAPACITY(10) first on unseen
+         SCSI devices but once we discover the need for
+         READ CAPACITY(16) use it for subsequent accesses
+       dev_interface.h: 
+       - struct scsi_device: add set_rcap16_first() and
+         use_rcap16() const methods
+       scsicmds.cpp:
+       - use scsi_device::set_rcap16_first() when READ
+         CAPACITY(10) reports 32 bits can't represent the
+         number of blocks
+
+2018-03-06  Alex Samorukov <samm@os2.kiev.ua>
+
+       drivedb.h: 
+       - add Transcend PSD SSD family (#979)
+       - add Toshiba HK4R Series SSD (#898)
+       - extend Western Digital Re regexp (#896)
+       - extend Wester Digital Se regexp (#953)
+       - add Smartbuy ignition plus (#976)
+
+2018-03-05  Gabriele Pohl <contact@dipohl.de>
+
+       drivedb.h:
+       - Add Seagate IronWolf 12TB ST12000VN0007-2GS116 (#988)
+
+2018-03-05  Alex Samorukov <samm@os2.kiev.ua>
+
+       drivedb.h: add Seagate Barracuda Pro family (#981)
+
+2018-03-01  Alex Samorukov <samm@os2.kiev.ua>
+
+       os_freebsd.cpp: Fix build under -CURRENT (patch by cy@)
+
+2018-02-28  Alex Samorukov <samm@os2.kiev.ua>
+
+       drivedb.h:
+       - Add SanDisk SDSSDH2128G (#982)
+
+2018-02-27  Alex Samorukov <samm@os2.kiev.ua>
+
+       drivedb.h:
+       - extend PLEXTOR PX regexp (#934)
+       - add Seagate Enterprise NAS HDD family (#946)
+       - add SanDisk SDSA6MM-* family (#965)
+       - fix Seagate Laptop HDD regexp (#955)
+       - add Seagate Barracuda Compute series (#927)
+       - extend Seagate Enterprise Capacity 3.5 HDD regexp (#956)
+
+2018-02-26  Alex Samorukov <samm@os2.kiev.ua>
+
+       drivedb.h:
+       - add Seagate XF1230 SSD (GH: issues/4)
+       - add Intel SSD Pro 5400s Series (GH: pull/5)
+       - add SanDisk-SD8SN8U-256G-1006 (GH: pull/3)
+       - add Toshiba Q300 SSD series (#932)
+       - extend HGST Deskstar NAS regexp (#975)
+       - add KINGSTON SNS4151S316GD SSD (#902)
+
+2018-02-20  Christian Franke  <franke@computer.org>
+
+       os_netbsd.cpp: Apply patch-os_netbsd.cpp 1.1 (2017-12-15) from
+       pkgsrc.se/sysutils/smartmontools: Add missing <sys/stat.h>.
+
+       configure.ac, int64.h: Remove support for pre-C99 environments without
+       <inttypes.h> and <stdint.h>.
+
+       configure.ac: Add '-Wformat=2 -fstack-protector-strong' unless
+       CXXFLAGS include other '-W' or '-f' options.
+
+2018-02-16  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - USB: Default to '-d sat' for Toshiba (0x0480), Seagate (0x0bc2),
+       Western Digital (0x1058), Initio (0x13fd), ASMedia (0x174c).
+       Keep known exceptions.  Merge some entries.
+
+2018-02-08  Douglas Gilbert  <dgilbert@interlog.com>
+
+       nvme on windows: just some code comments. Seems as though
+       W10 tries to completely neuter the idea of a pass-through.
+
+2018-01-06  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsi subsystem: improve dStrHex() signature, adjust
+       invocations. Adjust scsi_format_id_string() signature.
+       Add smartctl support for Pending Defects (sub-)log page;
+       seems similar to 'smartctl -l defects' but that is ATA
+       only. Needs to be generalized (as it will probably appear
+       in NVMe also).
+
+2018-01-04  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsi subsystem: preparation for decoding more log pages.
+
+2018-01-01  Alex Samorukov <samm@os2.kiev.ua>
+
+       os_freebsd.cpp: fix build with CLANG/6. Patch provided by
+       Dimitry Andric, PR 224826
+
+2018-01-01  Christian Franke  <franke@computer.org>
+
+       Happy New Year! Update copyright year in version info.
+
+2017-12-30  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsi subsystem: add code to check for both log pages
+       and subpages, subpages were not checked for previously.
+       Add decoding for Format Status log page. Associated
+       cleanups. Tighten checking for Seagate and Hitachi
+       vendor specific log pages; '-T permissive' will relax
+       checks back to the situation before this patch
+
+2017-12-29  Douglas Gilbert  <dgilbert@interlog.com>
+
+       unaligned.h: fix inconsistency in function argument
+       of get_unaligned_be24()
+
+2017-12-29  Douglas Gilbert  <dgilbert@interlog.com>
+
+       Add --enable-scsi-cdb-check option to ./configure that
+       results in a SCSI cdb sanity check prior to SCSI generic
+       pass-through in Linux. [So it does not sanity check
+       Megaraid and 3ware (etc) pass-throughs (but could).] When
+       selected defines SCSI_CDB_CHECK in config.h . This may be
+       temporary. This patch is an attempt to catch strange
+       frames (perhaps SATA FIS) being sent to the SCSI
+       pass-through.
+
+2017-12-29  Douglas Gilbert  <dgilbert@interlog.com>
+
+       Rework scsiGetSize() and remove scsiGetProtPBInfo().
+       Convert scsicmds.cpp to use unaligned.h get and put.
+
+2017-12-27  Alex Samorukov <samm@os2.kiev.ua>
+
+       Add unaligned.h header file to the Makefile.am
+
+2017-12-27  Douglas Gilbert  <dgilbert@interlog.com>
+
+       Add unaligned.h header file; has get and put variants of
+       unaligned be16,24,32,48,64 and le16,24,32,48,64 copies
+       plus all_zeros() and all_ffs() helpers. All inline.
+
+2017-12-27  Douglas Gilbert  <dgilbert@interlog.com>
+
+       Remove UINT8, INT8, UINT32 and INT32 typedefs in favour
+       of the types from <stdint.h>; for example uint8_t
+
+2017-12-27  Douglas Gilbert  <dgilbert@interlog.com>
+
+       nvmecmds.cpp: according to NVMe 1.3a spec, the SMART/
+       health information log page is global and should take
+       the global nsid (all ff_s). It also says the Error
+       info lpage is "global. Broke WD Black PCIe (NVMe)
+       SSD but worked on Intel SSDs. Fix; could break others.
+
+2017-12-27  Douglas Gilbert  <dgilbert@interlog.com>
+
+       os_freebsd.cpp: on error was setting set_nvme_err() to 1,
+       not the actual NVMe status value; fix.
+
+2017-12-24  Alex Samorukov  <samm@os2.kiev.ua>
+       CircleCI: add FreeBSD cross compilation
+
+2017-12-22  Alex Samorukov  <samm@os2.kiev.ua>
+       configure.ac: add -lsbuf to FreeBSD libs to fix static builds.
+
+2017-12-21  Douglas Gilbert  <dgilbert@interlog.com>
+
+       scsiprint.cpp: Start some JSON work. Other cleanups and helper
+       functions; potentially new header for those helpers.
+
+2017-12-17  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp: Add JSON support for '-g all', '-l scterc' and
+       '-l scttemp'.
+
+       ataprint.cpp: Don't print obsolete SCT Support Level (#940).
+
+2017-12-14  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp: JSON '-A' output: Add booleans and string for attribute
+       flags.  Remove string array.  Rename table.
+
+       ataprint.cpp: Add JSON support for '-l [x]error'.
+
+2017-12-13  Christian Franke  <franke@computer.org>
+
+       smartctl.cpp, smartctl.8.in: Rename '--json=a' to '--json=o'.
+
+       smartctl.cpp: Show command line error messages in JSON output.
+
+       ataprint.cpp: Add JSON support for '-l [x]selftest' and
+       '-l directory'.
+
+       atacmds.cpp, atacmds.h: Move self-test print functions to ...
+       ataprint.cpp: ... here.
+       smartd.cpp: Rework self-test error counting.
+
+       ataprint.cpp: Add JSON support for '-c'.
+
+       atacmds.cpp, atacmds.h: Change return type of capability checks to
+       bool.  Declare simple checks inline.
+
+2017-12-07  Christian Franke  <franke@computer.org>
+
+       json.cpp: Avoid 'cbegin()' and 'cend()' to fix build with older
+       libstdc++.
+
+       json.cpp, json.h, smartctl.cpp, smartctl.8.in: Add '--json=s' option.
+       Outputs JSON object elements sorted by key.
+       Add '--json=g' option.  Outputs JSON structure suitable for grep.
+
+2017-12-05  Christian Franke  <franke@computer.org>
+
+       ataprint.cpp: Add JSON support for '-l sataphy'.
+
+       smartctl.cpp: Add JSON support for '--scan'.
+       Add similar device info to regular JSON output.
+       ataprint.cpp, scsiprint.cpp: Remove now duplicate "protocol" element.
+
+       smartctl.cpp: Silence false positive warnings from clang analyzer.
+
+2017-12-02  Christian Franke  <franke@computer.org>
+
+       Add initial support for smartctl JSON output mode (#766):
+       json.cpp, json.h: New files with JSON support class.
+       Makefile.am, os_win32/vc14/smartctl.vcxproj*: Add new files.
+       ataprint.cpp: Add initial JSON support for -i, -H, -A and -l [x]error.
+       scsiprint.cpp: Add initial JSON support for -i and -H.
+       smartctl.cpp, smartctl.h: Add '-j, --json' option, global JSON object
+       and new print functions.
+       smartctl.8.in: Document new functionality.
+
+       atacmds.cpp: Remove no longer needed variable 'must_swap'.
+
+       os_win32.cpp: Remove include of smartctl.h, add extern declaration.
+       Decode Windows Server 1709 build number.
+
+       configure.ac, os_linux.cpp: Always include <sys/sysmacros.h> if available.
+       This silences a 'deprecated' warning from glibc 2.25 headers.
+
+2017-11-20  Alex Samorukov  <samm@os2.kiev.ua>
+
+       os_netbsd.cpp (fix regressions in smartmontools 6.6)
+        - fix BE platforms support, tested on sparc64 (#943)
+        - fix name based device type detection (#943)
+        - Add byte-swapping for IDENTIFY command and remove related hacks from the
+          atacmds.cpp (#117)
+
+2017-11-18  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h:
+       - Add Swissbit C440 industrial cf card series (#942)
+       - Fix Innolite Satadom D150QV entry (#939)
+
+2017-11-16  Christian Franke  <franke@computer.org>
+
+       smartd.initd.in: Remove FreeBSD section.
+
+       os_linux.cpp: Add missing braces to 3ware SELinux code.
+       This possibly harmless bug was introduced ~10 years ago in r2510.
+       It is now detected by g++ 6.3 -Wmisleading-indentation warning.
+
+       update-smart-drivedb.in: Include configured PATH in help and
+       error messages.  Allow digits in SVN Id user name.
+
+       configure.ac: Prepend '/usr/local/bin' to default for
+       '--with-scriptpath' (#941).
+
+2017-11-15  Christian Franke  <franke@computer.org>
+
+       smartd.cpp: Use 'sigaction()' instead of deprecated 'sigset()'
+       or 'signal()'.
+       configure.ac: Add '--with-signal-func' to select old function
+       if needed.
+
+       configure.ac: Remove '-with-initscriptdir=[auto|yes]' heuristics.
+       The default init script is no longer useful on most platforms.
+       INSTALL: Update related documentation.
+
+       configure.ac, Makefile.am: Use smartd.cygwin.initd.in on Cygwin.
+       smartd.cygwin.initd.in: New file.
+       smartd.initd.in: Remove Cygwin section.
+
+       configure.ac: Make some header checks platform specific.
+       Print '--with-nvme-devicescan' warning also on FreeBSD.
+       Remove '--with-solaris-sparc-ata' warning.
+
+       examplescripts/Example6: Add enhancements from Fedora package.
+
+2017-11-13  Christian Franke  <franke@computer.org>
+
+       drivedb.h:
+       - Western Digital Red: WD80EZZX
+       - USB: WD My Book (0x1058:0x25ee) (Red Hat Bugzilla 1446533)
+
+2017-11-13  Matt Coates  <me@mattjackets.com>
+
+       drivedb.h: USB: Seagate Backup Plus 4TB (0x0bc2:0xab43) (#926)
+
+2017-11-10  Alex Samorukov  <samm@os2.kiev.ua>
+
+       drivedb.h: add SanDisk iSSD SDIS6BM (#923)
+
+2017-11-08  Christian Franke  <franke@computer.org>
+
+       Makefile.am, os_win32/installer.nsi: Add VERSIONINFO resource to
+       installer.
+
+       os_win32/installer.nsi: Remove get/set of old 'Install_Dir' registry
+       entry.  No longer needed for recent versions of GSmartControl.
+       Remove deletion of old .exe.manifest files.
+       Search also for 64-bit version of Notepad++.
+
+       ataprint.cpp: Fix detection of Device Statistics log with
+       256 sectors (#922).
+
+       os_linux.cpp: Use 'realpath()' (BSD, POSIX) instead of
+       'canonicalize_file_name()' (GNU extension).
+       This fixes build on systems with musl libc (#921).
+
+2017-11-06  Alex Samorukov <samm@os2.kiev.ua>
+
+       os_freebsd.cpp: implement NVMe device scan (#687)
+       os_freebsd.cpp: show hint if /dev/nvd* is specified as device name
+
+2017-11-05  Christian Franke  <franke@computer.org>
+
+       configure.ac: Add separate version number for drivedb.h branch.
 
 2017-11-05  Christian Franke  <franke@computer.org>
 
diff --git a/INSTALL b/INSTALL
index fcffe649b81983d3980fc9430fefd39699c787e4..7a6fb6e363970c9e6eb7ff77e251f9a139a2806e 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,10 +1,10 @@
 Smartmontools installation instructions
 =======================================
 
-$Id: INSTALL 4574 2017-10-29 15:41:13Z chrfranke $
+$Id: INSTALL 4880 2018-12-29 20:27:01Z chrfranke $
 
 Please also see the smartmontools home page:
-http://www.smartmontools.org/
+https://www.smartmontools.org/
 
 Table of contents:
 
@@ -26,6 +26,9 @@ Table of contents:
 [1] System requirements
 =======================
 
+    Note: Some of this info is outdated as it refers to very old OS
+    versions.
+
     A) Linux
 
     Any Linux distribution will support smartmontools if it has a
@@ -42,29 +45,28 @@ Table of contents:
 
     C) Solaris
 
-    The SCSI code has been tested on a variety of Solaris 8 and 9
+    The SCSI code has been tested on a variety of Solaris 8 or later
     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.
+    also function under 1.6.1 and later releases.
 
     E) Cygwin
 
-    The code was tested on Cygwin 2.9.0-3 x86 and x86_64. It should also
+    The code was tested on Cygwin 2.11.* x86 and x86_64.  It should also
     work on other recent releases.
 
     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.
+    to access the raw devices.  The information in the "Windows" section
+    below also applies to the Cygwin version.
 
     F) Windows
 
     The code was tested on Windows XP SP3, 2003, Vista, Windows 7, 8, 8.1
-    and Windows 10 up to 1709.  Support von Windows 9x/ME and NT4 was
+    and Windows 10 up to 1809.  Support von Windows 9x/ME and NT4 was
     removed after smartmontools 5.43.
 
     ATA or SATA devices are supported if the device driver implements
@@ -77,13 +79,18 @@ Table of contents:
     SCSI and USB devices are accessed through SPTI. Special driver support
     is not required.
 
+    NVMe devices are supported with the Windows 10 NVMe driver or with
+    vendor specific drivers supporting NVME_PASS_THROUGH.
+
     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.
 
+    Only basic SMART commands are supported for ATA devices.
+
     It's important to know that on 10.3.x, some things don't work
-    (see WARNINGS): due to bugs in the libraries used, you cannot run
+    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.
@@ -92,32 +99,7 @@ Table of contents:
     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)
-
-   * Work on 10.4 and later, but not on 10.3:
-     - switching off SMART (switching *on* works fine)
-     - switching off auto-save (but why would you want to?)
-     - running the short test (that leaves you with only the extended test)
-
-    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.  On many newer Mac OS systems, the
-    hard drive comes with the offline test switched on by default, so
-    even that works.
+    SCSI devices are not currently supported.
 
     The OS X SAT SMART Driver provides access to SMART data for SAT capable
     USB and Firewire devices:
@@ -137,9 +119,10 @@ Table of contents:
 =======================
 
     Get the sources from the SVN repository:
-    svn co http://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools smartmontools
+    svn co https://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools smartmontools
 
     Then type:
+    cd smartmontools
     ./autogen.sh
     and continue with step [3] below, skipping the "unpack the tarball" step.
 
@@ -152,7 +135,7 @@ Table of contents:
 ======================================
 
     If you are NOT installing from SVN, then unpack the tarball:
-    tar zxvf smartmontools-5.VERSION.tar.gz
+    tar xvf smartmontools-VERSION.tar.gz
 
     Then:
     ./configure
@@ -172,26 +155,31 @@ Table of contents:
     --mandir='${datarootdir}/man'
     --docdir='${datarootdir}/doc/smartmontools'
     --disable-sample
-    --with-systemdsystemunitdir=auto
-    --with-systemdenvfile=auto
-    --with-initscriptdir=auto
+    --disable-scsi-cdb-check
+    --enable-fast-lebe
+    --without-initscriptdir
     --with-exampledir='${docdir}/examplescripts'
     --with-drivedbdir='${datadir}/smartmontools'
     --with-update-smart-drivedb
     --with-gnupg
     --with-smartdscriptdir='${sysconfdir}'
     --with-smartdplugindir='${smartdscriptdir}/smartd_warning.d'
-    --with-scriptpath='/bin:/usr/bin'
+    --with-scriptpath='/usr/local/bin:/bin:/usr/bin'
     --without-savestates
     --without-attributelog
     --with-os-deps='os_linux.o dev_areca.o' (platform specific)
     --without-selinux
     --with-libcap-ng=auto
-    --without-nvme-devicescan
+    --with-libsystemd=auto
+    --with-systemdsystemunitdir=auto
+    --with-systemdenvfile=auto
+    --with-nvme-devicescan (Linux, Windows: yes; Others: no)
     --without-solaris-sparc-ata (Solaris SPARC only)
-    --with-working-snprintf (Windows: guessed)
+    --with-signal-func=sigaction
+    --with-working-snprintf
     --with-mingw-aslr=auto (Windows only)
     --with-cxx11-option=auto
+    --without-cxx11-regex
 
     These will usually not overwrite existing "distribution" installations on
     Linux Systems since the FHS reserves this area for use by the system
@@ -200,11 +188,8 @@ Table of contents:
     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, add
-    CXXFLAGS='your options' to ./configure command.
-
     The first output line of smartctl and smartd provides information
-    about release number, last SVN checkin date and revison, platform,
+    about release number, last SVN checkin date and revision, platform,
     and package. The latter defaults to "(local build)" and can be
     changed by the variable BUILD_INFO, for example:
     make BUILD_INFO='"(Debian 5.39-2)"'
@@ -215,17 +200,6 @@ Table of contents:
 Note: Please send corrections/additions to:
 smartmontools-support@listi.jpberlin.de
 
-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                                 \
-              --docdir=/usr/local/share/doc/smartmontools             \
-              --with-initscriptdir=/usr/local/etc/init.d
-
 Red Hat:
   ./configure --sbindir=/usr/sbin                               \
               --sysconfdir=/etc                                 \
@@ -287,6 +261,7 @@ SuSE:
 
 [6] Guidelines for Darwin
 =========================
+
   ./configure --with-initscriptdir=/Library/StartupItems
 
   If you'd like to build the i386 version on a powerpc machine, you can
@@ -298,6 +273,7 @@ SuSE:
 
 [7] Guidelines for NetBSD
 =========================
+
   ./configure --prefix=/usr/pkg                                       \
               --docdir=/usr/pkg/share/doc/smartmontools
 
@@ -305,7 +281,7 @@ SuSE:
 ==========================
 
     smartmontools has been partially but not completely ported to
-    Solaris.  It includes complete SCSI support but no ATA or 3ware
+    Solaris.  It includes complete SCSI support but no ATA or NVMe
     support.  It can be compiled with either CC (Sun's C++ compiler)
     or GNU g++.
 
@@ -326,48 +302,13 @@ SuSE:
      --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                           \
-              --docdir=/usr/share/doc/smartmontools             \
               --with-initscriptdir=/etc/rc.d/init.d
 
-  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.
-
-  Files may also be checked out with a non-Cygwin SVN client which uses CR/LF
-  for svn:eol-style=native.  The "autogen.sh" script has svn:eol-style=LF.
-  Bash option "-o igncr" is not required.
-
 [10] Guidelines for Windows
 ===========================
 
@@ -387,22 +328,10 @@ Cross-compile statically linked 32-bit and 64-bit versions with MinGW-w64:
   ./configure --build=$(./config.guess) \
               --host=x86_64-w64-mingw32
 
-  Tested on Cygwin, Debian and Fedora.
-
-
-To build the Windows binary distribution, use:
-
-  make dist-win32
-
-  This builds the distribution in directory
+  Tested on Cygwin, Debian, Fedora and Ubuntu.
 
-  ./smartmontools-VERSION.win32/
 
-  and packs it into
-
-  ./smartmontools-VERSION.win32.zip
-
-To create a Windows installer, use:
+To create the Windows installer, use:
 
   make installer-win32
 
@@ -412,12 +341,7 @@ To create a Windows installer, use:
   ./smartmontools-VERSION.win32-setup.exe
 
   The installer is build using the command "makensis" from the NSIS
-  package. See http://nsis.sourceforge.net/ for documentation and
-  download location.
-
-  It is also possible to (cross-)build the installer on Linux.
-  This was successfully tested on Debian and Fedora with package
-  "nsis".
+  package (https://nsis.sourceforge.net/).
 
 To create a combined 32-/64-bit installer, use this in 32-bit build
 directory if 64-build directory is at ../build64:
@@ -432,7 +356,7 @@ To both create and run the (interactive) installer, use:
   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.
+  to DOS text file format and *.html and *.pdf preformatted man pages.
 
 To prepare os_win32/vc14 directory for MS Visual Studio C++ 2015 builds,
 use the following on MSYS or Cygwin:
@@ -444,8 +368,10 @@ use the following on MSYS or Cygwin:
   The MSVC project files (os_win32/vc14/*) are included in SVN (but not
   in source tarball). The target config-vc14 from a Makefile configured
   for MinGW creates os_win32/vc14/{config.h,smart*.rc,svnversion.h}.
-  The configure skript must be run outside of the source directory to
+  The configure script must be run outside of the source directory to
   avoid inclusion of the original config.h.
+  Additional MSVC related make targets are clean-vc14, distclean-vc14
+  and maintainer-clean-vc14.
 
 
 [11] Guidelines for OS/2, eComStation
@@ -459,13 +385,13 @@ To compile the OS/2 code, please run
 
 [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                                        \
               --docdir=/usr/local/share/doc/smartmontools-VERSION      \
-              --without-initscriptdir                                  \
               --enable-sample
 
   NOTE: --enable-sample will cause the smartd.conf and smartd RC files to
@@ -504,18 +430,12 @@ man smartd.conf
 man smartctl
 man smartd
 
-/usr/sbin/smartctl -s on -o on -S on /dev/sda (only root can do this)
-/usr/sbin/smartctl -a /dev/sda (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.
+sudo /usr/sbin/smartctl -x /dev/sda
 
 Source and binary packages for Windows are available at
-http://sourceforge.net/projects/smartmontools/files/
+https://sourceforge.net/projects/smartmontools/files/
 
-Refer to http://www.smartmontools.org/wiki/Download for any additional
+Refer to https://www.smartmontools.org/wiki/Download for any additional
 download and installation instructions.
 
 The following files are installed if ./configure has no options:
@@ -524,7 +444,7 @@ The following files are installed if ./configure has no options:
 /usr/local/sbin/smartd                                  [Executable daemon]
 /usr/local/sbin/update-smart-drivedb                    [Drive database update script]
 /usr/local/etc/smartd.conf                              [Configuration file for smartd daemon]
-/usr/local/etc/smartd_warning.sh                        [Warning skript for smartd daemon]
+/usr/local/etc/smartd_warning.sh                        [Warning script for smartd daemon]
 /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]
@@ -535,18 +455,14 @@ The following files are installed if ./configure has no options:
 /usr/local/share/doc/smartmontools/INSTALL              [Installation instructions: what you're reading!]
 /usr/local/share/doc/smartmontools/NEWS                 [Significant enhancements and fixes]
 /usr/local/share/doc/smartmontools/README               [Overview]
-/usr/local/share/doc/smartmontools/TODO                 [Things that need to be done/fixed]
+/usr/local/share/doc/smartmontools/TODO                 [No longer maintained]
 /usr/local/share/doc/smartmontools/smartd.conf          [Example configuration file for smartd]
 /usr/local/share/doc/smartmontools/examplescripts/      [Executable scripts for -M exec of smartd.conf (4 files)]
 /usr/local/share/smartmontools/drivedb.h                [Drive database]
 
-Due to checks done by '--with-systemdsystemunitdir=auto' and '--with-initscriptdir=auto',
-one of the following files may also be installed:
+Due to checks done by '--with-systemdsystemunitdir=auto', the following file may also be installed:
 
 /usr/local/lib/systemd/system/smartd.service            [Systemd service file for smartd]
-/usr/local/etc/rc.d/init.d/smartd                       [Init/Startup script for smartd]
-/usr/local/etc/init.d/smartd                            [Init/Startup script for smartd]
-/usr/local/etc/rc.d/smartd                              [Init/Startup script for smartd]
 
 If /usr/local/etc/smartd.conf exists and differs from the
 default then the default configuration file is installed as
@@ -569,19 +485,6 @@ if groff is available:
 make MAN2HTML='groff -man -Thtml' htmlman
 
 
-Some of the source files are prepared for the documentation
-generator Doxygen (http://www.doxygen.org/). If Doxygen is installed,
-the command:
-
-doxygen
-
-creates HTML documentation in doc/html and LaTeX documentation
-in doc/latex. If TeX is installed, the following command creates
-a documentation file doc/latex/refman.pdf:
-
-( cd doc/latex && make pdf )
-
-
 [14] Detailed description of arguments to configure command
 ===========================================================
 
@@ -601,15 +504,10 @@ Old option                 Replacement
 --disable-savestates       [no option needed]
 --enable-attrbutelog       --with-attributelog[=yes]
 --disable-savestates       [no option needed]
+--with-initscriptdir[=yes] --with-initscriptdir=DIR
+--with-initscriptdir=auto  --with-initscriptdir=DIR
 
 
-The defaults for --with-initscriptdir and --with-systemdsystemunitdir are
-guessed such that the following rules apply:
-- If --prefix=/usr --sysconfdir=/etc is specified, the guessed directories
-  should be the defaults used by the current OS or distribution.
-- If --sysconfdir=/etc is NOT specified, the guessed directories should
-  always be below ${prefix} or below ${sysconfdir}.
-
 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:
@@ -619,51 +517,18 @@ variables then the different directories that are used would be:
 --sysconfdir          /home/joe/etc
 --with-exampledir     /home/joe/share/doc/smartmontools/examplescripts
 --with-drivedbdir     /home/joe/share/smartmontools
---with-initscriptdir         [see below]
+--with-initscriptdir         [disabled]
 --with-systemdsystemunitdir  [see below]
 
 If systemd is present (and pkg-config reports /lib/systemd/system
 as the systemdsystemunitdir):
 
---with-initscriptdir         [disabled]
 --with-systemdsystemunitdir  /home/joe/lib/systemd/system
 
-else if /etc/rc.d/init.d exists:
-
---with-initscriptdir         /home/joe/etc/rc.d/init.d
---with-systemdsystemunitdir  [disabled]
-
-else if /etc/init.d exists:
-
---with-initscriptdir         /home/joe/etc/init.d
---with-systemdsystemunitdir  [disabled]
-
-else if /etc/rc.d exists:
-
---with-initscriptdir         /home/joe/etc/rc.d
---with-systemdsystemunitdir  [disabled]
-
 else
 
---with-initscriptdir         [disabled]
 --with-systemdsystemunitdir  [disabled]
 
 
-Here are the differences with and without --enable-sample, assuming
-that initscript location is set and 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:
 https://www.gnu.org/software/autoconf/manual/autoconf.html#Running-configure-Scripts
index 7540f57019ded8de4e97941935827415e127ab0d..c7f1561ff81485c07cc7904712976c3921b5bf80 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 #
-# $Id: Makefile.am 4519 2017-10-08 15:41:54Z chrfranke $
+# $Id: Makefile.am 4848 2018-12-05 18:30:46Z chrfranke $
 #
 
 @SET_MAKE@
@@ -32,10 +32,10 @@ if OS_WIN32_MINGW
 AM_CPPFLAGS += -I$(srcdir)/os_win32
 endif
 if NEED_GETOPT_LONG
-AM_CPPFLAGS += -I$(srcdir)/getopt -DHAVE_GETOPT_LONG -D__GNU_LIBRARY__
+AM_CPPFLAGS += -I$(srcdir)/getopt -D_GETOPT_STANDALONE
 endif
 if NEED_REGEX
-AM_CPPFLAGS += -I$(srcdir)/regex
+AM_CPPFLAGS += -I$(srcdir)/regex -D_REGEX_STANDALONE
 endif
 
 sbin_PROGRAMS = \
@@ -69,7 +69,8 @@ smartctl_SOURCES = \
         dev_interface.h \
         dev_tunnelled.h \
         drivedb.h \
-        int64.h \
+        json.cpp \
+        json.h \
         knowndrives.cpp \
         knowndrives.h \
         nvmecmds.cpp \
@@ -79,10 +80,12 @@ smartctl_SOURCES = \
         scsicmds.cpp \
         scsicmds.h \
         scsiata.cpp \
+        scsinvme.cpp \
         scsiprint.cpp \
         scsiprint.h \
         utility.cpp \
-        utility.h
+        utility.h \
+        sg_unaligned.h
 
 smartctl_LDADD = $(os_deps) $(os_libs)
 smartctl_DEPENDENCIES = $(os_deps)
@@ -119,6 +122,10 @@ EXTRA_smartctl_SOURCES = \
 
 if OS_WIN32_MINGW
 
+smartctl_SOURCES += \
+        os_win32/popen_win32.cpp \
+        os_win32/popen.h
+
 smartctl_LDADD        += smartctl_res.o
 smartctl_DEPENDENCIES += smartctl_res.o
 
@@ -139,7 +146,6 @@ smartd_SOURCES = \
         dev_interface.h \
         dev_tunnelled.h \
         drivedb.h \
-        int64.h \
         knowndrives.cpp \
         knowndrives.h \
         nvmecmds.cpp \
@@ -147,10 +153,12 @@ smartd_SOURCES = \
         scsicmds.cpp \
         scsicmds.h \
         scsiata.cpp \
+        scsinvme.cpp \
         utility.cpp \
-        utility.h
+        utility.h \
+        sg_unaligned.h
 
-smartd_LDADD = $(os_deps) $(os_libs) $(CAPNG_LDADD)
+smartd_LDADD = $(os_deps) $(os_libs) $(CAPNG_LDADD) $(SYSTEMD_LDADD)
 smartd_DEPENDENCIES = $(os_deps)
 
 EXTRA_smartd_SOURCES = \
@@ -190,6 +198,8 @@ if OS_WIN32_MINGW
 smartd_SOURCES += \
         os_win32/daemon_win32.cpp \
         os_win32/daemon_win32.h \
+        os_win32/popen_win32.cpp \
+        os_win32/popen.h \
         os_win32/syslog_win32.cpp \
         os_win32/syslog.h
 
@@ -207,12 +217,18 @@ if NEED_GETOPT_LONG
 smartctl_SOURCES += \
         getopt/getopt.c \
         getopt/getopt.h \
-        getopt/getopt1.c
+        getopt/getopt1.c \
+        getopt/getopt_int.h \
+        getopt/bits/getopt_core.h \
+        getopt/bits/getopt_ext.h
 
 smartd_SOURCES += \
         getopt/getopt.c \
         getopt/getopt.h \
-        getopt/getopt1.c
+        getopt/getopt1.c \
+        getopt/getopt_int.h \
+        getopt/bits/getopt_core.h \
+        getopt/bits/getopt_ext.h
 
 endif
 
@@ -372,6 +388,7 @@ EXTRA_DIST = \
         .editorconfig \
         autogen.sh \
         smartd.initd.in \
+        smartd.cygwin.initd.in \
         smartd.freebsd.initd.in \
         smartd.8.in \
         smartctl.8.in \
@@ -395,7 +412,6 @@ EXTRA_DIST = \
         os_win32/smartd_warning.cmd \
         os_win32/syslogevt.mc \
         os_win32/update-smart-drivedb.nsi \
-        os_win32/wbemcli_small.h \
         os_win32/versioninfo.rc.in \
         os_win32/wtssendmsg.c \
         $(docs_DATA) \
@@ -422,6 +438,7 @@ CLEANFILES = \
         smartd.conf.5.pdf \
         smartd.conf.5.txt \
         smartd.initd \
+        smartd.cygwin.initd \
         smartd.freebsd.initd \
         smartd.service \
         smartd_warning.sh \
@@ -450,7 +467,7 @@ MAINTAINERCLEANFILES = \
         $(srcdir)/missing \
         $(srcdir)/m4/pkg.m4
 
-utility.o: svnversion.h
+smartctl.o utility.o: svnversion.h
 
 if IS_SVN_BUILD
 # Get version info from SVN
@@ -617,6 +634,11 @@ MAN_FILTER = { \
     else \
       sed '/^\.\\" %IF ENABLE_CAPABILITIES/,/^\.\\" %ENDIF ENABLE_CAPABILITIES/ s,^,.\\"\# ,' ; \
     fi | \
+    if test -n '$(SYSTEMD_LDADD)'; then \
+      cat; \
+    else \
+      sed '/^\.\\" %IF ENABLE_SYSTEMD_NOTIFY/,/^\.\\" %ENDIF ENABLE_SYSTEMD_NOTIFY/ s,^,.\\"\# ,' ; \
+    fi | \
     if test '$(with_nvme_devicescan)' = 'yes'; then \
       cat; \
     else \
@@ -888,14 +910,16 @@ $(distzip_win32): distdir.mkdir $(FILES_WIN32)
 if OS_WIN32_NSIS
 # Build NSIS installer
 # Note: Only option character '-' is also compatible with Linux version of makensis
-$(distinst_win32): os_win32/installer.nsi distdir.mkdir $(FILES_WIN32)
+$(distinst_win32): os_win32/installer.nsi smartctl_res.rc distdir.mkdir $(FILES_WIN32)
        test -z '$(builddir_win64)' || ( cd $(builddir_win64) && make distdir-win32 )
        @date=`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h` && \
        rev=`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h` && \
+       version=`sed -n 's|^ *VALUE "FileVersion", "\([0-9.]*\)".*$$|\1|p' smartctl_res.rc` && \
+       yy=`echo "$$date" | sed -n 's,^20\([0-9][0-9]\).*$$,\1,p'`; yy="$${yy:-XX}" && \
        verstr="$(PACKAGE_VERSION) $$date $$rev "$(BUILD_INFO) && \
        d64= && if [ -n '$(builddir_win64)' ]; then d64='-DINPDIR64=$(builddir_win64)/$(PACKAGE)-$(VERSION).win64'; fi && \
-       echo "'$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSTR='$$verstr' $<" && \
-       '$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSTR="$$verstr" $<
+       echo "'$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSION=$$version -DYY=$$yy -DVERSTR='$$verstr' $<" && \
+       '$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSION=$$version -DYY=$$yy -DVERSTR="$$verstr" $<
        md5sum $@ > $@.md5
        sha1sum $@ > $@.sha1
        sha256sum $@ > $@.sha256
@@ -1001,7 +1025,7 @@ config-vc$(vcver): $(CONFIG_VC_FILES)
 
 $(srcdir)/os_win32/vc$(vcver)/config.h: config.h Makefile
        sed -e '1i/* os_win32/vc$(vcver)/config.h.  Generated from config.h by Makefile.  */' \
-           -e 's,^#define HAVE_\(ATTR_PACKED\|GETTIMEOFDAY\|[DK_]*NTDDDISK_H\|STRINGS_H\|UNISTD_H\|WORKING_SNPRINTF\) 1$$,/* #undef HAVE_\1 */ /* VC$(vcver) */,' \
+           -e 's,^#define HAVE_\(ATTR_PACKED\|GETTIMEOFDAY\|[DK_]*NTDDDISK_H\|LONG_DOUBLE_WIDER\|STRINGS_H\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */ /* VC$(vcver) */,' \
            -e 's,^\(#define SMARTMONTOOLS_BUILD_HOST "[^-]*\)[^"]*,\1-pc-w32vc$(vcver),' $< > $@
 
 $(srcdir)/os_win32/vc$(vcver)/svnversion.h: svnversion.h
index cd043c6ce1d277daf2633632f2dd2fa26ef815cb..297cbf8b5353ce89a135188571068d2aa12303ad 100644 (file)
@@ -15,7 +15,7 @@
 @SET_MAKE@
 
 #
-# $Id: Makefile.am 4519 2017-10-08 15:41:54Z chrfranke $
+# $Id: Makefile.am 4848 2018-12-05 18:30:46Z chrfranke $
 #
 
 
@@ -98,72 +98,84 @@ host_triplet = @host@
 @ENABLE_SAVESTATES_TRUE@am__append_2 = -DSMARTMONTOOLS_SAVESTATES='"$(savestates)"'
 @ENABLE_ATTRIBUTELOG_TRUE@am__append_3 = -DSMARTMONTOOLS_ATTRIBUTELOG='"$(attributelog)"'
 @OS_WIN32_MINGW_TRUE@am__append_4 = -I$(srcdir)/os_win32
-@NEED_GETOPT_LONG_TRUE@am__append_5 = -I$(srcdir)/getopt -DHAVE_GETOPT_LONG -D__GNU_LIBRARY__
-@NEED_REGEX_TRUE@am__append_6 = -I$(srcdir)/regex
+@NEED_GETOPT_LONG_TRUE@am__append_5 = -I$(srcdir)/getopt -D_GETOPT_STANDALONE
+@NEED_REGEX_TRUE@am__append_6 = -I$(srcdir)/regex -D_REGEX_STANDALONE
 sbin_PROGRAMS = smartctl$(EXEEXT) smartd$(EXEEXT)
-@OS_WIN32_MINGW_TRUE@am__append_7 = smartctl_res.o
+@OS_WIN32_MINGW_TRUE@am__append_7 = \
+@OS_WIN32_MINGW_TRUE@        os_win32/popen_win32.cpp \
+@OS_WIN32_MINGW_TRUE@        os_win32/popen.h
+
 @OS_WIN32_MINGW_TRUE@am__append_8 = smartctl_res.o
-@OS_WIN32_MINGW_TRUE@am__append_9 = \
+@OS_WIN32_MINGW_TRUE@am__append_9 = smartctl_res.o
+@OS_WIN32_MINGW_TRUE@am__append_10 = \
 @OS_WIN32_MINGW_TRUE@        os_win32/daemon_win32.cpp \
 @OS_WIN32_MINGW_TRUE@        os_win32/daemon_win32.h \
+@OS_WIN32_MINGW_TRUE@        os_win32/popen_win32.cpp \
+@OS_WIN32_MINGW_TRUE@        os_win32/popen.h \
 @OS_WIN32_MINGW_TRUE@        os_win32/syslog_win32.cpp \
 @OS_WIN32_MINGW_TRUE@        os_win32/syslog.h
 
-@OS_WIN32_MINGW_TRUE@am__append_10 = smartd_res.o
 @OS_WIN32_MINGW_TRUE@am__append_11 = smartd_res.o
-@NEED_GETOPT_LONG_TRUE@am__append_12 = \
+@OS_WIN32_MINGW_TRUE@am__append_12 = smartd_res.o
+@NEED_GETOPT_LONG_TRUE@am__append_13 = \
 @NEED_GETOPT_LONG_TRUE@        getopt/getopt.c \
 @NEED_GETOPT_LONG_TRUE@        getopt/getopt.h \
-@NEED_GETOPT_LONG_TRUE@        getopt/getopt1.c
+@NEED_GETOPT_LONG_TRUE@        getopt/getopt1.c \
+@NEED_GETOPT_LONG_TRUE@        getopt/getopt_int.h \
+@NEED_GETOPT_LONG_TRUE@        getopt/bits/getopt_core.h \
+@NEED_GETOPT_LONG_TRUE@        getopt/bits/getopt_ext.h
 
-@NEED_GETOPT_LONG_TRUE@am__append_13 = \
+@NEED_GETOPT_LONG_TRUE@am__append_14 = \
 @NEED_GETOPT_LONG_TRUE@        getopt/getopt.c \
 @NEED_GETOPT_LONG_TRUE@        getopt/getopt.h \
-@NEED_GETOPT_LONG_TRUE@        getopt/getopt1.c
+@NEED_GETOPT_LONG_TRUE@        getopt/getopt1.c \
+@NEED_GETOPT_LONG_TRUE@        getopt/getopt_int.h \
+@NEED_GETOPT_LONG_TRUE@        getopt/bits/getopt_core.h \
+@NEED_GETOPT_LONG_TRUE@        getopt/bits/getopt_ext.h
 
-@NEED_REGEX_TRUE@am__append_14 = \
+@NEED_REGEX_TRUE@am__append_15 = \
 @NEED_REGEX_TRUE@        regex/regex.c \
 @NEED_REGEX_TRUE@        regex/regex.h \
 @NEED_REGEX_TRUE@        regex/regex_internal.h
 
-@NEED_REGEX_TRUE@am__append_15 = \
+@NEED_REGEX_TRUE@am__append_16 = \
 @NEED_REGEX_TRUE@        regex/regex.c \
 @NEED_REGEX_TRUE@        regex/regex.h \
 @NEED_REGEX_TRUE@        regex/regex_internal.h
 
 
 # Included by regex.c:
-@NEED_REGEX_TRUE@am__append_16 = \
+@NEED_REGEX_TRUE@am__append_17 = \
 @NEED_REGEX_TRUE@        regex/regcomp.c \
 @NEED_REGEX_TRUE@        regex/regexec.c \
 @NEED_REGEX_TRUE@        regex/regex_internal.c
 
-@NEED_REGEX_TRUE@am__append_17 = \
+@NEED_REGEX_TRUE@am__append_18 = \
 @NEED_REGEX_TRUE@        regex/regcomp.c \
 @NEED_REGEX_TRUE@        regex/regexec.c \
 @NEED_REGEX_TRUE@        regex/regex_internal.c
 
-@OS_WIN32_TRUE@am__append_18 = \
+@OS_WIN32_TRUE@am__append_19 = \
 @OS_WIN32_TRUE@        csmisas.h \
 @OS_WIN32_TRUE@        os_win32/wmiquery.cpp \
 @OS_WIN32_TRUE@        os_win32/wmiquery.h
 
-@OS_WIN32_TRUE@am__append_19 = \
+@OS_WIN32_TRUE@am__append_20 = \
 @OS_WIN32_TRUE@        csmisas.h \
 @OS_WIN32_TRUE@        os_win32/wmiquery.cpp \
 @OS_WIN32_TRUE@        os_win32/wmiquery.h
 
-@OS_WIN32_TRUE@am__append_20 = -lole32 -loleaut32
 @OS_WIN32_TRUE@am__append_21 = -lole32 -loleaut32
-@ENABLE_UPDATE_SMART_DRIVEDB_TRUE@@OS_SOLARIS_TRUE@am__append_22 = update-smart-drivedb.1m
-@ENABLE_UPDATE_SMART_DRIVEDB_TRUE@@OS_SOLARIS_FALSE@am__append_23 = update-smart-drivedb.8
-@ENABLE_DRIVEDB_TRUE@@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@am__append_24 = \
+@OS_WIN32_TRUE@am__append_22 = -lole32 -loleaut32
+@ENABLE_UPDATE_SMART_DRIVEDB_TRUE@@OS_SOLARIS_TRUE@am__append_23 = update-smart-drivedb.1m
+@ENABLE_UPDATE_SMART_DRIVEDB_TRUE@@OS_SOLARIS_FALSE@am__append_24 = update-smart-drivedb.8
+@ENABLE_DRIVEDB_TRUE@@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@am__append_25 = \
 @ENABLE_DRIVEDB_TRUE@@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@        $(exedir_win32)/update-smart-drivedb.exe
 
-@ENABLE_DRIVEDB_TRUE@@OS_WIN32_MINGW_TRUE@am__append_25 = \
+@ENABLE_DRIVEDB_TRUE@@OS_WIN32_MINGW_TRUE@am__append_26 = \
 @ENABLE_DRIVEDB_TRUE@@OS_WIN32_MINGW_TRUE@        $(exedir_win32)/drivedb.h
 
-@OS_WIN32_MINGW_TRUE@am__append_26 = \
+@OS_WIN32_MINGW_TRUE@am__append_27 = \
 @OS_WIN32_MINGW_TRUE@        $(FILES_WIN32) \
 @OS_WIN32_MINGW_TRUE@        defadmin.manifest \
 @OS_WIN32_MINGW_TRUE@        distdir.mkdir \
@@ -202,23 +214,28 @@ am__smartctl_SOURCES_DIST = smartctl.cpp smartctl.h atacmdnames.cpp \
        ataidentify.h ataprint.cpp ataprint.h dev_ata_cmd_set.cpp \
        dev_ata_cmd_set.h dev_intelliprop.cpp dev_intelliprop.h \
        dev_interface.cpp dev_interface.h dev_tunnelled.h drivedb.h \
-       int64.h knowndrives.cpp knowndrives.h nvmecmds.cpp nvmecmds.h \
-       nvmeprint.cpp nvmeprint.h scsicmds.cpp scsicmds.h scsiata.cpp \
-       scsiprint.cpp scsiprint.h utility.cpp utility.h \
-       getopt/getopt.c getopt/getopt.h getopt/getopt1.c regex/regex.c \
-       regex/regex.h regex/regex_internal.h csmisas.h \
-       os_win32/wmiquery.cpp os_win32/wmiquery.h
-@NEED_GETOPT_LONG_TRUE@am__objects_1 = getopt.$(OBJEXT) \
+       json.cpp json.h knowndrives.cpp knowndrives.h nvmecmds.cpp \
+       nvmecmds.h nvmeprint.cpp nvmeprint.h scsicmds.cpp scsicmds.h \
+       scsiata.cpp scsinvme.cpp scsiprint.cpp scsiprint.h utility.cpp \
+       utility.h sg_unaligned.h os_win32/popen_win32.cpp \
+       os_win32/popen.h getopt/getopt.c getopt/getopt.h \
+       getopt/getopt1.c getopt/getopt_int.h getopt/bits/getopt_core.h \
+       getopt/bits/getopt_ext.h regex/regex.c regex/regex.h \
+       regex/regex_internal.h csmisas.h os_win32/wmiquery.cpp \
+       os_win32/wmiquery.h
+@OS_WIN32_MINGW_TRUE@am__objects_1 = popen_win32.$(OBJEXT)
+@NEED_GETOPT_LONG_TRUE@am__objects_2 = getopt.$(OBJEXT) \
 @NEED_GETOPT_LONG_TRUE@        getopt1.$(OBJEXT)
-@NEED_REGEX_TRUE@am__objects_2 = regex.$(OBJEXT)
-@OS_WIN32_TRUE@am__objects_3 = wmiquery.$(OBJEXT)
+@NEED_REGEX_TRUE@am__objects_3 = regex.$(OBJEXT)
+@OS_WIN32_TRUE@am__objects_4 = wmiquery.$(OBJEXT)
 am_smartctl_OBJECTS = smartctl.$(OBJEXT) atacmdnames.$(OBJEXT) \
        atacmds.$(OBJEXT) ataidentify.$(OBJEXT) ataprint.$(OBJEXT) \
        dev_ata_cmd_set.$(OBJEXT) dev_intelliprop.$(OBJEXT) \
-       dev_interface.$(OBJEXT) knowndrives.$(OBJEXT) \
+       dev_interface.$(OBJEXT) json.$(OBJEXT) knowndrives.$(OBJEXT) \
        nvmecmds.$(OBJEXT) nvmeprint.$(OBJEXT) scsicmds.$(OBJEXT) \
-       scsiata.$(OBJEXT) scsiprint.$(OBJEXT) utility.$(OBJEXT) \
-       $(am__objects_1) $(am__objects_2) $(am__objects_3)
+       scsiata.$(OBJEXT) scsinvme.$(OBJEXT) scsiprint.$(OBJEXT) \
+       utility.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
+       $(am__objects_3) $(am__objects_4)
 am__EXTRA_smartctl_SOURCES_DIST = os_darwin.cpp os_darwin.h \
        os_linux.cpp os_linux.h os_freebsd.cpp os_freebsd.h \
        os_netbsd.cpp os_netbsd.h os_openbsd.cpp os_openbsd.h \
@@ -232,22 +249,26 @@ am__DEPENDENCIES_1 =
 am__smartd_SOURCES_DIST = smartd.cpp atacmdnames.cpp atacmdnames.h \
        atacmds.cpp atacmds.h dev_ata_cmd_set.cpp dev_ata_cmd_set.h \
        dev_intelliprop.cpp dev_intelliprop.h dev_interface.cpp \
-       dev_interface.h dev_tunnelled.h drivedb.h int64.h \
-       knowndrives.cpp knowndrives.h nvmecmds.cpp nvmecmds.h \
-       scsicmds.cpp scsicmds.h scsiata.cpp utility.cpp utility.h \
+       dev_interface.h dev_tunnelled.h drivedb.h knowndrives.cpp \
+       knowndrives.h nvmecmds.cpp nvmecmds.h scsicmds.cpp scsicmds.h \
+       scsiata.cpp scsinvme.cpp utility.cpp utility.h sg_unaligned.h \
        os_win32/daemon_win32.cpp os_win32/daemon_win32.h \
+       os_win32/popen_win32.cpp os_win32/popen.h \
        os_win32/syslog_win32.cpp os_win32/syslog.h getopt/getopt.c \
-       getopt/getopt.h getopt/getopt1.c regex/regex.c regex/regex.h \
-       regex/regex_internal.h csmisas.h os_win32/wmiquery.cpp \
-       os_win32/wmiquery.h
-@OS_WIN32_MINGW_TRUE@am__objects_4 = daemon_win32.$(OBJEXT) \
+       getopt/getopt.h getopt/getopt1.c getopt/getopt_int.h \
+       getopt/bits/getopt_core.h getopt/bits/getopt_ext.h \
+       regex/regex.c regex/regex.h regex/regex_internal.h csmisas.h \
+       os_win32/wmiquery.cpp os_win32/wmiquery.h
+@OS_WIN32_MINGW_TRUE@am__objects_5 = daemon_win32.$(OBJEXT) \
+@OS_WIN32_MINGW_TRUE@  popen_win32.$(OBJEXT) \
 @OS_WIN32_MINGW_TRUE@  syslog_win32.$(OBJEXT)
 am_smartd_OBJECTS = smartd.$(OBJEXT) atacmdnames.$(OBJEXT) \
        atacmds.$(OBJEXT) dev_ata_cmd_set.$(OBJEXT) \
        dev_intelliprop.$(OBJEXT) dev_interface.$(OBJEXT) \
        knowndrives.$(OBJEXT) nvmecmds.$(OBJEXT) scsicmds.$(OBJEXT) \
-       scsiata.$(OBJEXT) utility.$(OBJEXT) $(am__objects_4) \
-       $(am__objects_1) $(am__objects_2) $(am__objects_3)
+       scsiata.$(OBJEXT) scsinvme.$(OBJEXT) utility.$(OBJEXT) \
+       $(am__objects_5) $(am__objects_2) $(am__objects_3) \
+       $(am__objects_4)
 am__EXTRA_smartd_SOURCES_DIST = os_darwin.cpp os_darwin.h os_linux.cpp \
        os_linux.h os_freebsd.cpp os_freebsd.h os_netbsd.cpp \
        os_netbsd.h os_openbsd.cpp os_openbsd.h os_os2.cpp os_os2.h \
@@ -453,6 +474,7 @@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
 SET_MAKE = @SET_MAKE@
 SHELL = @SHELL@
 STRIP = @STRIP@
+SYSTEMD_LDADD = @SYSTEMD_LDADD@
 VERSION = @VERSION@
 WINDMC = @WINDMC@
 WINDRES = @WINDRES@
@@ -537,7 +559,6 @@ top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 with_nvme_devicescan = @with_nvme_devicescan@
-with_selinux = @with_selinux@
 with_update_smart_drivedb = @with_update_smart_drivedb@
 ACLOCAL_AMFLAGS = -I m4
 
@@ -557,31 +578,32 @@ smartctl_SOURCES = smartctl.cpp smartctl.h atacmdnames.cpp \
        ataidentify.h ataprint.cpp ataprint.h dev_ata_cmd_set.cpp \
        dev_ata_cmd_set.h dev_intelliprop.cpp dev_intelliprop.h \
        dev_interface.cpp dev_interface.h dev_tunnelled.h drivedb.h \
-       int64.h knowndrives.cpp knowndrives.h nvmecmds.cpp nvmecmds.h \
-       nvmeprint.cpp nvmeprint.h scsicmds.cpp scsicmds.h scsiata.cpp \
-       scsiprint.cpp scsiprint.h utility.cpp utility.h \
-       $(am__append_12) $(am__append_14) $(am__append_18)
-smartctl_LDADD = $(os_deps) $(os_libs) $(am__append_7) \
-       $(am__append_20)
-smartctl_DEPENDENCIES = $(os_deps) $(am__append_8)
+       json.cpp json.h knowndrives.cpp knowndrives.h nvmecmds.cpp \
+       nvmecmds.h nvmeprint.cpp nvmeprint.h scsicmds.cpp scsicmds.h \
+       scsiata.cpp scsinvme.cpp scsiprint.cpp scsiprint.h utility.cpp \
+       utility.h sg_unaligned.h $(am__append_7) $(am__append_13) \
+       $(am__append_15) $(am__append_19)
+smartctl_LDADD = $(os_deps) $(os_libs) $(am__append_8) \
+       $(am__append_21)
+smartctl_DEPENDENCIES = $(os_deps) $(am__append_9)
 EXTRA_smartctl_SOURCES = os_darwin.cpp os_darwin.h os_linux.cpp \
        os_linux.h os_freebsd.cpp os_freebsd.h os_netbsd.cpp \
        os_netbsd.h os_openbsd.cpp os_openbsd.h os_os2.cpp os_os2.h \
        os_qnxnto.cpp os_qnxnto.h os_solaris.cpp os_solaris.h \
        os_win32.cpp os_generic.cpp os_generic.h aacraid.h cciss.cpp \
        cciss.h cissio_freebsd.h dev_areca.cpp dev_areca.h \
-       dev_legacy.cpp linux_nvme_ioctl.h megaraid.h $(am__append_16)
+       dev_legacy.cpp linux_nvme_ioctl.h megaraid.h $(am__append_17)
 smartd_SOURCES = smartd.cpp atacmdnames.cpp atacmdnames.h atacmds.cpp \
        atacmds.h dev_ata_cmd_set.cpp dev_ata_cmd_set.h \
        dev_intelliprop.cpp dev_intelliprop.h dev_interface.cpp \
-       dev_interface.h dev_tunnelled.h drivedb.h int64.h \
-       knowndrives.cpp knowndrives.h nvmecmds.cpp nvmecmds.h \
-       scsicmds.cpp scsicmds.h scsiata.cpp utility.cpp utility.h \
-       $(am__append_9) $(am__append_13) $(am__append_15) \
-       $(am__append_19)
-smartd_LDADD = $(os_deps) $(os_libs) $(CAPNG_LDADD) $(am__append_10) \
-       $(am__append_21)
-smartd_DEPENDENCIES = $(os_deps) $(am__append_11)
+       dev_interface.h dev_tunnelled.h drivedb.h knowndrives.cpp \
+       knowndrives.h nvmecmds.cpp nvmecmds.h scsicmds.cpp scsicmds.h \
+       scsiata.cpp scsinvme.cpp utility.cpp utility.h sg_unaligned.h \
+       $(am__append_10) $(am__append_14) $(am__append_16) \
+       $(am__append_20)
+smartd_LDADD = $(os_deps) $(os_libs) $(CAPNG_LDADD) $(SYSTEMD_LDADD) \
+       $(am__append_11) $(am__append_22)
+smartd_DEPENDENCIES = $(os_deps) $(am__append_12)
 EXTRA_smartd_SOURCES = os_darwin.cpp os_darwin.h os_linux.cpp \
        os_linux.h os_freebsd.cpp os_freebsd.h os_netbsd.cpp \
        os_netbsd.h os_openbsd.cpp os_openbsd.h os_os2.cpp os_os2.h \
@@ -589,7 +611,7 @@ EXTRA_smartd_SOURCES = os_darwin.cpp os_darwin.h os_linux.cpp \
        os_win32.cpp os_generic.cpp os_generic.h aacraid.h cciss.cpp \
        cciss.h cissio_freebsd.h dev_areca.cpp dev_areca.h \
        dev_legacy.cpp linux_nvme_ioctl.h freebsd_nvme_ioctl.h \
-       netbsd_nvme_ioctl.h megaraid.h $(am__append_17)
+       netbsd_nvme_ioctl.h megaraid.h $(am__append_18)
 
 # Exclude from source tarball
 nodist_EXTRA_smartctl_SOURCES = os_solaris_ata.s
@@ -601,10 +623,10 @@ nodist_EXTRA_smartd_SOURCES = os_solaris_ata.s
 # 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 smartctl.1m smartd.1m \
-@OS_SOLARIS_TRUE@      $(am__append_22)
+@OS_SOLARIS_TRUE@      $(am__append_23)
 # For systems that adopts traditional manner
 @OS_SOLARIS_FALSE@man_MANS = smartd.conf.5 smartctl.8 smartd.8 \
-@OS_SOLARIS_FALSE@     $(am__append_23)
+@OS_SOLARIS_FALSE@     $(am__append_24)
 docsdir = $(docdir)
 docs_DATA = \
         AUTHORS \
@@ -635,6 +657,7 @@ EXTRA_DIST = \
         .editorconfig \
         autogen.sh \
         smartd.initd.in \
+        smartd.cygwin.initd.in \
         smartd.freebsd.initd.in \
         smartd.8.in \
         smartctl.8.in \
@@ -658,7 +681,6 @@ EXTRA_DIST = \
         os_win32/smartd_warning.cmd \
         os_win32/syslogevt.mc \
         os_win32/update-smart-drivedb.nsi \
-        os_win32/wbemcli_small.h \
         os_win32/versioninfo.rc.in \
         os_win32/wtssendmsg.c \
         $(docs_DATA) \
@@ -670,12 +692,12 @@ CLEANFILES = smartd.8 smartd.1m smartd.8.html smartd.8.html.tmp \
        smartctl.8.html smartctl.8.html.tmp smartctl.8.pdf \
        smartctl.8.txt smartd.conf.5 smartd.conf.4 smartd.conf.5.html \
        smartd.conf.5.html.tmp smartd.conf.5.pdf smartd.conf.5.txt \
-       smartd.initd smartd.freebsd.initd smartd.service \
-       smartd_warning.sh svnversion.h update-smart-drivedb \
-       update-smart-drivedb.8 update-smart-drivedb.1m \
-       update-smart-drivedb.8.html update-smart-drivedb.8.html.tmp \
-       update-smart-drivedb.8.pdf update-smart-drivedb.8.txt SMART \
-       $(am__append_26)
+       smartd.initd smartd.cygwin.initd smartd.freebsd.initd \
+       smartd.service smartd_warning.sh svnversion.h \
+       update-smart-drivedb update-smart-drivedb.8 \
+       update-smart-drivedb.1m update-smart-drivedb.8.html \
+       update-smart-drivedb.8.html.tmp update-smart-drivedb.8.pdf \
+       update-smart-drivedb.8.txt SMART $(am__append_27)
 
 # 'make maintainer-clean' also removes files generated by './autogen.sh'
 MAINTAINERCLEANFILES = \
@@ -758,6 +780,11 @@ MAN_FILTER = { \
     else \
       sed '/^\.\\" %IF ENABLE_CAPABILITIES/,/^\.\\" %ENDIF ENABLE_CAPABILITIES/ s,^,.\\"\# ,' ; \
     fi | \
+    if test -n '$(SYSTEMD_LDADD)'; then \
+      cat; \
+    else \
+      sed '/^\.\\" %IF ENABLE_SYSTEMD_NOTIFY/,/^\.\\" %ENDIF ENABLE_SYSTEMD_NOTIFY/ s,^,.\\"\# ,' ; \
+    fi | \
     if test '$(with_nvme_devicescan)' = 'yes'; then \
       cat; \
     else \
@@ -839,7 +866,7 @@ FIXHTML = sed -e '1s,^Content-type.*,,' \
 @OS_WIN32_MINGW_TRUE@  $(exedir_win32)/runcmda.exe \
 @OS_WIN32_MINGW_TRUE@  $(exedir_win32)/runcmdu.exe \
 @OS_WIN32_MINGW_TRUE@  $(exedir_win32)/wtssendmsg.exe \
-@OS_WIN32_MINGW_TRUE@  $(am__append_24)
+@OS_WIN32_MINGW_TRUE@  $(am__append_25)
 @OS_WIN32_MINGW_TRUE@FILES_WIN32 = $(EXEFILES_WIN32) \
 @OS_WIN32_MINGW_TRUE@  $(docdir_win32)/AUTHORS.txt \
 @OS_WIN32_MINGW_TRUE@  $(docdir_win32)/ChangeLog.txt \
@@ -857,7 +884,7 @@ FIXHTML = sed -e '1s,^Content-type.*,,' \
 @OS_WIN32_MINGW_TRUE@  $(docdir_win32)/smartd.8.pdf \
 @OS_WIN32_MINGW_TRUE@  $(docdir_win32)/smartd.conf.5.html \
 @OS_WIN32_MINGW_TRUE@  $(docdir_win32)/smartd.conf.5.pdf \
-@OS_WIN32_MINGW_TRUE@  $(am__append_25)
+@OS_WIN32_MINGW_TRUE@  $(am__append_26)
 
 # Note: Only use without options to be compatible with all variants
 @OS_WIN32_MINGW_TRUE@UNIX2DOS = unix2dos
@@ -1103,6 +1130,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dev_legacy.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt1.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/knowndrives.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nvmecmds.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nvmeprint.Po@am__quote@
@@ -1116,12 +1144,14 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_qnxnto.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)/popen_win32.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regcomp.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regex_internal.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/regexec.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scsiata.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scsicmds.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scsinvme.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@
@@ -1241,6 +1271,20 @@ regex_internal.obj: regex/regex_internal.c
 @AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
 
+popen_win32.o: os_win32/popen_win32.cpp
+@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT popen_win32.o -MD -MP -MF $(DEPDIR)/popen_win32.Tpo -c -o popen_win32.o `test -f 'os_win32/popen_win32.cpp' || echo '$(srcdir)/'`os_win32/popen_win32.cpp
+@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/popen_win32.Tpo $(DEPDIR)/popen_win32.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='os_win32/popen_win32.cpp' object='popen_win32.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o popen_win32.o `test -f 'os_win32/popen_win32.cpp' || echo '$(srcdir)/'`os_win32/popen_win32.cpp
+
+popen_win32.obj: os_win32/popen_win32.cpp
+@am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT popen_win32.obj -MD -MP -MF $(DEPDIR)/popen_win32.Tpo -c -o popen_win32.obj `if test -f 'os_win32/popen_win32.cpp'; then $(CYGPATH_W) 'os_win32/popen_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/popen_win32.cpp'; fi`
+@am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/popen_win32.Tpo $(DEPDIR)/popen_win32.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     $(AM_V_CXX)source='os_win32/popen_win32.cpp' object='popen_win32.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@     DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o popen_win32.obj `if test -f 'os_win32/popen_win32.cpp'; then $(CYGPATH_W) 'os_win32/popen_win32.cpp'; else $(CYGPATH_W) '$(srcdir)/os_win32/popen_win32.cpp'; fi`
+
 wmiquery.o: os_win32/wmiquery.cpp
 @am__fastdepCXX_TRUE@  $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT wmiquery.o -MD -MP -MF $(DEPDIR)/wmiquery.Tpo -c -o wmiquery.o `test -f 'os_win32/wmiquery.cpp' || echo '$(srcdir)/'`os_win32/wmiquery.cpp
 @am__fastdepCXX_TRUE@  $(AM_V_at)$(am__mv) $(DEPDIR)/wmiquery.Tpo $(DEPDIR)/wmiquery.Po
@@ -1896,7 +1940,7 @@ uninstall-sysconfDATA:
        echo " rm -f '$$f'"; \
        rm -f "$$f"
 
-utility.o: svnversion.h
+smartctl.o utility.o: svnversion.h
 
 # Get version info from SVN
 @IS_SVN_BUILD_TRUE@svnversion.h: ChangeLog Makefile $(svn_deps)
@@ -2107,14 +2151,16 @@ check:
 
 # Build NSIS installer
 # Note: Only option character '-' is also compatible with Linux version of makensis
-@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@$(distinst_win32): os_win32/installer.nsi distdir.mkdir $(FILES_WIN32)
+@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@$(distinst_win32): os_win32/installer.nsi smartctl_res.rc distdir.mkdir $(FILES_WIN32)
 @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      test -z '$(builddir_win64)' || ( cd $(builddir_win64) && make distdir-win32 )
 @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      @date=`sed -n 's,^.*DATE[^"]*"\([^"]*\)".*$$,\1,p' svnversion.h` && \
 @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      rev=`sed -n 's,^.*REV[^"]*"\([^"]*\)".*$$,r\1,p' svnversion.h` && \
+@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      version=`sed -n 's|^ *VALUE "FileVersion", "\([0-9.]*\)".*$$|\1|p' smartctl_res.rc` && \
+@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      yy=`echo "$$date" | sed -n 's,^20\([0-9][0-9]\).*$$,\1,p'`; yy="$${yy:-XX}" && \
 @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      verstr="$(PACKAGE_VERSION) $$date $$rev "$(BUILD_INFO) && \
 @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      d64= && if [ -n '$(builddir_win64)' ]; then d64='-DINPDIR64=$(builddir_win64)/$(PACKAGE)-$(VERSION).win64'; fi && \
-@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      echo "'$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSTR='$$verstr' $<" && \
-@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      '$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSTR="$$verstr" $<
+@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      echo "'$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSION=$$version -DYY=$$yy -DVERSTR='$$verstr' $<" && \
+@OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      '$(MAKENSIS)' -V2 -NOCD -DINPDIR=$(distdir_win32) $$d64 -DOUTFILE=$@ -DVERSION=$$version -DYY=$$yy -DVERSTR="$$verstr" $<
 @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      md5sum $@ > $@.md5
 @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      sha1sum $@ > $@.sha1
 @OS_WIN32_MINGW_TRUE@@OS_WIN32_NSIS_TRUE@      sha256sum $@ > $@.sha256
@@ -2208,7 +2254,7 @@ check:
 
 @OS_WIN32_MINGW_TRUE@$(srcdir)/os_win32/vc$(vcver)/config.h: config.h Makefile
 @OS_WIN32_MINGW_TRUE@  sed -e '1i/* os_win32/vc$(vcver)/config.h.  Generated from config.h by Makefile.  */' \
-@OS_WIN32_MINGW_TRUE@      -e 's,^#define HAVE_\(ATTR_PACKED\|GETTIMEOFDAY\|[DK_]*NTDDDISK_H\|STRINGS_H\|UNISTD_H\|WORKING_SNPRINTF\) 1$$,/* #undef HAVE_\1 */ /* VC$(vcver) */,' \
+@OS_WIN32_MINGW_TRUE@      -e 's,^#define HAVE_\(ATTR_PACKED\|GETTIMEOFDAY\|[DK_]*NTDDDISK_H\|LONG_DOUBLE_WIDER\|STRINGS_H\|UNISTD_H\) 1$$,/* #undef HAVE_\1 */ /* VC$(vcver) */,' \
 @OS_WIN32_MINGW_TRUE@      -e 's,^\(#define SMARTMONTOOLS_BUILD_HOST "[^-]*\)[^"]*,\1-pc-w32vc$(vcver),' $< > $@
 
 @OS_WIN32_MINGW_TRUE@$(srcdir)/os_win32/vc$(vcver)/svnversion.h: svnversion.h
diff --git a/NEWS b/NEWS
index b070e9ff7454c25c2b7063468d34d0f87d3e1d7f..e21d6920b3654a0140d717129721531f3cd7210f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,10 +1,58 @@
 smartmontools NEWS
 ------------------
-$Id: NEWS 4594 2017-11-05 15:21:35Z chrfranke $
+$Id: NEWS 4883 2018-12-30 14:48:54Z chrfranke $
 
 The most up-to-date version of this file is:
 https://sourceforge.net/p/smartmontools/code/HEAD/tree/trunk/smartmontools/NEWS
 
+Date 2018-12-30
+Summary: smartmontools release 7.0
+-----------------------------------------------------------
+- smartctl: New options '-j' and '--json[=giosu]' to enable experimental
+  JSON output mode.  Also supports an alternative flat format suitable
+  for grep ('--json=g').
+- smartctl '-l devstat': Fix for logs with 256 sectors.
+- smartctl '-l error': Prints SCSI Pending Defects log page if supported.
+- smartctl '-H': Prints SCSI Format Status log page if supported.
+- smartctl '-x': Now includes '-l defects'.
+- NVMe/USB: Device type '-d sntjmicron' for NVMe drives behind JMicron USB
+  to NVMe bridges.
+- NVMe: SMART/Health Information and Error Information are always read with
+  broadcast NSID.
+- SCSI: Various improvements for the decoding of log pages.
+- SCSI: Improved handling of READ CAPACITY command.
+- SCSI: Support for SAS host managed drives.
+- New option '-d scsi+TYPE' to disable SAT auto detection for 'aacraid'
+  and 'cciss'.
+- update-smart-drivedb: New option '-u github'.
+- update-smart-drivedb: New signing key.
+- configure: New options '--with-cxx11-regex' and '--with-libsystemd'.
+- configure: Default for '--with-nvme-devicescan' is now 'yes' on Linux
+  and Windows.
+- configure: Default for '--with-scriptpath' now includes '/usr/local/bin'.
+- configure: Options '-with-initscriptdir=[auto|yes]' are no longer
+  supported.
+- Reworked handling of unaligned LE and BE integers.
+- Dropped various backward compatibility fixes for very old compilers.
+- Replaced GPL header with 'SPDX-License-Identifier' in all source files.
+- HDD, SSD and USB additions to drive database.
+- Always use '-d sat' for USB vendor IDs with known SAT support.
+- Linux: Device scan with '-d by-id' scans '/dev/disk/by-id/*' symlinks.
+- Linux: Dropped device scan support for devfs.
+- Linux: Fixed device scan with multiple '-d TYPE' options.
+- Linux: Fixed device scan crash on missing '/proc/devices'.
+- Linux: USB ID detection also for '/dev/sgN'.
+- Linux: Compile fix for systems with musl libc.
+- Linux smartd: Could now run as systemd service with 'Type=notify' set.
+- FreeBSD: NVMe device scanning.
+- FreeBSD: Various compile fixes.
+- NetBSD: Fixed regression in name based device type detection.
+- NetBSD big endian: Fixed regression in ATA code.
+- NetBSD big endian: Fixed byte ordering of ATA IDENTIFY data from USB/SAT
+  drives.
+- Windows: Improved search for actual CSMI port number.
+- Windows: Installer includes VERSIONINFO resource.
+
 Date 2017-11-05
 Summary: smartmontools release 6.6
 -----------------------------------------------------------
diff --git a/README b/README
index f7dc1036e2cf4914c5b05a2942d24d96118ab7c5..499edc8fc8ca857935902bf1a56b1478c5391eee 100644 (file)
--- a/README
+++ b/README
@@ -3,7 +3,7 @@ smartmontools - S.M.A.R.T. utility toolset for Darwin/Mac
 OSX, FreeBSD, Linux, NetBSD, OpenBSD, Solaris, and Windows.
 ==========================================================
 
-$Id: README 4431 2017-08-08 19:38:15Z chrfranke $
+$Id: README 4760 2018-08-19 18:45:53Z chrfranke $
 
 == HOME ==
 The home for smartmontools is located at:
@@ -20,7 +20,7 @@ You will find a mailing list for support and other questions at:
 
 == COPYING ==
 Copyright (C) 2002-9 Bruce Allen
-Copyright (C) 2004-15 Christian Franke
+Copyright (C) 2004-18 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
@@ -30,6 +30,8 @@ version.
 You should have received a copy of the GNU General Public License (for
 example COPYING).  If not, see <http://www.gnu.org/licenses/>.
 
+SPDX-License-Identifier: GPL-2.0-or-later
+
 
 == CREDITS ==
 See AUTHORS file.
index d62fbca0ca35dad41e56857b518144e1e95e3428..5bc4d63a636ecf7810d0a3b777ecefdaaa332927 100644 (file)
--- a/aacraid.h
+++ b/aacraid.h
@@ -2,14 +2,7 @@
  * Copyright (C) 2014 Raghava Aditya <Raghava.Aditya@pmcs.com>
  * Copyright (C) 2015 Nidhi Malhotra <Nidhi.Malhotra@pmcs.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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 // Check windows
index ad4eaddffd8ba27d24eab3dcb17d07b3c9d60446..3527b0f4b9ef5fe8831d0292483a414389e2f370 100644 (file)
@@ -6,21 +6,14 @@
  * Copyright (C) 2003-8 Philip Williams
  * Copyright (C) 2012 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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "atacmdnames.h"
 #include <stdlib.h>
 #include <stdio.h>
 
-const char * atacmdnames_cpp_cvsid = "$Id: atacmdnames.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
+const char * atacmdnames_cpp_cvsid = "$Id: atacmdnames.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
   ATACMDNAMES_H_CVSID;
 
 const char cmd_reserved[]        = "[RESERVED]";
index 63f09d89afd0f5722e2b5fb57bf8e33de79f9d87..0842e05879efdb383dc2acfc5d3d287ed344e067 100644 (file)
@@ -8,21 +8,13 @@
  *
  * Copyright (C) 2003-8 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef ATACMDNAMES_H_
 #define ATACMDNAMES_H_
 
-#define ATACMDNAMES_H_CVSID "$Id: atacmdnames.h 4431 2017-08-08 19:38:15Z chrfranke $\n"
+#define ATACMDNAMES_H_CVSID "$Id: atacmdnames.h 4760 2018-08-19 18:45:53Z chrfranke $\n"
 
 /* Returns the name of the command (and possibly sub-command) with the given
    command code and feature register values. */
index d92d4cd523ab05d504ed4d556c73c1f173940f90..e708e6bab857e007c26cdab1f8f8033060f7d484 100644 (file)
@@ -4,39 +4,29 @@
  * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-11 Bruce Allen
- * Copyright (C) 2008-17 Christian Franke
+ * Copyright (C) 2008-18 Christian Franke
  * 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, see <http://www.gnu.org/licenses/>.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- * 
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
+#include "config.h"
+#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
+
+#include <inttypes.h>
 #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 "knowndrives.h"  // get_default_attr_defs()
 #include "utility.h"
 #include "dev_ata_cmd_set.h" // for parsed_ata_device
 
-const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp 4582 2017-11-03 20:54:56Z chrfranke $"
+const char * atacmds_cpp_cvsid = "$Id: atacmds.cpp 4842 2018-12-02 16:07:26Z chrfranke $"
                                  ATACMDS_H_CVSID;
 
 // Print ATA debug messages?
@@ -52,7 +42,7 @@ bool dont_print_serial_number = false;
 
 // SMART RETURN STATUS yields SMART_CYL_HI,SMART_CYL_LOW to indicate drive
 // is healthy and SRET_STATUS_HI_EXCEEDED,SRET_STATUS_MID_EXCEEDED to
-// indicate that a threshhold exceeded condition has been detected.
+// indicate that a threshold exceeded condition has been detected.
 // Those values (byte pairs) are placed in ATA register "LBA 23:8".
 #define SRET_STATUS_HI_EXCEEDED 0x2C
 #define SRET_STATUS_MID_EXCEEDED 0xF4
@@ -267,7 +257,7 @@ bool parse_attribute_def(const char * opt, ata_vendor_attr_defs & defs,
 
 
 // Return a multiline string containing a list of valid arguments for
-// parse_attribute_def().  The strings are preceeded by tabs and followed
+// parse_attribute_def().  The strings are preceded by tabs and followed
 // (except for the last) by newlines.
 std::string create_vendor_attribute_arg_list()
 {
@@ -362,17 +352,12 @@ static void invalidate_serno(ata_identify_device * id)
     sum += b[i]; sum -= b[i] = 0x00;
   }
 
-#ifndef __NetBSD__
-  bool must_swap = !!isbigendian();
-  if (must_swap)
+  if (isbigendian())
     SWAPV(id->words088_255[255-88]);
-#endif
   if ((id->words088_255[255-88] & 0x00ff) == 0x00a5)
     id->words088_255[255-88] += sum << 8;
-#ifndef __NetBSD__
-  if (must_swap)
+  if (isbigendian())
     SWAPV(id->words088_255[255-88]);
-#endif
 }
 
 static const char * const commandstrings[]={
@@ -778,20 +763,9 @@ static void trim(char * out, const char * in)
 // Convenience function for formatting strings from ata_identify_device
 void ata_format_id_string(char * out, const unsigned char * in, int n)
 {
-  bool must_swap = true;
-#ifdef __NetBSD__
-  /* NetBSD kernel delivers IDENTIFY data in host byte order (but all else is LE) */
-  // TODO: Handle NetBSD case in os_netbsd.cpp
-  if (isbigendian())
-    must_swap = !must_swap;
-#endif
-
   char tmp[65];
   n = n > 64 ? 64 : n;
-  if (!must_swap)
-    strncpy(tmp, (const char *)in, n);
-  else
-    swapbytes(tmp, (const char *)in, n);
+  swapbytes(tmp, (const char *)in, n);
   tmp[n] = '\0';
   trim(out, tmp);
 }
@@ -873,10 +847,7 @@ int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_s
   if (raw_buf)
     memcpy(raw_buf, buf, sizeof(*buf));
 
-#ifndef __NetBSD__
   // if machine is big-endian, swap byte order as needed
-  // NetBSD kernel delivers IDENTIFY data in host byte order
-  // TODO: Handle NetBSD case in os_netbsd.cpp
   if (isbigendian()){
     // swap various capability words that are needed
     unsigned i;
@@ -887,7 +858,6 @@ int ata_read_identity(ata_device * device, ata_identify_device * buf, bool fix_s
     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))
@@ -1277,7 +1247,7 @@ int ataWriteSelectiveSelfTestLog(ata_device * device, ata_selective_selftest_arg
     int mode = args.span[i].mode;
     uint64_t start = args.span[i].start;
     uint64_t end   = args.span[i].end;
-    if (mode == SEL_CONT) {// redo or next dependig on last test status
+    if (mode == SEL_CONT) {// redo or next depending on last test status
       switch (sv->self_test_exec_status >> 4) {
         case 1: case 2: // Aborted/Interrupted by host
           pout("Continue Selective Self-Test: Redo last span\n");
@@ -1293,7 +1263,7 @@ int ataWriteSelectiveSelfTestLog(ata_device * device, ata_selective_selftest_arg
     if (   (mode == SEL_REDO || mode == SEL_NEXT)
         && prev_args && i < prev_args->num_spans
         && !data->span[i].start && !data->span[i].end) {
-      // Some drives do not preserve the selective self-test log accross
+      // Some drives do not preserve the selective self-test log across
       // power-cyles.  If old span on drive is cleared use span provided
       // by caller.  This is used by smartd (first span only).
       data->span[i].start = prev_args->span[i].start;
@@ -1760,7 +1730,7 @@ int TestTime(const ata_smart_values *data, int testtype)
 // word 84 and 87.  Top two bits must match the pattern 01. BEFORE
 // ATA-6 these top two bits still had to match the pattern 01, but the
 // remaining bits were reserved (==0).
-int isSmartErrorLogCapable (const ata_smart_values * data, const ata_identify_device * identity)
+bool isSmartErrorLogCapable(const ata_smart_values * data, const ata_identify_device * identity)
 {
   unsigned short word84=identity->command_set_extension;
   unsigned short word87=identity->csf_default;
@@ -1768,18 +1738,18 @@ int isSmartErrorLogCapable (const ata_smart_values * data, const ata_identify_de
   int isata7=identity->major_rev_num & (0x01<<7);
 
   if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x01))
-    return 1;
+    return true;
   
   if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x01))
-    return 1;
+    return true;
   
   // otherwise we'll use the poorly documented capability bit
-  return data->errorlog_capability & 0x01;
+  return !!(data->errorlog_capability & 0x01);
 }
 
 // See previous function.  If the error log exists then the self-test
 // log should (must?) also exist.
-int isSmartTestLogCapable (const ata_smart_values * data, const ata_identify_device *identity)
+bool isSmartTestLogCapable(const ata_smart_values * data, const ata_identify_device *identity)
 {
   unsigned short word84=identity->command_set_extension;
   unsigned short word87=identity->csf_default;
@@ -1787,18 +1757,18 @@ int isSmartTestLogCapable (const ata_smart_values * data, const ata_identify_dev
   int isata7=identity->major_rev_num & (0x01<<7);
 
   if ((isata6 || isata7) && (word84>>14) == 0x01 && (word84 & 0x02))
-    return 1;
+    return true;
   
   if ((isata6 || isata7) && (word87>>14) == 0x01 && (word87 & 0x02))
-    return 1;
+    return true;
 
 
   // otherwise we'll use the poorly documented capability bit
-  return data->errorlog_capability & 0x01;
+  return !!(data->errorlog_capability & 0x01);
 }
 
 
-int isGeneralPurposeLoggingCapable(const ata_identify_device *identity)
+bool isGeneralPurposeLoggingCapable(const ata_identify_device *identity)
 {
   unsigned short word84=identity->command_set_extension;
   unsigned short word87=identity->csf_default;
@@ -1810,7 +1780,7 @@ int isGeneralPurposeLoggingCapable(const ata_identify_device *identity)
   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));
+    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
@@ -1818,50 +1788,10 @@ int isGeneralPurposeLoggingCapable(const ata_identify_device *identity)
   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));
+    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(const ata_smart_values *data)
-{
-  return data->offline_data_collection_capability & 0x01;
-}
-
-// Note in the ATA-5 standard, the following bit is listed as "Vendor
-// Specific".  So it may not be reliable. The only use of this that I
-// have found is in IBM drives, where it is well-documented.  See for
-// example page 170, section 13.32.1.18 of the IBM Travelstar 40GNX
-// hard disk drive specifications page 164 Revision 1.1 22 Apr 2002.
-int isSupportAutomaticTimer(const ata_smart_values * data)
-{
-  return data->offline_data_collection_capability & 0x02;
-}
-int isSupportOfflineAbort(const ata_smart_values *data)
-{
-  return data->offline_data_collection_capability & 0x04;
-}
-int isSupportOfflineSurfaceScan(const ata_smart_values * data)
-{
-   return data->offline_data_collection_capability & 0x08;
-}
-int isSupportSelfTest (const ata_smart_values * data)
-{
-   return data->offline_data_collection_capability & 0x10;
-}
-int isSupportConveyanceSelfTest(const ata_smart_values * data)
-{
-   return data->offline_data_collection_capability & 0x20;
-}
-int isSupportSelectiveSelfTest(const ata_smart_values * data)
-{
-   return data->offline_data_collection_capability & 0x40;
+  return false;
 }
 
 // Get attribute state
@@ -2580,169 +2510,6 @@ int ataSetSCTErrorRecoveryControltime(ata_device * device, unsigned type, unsign
 }
 
 
-// Print one self-test log entry.
-// Returns:
-// -1: self-test failed
-//  1: extended self-test completed without error
-//  0: otherwise
-int ataPrintSmartSelfTestEntry(unsigned testnum, unsigned char test_type,
-                               unsigned char test_status,
-                               unsigned short timestamp,
-                               uint64_t failing_lba,
-                               bool print_error_only, bool & print_header)
-{
-  // Check status and type for return value
-  int retval = 0;
-  switch (test_status >> 4) {
-    case 0x0:
-      if ((test_type & 0x0f) == 0x02)
-        retval = 1; // extended self-test completed without error
-      break;
-    case 0x3: case 0x4:
-    case 0x5: case 0x6:
-    case 0x7: case 0x8:
-      retval = -1; // self-test failed
-      break;
-  }
-
-  if (retval >= 0 && print_error_only)
-    return retval;
-
-  std::string msgtest;
-  switch (test_type) {
-    case 0x00: msgtest = "Offline";            break;
-    case 0x01: msgtest = "Short offline";      break;
-    case 0x02: msgtest = "Extended offline";   break;
-    case 0x03: msgtest = "Conveyance offline"; break;
-    case 0x04: msgtest = "Selective offline";  break;
-    case 0x7f: msgtest = "Abort offline test"; break;
-    case 0x81: msgtest = "Short captive";      break;
-    case 0x82: msgtest = "Extended captive";   break;
-    case 0x83: msgtest = "Conveyance captive"; break;
-    case 0x84: msgtest = "Selective captive";  break;
-    default:
-      if ((0x40 <= test_type && test_type <= 0x7e) || 0x90 <= test_type)
-        msgtest = strprintf("Vendor (0x%02x)", test_type);
-      else
-        msgtest = strprintf("Reserved (0x%02x)", test_type);
-  }
-
-  std::string msgstat;
-  switch (test_status >> 4) {
-    case 0x0: msgstat = "Completed without error";       break;
-    case 0x1: msgstat = "Aborted by host";               break;
-    case 0x2: msgstat = "Interrupted (host reset)";      break;
-    case 0x3: msgstat = "Fatal or unknown error";        break;
-    case 0x4: msgstat = "Completed: unknown failure";    break;
-    case 0x5: msgstat = "Completed: electrical failure"; break;
-    case 0x6: msgstat = "Completed: servo/seek failure"; break;
-    case 0x7: msgstat = "Completed: read failure";       break;
-    case 0x8: msgstat = "Completed: handling damage??";  break;
-    case 0xf: msgstat = "Self-test routine in progress"; break;
-    default:  msgstat = strprintf("Unknown status (0x%x)", test_status >> 4);
-  }
-
-  // Print header once
-  if (print_header) {
-    print_header = false;
-    pout("Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error\n");
-  }
-
-  char msglba[32];
-  if (retval < 0 && failing_lba < 0xffffffffffffULL)
-    snprintf(msglba, sizeof(msglba), "%" PRIu64, failing_lba);
-  else {
-    msglba[0] = '-'; msglba[1] = 0;
-  }
-
-  pout("#%2u  %-19s %-29s %1d0%%  %8u         %s\n", testnum,
-       msgtest.c_str(), msgstat.c_str(), test_status & 0x0f, timestamp, msglba);
-
-  return retval;
-}
-
-// Print Smart self-test log, used by smartctl and smartd.
-// return value is:
-// bottom 8 bits: number of entries found where self-test showed an error
-// remaining bits: if nonzero, power on hours of last self-test where error was found
-int ataPrintSmartSelfTestlog(const ata_smart_selftestlog * data, bool allentries,
-                             firmwarebug_defs firmwarebugs)
-{
-  if (allentries)
-    pout("SMART Self-test log structure revision number %d\n",(int)data->revnumber);
-  if (data->revnumber != 0x0001 && allentries && !firmwarebugs.is_set(BUG_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");
-    return 0;
-  }
-
-  bool noheaderprinted = true;
-  int errcnt = 0, hours = 0, igncnt = 0;
-  int testno = 0, ext_ok_testno = -1;
-
-  // print log
-  for (int i = 20; i >= 0; i--) {
-    // log is a circular buffer
-    int j = (i+data->mostrecenttest)%21;
-    const ata_smart_selftestlog_struct * log = data->selftest_struct+j;
-
-    if (nonempty(log, sizeof(*log))) {
-      // count entry based on non-empty structures -- needed for
-      // Seagate only -- other vendors don't have blank entries 'in
-      // the middle'
-      testno++;
-
-      // T13/1321D revision 1c: (Data structure Rev #1)
-
-      //The failing LBA shall be the LBA of the uncorrectable sector
-      //that caused the test to fail. If the device encountered more
-      //than one uncorrectable sector during the test, this field
-      //shall indicate the LBA of the first uncorrectable sector
-      //encountered. If the test passed or the test failed for some
-      //reason other than an uncorrectable sector, the value of this
-      //field is undefined.
-
-      // This is true in ALL ATA-5 specs
-      uint64_t lba48 = (log->lbafirstfailure < 0xffffffff ? log->lbafirstfailure : 0xffffffffffffULL);
-
-      // Print entry
-      int state = ataPrintSmartSelfTestEntry(testno,
-        log->selftestnumber, log->selfteststatus,
-        log->timestamp, lba48, !allentries, noheaderprinted);
-
-      if (state < 0) {
-        // Self-test showed an error
-        if (ext_ok_testno < 0) {
-          errcnt++;
-
-          // keep track of time of most recent error
-          if (!hours)
-            hours = log->timestamp;
-        }
-        else
-          // Newer successful extended self-test exits
-          igncnt++;
-      }
-      else if (state > 0 && ext_ok_testno < 0) {
-        // Latest successful extended self-test
-        ext_ok_testno = testno;
-      }
-    }
-  }
-
-  if (igncnt)
-    pout("%d of %d failed self-tests are outdated by newer successful extended offline self-test #%2d\n",
-      igncnt, igncnt+errcnt, ext_ok_testno);
-
-  if (!allentries && !noheaderprinted)
-    pout("\n");
-
-  return ((hours << 8) | errcnt);
-}
-
-
 /////////////////////////////////////////////////////////////////////////////
 // Pseudo-device to parse "smartctl -r ataioctl,2 ..." output and simulate
 // an ATA device with same behaviour
@@ -2806,7 +2573,8 @@ static int name2command(const char * s)
   return -1;
 }
 
-static bool matchcpy(char * dest, size_t size, const char * src, const regmatch_t & srcmatch)
+static bool matchcpy(char * dest, size_t size, const char * src,
+  const regular_expression::match_range & srcmatch)
 {
   if (srcmatch.rm_so < 0)
     return false;
@@ -2818,7 +2586,7 @@ static bool matchcpy(char * dest, size_t size, const char * src, const regmatch_
   return true;
 }
 
-static inline int matchtoi(const char * src, const regmatch_t & srcmatch, int defval)
+static inline int matchtoi(const char * src, const regular_expression::match_range & srcmatch, int defval)
 {
   if (srcmatch.rm_so < 0)
     return defval;
@@ -2837,7 +2605,7 @@ parsed_ata_device::parsed_ata_device(smart_interface * intf, const char * dev_na
 
 parsed_ata_device::~parsed_ata_device() throw()
 {
-  close();
+  parsed_ata_device::close();
 }
 
 bool parsed_ata_device::is_open() const
@@ -2884,7 +2652,7 @@ bool parsed_ata_device::open()
   ")"; // )
 
   // Compile regex
-  const regular_expression regex(pattern, REG_EXTENDED);
+  const regular_expression regex(pattern);
 
   // Parse buffer
   const char * errmsg = 0;
@@ -2894,7 +2662,7 @@ bool parsed_ata_device::open()
     if (!(line[0] == 'R' || line[0] == '=' || line[0] == ' '))
       continue;
     const int nmatch = 1+11;
-    regmatch_t match[nmatch];
+    regular_expression::match_range match[nmatch];
     if (!regex.execute(line, nmatch, match))
       continue;
 
index cbb20b62628969ccd11ec1adaa5e8c7d45e8c5fa..bd08ce8dbba7eab767598da01825f7e3408cbe3c 100644 (file)
--- a/atacmds.h
+++ b/atacmds.h
@@ -7,25 +7,13 @@
  * Copyright (C) 2008-17 Christian Franke
  * 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, see <http://www.gnu.org/licenses/>.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef ATACMDS_H_
 #define ATACMDS_H_
 
-#define ATACMDS_H_CVSID "$Id: atacmds.h 4419 2017-04-17 13:20:31Z chrfranke $"
+#define ATACMDS_H_CVSID "$Id: atacmds.h 4848 2018-12-05 18:30:46Z chrfranke $"
 
 #include "dev_interface.h" // ata_device
 
@@ -93,7 +81,7 @@ typedef enum {
 
 // 48-bit commands
 #define ATA_READ_LOG_EXT                0x2F
-#define ATA_WRITE_LOG_EXT               0x3F
+#define ATA_WRITE_LOG_EXT               0x3f
 
 // ATA Specification Feature Register Values (SMART Subcommands).
 // Note that some are obsolete as of ATA-7.
@@ -220,7 +208,7 @@ struct ata_smart_values {
   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 vendor_specific_366; // Maxtor & IBM current segment pointer
   unsigned char offline_data_collection_capability;
   unsigned short int smart_capability;
   unsigned char errorlog_capability;
@@ -526,7 +514,7 @@ ASSERT_SIZEOF_STRUCT(ata_selective_self_test_log, 512);
 //   T13/1699-D Revision 3f (Working Draft), December 11, 2006.
 
 // SCT Status response (read with SMART_READ_LOG page 0xe0)
-// Table 182 of T13/BSR INCITS 529 (ACS-4) Revision 04, August 25, 2014
+// Table 194 of T13/BSR INCITS 529 (ACS-4) Revision 20, October 26, 2017
 #pragma pack(1)
 struct ata_sct_status_response
 {
@@ -547,7 +535,7 @@ struct ata_sct_status_response
   signed char max_temp;             // 202: Maximum temperature this power cycle
   signed char life_min_temp;        // 203: Minimum lifetime temperature
   signed char life_max_temp;        // 204: Maximum lifetime temperature
-  unsigned char byte205;            // 205: reserved (T13/e06152r0-2: Average lifetime temperature)
+  signed char max_op_limit;         // 205: Specified maximum operating temperature (ACS-4)
   unsigned int over_limit_count;    // 206-209: # intervals since last reset with temperature > Max Op Limit
   unsigned int under_limit_count;   // 210-213: # intervals since last reset with temperature < Min Op Limit
   unsigned short smart_status;      // 214-215: LBA(32:8) of SMART RETURN STATUS (0, 0x2cf4, 0xc24f) (ACS-4)
@@ -855,7 +843,7 @@ int ataSmartSupport(const ata_identify_device * drive);
 int ataGetSetSCTWriteCacheReordering(ata_device * device, bool enable, bool persistent, bool set);
 
 // Return values:
-// 1: Write cache controled by ATA Set Features command
+// 1: Write cache controlled by ATA Set Features command
 // 2: Force enable write cache
 // 3: Force disable write cache
 int ataGetSetSCTWriteCache(ata_device * device, unsigned short state, bool persistent, bool set);
@@ -868,25 +856,39 @@ int ataIsSmartEnabled(const ata_identify_device * drive);
 
 int ataSmartStatus2(ata_device * device);
 
-int isSmartErrorLogCapable(const ata_smart_values * data, const ata_identify_device * identity);
+bool isSmartErrorLogCapable(const ata_smart_values * data, const ata_identify_device * identity);
 
-int isSmartTestLogCapable(const ata_smart_values * data, const ata_identify_device * identity);
+bool isSmartTestLogCapable(const ata_smart_values * data, const ata_identify_device * identity);
 
-int isGeneralPurposeLoggingCapable(const ata_identify_device * identity);
+bool isGeneralPurposeLoggingCapable(const ata_identify_device * identity);
 
-int isSupportExecuteOfflineImmediate(const ata_smart_values * data);
+// 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).
+inline bool isSupportExecuteOfflineImmediate(const ata_smart_values *data)
+  { return !!(data->offline_data_collection_capability & 0x01); }
 
-int isSupportAutomaticTimer(const ata_smart_values * data);
+// TODO: Remove uses of this check.  Bit 1 is vendor specific since ATA-4.
+// Automatic timer support was only documented for very old IBM drives
+// (for example IBM Travelstar 40GNX).
+inline bool isSupportAutomaticTimer(const ata_smart_values * data)
+  { return !!(data->offline_data_collection_capability & 0x02); }
 
-int isSupportOfflineAbort(const ata_smart_values * data);
+inline bool isSupportOfflineAbort(const ata_smart_values *data)
+  { return !!(data->offline_data_collection_capability & 0x04); }
 
-int isSupportOfflineSurfaceScan(const ata_smart_values * data);
+inline bool isSupportOfflineSurfaceScan(const ata_smart_values * data)
+  { return !!(data->offline_data_collection_capability & 0x08); }
 
-int isSupportSelfTest(const ata_smart_values * data);
+inline bool isSupportSelfTest(const ata_smart_values * data)
+  { return !!(data->offline_data_collection_capability & 0x10); }
 
-int isSupportConveyanceSelfTest(const ata_smart_values * data);
+inline bool isSupportConveyanceSelfTest(const ata_smart_values * data)
+  { return !!(data->offline_data_collection_capability & 0x20); }
 
-int isSupportSelectiveSelfTest(const ata_smart_values * data);
+inline bool isSupportSelectiveSelfTest(const ata_smart_values * data)
+  { return !!(data->offline_data_collection_capability & 0x40); }
 
 inline bool isSCTCapable(const ata_identify_device *drive)
   { return !!(drive->words088_255[206-88] & 0x01); } // 0x01 = SCT support
@@ -984,21 +986,6 @@ const char * get_valid_firmwarebug_args();
 // This function is exported to give low-level capability
 int smartcommandhandler(ata_device * device, smart_command_set command, int select, char *data);
 
-// Print one self-test log entry.
-// Returns:
-// -1: failed self-test
-//  1: extended self-test completed without error
-//  0: otherwise
-int ataPrintSmartSelfTestEntry(unsigned testnum, unsigned char test_type,
-                               unsigned char test_status,
-                               unsigned short timestamp,
-                               uint64_t failing_lba,
-                               bool print_error_only, bool & print_header);
-
-// Print Smart self-test log, used by smartctl and smartd.
-int ataPrintSmartSelfTestlog(const ata_smart_selftestlog * data, bool allentries,
-                             firmwarebug_defs firmwarebugs);
-
 // Get capacity and sector sizes from IDENTIFY data
 struct ata_size_info
 {
@@ -1017,17 +1004,6 @@ void ata_format_id_string(char * out, const unsigned char * in, int n);
 // Utility routines.
 unsigned char checksum(const void * data);
 
-void swap2(char *location);
-void swap4(char *location);
-void swap8(char *location);
-// Typesafe variants using overloading
-inline void swapx(unsigned short * p)
-  { swap2((char*)p); }
-inline void swapx(unsigned int * p)
-  { swap4((char*)p); }
-inline void swapx(uint64_t * p)
-  { swap8((char*)p); }
-
 // Return pseudo-device to parse "smartctl -r ataioctl,2 ..." output
 // and simulate an ATA device with same behaviour
 ata_device * get_parsed_ata_device(smart_interface * intf, const char * dev_name);
index c56ba888a31190c7bb2762f1f515dac14a068de4..b778c38661d0f026c215655a61a5112a3f2c3622 100644 (file)
@@ -3,27 +3,22 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2012-17 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2012-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
+#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
+
 #include "ataidentify.h"
 
-const char * ataidentify_cpp_cvsid = "$Id: ataidentify.cpp 4573 2017-10-29 15:13:58Z chrfranke $"
+const char * ataidentify_cpp_cvsid = "$Id: ataidentify.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
   ATAIDENTIFY_H_CVSID;
 
-#include "int64.h"
 #include "utility.h"
 
+#include <inttypes.h>
 
 // Table 12 of X3T10/0948D (ATA-2) Revision 4c, March 18, 1996
 // Table 9 of X3T13/2008D (ATA-3) Revision 7b, January 27, 1997
index 5efb4275952cf1335f05bc655998201e71df49de..64b6757a3df1afbc539cf6433fe43971b27c2d9d 100644 (file)
@@ -5,20 +5,13 @@
  *
  * Copyright (C) 2012 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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef ATAIDENTIFY_H
 #define ATAIDENTIFY_H
 
-#define ATAIDENTIFY_H_CVSID "$Id: ataidentify.h 4431 2017-08-08 19:38:15Z chrfranke $"
+#define ATAIDENTIFY_H_CVSID "$Id: ataidentify.h 4760 2018-08-19 18:45:53Z chrfranke $"
 
 void ata_print_identify_data(const void * id, bool all_words, int bit_level);
 
index 451622214cf6d7f589b89a78808358c62e82fefb..1ca222fcb6c36e01756e9efc9af22ed399ebeb69 100644 (file)
@@ -4,43 +4,33 @@
  * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-11 Bruce Allen
- * Copyright (C) 2008-17 Christian Franke
+ * Copyright (C) 2008-18 Christian Franke
  * 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, see <http://www.gnu.org/licenses/>.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
+#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
 
 #include <ctype.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
-#include "int64.h"
 #include "atacmdnames.h"
 #include "atacmds.h"
 #include "ataidentify.h"
 #include "dev_interface.h"
 #include "ataprint.h"
 #include "smartctl.h"
+#include "sg_unaligned.h"
 #include "utility.h"
 #include "knowndrives.h"
 
-const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 4573 2017-10-29 15:13:58Z chrfranke $"
+const char * ataprint_cpp_cvsid = "$Id: ataprint.cpp 4842 2018-12-02 16:07:26Z chrfranke $"
                                   ATAPRINT_H_CVSID;
 
 
@@ -414,9 +404,10 @@ static inline std::string format_st_er_desc(
 
 static const char * get_form_factor(unsigned short word168)
 {
+  // Bits 0:3 are the form factor
   // Table A.32 of T13/2161-D (ACS-3) Revision 4p, September 19, 2013
   // Table 236 of T13/BSR INCITS 529 (ACS-4) Revision 04, August 25, 2014
-  switch (word168) {
+  switch (word168 & 0xF) {
     case 0x1: return "5.25 inches";
     case 0x2: return "3.5 inches";
     case 0x3: return "2.5 inches";
@@ -561,15 +552,15 @@ static const char * get_sata_version(unsigned short word222)
     case  2: return "SATA II Ext";
     case  1: return "SATA 1.0a";
     case  0: return "ATA8-AST";
-    default: return "Unknown";
+    default: return 0;
   }
 }
 
-static const char * get_sata_speed(int level)
+static const char * get_sata_speed(int speed)
 {
-  if (level <= 0)
+  if (speed <= 0)
     return 0;
-  switch (level) {
+  switch (speed) {
     default: return ">6.0 Gb/s (7)";
     case 6:  return ">6.0 Gb/s (6)";
     case 5:  return ">6.0 Gb/s (5)";
@@ -580,23 +571,48 @@ static const char * get_sata_speed(int level)
   }
 }
 
-static const char * get_sata_maxspeed(const ata_identify_device * drive)
+static void jset_sata_speed(const char * key, int value, int speed, const char * str)
 {
-  unsigned short word076 = drive->words047_079[76-47];
-  if (word076 & 0x0001)
-    return 0;
-  return get_sata_speed(find_msb(word076 & 0x00fe));
+  if (speed <= 0)
+    return;
+  json::ref jref = jglb["interface_speed"][key];
+  jref["sata_value"] = value;
+  if (str)
+    jref["string"] = str;
+  int ups;
+  switch (speed) {
+    case 3: ups = 60; break;
+    case 2: ups = 30; break;
+    case 1: ups = 15; break;
+    default: return;
+  }
+  jref["units_per_second"] = ups;
+  jref["bits_per_unit"] = 100000000;
 }
 
-static const char * get_sata_curspeed(const ata_identify_device * drive)
+static void print_sata_version_and_speed(unsigned short word222,
+                                         unsigned short word076,
+                                         unsigned short word077)
 {
-  unsigned short word077 = drive->words047_079[77-47];
-  if (word077 & 0x0001)
-    return 0;
-  return get_sata_speed((word077 >> 1) & 0x7);
+  int allspeeds = (!(word076 & 0x0001) ? (word076 & 0x00fe) : 0);
+  int maxspeed = (allspeeds ? find_msb(allspeeds) : 0);
+  int curspeed = (!(word077 & 0x0001) ? ((word077 >> 1) & 0x7) : 0);
+
+  const char * verstr = get_sata_version(word222);
+  const char * maxstr = get_sata_speed(maxspeed);
+  const char * curstr = get_sata_speed(curspeed);
+  jout("SATA Version is:  %s%s%s%s%s%s\n",
+       (verstr ? verstr : "Unknown"),
+       (maxstr ? ", " : ""), (maxstr ? maxstr : ""),
+       (curstr ? " (current: " : ""), (curstr ? curstr : ""),
+       (curstr ? ")" : ""));
+  if (verstr)
+    jglb["sata_version"]["string"] = verstr;
+  jglb["sata_version"]["value"] = word222 & 0x0fff;
+  jset_sata_speed("max", allspeeds, maxspeed, maxstr);
+  jset_sata_speed("current", curspeed, curspeed, curstr);
 }
 
-
 static void print_drive_info(const ata_identify_device * drive,
                              const ata_size_info & sizes, int rpm,
                              const drive_settings * dbentry)
@@ -608,18 +624,26 @@ static void print_drive_info(const ata_identify_device * drive,
   ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1);
 
   // Print model family if known
-  if (dbentry && *dbentry->modelfamily)
-    pout("Model Family:     %s\n", dbentry->modelfamily);
+  if (dbentry && *dbentry->modelfamily) {
+    jout("Model Family:     %s\n", dbentry->modelfamily);
+    jglb["model_family"] = dbentry->modelfamily;
+  }
 
-  pout("Device Model:     %s\n", infofound(model));
+  jout("Device Model:     %s\n", infofound(model));
+  jglb["model_name"] = model;
 
   if (!dont_print_serial_number) {
-    pout("Serial Number:    %s\n", infofound(serial));
+    jout("Serial Number:    %s\n", infofound(serial));
+    jglb["serial_number"] = serial;
 
     unsigned oui = 0; uint64_t unique_id = 0;
     int naa = ata_get_wwn(drive, oui, unique_id);
-    if (naa >= 0)
-      pout("LU WWN Device Id: %x %06x %09" PRIx64 "\n", naa, oui, unique_id);
+    if (naa >= 0) {
+      jout("LU WWN Device Id: %x %06x %09" PRIx64 "\n", naa, oui, unique_id);
+      jglb["wwn"]["naa"] = naa;
+      jglb["wwn"]["oui"] = oui;
+      jglb["wwn"]["id"]  = unique_id;
+    }
   }
 
   // Additional Product Identifier (OEM Id) string in words 170-173
@@ -627,39 +651,48 @@ static void print_drive_info(const ata_identify_device * drive,
   if (0x2020 <= drive->words088_255[170-88] && drive->words088_255[170-88] <= 0x7e7e) {
     char add[8+1];
     ata_format_id_string(add, (const unsigned char *)(drive->words088_255+170-88), sizeof(add)-1);
-    if (add[0])
-      pout("Add. Product Id:  %s\n", add);
+    if (add[0]) {
+      jout("Add. Product Id:  %s\n", add);
+      jglb["ata_additional_product_id"] = add;
+    }
   }
 
-  pout("Firmware Version: %s\n", infofound(firmware));
+  jout("Firmware Version: %s\n", infofound(firmware));
+  jglb["firmware_version"] = firmware;
 
   if (sizes.capacity) {
     // Print capacity
     char num[64], cap[32];
-    pout("User Capacity:    %s bytes [%s]\n",
+    jout("User Capacity:    %s bytes [%s]\n",
       format_with_thousands_sep(num, sizeof(num), sizes.capacity),
       format_capacity(cap, sizeof(cap), sizes.capacity));
+    jglb["user_capacity"]["blocks"].set_unsafe_uint64(sizes.sectors);
+    jglb["user_capacity"]["bytes"].set_unsafe_uint64(sizes.capacity);
 
     // Print sector sizes.
     if (sizes.phy_sector_size == sizes.log_sector_size)
-      pout("Sector Size:      %u bytes logical/physical\n", sizes.log_sector_size);
+      jout("Sector Size:      %u bytes logical/physical\n", sizes.log_sector_size);
     else {
-      pout("Sector Sizes:     %u bytes logical, %u bytes physical",
-         sizes.log_sector_size, sizes.phy_sector_size);
+      jout("Sector Sizes:     %u bytes logical, %u bytes physical",
+        sizes.log_sector_size, sizes.phy_sector_size);
       if (sizes.log_sector_offset)
         pout(" (offset %u bytes)", sizes.log_sector_offset);
-      pout("\n");
+      jout("\n");
     }
+    jglb["logical_block_size"]  = sizes.log_sector_size;
+    jglb["physical_block_size"] = sizes.phy_sector_size;
   }
 
   // Print nominal media rotation rate if reported
   if (rpm) {
     if (rpm == 1)
-      pout("Rotation Rate:    Solid State Device\n");
+      jout("Rotation Rate:    Solid State Device\n");
     else if (rpm > 1)
-      pout("Rotation Rate:    %d rpm\n", rpm);
+      jout("Rotation Rate:    %d rpm\n", rpm);
     else
       pout("Rotation Rate:    Unknown (0x%04x)\n", -rpm);
+    if (rpm > 0)
+      jglb["rotation_rate"] = (rpm == 1 ? 0 : rpm);
   }
 
   // Print form factor if reported
@@ -667,15 +700,18 @@ static void print_drive_info(const ata_identify_device * drive,
   if (word168) {
     const char * form_factor = get_form_factor(word168);
     if (form_factor)
-      pout("Form Factor:      %s\n", form_factor);
+      jout("Form Factor:      %s\n", form_factor);
     else
-      pout("Form Factor:      Unknown (0x%04x)\n", word168);
+      jout("Form Factor:      Unknown (0x%04x)\n", word168);
+    jglb["form_factor"]["ata_value"] = word168;
+    jglb["form_factor"]["name"] = form_factor;
   }
 
   // See if drive is recognized
-  pout("Device is:        %s\n", !dbentry ?
+  jout("Device is:        %s\n", !dbentry ?
        "Not in smartctl database [for details use: -P showall]":
        "In smartctl database [for details use: -P show]");
+  jglb["in_smartctl_database"] = !!dbentry;
 
   // Print ATA version
   std::string ataver;
@@ -702,7 +738,12 @@ static void print_drive_info(const ata_identify_device * drive,
         ataver += " (minor revision not indicated)";
     }
   }
-  pout("ATA Version is:   %s\n", infofound(ataver.c_str()));
+  jout("ATA Version is:   %s\n", infofound(ataver.c_str()));
+  if (!ataver.empty()) {
+    jglb["ata_version"]["string"] = ataver;
+    jglb["ata_version"]["major_value"] = drive->major_rev_num;
+    jglb["ata_version"]["minor_value"] = drive->minor_rev_num;
+  }
 
   // Print Transport specific version
   unsigned short word222 = drive->words088_255[222-88];
@@ -714,15 +755,9 @@ static void print_drive_info(const ata_identify_device * drive,
       }
       break;
     case 0x1: // SATA
-      {
-        const char * sataver = get_sata_version(word222);
-        const char * maxspeed = get_sata_maxspeed(drive);
-        const char * curspeed = get_sata_curspeed(drive);
-        pout("SATA Version is:  %s%s%s%s%s%s\n", sataver,
-             (maxspeed ? ", " : ""), (maxspeed ? maxspeed : ""),
-             (curspeed ? " (current: " : ""), (curspeed ? curspeed : ""),
-             (curspeed ? ")" : ""));
-      }
+      print_sata_version_and_speed(word222,
+                                   drive->words047_079[76-47],
+                                   drive->words047_079[77-47]);
       break;
     case 0xe: // PCIe (ACS-4)
       pout("Transport Type:   PCIe (0x%03x)\n", word222 & 0x0fff);
@@ -733,8 +768,11 @@ static void print_drive_info(const ata_identify_device * drive,
   }
 
   // print current time and date and timezone
-  char timedatetz[DATEANDEPOCHLEN]; dateandtimezone(timedatetz);
-  pout("Local Time is:    %s\n", timedatetz);
+  time_t now = time(0);
+  char timedatetz[DATEANDEPOCHLEN]; dateandtimezoneepoch(timedatetz, now);
+  jout("Local Time is:    %s\n", timedatetz);
+  jglb["local_time"]["time_t"] = now;
+  jglb["local_time"]["asctime"] = timedatetz;
 
   // Print warning message, if there is one
   if (dbentry && *dbentry->warningmsg)
@@ -773,14 +811,22 @@ static const char *OfflineDataCollectionStatus(unsigned char status_byte)
 //  prints verbose value Off-line data collection status byte
 static void PrintSmartOfflineStatus(const ata_smart_values * data)
 {
-  pout("Offline data collection status:  (0x%02x)\t",
+  json::ref jref = jglb["ata_smart_data"]["offline_data_collection"]["status"];
+
+  jout("Offline data collection status:  (0x%02x)\t",
        (int)data->offline_data_collection_status);
+  jref["value"] = 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"
+  jout("Offline data collection activity\n"
        "\t\t\t\t\t%s.\n", OfflineDataCollectionStatus(data->offline_data_collection_status));
-  
+  jref["string"] = OfflineDataCollectionStatus(data->offline_data_collection_status);
+  switch (data->offline_data_collection_status & 0x7f) {
+    case 0x02: jref["passed"] = true; break;
+    case 0x06: jref["passed"] = false; break;
+  }
+
   // 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)
@@ -794,198 +840,230 @@ static void PrintSmartOfflineStatus(const ata_smart_values * data)
 static void PrintSmartSelfExecStatus(const ata_smart_values * data,
                                      firmwarebug_defs firmwarebugs)
 {
-   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 8:
-          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\n\t\t\t\t\t");
-          pout("device is suspected of having handling\n\t\t\t\t\t");
-          pout("damage.\n");
-          break;
-       case 15:
-          if (firmwarebugs.is_set(BUG_SAMSUNG3) && data->self_test_exec_status == 0xf0) {
-            pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t",
-                    (int)data->self_test_exec_status);
-            pout("with unknown result or self-test in\n\t\t\t\t\t");
-            pout("progress with less than 10%% remaining.\n");
-          }
-          else {
-            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;
-   }
-        
+  unsigned char status = data->self_test_exec_status;
+  jout("Self-test execution status:      ");
+
+  switch (data->self_test_exec_status >> 4) {
+    case 0:
+      jout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t", status);
+      jout("without error or no self-test has ever \n\t\t\t\t\tbeen run.\n");
+      break;
+    case 1:
+      jout("(%4d)\tThe self-test routine was aborted by\n\t\t\t\t\t", status);
+      jout("the host.\n");
+      break;
+    case 2:
+      jout("(%4d)\tThe self-test routine was interrupted\n\t\t\t\t\t", status);
+      jout("by the host with a hard or soft reset.\n");
+      break;
+    case 3:
+      jout("(%4d)\tA fatal error or unknown test error\n\t\t\t\t\t", status);
+      jout("occurred while the device was executing\n\t\t\t\t\t");
+      jout("its self-test routine and the device \n\t\t\t\t\t");
+      jout("was unable to complete the self-test \n\t\t\t\t\t");
+      jout("routine.\n");
+      break;
+    case 4:
+      jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status);
+      jout("a test element that failed and the test\n\t\t\t\t\t");
+      jout("element that failed is not known.\n");
+      break;
+    case 5:
+      jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status);
+      jout("the electrical element of the test\n\t\t\t\t\t");
+      jout("failed.\n");
+      break;
+    case 6:
+      jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status);
+      jout("the servo (and/or seek) element of the \n\t\t\t\t\t");
+      jout("test failed.\n");
+      break;
+    case 7:
+      jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status);
+      jout("the read element of the test failed.\n");
+      break;
+    case 8:
+      jout("(%4d)\tThe previous self-test completed having\n\t\t\t\t\t", status);
+      jout("a test element that failed and the\n\t\t\t\t\t");
+      jout("device is suspected of having handling\n\t\t\t\t\t");
+      jout("damage.\n");
+      break;
+    case 15:
+      if (firmwarebugs.is_set(BUG_SAMSUNG3) && data->self_test_exec_status == 0xf0) {
+        pout("(%4d)\tThe previous self-test routine completed\n\t\t\t\t\t", status);
+        pout("with unknown result or self-test in\n\t\t\t\t\t");
+        pout("progress with less than 10%% remaining.\n");
+      }
+      else {
+        jout("(%4d)\tSelf-test routine in progress...\n\t\t\t\t\t", status);
+        jout("%1d0%% of test remaining.\n", status & 0x0f);
+      }
+      break;
+    default:
+      jout("(%4d)\tReserved.\n", status);
+      break;
+  }
+
+  json::ref jref = jglb["ata_smart_data"]["self_test"]["status"];
+
+  jref["value"] = status;
+  const char * msg;
+  // TODO: Use common function for smartctl/smartd
+  switch (status >> 4) {
+    case 0x0: msg = "completed without error"; break;
+    case 0x1: msg = "was aborted by the host"; break;
+    case 0x2: msg = "was interrupted by the host with a reset"; break;
+    case 0x3: msg = "could not complete due to a fatal or unknown error"; break;
+    case 0x4: msg = "completed with error (unknown test element)"; break;
+    case 0x5: msg = "completed with error (electrical test element)"; break;
+    case 0x6: msg = "completed with error (servo/seek test element)"; break;
+    case 0x7: msg = "completed with error (read test element)"; break;
+    case 0x8: msg = "completed with error (handling damage?)"; break;
+    default:  msg = 0;
+  }
+  if (msg) {
+    jref["string"] = msg;
+    switch (status >> 4) {
+      case 0x1: case 0x2: case 0x3: break; // aborted -> unknown
+      default: jref["passed"] = ((status >> 4) == 0x0);
+    }
+  }
+  else if ((status >> 4) == 0xf) {
+    jref["string"] = strprintf("in progress, %u0%% remaining", status & 0xf);
+    jref["remaining_percent"] = (status & 0xf) * 10;
+  }
 }
 
 static void PrintSmartTotalTimeCompleteOffline (const ata_smart_values * data)
 {
-  pout("Total time to complete Offline \n");
-  pout("data collection: \t\t(%5d) seconds.\n", 
+  jout("Total time to complete Offline \n");
+  jout("data collection: \t\t(%5d) seconds.\n",
        (int)data->total_time_to_complete_off_line);
+
+  jglb["ata_smart_data"]["offline_data_collection"]["completion_seconds"] =
+      data->total_time_to_complete_off_line;
 }
 
 static void PrintSmartOfflineCollectCap(const ata_smart_values *data)
 {
-  pout("Offline data collection\n");
-  pout("capabilities: \t\t\t (0x%02x) ",
+  json::ref jref = jglb["ata_smart_data"]["capabilities"];
+
+  jout("Offline data collection\n");
+  jout("capabilities: \t\t\t (0x%02x) ",
        (int)data->offline_data_collection_capability);
+  jref["values"][0] = data->offline_data_collection_capability;
   
   if (data->offline_data_collection_capability == 0x00){
-    pout("\tOffline data collection not supported.\n");
+    jout("\tOffline data collection not supported.\n");
   } 
   else {
-    pout( "%s\n", isSupportExecuteOfflineImmediate(data)?
+    jout( "%s\n", isSupportExecuteOfflineImmediate(data)?
           "SMART execute Offline immediate." :
           "No SMART execute Offline immediate.");
-    
+    jref["exec_offline_immediate_supported"] = isSupportExecuteOfflineImmediate(data);
+
+    // TODO: Bit 1 is vendor specific
     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)? 
+
+    jout( "\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)? 
+    jref["offline_is_aborted_upon_new_cmd"] = isSupportOfflineAbort(data);
+
+    jout( "\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)? 
+    jref["offline_surface_scan_supported"] = isSupportOfflineSurfaceScan(data);
+
+    jout( "\t\t\t\t\t%s\n", isSupportSelfTest(data)?
           "Self-test supported.":
           "No Self-test supported.");
+    jref["self_tests_supported"] = isSupportSelfTest(data);
 
-    pout( "\t\t\t\t\t%s\n", isSupportConveyanceSelfTest(data)? 
+    jout( "\t\t\t\t\t%s\n", isSupportConveyanceSelfTest(data)?
           "Conveyance Self-test supported.":
           "No Conveyance Self-test supported.");
+    jref["conveyance_self_test_supported"] = isSupportConveyanceSelfTest(data);
 
-    pout( "\t\t\t\t\t%s\n", isSupportSelectiveSelfTest(data)? 
+    jout( "\t\t\t\t\t%s\n", isSupportSelectiveSelfTest(data)?
           "Selective Self-test supported.":
           "No Selective Self-test supported.");
+    jref["selective_self_test_supported"] = isSupportSelectiveSelfTest(data);
   }
 }
 
 static void PrintSmartCapability(const 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");
-      }
-   }
+  json::ref jref = jglb["ata_smart_data"]["capabilities"];
+
+  jout("SMART capabilities:            ");
+  jout("(0x%04x)\t", (int)data->smart_capability);
+  jref["values"][1] = data->smart_capability;
+
+  if (data->smart_capability == 0x00)
+    jout("Automatic saving of SMART data\t\t\t\t\tis not implemented.\n");
+  else {
+    jout("%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.");
+    jref["attribute_autosave_enabled"] = !!(data->smart_capability & 0x01);
+
+    // TODO: Info possibly invalid or misleading
+    // ATA-3 - ATA-5: Bit shall be set
+    // ATA-6 - ACS-3: Bit shall be set to indicate support for
+    // SMART ENABLE/DISABLE ATTRIBUTE AUTOSAVE
+    if (data->smart_capability & 0x02)
+      pout("\t\t\t\t\tSupports SMART auto save timer.\n");
+  }
 }
 
 static void PrintSmartErrorLogCapability(const ata_smart_values * data, const 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);
-   }
+  bool capable = isSmartErrorLogCapable(data, identity);
+  jout("Error logging capability:        (0x%02x)\tError logging %ssupported.\n",
+       data->errorlog_capability, (capable ? "" : "NOT "));
+  jglb["ata_smart_data"]["capabilities"]["error_logging_supported"] = capable;
 }
 
 static void PrintSmartShortSelfTestPollingTime(const ata_smart_values * data)
 {
-  pout("Short self-test routine \n");
-  if (isSupportSelfTest(data))
-    pout("recommended polling time: \t (%4d) minutes.\n", 
+  jout("Short self-test routine \n");
+  if (isSupportSelfTest(data)) {
+    jout("recommended polling time: \t (%4d) minutes.\n",
          (int)data->short_test_completion_time);
+    jglb["ata_smart_data"]["self_test"]["polling_minutes"]["short"] =
+        data->short_test_completion_time;
+  }
   else
-    pout("recommended polling time: \t        Not Supported.\n");
+    jout("recommended polling time: \t        Not Supported.\n");
 }
 
 static void PrintSmartExtendedSelfTestPollingTime(const ata_smart_values * data)
 {
-  pout("Extended self-test routine\n");
-  if (isSupportSelfTest(data))
-    pout("recommended polling time: \t (%4d) minutes.\n", 
+  jout("Extended self-test routine\n");
+  if (isSupportSelfTest(data)) {
+    jout("recommended polling time: \t (%4d) minutes.\n",
          TestTime(data, EXTEND_SELF_TEST));
+    jglb["ata_smart_data"]["self_test"]["polling_minutes"]["extended"] =
+        TestTime(data, EXTEND_SELF_TEST);
+  }
   else
-    pout("recommended polling time: \t        Not Supported.\n");
+    jout("recommended polling time: \t        Not Supported.\n");
 }
 
 static void PrintSmartConveyanceSelfTestPollingTime(const ata_smart_values * data)
 {
-  pout("Conveyance self-test routine\n");
-  if (isSupportConveyanceSelfTest(data))
-    pout("recommended polling time: \t (%4d) minutes.\n", 
+  jout("Conveyance self-test routine\n");
+  if (isSupportConveyanceSelfTest(data)) {
+    jout("recommended polling time: \t (%4d) minutes.\n",
          (int)data->conveyance_test_completion_time);
+    jglb["ata_smart_data"]["self_test"]["polling_minutes"]["conveyance"] =
+        data->conveyance_test_completion_time;
+  }
   else
-    pout("recommended polling time: \t        Not Supported.\n");
+    jout("recommended polling time: \t        Not Supported.\n");
 }
 
 // Check SMART attribute table for Threshold failure
@@ -1012,6 +1090,54 @@ static int find_failed_attr(const ata_smart_values * data,
   return 0;
 }
 
+static void set_json_globals_from_smart_attrib(int id, const char * name,
+                                               const ata_vendor_attr_defs & defs,
+                                               uint64_t rawval)
+{
+  switch (id) {
+    case 9:
+      if (!str_starts_with(name, "Power_On_"))
+        return;
+      {
+        int minutes = -1;
+        switch (defs[id].raw_format) {
+          case RAWFMT_DEFAULT: case RAWFMT_RAW48: case RAWFMT_RAW64:
+          case RAWFMT_RAW16_OPT_RAW16: case RAWFMT_RAW24_OPT_RAW8: break;
+          case RAWFMT_SEC2HOUR: minutes = (rawval / 60) % 60; rawval /= 60*60; break;
+          case RAWFMT_MIN2HOUR: minutes = rawval % 60; rawval /= 60; break;
+          case RAWFMT_HALFMIN2HOUR: minutes = (rawval / 2) % 60; rawval /= 2*60; break;
+          case RAWFMT_MSEC24_HOUR32:
+            minutes = (int)(rawval >> 32) / (1000*60);
+            if (minutes >= 60)
+              minutes = -1;
+            rawval &= 0xffffffffULL;
+            break;
+          default: return;
+        }
+        if (rawval > 0x00ffffffULL)
+          return; // assume bogus value
+        jglb["power_on_time"]["hours"] = rawval;
+        if (minutes >= 0)
+          jglb["power_on_time"]["minutes"] = minutes;
+      }
+      break;
+    case 12:
+      if (strcmp(name, "Power_Cycle_Count"))
+        return;
+      switch (defs[id].raw_format) {
+        case RAWFMT_DEFAULT: case RAWFMT_RAW48: case RAWFMT_RAW64:
+        case RAWFMT_RAW16_OPT_RAW16: case RAWFMT_RAW24_OPT_RAW8: break;
+        default: return;
+      }
+      if (rawval > 0x00ffffffULL)
+        return; // assume bogus value
+      jglb["power_cycle_count"] = rawval;
+      break;
+    //case 194:
+    // Temperature set separately from ata_return_temperature_value() below
+  }
+}
+
 // 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
@@ -1026,7 +1152,7 @@ static void PrintSmartAttribWithThres(const ata_smart_values * data,
   bool needheader = true;
 
   // step through all vendor attributes
-  for (int i = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
+  for (int i = 0, ji = 0; i < NUMBER_ATA_SMART_ATTRIBUTES; i++) {
     const ata_smart_attribute & attr = data->vendor_attributes[i];
 
     // Check attribute and threshold
@@ -1045,14 +1171,15 @@ static void PrintSmartAttribWithThres(const ata_smart_values * data,
     // 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");
+        jout("SMART Attributes Data Structure revision number: %d\n",(int)data->revnumber);
+        jglb["ata_smart_attributes"]["revision"] = data->revnumber;
+        jout("Vendor Specific SMART Attributes with Thresholds:\n");
       }
       if (!brief)
-        pout("ID#%s ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE\n",
+        jout("ID#%s ATTRIBUTE_NAME          FLAG     VALUE WORST THRESH TYPE      UPDATED  WHEN_FAILED RAW_VALUE\n",
              (!hexid ? "" : " "));
       else
-        pout("ID#%s ATTRIBUTE_NAME          FLAGS    VALUE WORST THRESH FAIL RAW_VALUE\n",
+        jout("ID#%s ATTRIBUTE_NAME          FLAGS    VALUE WORST THRESH FAIL RAW_VALUE\n",
              (!hexid ? "" : " "));
       needheader = false;
     }
@@ -1081,8 +1208,19 @@ static void PrintSmartAttribWithThres(const ata_smart_values * data,
     std::string attrname = ata_get_smart_attr_name(attr.id, defs, rpm);
     std::string rawstr = ata_format_attr_raw_value(attr, defs);
 
+    char flagstr[] = {
+      (ATTRIBUTE_FLAGS_PREFAILURE(attr.flags)     ? 'P' : '-'),
+      (ATTRIBUTE_FLAGS_ONLINE(attr.flags)         ? 'O' : '-'),
+      (ATTRIBUTE_FLAGS_PERFORMANCE(attr.flags)    ? 'S' : '-'),
+      (ATTRIBUTE_FLAGS_ERRORRATE(attr.flags)      ? 'R' : '-'),
+      (ATTRIBUTE_FLAGS_EVENTCOUNT(attr.flags)     ? 'C' : '-'),
+      (ATTRIBUTE_FLAGS_SELFPRESERVING(attr.flags) ? 'K' : '-'),
+      (ATTRIBUTE_FLAGS_OTHER(attr.flags)          ? '+' : ' '),
+      0
+    };
+
     if (!brief)
-      pout("%s %-24s0x%04x   %-4s  %-4s  %-4s   %-10s%-9s%-12s%s\n",
+      jout("%s %-24s0x%04x   %-4s  %-4s  %-4s   %-10s%-9s%-12s%s\n",
            idstr.c_str(), attrname.c_str(), attr.flags,
            valstr.c_str(), worstr.c_str(), threstr.c_str(),
            (ATTRIBUTE_FLAGS_PREFAILURE(attr.flags) ? "Pre-fail" : "Old_age"),
@@ -1092,27 +1230,54 @@ static void PrintSmartAttribWithThres(const ata_smart_values * data,
                                            : "    -"        ) ,
             rawstr.c_str());
     else
-      pout("%s %-24s%c%c%c%c%c%c%c  %-4s  %-4s  %-4s   %-5s%s\n",
-           idstr.c_str(), attrname.c_str(),
-           (ATTRIBUTE_FLAGS_PREFAILURE(attr.flags)     ? 'P' : '-'),
-           (ATTRIBUTE_FLAGS_ONLINE(attr.flags)         ? 'O' : '-'),
-           (ATTRIBUTE_FLAGS_PERFORMANCE(attr.flags)    ? 'S' : '-'),
-           (ATTRIBUTE_FLAGS_ERRORRATE(attr.flags)      ? 'R' : '-'),
-           (ATTRIBUTE_FLAGS_EVENTCOUNT(attr.flags)     ? 'C' : '-'),
-           (ATTRIBUTE_FLAGS_SELFPRESERVING(attr.flags) ? 'K' : '-'),
-           (ATTRIBUTE_FLAGS_OTHER(attr.flags)          ? '+' : ' '),
+      jout("%s %-24s%s  %-4s  %-4s  %-4s   %-5s%s\n",
+           idstr.c_str(), attrname.c_str(), flagstr,
            valstr.c_str(), worstr.c_str(), threstr.c_str(),
            (state == ATTRSTATE_FAILED_NOW  ? "NOW"  :
             state == ATTRSTATE_FAILED_PAST ? "Past"
                                            : "-"     ),
             rawstr.c_str());
 
+    if (!jglb.is_enabled())
+      continue;
+
+    json::ref jref = jglb["ata_smart_attributes"]["table"][ji++];
+    jref["id"] = attr.id;
+    jref["name"] = attrname;
+    if (state > ATTRSTATE_NO_NORMVAL)
+      jref["value"] = attr.current;
+    if (!(defs[attr.id].flags & ATTRFLAG_NO_WORSTVAL))
+      jref["worst"] = attr.worst;
+    if (state > ATTRSTATE_NO_THRESHOLD) {
+      jref["thresh"] = threshold;
+      jref["when_failed"] = (state == ATTRSTATE_FAILED_NOW  ? "now" :
+                             state == ATTRSTATE_FAILED_PAST ? "past"
+                                                            : ""     );
+    }
+
+    json::ref jreff = jref["flags"];
+    jreff["value"] = attr.flags;
+    jreff["string"] = flagstr;
+    jreff["prefailure"]     = !!ATTRIBUTE_FLAGS_PREFAILURE(attr.flags);
+    jreff["updated_online"] = !!ATTRIBUTE_FLAGS_ONLINE(attr.flags);
+    jreff["performance"]    = !!ATTRIBUTE_FLAGS_PERFORMANCE(attr.flags);
+    jreff["error_rate"]     = !!ATTRIBUTE_FLAGS_ERRORRATE(attr.flags);
+    jreff["event_count"]    = !!ATTRIBUTE_FLAGS_EVENTCOUNT(attr.flags);
+    jreff["auto_keep"]      = !!ATTRIBUTE_FLAGS_SELFPRESERVING(attr.flags);
+    if (ATTRIBUTE_FLAGS_OTHER(attr.flags))
+      jreff["other"] = ATTRIBUTE_FLAGS_OTHER(attr.flags);
+
+    uint64_t rawval = ata_get_attr_raw_value(attr, defs);
+    jref["raw"]["value"] = rawval;
+    jref["raw"]["string"] = rawstr;
+
+    set_json_globals_from_smart_attrib(attr.id, attrname.c_str(), defs, rawval);
   }
 
   if (!needheader) {
     if (!onlyfailed && brief) {
         int n = (!hexid ? 28 : 29);
-        pout("%*s||||||_ K auto-keep\n"
+        jout("%*s||||||_ K auto-keep\n"
              "%*s|||||__ C event count\n"
              "%*s||||___ R error rate\n"
              "%*s|||____ S speed/performance\n"
@@ -1122,6 +1287,14 @@ static void PrintSmartAttribWithThres(const ata_smart_values * data,
     }
     pout("\n");
   }
+
+  if (!jglb.is_enabled())
+    return;
+
+  // Protocol independent temperature
+  unsigned char t = ata_return_temperature_value(data, defs);
+  if (t)
+    jglb["temperature"]["current"] = t;
 }
 
 // Print SMART related SCT capabilities
@@ -1130,20 +1303,25 @@ static void ataPrintSCTCapability(const ata_identify_device *drive)
   unsigned short sctcaps = drive->words088_255[206-88];
   if (!(sctcaps & 0x01))
     return;
-  pout("SCT capabilities: \t       (0x%04x)\tSCT Status supported.\n", sctcaps);
+  json::ref jref = jglb["ata_sct_capabilities"];
+  jout("SCT capabilities: \t       (0x%04x)\tSCT Status supported.\n", sctcaps);
+  jref["value"] = sctcaps;
   if (sctcaps & 0x08)
-    pout("\t\t\t\t\tSCT Error Recovery Control supported.\n");
+    jout("\t\t\t\t\tSCT Error Recovery Control supported.\n");
+  jref["error_recovery_control_supported"] = !!(sctcaps & 0x08);
   if (sctcaps & 0x10)
-    pout("\t\t\t\t\tSCT Feature Control supported.\n");
+    jout("\t\t\t\t\tSCT Feature Control supported.\n");
+  jref["feature_control_supported"] = !!(sctcaps & 0x10);
   if (sctcaps & 0x20)
-    pout("\t\t\t\t\tSCT Data Table supported.\n");
+    jout("\t\t\t\t\tSCT Data Table supported.\n");
+  jref["data_table_supported"] = !!(sctcaps & 0x20);
 }
 
 
 static void PrintGeneralSmartValues(const ata_smart_values *data, const ata_identify_device *drive,
                                     firmwarebug_defs firmwarebugs)
 {
-  pout("General SMART Values:\n");
+  jout("General SMART Values:\n");
   
   PrintSmartOfflineStatus(data); 
   
@@ -1157,9 +1335,11 @@ static void PrintGeneralSmartValues(const ata_smart_values *data, const ata_iden
   
   PrintSmartErrorLogCapability(data, drive);
 
-  pout( "\t\t\t\t\t%s\n", isGeneralPurposeLoggingCapable(drive)?
+  jout( "\t\t\t\t\t%s\n", isGeneralPurposeLoggingCapable(drive)?
         "General Purpose Logging supported.":
         "No General Purpose Logging support.");
+  jglb["ata_smart_data"]["capabilities"]["gp_logging_supported"] =
+       isGeneralPurposeLoggingCapable(drive);
 
   if (isSupportSelfTest(data)){
     PrintSmartShortSelfTestPollingTime (data);
@@ -1170,7 +1350,7 @@ static void PrintGeneralSmartValues(const ata_smart_values *data, const ata_iden
 
   ataPrintSCTCapability(drive);
 
-  pout("\n");
+  jout("\n");
 }
 
 // Get # sectors of a log addr, 0 if log does not exist.
@@ -1290,16 +1470,22 @@ const ata_smart_log_directory * fake_logdir(ata_smart_log_directory * logdir,
 static void PrintLogDirectories(const ata_smart_log_directory * gplogdir,
                                 const ata_smart_log_directory * smartlogdir)
 {
-  if (gplogdir)
-    pout("General Purpose Log Directory Version %u\n", gplogdir->logversion);
-  if (smartlogdir)
-    pout("SMART %sLog Directory Version %u%s\n",
+  json::ref jref = jglb["ata_log_directory"];
+  if (gplogdir) {
+    jout("General Purpose Log Directory Version %u\n", gplogdir->logversion);
+    jref["gp_dir_version"] = gplogdir->logversion;
+  }
+  if (smartlogdir) {
+    jout("SMART %sLog Directory Version %u%s\n",
          (gplogdir ? "          " : ""), smartlogdir->logversion,
          (smartlogdir->logversion==1 ? " [multi-sector log support]" : ""));
+    jref["smart_dir_version"] = smartlogdir->logversion;
+    jref["smart_dir_multi_sector"] = (smartlogdir->logversion == 1);
+  }
 
-  pout("Address    Access  R/W   Size  Description\n");
+  jout("Address    Access  R/W   Size  Description\n");
 
-  for (unsigned i = 0; i <= 0xff; i++) {
+  for (unsigned i = 0, ji = 0; i <= 0xff; i++) {
     // Get number of sectors
     unsigned smart_numsect = GetNumLogSectors(smartlogdir, i, false);
     unsigned gp_numsect    = GetNumLogSectors(gplogdir   , i, true );
@@ -1338,19 +1524,34 @@ static void PrintLogDirectories(const ata_smart_log_directory * gplogdir,
     const char * name = GetLogName(i);
     const char * rw = get_log_rw(i);
 
-    if (i2 > i) {
-      pout("0x%02x-0x%02x  %-6s  %-3s  %5u  %s\n", i, i2, acc, rw, size, name);
-      i = i2;
-    }
+    if (i2 > i)
+      jout("0x%02x-0x%02x  %-6s  %-3s  %5u  %s\n", i, i2, acc, rw, size, name);
     else if (acc)
-      pout(  "0x%02x       %-6s  %-3s  %5u  %s\n", i, acc, rw, size, name);
+      jout(  "0x%02x       %-6s  %-3s  %5u  %s\n", i, acc, rw, size, name);
     else {
       // GPL and SL support different sizes
-      pout(  "0x%02x       %-6s  %-3s  %5u  %s\n", i, "GPL", rw, gp_numsect, name);
-      pout(  "0x%02x       %-6s  %-3s  %5u  %s\n", i, "SL", rw, smart_numsect, name);
+      jout(  "0x%02x       %-6s  %-3s  %5u  %s\n", i, "GPL", rw, gp_numsect, name);
+      jout(  "0x%02x       %-6s  %-3s  %5u  %s\n", i, "SL", rw, smart_numsect, name);
+    }
+
+    for (;;) {
+      json::ref jrefi = jref["table"][ji++];
+      jrefi["address"] = i;
+      jrefi["name"] = name;
+      if (rw[0] == 'R' && rw[1] && rw[2]) {
+        jrefi["read"] = true;
+        jrefi["write"] = (rw[2] == 'W');
+      }
+      if (gp_numsect)
+        jrefi["gp_sectors"] = gp_numsect;
+      if (smart_numsect)
+        jrefi["smart_sectors"] = smart_numsect;
+      if (i >= i2)
+        break;
+      i++;
     }
   }
-  pout("\n");
+  jout("\n");
 }
 
 // Print hexdump of log pages.
@@ -1497,7 +1698,30 @@ static const char * get_device_statistics_page_name(int page)
   return "Unknown Statistics";
 }
 
-static void print_device_statistics_page(const unsigned char * data, int page)
+static void set_json_globals_from_device_statistics(int page, int offset, int64_t val)
+{
+  switch (page) {
+    case 1:
+      switch (offset) {
+        case 0x008: jglb["power_cycle_count"] = val; break; // ~= Lifetime Power-On Resets
+        case 0x010: jglb["power_on_time"]["hours"]= val; break;
+      }
+      break;
+    case 5:
+      switch (offset) {
+        case 0x008: jglb["temperature"]["current"] = val; break;
+        case 0x020: jglb["temperature"]["lifetime_max"] = val; break;
+        case 0x028: jglb["temperature"]["lifetime_min"] = val; break;
+        case 0x050: jglb["temperature"]["lifetime_over_limit_minutes"] = val; break;
+        case 0x058: jglb["temperature"]["op_limit_max"] = val; break;
+        case 0x060: jglb["temperature"]["lifetime_under_limit_minutes"] = val; break;
+        case 0x068: jglb["temperature"]["op_limit_min"] = val; break;
+      }
+      break;
+  }
+}
+
+static void print_device_statistics_page(const json::ref & jref, const unsigned char * data, int page)
 {
   const devstat_entry_info * info = (page < num_devstat_infos ? devstat_infos[page] : 0);
   const char * name = get_device_statistics_page_name(page);
@@ -1513,9 +1737,14 @@ static void print_device_statistics_page(const unsigned char * data, int page)
     return;
   }
 
-  pout("0x%02x%s%s (rev %d) ==\n", page, line, name, data[0] | (data[1] << 8));
+  int rev = data[0] | (data[1] << 8);
+  jout("0x%02x%s%s (rev %d) ==\n", page, line, name, rev);
+  jref["number"] = page;
+  jref["name"] = name;
+  jref["revision"] = rev;
 
   // Print entries
+  int ji = 0;
   for (int i = 1, offset = 8; offset < 512-7; i++, offset+=8) {
     // Check for last known entry
     if (info && !info[i].size)
@@ -1532,19 +1761,30 @@ static void print_device_statistics_page(const unsigned char * data, int page)
       break;
     }
 
+    // Get value name
+    const char * valname = (info           ? info[i].name :
+                            (page == 0xff) ? "Vendor Specific" // ACS-4
+                                           : "Unknown"        );
+
     // Get value size, default to max if unknown
     int size = (info ? info[i].size : 7);
 
+    // Get flags (supported flag already checked above)
+    bool valid = !!(flags & 0x40);
+    bool normalized = !!(flags & 0x20);
+    bool supports_dsn = !!(flags & 0x10); // ACS-3
+    bool monitored_condition_met = !!(flags & 0x08); // ACS-3
+    unsigned char reserved_flags = (flags & 0x07);
+
     // Format value
+    int64_t val = 0;
     char valstr[32];
-    if (flags & 0x40) { // valid flag
+    if (valid) {
       // Get value
-      int64_t val;
       if (size < 0) {
         val = (signed char)data[offset];
       }
       else {
-        val = 0;
         for (int j = 0; j < size; j++)
           val |= (int64_t)data[offset+j] << (j*8);
       }
@@ -1555,17 +1795,40 @@ static void print_device_statistics_page(const unsigned char * data, int page)
       valstr[0] = '-'; valstr[1] = 0;
     }
 
-    pout("0x%02x  0x%03x  %d %15s  %c%c%c%c %s\n",
-      page, offset,
-      abs(size),
-      valstr,
-      ((flags & 0x20) ? 'N' : '-'), // normalized statistics
-      ((flags & 0x10) ? 'D' : '-'), // supports DSN (ACS-3)
-      ((flags & 0x08) ? 'C' : '-'), // monitored condition met (ACS-3)
-      ((flags & 0x07) ? '+' : ' '), // reserved flags
-      ( info          ? info[i].name :
-       (page == 0xff) ? "Vendor Specific" // ACS-4
-                      : "Unknown"        ));
+    char flagstr[] = {
+      (valid ? 'V' : '-'), // JSON only
+      (normalized ? 'N' : '-'),
+      (supports_dsn ? 'D' : '-'),
+      (monitored_condition_met ? 'C' : '-'),
+      (reserved_flags ? '+' : ' '),
+      0
+    };
+
+    jout("0x%02x  0x%03x  %d %15s  %s %s\n",
+      page, offset, abs(size), valstr, flagstr+1, valname);
+
+    if (!jglb.is_enabled())
+      continue;
+
+    json::ref jrefi = jref["table"][ji++];
+    jrefi["offset"] = offset;
+    jrefi["name"] = valname;
+    jrefi["size"] = abs(size);
+    if (valid)
+      jrefi["value"] = val; // TODO: May be unsafe JSON int if size > 6
+
+    json::ref jreff = jrefi["flags"];
+    jreff["value"] = flags;
+    jreff["string"] = flagstr;
+    jreff["valid"] = valid;
+    jreff["normalized"] = normalized;
+    jreff["supports_dsn"] = supports_dsn;
+    jreff["monitored_condition_met"] = monitored_condition_met;
+    if (reserved_flags)
+      jreff["other"] = reserved_flags;
+
+    if (valid)
+      set_json_globals_from_device_statistics(page, offset, val);
   }
 }
 
@@ -1582,13 +1845,13 @@ static bool print_device_statistics(ata_device * device, unsigned nsectors,
   else
     rc = ataReadSmartLog(device, 0x04, page_0, 1);
   if (!rc) {
-    pout("Read Device Statistics page 0x00 failed\n\n");
+    jerr("Read Device Statistics page 0x00 failed\n\n");
     return false;
   }
 
   unsigned char nentries = page_0[8];
   if (!(page_0[2] == 0 && nentries > 0)) {
-    pout("Device Statistics page 0x00 is invalid (page=0x%02x, nentries=%d)\n\n", page_0[2], nentries);
+    jerr("Device Statistics page 0x00 is invalid (page=0x%02x, nentries=%d)\n\n", page_0[2], nentries);
     return false;
   }
 
@@ -1618,23 +1881,28 @@ static bool print_device_statistics(ata_device * device, unsigned nsectors,
       ssd_page = false;
   }
 
+  json::ref jref = jglb["ata_device_statistics"];
+
   // Print list of supported pages if requested
   if (print_page_0) {
     pout("Device Statistics (%s Log 0x04) supported pages\n", 
       use_gplog ? "GP" : "SMART");
-    pout("Page  Description\n");
+    jout("Page  Description\n");
     for (i = 0; i < nentries; i++) {
       int page = page_0[8+1+i];
-      pout("0x%02x  %s\n", page, get_device_statistics_page_name(page));
+      const char * name = get_device_statistics_page_name(page);
+      jout("0x%02x  %s\n", page, name);
+      jref["supported_pages"][i]["number"] = page;
+      jref["supported_pages"][i]["name"] = name;
     }
-    pout("\n");
+    jout("\n");
   }
 
   // Read & print pages
   if (!pages.empty()) {
     pout("Device Statistics (%s Log 0x04)\n",
       use_gplog ? "GP" : "SMART");
-    pout("Page  Offset Size        Value Flags Description\n");
+    jout("Page  Offset Size        Value Flags Description\n");
     int max_page = 0;
 
     if (!use_gplog)
@@ -1647,15 +1915,16 @@ static bool print_device_statistics(ata_device * device, unsigned nsectors,
     raw_buffer pages_buf((max_page+1) * 512);
 
     if (!use_gplog && !ataReadSmartLog(device, 0x04, pages_buf.data(), max_page+1)) {
-      pout("Read Device Statistics pages 0x00-0x%02x failed\n\n", max_page);
+      jerr("Read Device Statistics pages 0x00-0x%02x failed\n\n", max_page);
       return false;
     }
 
+    int ji = 0;
     for (i = 0; i <  pages.size(); i++) {
       int page = pages[i];
       if (use_gplog) {
         if (!ataReadLogExt(device, 0x04, 0, page, pages_buf.data(), 1)) {
-          pout("Read Device Statistics page 0x%02x failed\n\n", page);
+          jerr("Read Device Statistics page 0x%02x failed\n\n", page);
           return false;
         }
       }
@@ -1663,12 +1932,12 @@ static bool print_device_statistics(ata_device * device, unsigned nsectors,
         continue;
 
       int offset = (use_gplog ? 0 : page * 512);
-      print_device_statistics_page(pages_buf.data() + offset, page);
+      print_device_statistics_page(jref["pages"][ji++], pages_buf.data() + offset, page);
     }
 
-    pout("%32s|||_ C monitored condition met\n", "");
-    pout("%32s||__ D supports DSN\n", "");
-    pout("%32s|___ N normalized value\n\n", "");
+    jout("%32s|||_ C monitored condition met\n", "");
+    jout("%32s||__ D supports DSN\n", "");
+    jout("%32s|___ N normalized value\n\n", "");
   }
 
   return true;
@@ -1680,20 +1949,6 @@ static bool print_device_statistics(ata_device * device, unsigned nsectors,
 
 // Section 9.26 of T13/BSR INCITS 529 (ACS-4) Revision 20, October 26, 2017
 
-// TODO: Move to utility.h:
-static inline unsigned le32_to_uint(const unsigned char * val)
-{
-  return (   (unsigned)val[0]
-          | ((unsigned)val[1] <<  8)
-          | ((unsigned)val[2] << 16)
-          | ((unsigned)val[3] << 24));
-}
-
-static inline uint64_t le64_to_uint(const unsigned char * val)
-{
-  return (le32_to_uint(val) | ((uint64_t)le32_to_uint(val + 4) << 32));
-}
-
 static bool print_pending_defects_log(ata_device * device, unsigned nsectors,
   unsigned max_entries)
 {
@@ -1704,15 +1959,18 @@ static bool print_pending_defects_log(ata_device * device, unsigned nsectors,
     return false;
   }
 
-  pout("Pending Defects log (GP Log 0x0c)\n");
-  unsigned nentries = le32_to_uint(page_buf);
+  jout("Pending Defects log (GP Log 0x0c)\n");
+  unsigned nentries = sg_get_unaligned_le32(page_buf);
+  json::ref jref = jglb["ata_pending_defects_log"];
+  jref["size"] = nsectors * 32 - 1;
+  jref["count"] = nentries;
   if (!nentries) {
-    pout("No Defects Logged\n\n");
+    jout("No Defects Logged\n\n");
     return true;
   }
 
   // Print entries
-  pout("Index                LBA    Hours\n");
+  jout("Index                LBA    Hours\n");
   for (unsigned i = 0, pi = 1, page = 0; i < nentries && i < max_entries; i++, pi++) {
     // Read new page if required
     if (pi >= 32) {
@@ -1729,21 +1987,24 @@ static bool print_pending_defects_log(ata_device * device, unsigned nsectors,
     }
 
     const unsigned char * entry = page_buf + 16 * pi;
-    unsigned hours = le32_to_uint(entry);
+    unsigned hours = sg_get_unaligned_le32(entry);
     char hourstr[32];
     if (hours != 0xffffffffU)
       snprintf(hourstr, sizeof(hourstr), "%u", hours);
     else
       hourstr[0] = '-', hourstr[1] = 0;
-    uint64_t lba = le64_to_uint(entry + 8);
-    pout("%5u %18" PRIu64 " %8s\n", i, lba, hourstr);
+    uint64_t lba = sg_get_unaligned_le64(entry + 8);
+    jout("%5u %18" PRIu64 " %8s\n", i, lba, hourstr);
+
+    json::ref jrefi = jref["table"][i];
+    jrefi["lba"].set_unsafe_uint64(lba);
+    if (hours != 0xffffffffU)
+      jrefi["power_on_hours"] = hours;
   }
 
   if (nentries > max_entries)
     pout("... (%u entries not shown)\n", nentries - max_entries);
-  // TODO: Remove when no longer EXPERIMENTAL
-  pout("Please send sample output of above table to:\n" PACKAGE_BUGREPORT "\n");
-  pout("\n");
+  jout("\n");
   return true;
 }
 
@@ -1755,13 +2016,13 @@ static void PrintSataPhyEventCounters(const unsigned char * data, bool reset)
 {
   if (checksum(data))
     checksumwarning("SATA Phy Event Counters");
-  pout("SATA Phy Event Counters (GP Log 0x11)\n");
+  jout("SATA Phy Event Counters (GP Log 0x11)\n");
   if (data[0] || data[1] || data[2] || data[3])
     pout("[Reserved: 0x%02x 0x%02x 0x%02x 0x%02x]\n",
     data[0], data[1], data[2], data[3]);
-  pout("ID      Size     Value  Description\n");
+  jout("ID      Size     Value  Description\n");
 
-  for (unsigned i = 4; ; ) {
+  for (unsigned i = 4, ji = 0; ; ) {
     // Get counter id and size (bits 14:12)
     unsigned id = data[i] | (data[i+1] << 8);
     unsigned size = ((id >> 12) & 0x7) << 1;
@@ -1808,12 +2069,20 @@ static void PrintSataPhyEventCounters(const unsigned char * data, bool reset)
     }
 
     // Counters stop at max value, add '+' in this case
-    pout("0x%04x  %u %12" PRIu64 "%c %s\n", id, size, val,
+    jout("0x%04x  %u %12" PRIu64 "%c %s\n", id, size, val,
       (val == max_val ? '+' : ' '), name);
+
+    json::ref jref = jglb["sata_phy_event_counters"]["table"][ji++];
+    jref["id"] = id;
+    jref["name"] = name;
+    jref["size"] = size;
+    jref["value"] = val;
+    jref["overflow"] = (val == max_val);
   }
   if (reset)
-    pout("All counters reset\n");
-  pout("\n");
+    jout("All counters reset\n");
+  jout("\n");
+  jglb["sata_phy_event_counters"]["reset"] = reset;
 }
 
 // Format milliseconds from error log entry as "DAYS+H:M:S.MSEC"
@@ -1855,11 +2124,14 @@ static const char * get_error_log_state_desc(unsigned state)
 static int PrintSmartErrorlog(const ata_smart_errorlog *data,
                               firmwarebug_defs firmwarebugs)
 {
-  pout("SMART Error Log Version: %d\n", (int)data->revnumber);
-  
+  json::ref jref = jglb["ata_smart_error_log"]["summary"];
+  jout("SMART Error Log Version: %d\n", (int)data->revnumber);
+  jref["revision"] = data->revnumber;
+
   // if no errors logged, return
   if (!data->error_log_pointer){
-    pout("No Errors Logged\n\n");
+    jout("No Errors Logged\n\n");
+    jref["count"] = 0;
     return 0;
   }
   print_on();
@@ -1879,12 +2151,15 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data,
   
   // starting printing error log info
   if (data->ata_error_count<=5)
-    pout( "ATA Error Count: %d\n", (int)data->ata_error_count);
+    jout( "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",
+    jout( "ATA Error Count: %d (device log contains only the most recent five errors)\n",
            (int)data->ata_error_count);
+  jref["count"] = data->ata_error_count;
+  jref["logged_count"] = (data->ata_error_count <= 5 ? data->ata_error_count : 5);
+
   print_off();
-  pout("\tCR = Command Register [HEX]\n"
+  jout("\tCR = Command Register [HEX]\n"
        "\tFR = Features Register [HEX]\n"
        "\tSC = Sector Count Register [HEX]\n"
        "\tSN = Sector Number Register [HEX]\n"
@@ -1899,7 +2174,7 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data,
        "SS=sec, and sss=millisec. It \"wraps\" after 49.710 days.\n\n");
   
   // now step through the five error log data structures (table 39 of spec)
-  for (int k = 4; k >= 0; k-- ) {
+  for (int k = 4, ji = 0; k >= 0; k--) {
 
     // The error log data structure entries are a circular buffer
     int i = (data->error_log_pointer + k) % 5;
@@ -1914,11 +2189,16 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data,
 
       // See table 42 of ATA5 spec
       print_on();
-      pout("Error %d occurred at disk power-on lifetime: %d hours (%d days + %d hours)\n",
+      jout("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();
-      pout("  When the command that caused the error occurred, the device was %s.\n\n",msgstate);
-      pout("  After command completion occurred, registers were:\n"
+
+      json::ref jrefi = jref["table"][ji++];
+      jrefi["error_number"] = data->ata_error_count + k - 4;
+      jrefi["lifetime_hours"] = summary->timestamp;
+
+      jout("  When the command that caused the error occurred, the device was %s.\n\n", msgstate);
+      jout("  After command completion occurred, registers were:\n"
            "  ER ST SC SN CL CH DH\n"
            "  -- -- -- -- -- -- --\n"
            "  %02x %02x %02x %02x %02x %02x %02x",
@@ -1929,21 +2209,36 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data,
            (int)summary->cylinder_low,
            (int)summary->cylinder_high,
            (int)summary->drive_head);
+
+      {
+        json::ref jrefir = jrefi["completion_registers"];
+        jrefir["error"] = summary->error_register;
+        jrefir["status"] = summary->status;
+        jrefir["count"] = summary->sector_count;
+        jrefir["lba"] = (summary->sector_number      )
+                      | (summary->cylinder_low  <<  8)
+                      | (summary->cylinder_high << 16);
+        jrefir["device"] = summary->drive_head;
+      }
+
       // Add a description of the contents of the status and error registers
       // if possible
       std::string st_er_desc = format_st_er_desc(elog);
-      if (!st_er_desc.empty())
-        pout("  %s", st_er_desc.c_str());
-      pout("\n\n");
-      pout("  Commands leading to the command that caused the error were:\n"
+      if (!st_er_desc.empty()) {
+        jout("  %s", st_er_desc.c_str());
+        jrefi["error_description"] = st_er_desc;
+      }
+      jout("\n\n");
+      jout("  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 (int j = 4; j >= 0; j--) {
+      for (int j = 4, jj = 0; j >= 0; j--) {
         const ata_smart_errorlog_command_struct * thiscommand = elog->commands+j;
 
         // Spec says: unused data command structures shall be zero filled
         if (nonempty(thiscommand, sizeof(*thiscommand))) {
-          pout("  %02x %02x %02x %02x %02x %02x %02x %02x  %16s  %s\n",
+          const char * atacmd = look_up_ata_command(thiscommand->commandreg, thiscommand->featuresreg);
+          jout("  %02x %02x %02x %02x %02x %02x %02x %02x  %16s  %s\n",
                (int)thiscommand->commandreg,
                (int)thiscommand->featuresreg,
                (int)thiscommand->sector_count,
@@ -1953,10 +2248,23 @@ static int PrintSmartErrorlog(const ata_smart_errorlog *data,
                (int)thiscommand->drive_head,
                (int)thiscommand->devicecontrolreg,
                format_milliseconds(thiscommand->timestamp).c_str(),
-               look_up_ata_command(thiscommand->commandreg, thiscommand->featuresreg));
-       }
+               atacmd);
+
+          json::ref jrefic = jrefi["previous_commands"][jj++];
+          json::ref jreficr = jrefic["registers"];
+          jreficr["command"] = thiscommand->commandreg;
+          jreficr["features"] = thiscommand->featuresreg,
+          jreficr["count"] = thiscommand->sector_count;
+          jreficr["lba"] = (thiscommand->sector_number      )
+                         | (thiscommand->cylinder_low   << 8)
+                         | (thiscommand->cylinder_high << 16);
+          jreficr["device"] = thiscommand->drive_head;
+          jreficr["device_control"] = thiscommand->devicecontrolreg;
+          jrefic["powerup_milliseconds"] = thiscommand->timestamp;
+          jrefic["command_name"] = atacmd;
+        }
       }
-      pout("\n");
+      jout("\n");
     }
   }
   print_on();
@@ -1972,11 +2280,15 @@ static int PrintSmartExtErrorLog(ata_device * device,
                                  const ata_smart_exterrlog * log,
                                  unsigned nsectors, unsigned max_errors)
 {
-  pout("SMART Extended Comprehensive Error Log Version: %u (%u sectors)\n",
+  json::ref jref = jglb["ata_smart_error_log"]["extended"];
+  jout("SMART Extended Comprehensive Error Log Version: %u (%u sectors)\n",
        log->version, nsectors);
+  jref["revision"] = log->version;
+  jref["sectors"] = nsectors;
 
   if (!log->device_error_count) {
-    pout("No Errors Logged\n\n");
+    jout("No Errors Logged\n\n");
+    jref["count"] = 0;
     return 0;
   }
   print_on();
@@ -2004,18 +2316,20 @@ static int PrintSmartExtErrorLog(ata_device * device,
   unsigned errcnt = log->device_error_count;
 
   if (errcnt <= nentries)
-    pout("Device Error Count: %u\n", log->device_error_count);
+    jout("Device Error Count: %u\n", log->device_error_count);
   else {
     errcnt = nentries;
-    pout("Device Error Count: %u (device log contains only the most recent %u errors)\n",
+    jout("Device Error Count: %u (device log contains only the most recent %u errors)\n",
          log->device_error_count, errcnt);
   }
+  jref["count"] = log->device_error_count;
+  jref["logged_count"] = errcnt;
 
   if (max_errors < errcnt)
     errcnt = max_errors;
 
   print_off();
-  pout("\tCR     = Command Register\n"
+  jout("\tCR     = Command Register\n"
        "\tFEATR  = Features Register\n"
        "\tCOUNT  = Count (was: Sector Count) Register\n"
        "\tLBA_48 = Upper bytes of LBA High/Mid/Low Registers ]  ATA-8\n"
@@ -2055,24 +2369,31 @@ static int PrintSmartExtErrorLog(ata_device * device,
 
     const ata_smart_exterrlog_error_log & entry = log_p->error_logs[erridx % 4];
 
+    json::ref jrefi = jref["table"][i];
+    jrefi["error_number"] = errnum;
+    jrefi["log_index"] = erridx;
+
     // Skip unused entries
     if (!nonempty(&entry, sizeof(entry))) {
-      pout("Error %u [%u] log entry is empty\n", errnum, erridx);
+      jout("Error %u [%u] log entry is empty\n", errnum, erridx);
       continue;
     }
 
     // Print error information
     print_on();
     const ata_smart_exterrlog_error & err = entry.error;
-    pout("Error %u [%u] occurred at disk power-on lifetime: %u hours (%u days + %u hours)\n",
+    jout("Error %u [%u] occurred at disk power-on lifetime: %u hours (%u days + %u hours)\n",
          errnum, erridx, err.timestamp, err.timestamp / 24, err.timestamp % 24);
     print_off();
+    jrefi["lifetime_hours"] = err.timestamp;
 
-    pout("  When the command that caused the error occurred, the device was %s.\n\n",
-      get_error_log_state_desc(err.state));
+    const char * msgstate = get_error_log_state_desc(err.state);
+    jout("  When the command that caused the error occurred, the device was %s.\n\n", msgstate);
+    jrefi["device_state"]["value"] = err.state;
+    jrefi["device_state"]["string"] = msgstate;
 
     // Print registers
-    pout("  After command completion occurred, registers were:\n"
+    jout("  After command completion occurred, registers were:\n"
          "  ER -- ST COUNT  LBA_48  LH LM LL DV DC\n"
          "  -- -- -- == -- == == == -- -- -- -- --\n"
          "  %02x -- %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
@@ -2089,18 +2410,35 @@ static int PrintSmartExtErrorLog(ata_device * device,
          err.device_register,
          err.device_control_register);
 
+    {
+      json::ref jrefir = jrefi["completion_registers"];
+      jrefir["error"] = err.error_register;
+      jrefir["status"] = err.status_register,
+      jrefir["count"] = (err.count_register_hi << 8) | err.count_register;
+      jrefir["lba"] = ((uint64_t)err.lba_high_register_hi << 40)
+                    | ((uint64_t)err.lba_mid_register_hi  << 32)
+                    | ((uint64_t)err.lba_low_register_hi  << 24)
+                    | ((unsigned)err.lba_high_register    << 16)
+                    | ((unsigned)err.lba_mid_register     <<  8)
+                    | ((unsigned)err.lba_low_register          );
+      jrefir["device"] = err.device_register;
+      jrefir["device_control"] = err.device_control_register;
+    }
+
     // Add a description of the contents of the status and error registers
     // if possible
     std::string st_er_desc = format_st_er_desc(&entry);
-    if (!st_er_desc.empty())
-      pout("  %s", st_er_desc.c_str());
-    pout("\n\n");
+    if (!st_er_desc.empty()) {
+      jout("  %s", st_er_desc.c_str());
+      jrefi["error_description"] = st_er_desc;
+    }
+    jout("\n\n");
 
     // Print command history
-    pout("  Commands leading to the command that caused the error were:\n"
+    jout("  Commands leading to the command that caused the error were:\n"
          "  CR FEATR COUNT  LBA_48  LH LM LL DV DC  Powered_Up_Time  Command/Feature_Name\n"
          "  -- == -- == -- == == == -- -- -- -- --  ---------------  --------------------\n");
-    for (int ci = 4; ci >= 0; ci--) {
+    for (int ci = 4, cji = 0; ci >= 0; ci--) {
       const ata_smart_exterrlog_command & cmd = entry.commands[ci];
 
       // Skip unused entries
@@ -2108,7 +2446,8 @@ static int PrintSmartExtErrorLog(ata_device * device,
         continue;
 
       // Print registers, timestamp and ATA command name
-      pout("  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %16s  %s\n",
+      const char * atacmd = look_up_ata_command(cmd.command_register, cmd.features_register);
+      jout("  %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %16s  %s\n",
            cmd.command_register,
            cmd.features_register_hi,
            cmd.features_register,
@@ -2123,9 +2462,25 @@ static int PrintSmartExtErrorLog(ata_device * device,
            cmd.device_register,
            cmd.device_control_register,
            format_milliseconds(cmd.timestamp).c_str(),
-           look_up_ata_command(cmd.command_register, cmd.features_register));
+           atacmd);
+
+      json::ref jrefic = jrefi["previous_commands"][cji++];
+      json::ref jreficr = jrefic["registers"];
+      jreficr["command"] = cmd.command_register;
+      jreficr["features"] = (cmd.features_register_hi << 8) | cmd.features_register;
+      jreficr["count"] = (cmd.count_register_hi << 8) | cmd.count_register;
+      jreficr["lba"] = ((uint64_t)cmd.lba_high_register_hi << 40)
+                     | ((uint64_t)cmd.lba_mid_register_hi  << 32)
+                     | ((uint64_t)cmd.lba_low_register_hi  << 24)
+                     | ((unsigned)cmd.lba_high_register    << 16)
+                     | ((unsigned)cmd.lba_mid_register     <<  8)
+                     | ((unsigned)cmd.lba_low_register          );
+      jreficr["device"] = cmd.device_register;
+      jreficr["device_control"] = cmd.device_control_register;
+      jrefic["powerup_milliseconds"] = cmd.timestamp;
+      jrefic["command_name"] = atacmd;
     }
-    pout("\n");
+    jout("\n");
   }
 
   print_on();
@@ -2135,15 +2490,187 @@ static int PrintSmartExtErrorLog(ata_device * device,
   return log->device_error_count;
 }
 
+// Print one self-test log entry.
+// Returns:
+// -1: self-test failed
+//  1: extended self-test completed without error
+//  0: otherwise
+static int ataPrintSmartSelfTestEntry(const json::ref & jref,
+                                      unsigned testnum, unsigned char test_type,
+                                      unsigned char test_status,
+                                      unsigned short timestamp,
+                                      uint64_t failing_lba,
+                                      bool print_error_only, bool & print_header)
+{
+  // Check status and type for return value
+  int retval = 0;
+  switch (test_status >> 4) {
+    case 0x0:
+      if ((test_type & 0x7f) == 0x02)
+        retval = 1; // extended self-test completed without error
+      break;
+    case 0x3: case 0x4:
+    case 0x5: case 0x6:
+    case 0x7: case 0x8:
+      retval = -1; // self-test failed
+      break;
+  }
+
+  if (retval >= 0 && print_error_only)
+    return retval;
+
+  std::string msgtest;
+  switch (test_type) {
+    case 0x00: msgtest = "Offline";            break;
+    case 0x01: msgtest = "Short offline";      break;
+    case 0x02: msgtest = "Extended offline";   break;
+    case 0x03: msgtest = "Conveyance offline"; break;
+    case 0x04: msgtest = "Selective offline";  break;
+    case 0x7f: msgtest = "Abort offline test"; break;
+    case 0x81: msgtest = "Short captive";      break;
+    case 0x82: msgtest = "Extended captive";   break;
+    case 0x83: msgtest = "Conveyance captive"; break;
+    case 0x84: msgtest = "Selective captive";  break;
+    default:
+      if ((0x40 <= test_type && test_type <= 0x7e) || 0x90 <= test_type)
+        msgtest = strprintf("Vendor (0x%02x)", test_type);
+      else
+        msgtest = strprintf("Reserved (0x%02x)", test_type);
+  }
+
+  std::string msgstat;
+  switch (test_status >> 4) {
+    case 0x0: msgstat = "Completed without error";       break;
+    case 0x1: msgstat = "Aborted by host";               break;
+    case 0x2: msgstat = "Interrupted (host reset)";      break;
+    case 0x3: msgstat = "Fatal or unknown error";        break;
+    case 0x4: msgstat = "Completed: unknown failure";    break;
+    case 0x5: msgstat = "Completed: electrical failure"; break;
+    case 0x6: msgstat = "Completed: servo/seek failure"; break;
+    case 0x7: msgstat = "Completed: read failure";       break;
+    case 0x8: msgstat = "Completed: handling damage??";  break;
+    case 0xf: msgstat = "Self-test routine in progress"; break;
+    default:  msgstat = strprintf("Unknown status (0x%x)", test_status >> 4);
+  }
+
+  // Print header once
+  if (print_header) {
+    print_header = false;
+    jout("Num  Test_Description    Status                  Remaining  LifeTime(hours)  LBA_of_first_error\n");
+  }
+
+  char msglba[32];
+  if (retval < 0 && failing_lba < 0xffffffffffffULL)
+    snprintf(msglba, sizeof(msglba), "%" PRIu64, failing_lba);
+  else {
+    msglba[0] = '-'; msglba[1] = 0;
+  }
+
+  jout("#%2u  %-19s %-29s %1d0%%  %8u         %s\n", testnum,
+       msgtest.c_str(), msgstat.c_str(), test_status & 0x0f, timestamp, msglba);
+
+  jref["type"]["value"] = test_type;
+  jref["type"]["string"] = msgtest;
+
+  jref["status"]["value"] = test_status;
+  jref["status"]["string"] = msgstat;
+  if (test_status & 0x0f)
+    jref["status"]["remaining_percent"] = (test_status & 0x0f) * 10;
+  switch (test_status >> 4) {
+    case 0x1: case 0x2: case 0x3: break; // aborted -> unknown
+    default: jref["status"]["passed"] = (retval >= 0);
+  }
+
+  jref["lifetime_hours"] = timestamp;
+
+  if (retval < 0 && failing_lba < 0xffffffffffffULL)
+    jref["lba"] = failing_lba;
+
+  return retval;
+}
+
+// Print SMART Self-test log, return error count
+static int ataPrintSmartSelfTestlog(const ata_smart_selftestlog * log, bool allentries,
+                                    firmwarebug_defs firmwarebugs)
+{
+  json::ref jref = jglb["ata_smart_self_test_log"]["standard"];
+
+  if (allentries)
+    jout("SMART Self-test log structure revision number %d\n", log->revnumber);
+  jref["revision"] = log->revnumber;
+  if (log->revnumber != 0x0001 && allentries && !firmwarebugs.is_set(BUG_SAMSUNG))
+    pout("Warning: ATA Specification requires self-test log structure revision number = 1\n");
+  if (!log->mostrecenttest){
+    if (allentries)
+      jout("No self-tests have been logged.  [To run self-tests, use: smartctl -t]\n");
+    jref["count"] = 0;
+    return 0;
+  }
+
+  bool noheaderprinted = true;
+  int errcnt = 0, igncnt = 0;
+  int testnum = 1, ext_ok_testnum = -1;
+
+  // Iterate through circular buffer in reverse direction
+  for (int i = 20, ji = 0; i >= 0; i--) {
+    int j = (i + log->mostrecenttest) % 21;
+    const ata_smart_selftestlog_struct & entry = log->selftest_struct[j];
+
+    // Skip unused entries
+    if (!nonempty(&entry, sizeof(entry)))
+      continue;
+
+    // Get LBA if valid
+    uint64_t lba48 = (entry.lbafirstfailure < 0xffffffff ?
+                      entry.lbafirstfailure : 0xffffffffffffULL);
+
+    // Print entry
+    int state = ataPrintSmartSelfTestEntry(jref["table"][ji++],
+      testnum, entry.selftestnumber, entry.selfteststatus,
+      entry.timestamp, lba48, !allentries, noheaderprinted);
+
+    if (state < 0) {
+      // Self-test showed an error
+      if (ext_ok_testnum < 0)
+        errcnt++;
+      else
+        // Newer successful extended self-test exits
+        igncnt++;
+    }
+    else if (state > 0 && ext_ok_testnum < 0) {
+      // Latest successful extended self-test
+      ext_ok_testnum = testnum;
+    }
+    testnum++;
+  }
+
+  if (igncnt)
+    jout("%d of %d failed self-tests are outdated by newer successful extended offline self-test #%2d\n",
+      igncnt, igncnt+errcnt, ext_ok_testnum);
+  jref["count"] = testnum - 1;
+  jref["error_count_total"] = igncnt + errcnt;
+  jref["error_count_outdated"] = igncnt;
+
+  if (!allentries && !noheaderprinted)
+    jout("\n");
+
+  return errcnt;
+}
+
 // Print SMART Extended Self-test Log (GP Log 0x07)
 static int PrintSmartExtSelfTestLog(const ata_smart_extselftestlog * log,
                                     unsigned nsectors, unsigned max_entries)
 {
-  pout("SMART Extended Self-test Log Version: %u (%u sectors)\n",
+  json::ref jref = jglb["ata_smart_self_test_log"]["extended"];
+
+  jout("SMART Extended Self-test Log Version: %u (%u sectors)\n",
        log->version, nsectors);
+  jref["revision"] = log->version;
+  jref["sectors"] = nsectors;
 
   if (!log->log_desc_index){
-    pout("No self-tests have been logged.  [To run self-tests, use: smartctl -t]\n\n");
+    jout("No self-tests have been logged.  [To run self-tests, use: smartctl -t]\n\n");
+    jref["count"] = 0;
     return 0;
   }
 
@@ -2162,10 +2689,10 @@ static int PrintSmartExtSelfTestLog(const ata_smart_extselftestlog * log,
   bool print_header = true;
   int errcnt = 0, igncnt = 0;
   int ext_ok_testnum = -1;
+  unsigned testnum = 1;
 
   // Iterate through circular buffer in reverse direction
-  for (unsigned i = 0, testnum = 1;
-       i < nentries && testnum <= max_entries;
+  for (unsigned i = 0, ji = 0; i < nentries && testnum <= max_entries;
        i++, logidx = (logidx > 0 ? logidx - 1 : nentries - 1)) {
 
     const ata_smart_extselftestlog_desc & entry = log[logidx / 19].log_descs[logidx % 19];
@@ -2184,7 +2711,8 @@ static int PrintSmartExtSelfTestLog(const ata_smart_extselftestlog * log,
         | ((uint64_t)b[5] << 40);
 
     // Print entry
-    int state = ataPrintSmartSelfTestEntry(testnum, entry.self_test_type,
+    int state = ataPrintSmartSelfTestEntry(jref["table"][ji++],
+      testnum, entry.self_test_type,
       entry.self_test_status, entry.timestamp, lba48,
       false /*!print_error_only*/, print_header);
 
@@ -2204,27 +2732,27 @@ static int PrintSmartExtSelfTestLog(const ata_smart_extselftestlog * log,
   }
 
   if (igncnt)
-    pout("%d of %d failed self-tests are outdated by newer successful extended offline self-test #%2d\n",
+    jout("%d of %d failed self-tests are outdated by newer successful extended offline self-test #%2d\n",
       igncnt, igncnt+errcnt, ext_ok_testnum);
+  jref["count"] = testnum - 1;
+  jref["error_count_total"] = igncnt + errcnt;
+  jref["error_count_outdated"] = igncnt;
 
-  pout("\n");
+  jout("\n");
   return errcnt;
 }
 
 static void ataPrintSelectiveSelfTestLog(const ata_selective_self_test_log * log, const ata_smart_values * sv)
 {
-  int i,field1,field2;
-  const char *msg;
-  char tmp[64];
-  uint64_t maxl=0,maxr=0;
-  uint64_t current=log->currentlba;
-  uint64_t currentend=current+65535;
+  json::ref jref = jglb["ata_smart_selective_self_test_log"];
 
   // print data structure revision number
-  pout("SMART Selective self-test log data structure revision number %d\n",(int)log->logversion);
+  jout("SMART Selective self-test log data structure revision number %d\n", log->logversion);
+  jref["revision"] = log->logversion;
   if (1 != log->logversion)
     pout("Note: revision number not 1 implies that no selective self-test has ever been run\n");
   
+  const char *msg;
   switch((sv->self_test_exec_status)>>4){
   case  0:msg="Completed";
     break;
@@ -2252,11 +2780,14 @@ static void ataPrintSelectiveSelfTestLog(const ata_selective_self_test_log * log
 
   // find the number of columns needed for printing. If in use, the
   // start/end of span being read-scanned...
+  uint64_t maxl = 0, maxr = 0;
+  uint64_t current = log->currentlba;
+  uint64_t currentend = current + 0xffff;
   if (log->currentspan>5) {
     maxl=current;
     maxr=currentend;
   }
-  for (i=0; i<5; i++) {
+  for (int 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.
@@ -2266,37 +2797,57 @@ static void ataPrintSelectiveSelfTestLog(const ata_selective_self_test_log * log
       maxr=end;
   }
   
-  // we need at least 7 characters wide fields to accomodate the
+  // we need at least 7 characters wide fields to accommodate the
   // labels
+  int field1,field2;
+  char tmp[64];
   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");
+  jout(" SPAN  %*s  %*s  CURRENT_TEST_STATUS\n", field1, "MIN_LBA", field2, "MAX_LBA");
 
-  for (i=0; i<5; i++) {
+  for (int 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)
+    bool active = (i + 1 == log->currentspan);
+
+    if (active)
       // 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 & 0xf), current, currentend);
+      jout("    %d  %*" PRIu64 "  %*" PRIu64 "  %s [%01d0%% left] (%" PRIu64 "-%" PRIu64 ")\n",
+           i + 1, field1, start, field2, end, msg,
+           (sv->self_test_exec_status & 0xf), current, currentend);
     else
       // this span is not currently under test
-      pout("    %d  %*" PRIu64 "  %*" PRIu64 "  Not_testing\n",
-          i+1, field1, start, field2, end);
-  }  
-  
+      jout("    %d  %*" PRIu64 "  %*" PRIu64 "  Not_testing\n",
+           i + 1, field1, start, field2, end);
+
+    json::ref jrefi = jref["table"][i];
+    jrefi["lba_min"] = start;
+    jrefi["lba_max"] = end;
+    jrefi["status"]["value"] = sv->self_test_exec_status;
+    jrefi["status"]["string"] = (active ? msg : "Not_testing");
+    if (active) {
+      jrefi["status"]["remaining_percent"] = sv->self_test_exec_status & 0xf;
+      jrefi["current_lba_min"] = current;
+      jrefi["current_lba_max"] = currentend;
+    }
+  }
+
   // 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));
+  if (log->currentspan > 5) {
+    const char * ost = OfflineDataCollectionStatus(sv->offline_data_collection_status);
+    jout("%5d  %*" PRIu64 "  %*" PRIu64 "  Read_scanning %s\n",
+         log->currentspan, field1, current, field2, currentend, ost);
+    json::ref jrefc = jref["current_read_scan"];
+    jrefc["lba_min"] = current;
+    jrefc["lba_max"] = currentend;
+    jrefc["status"]["value"] = sv->offline_data_collection_status;
+    jrefc["status"]["string"] = ost;
+  }
   
   /* Print selective self-test flags.  Possible flag combinations are
      (numbering bits from 0-15):
@@ -2309,24 +2860,28 @@ static void ataPrintSelectiveSelfTestLog(const ata_selective_self_test_log * log
      1     1       1       Currently scanning
   */
   
-  pout("Selective self-test flags (0x%x):\n", (unsigned int)log->flags);
+  jout("Selective self-test flags (0x%x):\n", (unsigned)log->flags);
+  json::ref jreff = jref["flags"];
+  jreff["value"] = log->flags;
+  jreff["remainder_scan_enabled"] = !!(log->flags & SELECTIVE_FLAG_DOSCAN);
   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");
+   if (log->flags & SELECTIVE_FLAG_ACTIVE)
+     jout("  Currently read-scanning the remainder of the disk.\n");
+   else if (log->flags & SELECTIVE_FLAG_PENDING)
+     jout("  Read-scan of remainder of disk interrupted; will resume %d min after power-up.\n",
+          log->pendingtime);
+   else
+     jout("  After scanning selected spans, read-scan remainder of disk.\n");
+   jreff["remainder_scan_active"] = !!(log->flags & SELECTIVE_FLAG_ACTIVE);
+   jreff["power_up_scan_pending"] = !!(log->flags & SELECTIVE_FLAG_PENDING);
   }
   else
-    pout("  After scanning selected spans, do NOT read-scan remainder of disk.\n");
+    jout("  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; 
+  jout("If Selective self-test is pending on power-up, resume after %d minute delay.\n",
+       log->pendingtime);
+  jref["power_up_scan_resume_minutes"] = log->pendingtime;
 }
 
 // Format SCT Temperature value
@@ -2338,6 +2893,14 @@ static const char * sct_ptemp(signed char x, char (& buf)[20])
   return buf;
 }
 
+static void sct_jtemp2(const json::ref & jref, const char * name, signed char x)
+{
+  if (x == -128 /*0x80 = unknown*/)
+    return;
+  jglb["temperature"][name] = x;
+  jref["temperature"][name] = x;
+}
+
 static const char * sct_pbar(int x, char (& buf)[64])
 {
   if (x <= 19)
@@ -2376,56 +2939,83 @@ static const char * sct_device_state_msg(unsigned char state)
 // Print SCT Status
 static int ataPrintSCTStatus(const ata_sct_status_response * sts)
 {
-  pout("SCT Status Version:                  %u\n", sts->format_version);
-  pout("SCT Version (vendor specific):       %u (0x%04x)\n", sts->sct_version, sts->sct_version);
-  pout("SCT Support Level:                   %u\n", sts->sct_spec);
-  pout("Device State:                        %s (%u)\n",
-    sct_device_state_msg(sts->device_state), sts->device_state);
+  json::ref jref = jglb["ata_sct_status"];
+
+  jout("SCT Status Version:                  %u\n", sts->format_version);
+  jref["format_version"] = sts->format_version;
+  jout("SCT Version (vendor specific):       %u (0x%04x)\n", sts->sct_version, sts->sct_version);
+  jref["sct_version"] = sts->sct_version;
+  // SCT Support Level (1) from original SCT draft was later declared obsolete in ATA-8 ACS.
+  // Drives typically return 0 or 1.  Print only if unknown value is returned.
+  if (sts->sct_spec > 1)
+    pout("SCT Support Level:                   %u\n", sts->sct_spec);
+  const char * statestr = sct_device_state_msg(sts->device_state);
+  jout("Device State:                        %s (%u)\n", statestr, sts->device_state);
+  jref["device_state"]["value"] = sts->device_state;
+  jref["device_state"]["string"] = statestr;
+
+  // If "Reserved" fields not set, assume "old" format version 2:
+  // Table 11 of T13/1701DT-N (SMART Command Transport) Revision 5, February 2005
+  // Table 54 of T13/1699-D (ATA8-ACS) Revision 3e, July 2006
+  // ... else assume "new" format version 2 or version 3:
+  // T13/e06152r0-3 (Additional SCT Temperature Statistics), August - October 2006
+  // Table 60 of T13/1699-D (ATA8-ACS) Revision 3f, December 2006  (format version 2)
+  // Table 80 of T13/1699-D (ATA8-ACS) Revision 6a, September 2008 (format version 3)
+  // Table 194 of T13/BSR INCITS 529 (ACS-4) Revision 20, October 26, 2017
+  // (max_op_limit, smart_status, min_erc_time)
+  bool old_format_2 = (   !sts->min_temp && !sts->life_min_temp
+                       && !sts->under_limit_count && !sts->over_limit_count);
+
   char buf1[20], buf2[20];
-  if (   !sts->min_temp && !sts->life_min_temp
-      && !sts->under_limit_count && !sts->over_limit_count) {
-    // "Reserved" fields not set, assume "old" format version 2
-    // Table 11 of T13/1701DT-N (SMART Command Transport) Revision 5, February 2005
-    // Table 54 of T13/1699-D (ATA8-ACS) Revision 3e, July 2006
-    pout("Current Temperature:                 %s Celsius\n",
-      sct_ptemp(sts->hda_temp, buf1));
-    pout("Power Cycle Max Temperature:         %s Celsius\n",
-      sct_ptemp(sts->max_temp, buf2));
-    pout("Lifetime    Max Temperature:         %s Celsius\n",
-      sct_ptemp(sts->life_max_temp, buf2));
+  jout("Current Temperature:                    %s Celsius\n",
+  sct_ptemp(sts->hda_temp, buf1));
+  sct_jtemp2(jref, "current", sts->hda_temp);
+  jout("Power Cycle Min/Max Temperature:     %s/%s Celsius\n",
+    (!old_format_2 ? sct_ptemp(sts->min_temp, buf1) : "--"),
+    sct_ptemp(sts->max_temp, buf2));
+  if (!old_format_2)
+    sct_jtemp2(jref, "power_cycle_min", sts->min_temp);
+  sct_jtemp2(jref, "power_cycle_max", sts->max_temp);
+  jout("Lifetime    Min/Max Temperature:     %s/%s Celsius\n",
+    (!old_format_2 ? sct_ptemp(sts->life_min_temp, buf1) : "--"),
+    sct_ptemp(sts->life_max_temp, buf2));
+  if (!old_format_2)
+    sct_jtemp2(jref, "lifetime_min", sts->life_min_temp);
+  sct_jtemp2(jref, "lifetime_max", sts->life_max_temp);
+  if (old_format_2)
+    return 0;
+
+  if (sts->max_op_limit > 0) { // e06152r0-2: "Average Temperature"
+    jout("Specified Max Operating Temperature:   %3d Celsius\n", sts->max_op_limit);
+    sct_jtemp2(jref, "op_limit_max", sts->max_op_limit);
   }
-  else {
-    // Assume "new" format version 2 or version 3
-    // T13/e06152r0-3 (Additional SCT Temperature Statistics), August - October 2006
-    // Table 60 of T13/1699-D (ATA8-ACS) Revision 3f, December 2006  (format version 2)
-    // Table 80 of T13/1699-D (ATA8-ACS) Revision 6a, September 2008 (format version 3)
-    // Table 185 of T13/BSR INCITS 529 (ACS-4) Revision 16, February 21, 2017
-    // (smart_status, min_erc_time)
-    pout("Current Temperature:                    %s Celsius\n",
-      sct_ptemp(sts->hda_temp, buf1));
-    pout("Power Cycle Min/Max Temperature:     %s/%s Celsius\n",
-      sct_ptemp(sts->min_temp, buf1), sct_ptemp(sts->max_temp, buf2));
-    pout("Lifetime    Min/Max Temperature:     %s/%s Celsius\n",
-      sct_ptemp(sts->life_min_temp, buf1), sct_ptemp(sts->life_max_temp, buf2));
-    signed char avg = sts->byte205; // Average Temperature from e06152r0-2, removed in e06152r3
-    if (0 < avg && sts->life_min_temp <= avg && avg <= sts->life_max_temp)
-      pout("Lifetime    Average Temperature:        %2d Celsius\n", avg);
-    pout("Under/Over Temperature Limit Count:  %2u/%u\n",
-      sts->under_limit_count, sts->over_limit_count);
-
-    if (sts->smart_status) // ACS-4
-      pout("SMART Status:                        0x%04x (%s)\n", sts->smart_status,
-           (sts->smart_status == 0x2cf4 ? "FAILED" :
-            sts->smart_status == 0xc24f ? "PASSED" : "Reserved"));
-
-    if (sts->min_erc_time) // ACS-4
-      pout("Minimum supported ERC Time Limit:    %d (%0.1f seconds)\n",
-           sts->min_erc_time, sts->min_erc_time/10.0);
-
-    if (nonempty(sts->vendor_specific, sizeof(sts->vendor_specific))) {
-      pout("Vendor specific:\n");
-      for (unsigned i = 0; i < sizeof(sts->vendor_specific); i++)
-        pout("%02x%c", sts->vendor_specific[i], ((i & 0xf) != 0xf ? ' ' : '\n'));
+  jout("Under/Over Temperature Limit Count:  %2u/%u\n",
+    sts->under_limit_count, sts->over_limit_count);
+  jref["temperature"]["under_limit_count"] = sts->under_limit_count;
+  jref["temperature"]["over_limit_count"] = sts->over_limit_count;
+
+  if (sts->smart_status) { // ACS-4
+    int passed = (sts->smart_status == 0x2cf4 ? 0 :
+                  sts->smart_status == 0xc24f ? 1 : -1);
+    jout("SMART Status:                        0x%04x (%s)\n", sts->smart_status,
+          (passed == 0 ? "FAILED" : passed > 0 ? "PASSED" : "Reserved"));
+    if (passed >= 0) {
+      jref["smart_status"]["passed"] = !!passed;
+      jglb["smart_status"]["passed"] = !!passed;
+    }
+    else
+      jref["smart_status"]["reserved_value"] = sts->smart_status;
+  }
+
+  if (sts->min_erc_time) // ACS-4
+    pout("Minimum supported ERC Time Limit:    %d (%0.1f seconds)\n",
+          sts->min_erc_time, sts->min_erc_time/10.0);
+
+  if (nonempty(sts->vendor_specific, sizeof(sts->vendor_specific))) {
+    jout("Vendor specific:\n");
+    for (unsigned i = 0; i < sizeof(sts->vendor_specific); i++) {
+      jout("%02x%c", sts->vendor_specific[i], ((i & 0xf) != 0xf ? ' ' : '\n'));
+      jref["vendor_specific"][i] = sts->vendor_specific[i];
     }
   }
   return 0;
@@ -2434,18 +3024,30 @@ static int ataPrintSCTStatus(const ata_sct_status_response * sts)
 // Print SCT Temperature History Table
 static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh)
 {
+  json::ref jref = jglb["ata_sct_temperature_history"];
+
   char buf1[20], buf2[20], buf3[64];
-  pout("SCT Temperature History Version:     %u%s\n", tmh->format_version,
+  jout("SCT Temperature History Version:     %u%s\n", tmh->format_version,
        (tmh->format_version != 2 ? " (Unknown, should be 2)" : ""));
-  pout("Temperature Sampling Period:         %u minute%s\n",
+  jref["version"] = tmh->format_version;
+  jout("Temperature Sampling Period:         %u minute%s\n",
     tmh->sampling_period, (tmh->sampling_period==1?"":"s"));
-  pout("Temperature Logging Interval:        %u minute%s\n",
+  jref["sampling_period_minutes"] = tmh->sampling_period;
+  jout("Temperature Logging Interval:        %u minute%s\n",
     tmh->interval,        (tmh->interval==1?"":"s"));
-  pout("Min/Max recommended Temperature:     %s/%s Celsius\n",
+  jref["logging_interval_minutes"] = tmh->interval;
+
+  jout("Min/Max recommended Temperature:     %s/%s Celsius\n",
     sct_ptemp(tmh->min_op_limit, buf1), sct_ptemp(tmh->max_op_limit, buf2));
-  pout("Min/Max Temperature Limit:           %s/%s Celsius\n",
+  sct_jtemp2(jref, "op_limit_min", tmh->min_op_limit);
+  sct_jtemp2(jref, "op_limit_max", tmh->max_op_limit);
+  jout("Min/Max Temperature Limit:           %s/%s Celsius\n",
     sct_ptemp(tmh->under_limit, buf1), sct_ptemp(tmh->over_limit, buf2));
-  pout("Temperature History Size (Index):    %u (%u)\n", tmh->cb_size, tmh->cb_index);
+  sct_jtemp2(jref, "limit_min", tmh->under_limit);
+  sct_jtemp2(jref, "limit_max", tmh->over_limit);
+  jout("Temperature History Size (Index):    %u (%u)\n", tmh->cb_size, tmh->cb_index);
+  jref["size"] = tmh->cb_size;
+  jref["index"] = tmh->cb_index;
 
   if (!(0 < tmh->cb_size && tmh->cb_size <= sizeof(tmh->cb) && tmh->cb_index < tmh->cb_size)) {
     if (!tmh->cb_size)
@@ -2456,7 +3058,7 @@ static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh)
   }
 
   // Print table
-  pout("\nIndex    Estimated Time   Temperature Celsius\n");
+  jout("\nIndex    Estimated Time   Temperature Celsius\n");
   unsigned n = 0, i = (tmh->cb_index+1) % tmh->cb_size;
   unsigned interval = (tmh->interval > 0 ? tmh->interval : 1);
   time_t t = time(0) - (tmh->cb_size-1) * interval * 60;
@@ -2473,13 +3075,15 @@ static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh)
         char date[30];
         // TODO: Don't print times < boot time
         strftime(date, sizeof(date), "%Y-%m-%d %H:%M", localtime(&t));
-        pout(" %3u    %s    %s  %s\n", i, date,
+        jout(" %3u    %s    %s  %s\n", i, date,
           sct_ptemp(tmh->cb[i], buf1), sct_pbar(tmh->cb[i], buf3));
       }
       else if (n == n1+1) {
-        pout(" ...    ..(%3u skipped).    ..  %s\n",
+        jout(" ...    ..(%3u skipped).    ..  %s\n",
           n2-n1-2, sct_pbar(tmh->cb[i], buf3));
       }
+      if (tmh->cb[i] != -128)
+        jref["table"][n] = tmh->cb[i];
       t += interval * 60; i = (i+1) % tmh->cb_size; n++;
     }
   }
@@ -2491,15 +3095,24 @@ static int ataPrintSCTTempHist(const ata_sct_temperature_history_table * tmh)
 // Print SCT Error Recovery Control timers
 static void ataPrintSCTErrorRecoveryControl(bool set, unsigned short read_timer, unsigned short write_timer)
 {
-  pout("SCT Error Recovery Control%s:\n", (set ? " set to" : ""));
+  json::ref jref = jglb["ata_sct_erc"];
+  jout("SCT Error Recovery Control%s:\n", (set ? " set to" : ""));
+
+  jref["read"]["enabled"] = !!read_timer;
   if (!read_timer)
-    pout("           Read: Disabled\n");
-  else
-    pout("           Read: %6d (%0.1f seconds)\n", read_timer, read_timer/10.0);
+    jout("           Read: Disabled\n");
+  else {
+    jout("           Read: %6d (%0.1f seconds)\n", read_timer, read_timer/10.0);
+    jref["read"]["deciseconds"] = read_timer;
+  }
+
+  jref["write"]["enabled"] = !!write_timer;
   if (!write_timer)
-    pout("          Write: Disabled\n");
-  else
-    pout("          Write: %6d (%0.1f seconds)\n", write_timer, write_timer/10.0);
+    jout("          Write: Disabled\n");
+  else {
+    jout("          Write: %6d (%0.1f seconds)\n", write_timer, write_timer/10.0);
+    jref["write"]["deciseconds"] = write_timer;
+  }
 }
 
 static void print_aam_level(const char * msg, int level, int recommended = -1)
@@ -2521,9 +3134,16 @@ static void print_aam_level(const char * msg, int level, int recommended = -1)
     s = "reserved";
 
   if (recommended >= 0)
-    pout("%s%d (%s), recommended: %d\n", msg, level, s, recommended);
+    jout("%s%d (%s), recommended: %d\n", msg, level, s, recommended);
   else
-    pout("%s%d (%s)\n", msg, level, s);
+    jout("%s%d (%s)\n", msg, level, s);
+
+  json::ref jref = jglb["ata_aam"];
+  jref["enabled"] = true;
+  jref["level"] = level;
+  jref["string"] = s;
+  if (recommended >= 0)
+    jref["recommended_level"] = recommended;
 }
 
 static void print_apm_level(const char * msg, int level)
@@ -2543,45 +3163,73 @@ static void print_apm_level(const char * msg, int level)
   else
     s = "maximum performance";
 
-  pout("%s%d (%s)\n", msg, level, s);
+  jout("%s%d (%s)\n", msg, level, s);
+
+  json::ref jref = jglb["ata_apm"];
+  jref["enabled"] = true;
+  jref["level"] = level;
+  jref["string"] = s;
+  if (1 <= level && level <= 254) {
+    jref["max_performance"] = (level == 254);
+    jref["min_power"] = (level == 1 || level == 128);
+    jref["with_standby"] = (level < 128);
+  }
 }
 
 static void print_ata_security_status(const char * msg, unsigned short state)
 {
-    const char * s1, * s2 = "", * s3 = "", * s4 = "";
+  // Table 6 of T13/2015-D (ACS-2) Revision 7, June 22, 2011
+  if (!(state & 0x0001)) {
+    pout("%sUnavailable\n", msg);
+    return;
+  }
+
+  const char * s1, * s2 = "", * s3 = "", * s4 = "";
+  bool enabled = false, locked = false;
+  if (!(state & 0x0002)) {
+    s1 = "Disabled, ";
+    if (!(state & 0x0008))
+      s2 = "NOT FROZEN [SEC1]";
+    else
+      s2 = "frozen [SEC2]";
+  }
+  else {
+    enabled = true;
+    s1 = "ENABLED, PW level ";
+    if (!(state & 0x0100))
+      s2 = "HIGH";
+    else
+      s2 = "MAX";
 
-    // Table 6 of T13/2015-D (ACS-2) Revision 7, June 22, 2011
-    if (!(state & 0x0001))
-      s1 = "Unavailable";
-    else if (!(state & 0x0002)) {
-      s1 = "Disabled, ";
+    if (!(state & 0x0004)) {
+      s3 = ", not locked, ";
       if (!(state & 0x0008))
-        s2 = "NOT FROZEN [SEC1]";
+        s4 = "not frozen [SEC5]";
       else
-        s2 = "frozen [SEC2]";
+        s4 = "frozen [SEC6]";
     }
     else {
-      s1 = "ENABLED, PW level ";
-      if (!(state & 0x0100))
-        s2 = "HIGH";
-      else
-        s2 = "MAX";
-
-      if (!(state & 0x0004)) {
-         s3 = ", not locked, ";
-        if (!(state & 0x0008))
-          s4 = "not frozen [SEC5]";
-        else
-          s4 = "frozen [SEC6]";
-      }
-      else {
-        s3 = ", **LOCKED** [SEC4]";
-        if (state & 0x0010)
-          s4 = ", PW ATTEMPTS EXCEEDED";
-      }
+      locked = true;
+      s3 = ", **LOCKED** [SEC4]";
+      if (state & 0x0010)
+        s4 = ", PW ATTEMPTS EXCEEDED";
     }
+  }
 
-    pout("%s%s%s%s%s\n", msg, s1, s2, s3, s4);
+  jout("%s%s%s%s%s\n", msg, s1, s2, s3, s4);
+
+  json::ref jref = jglb["ata_security"];
+  jref["state"] = state;
+  jref["string"] = strprintf("%s%s%s%s", s1, s2, s3, s4);
+  jref["enabled"] = enabled;
+  if (!enabled || !locked)
+    jref["frozen"] = !!(state & 0x0008);
+  if (enabled) {
+    jref["pw_level_max"] = !!(state & 0x0100);
+    jref["locked"] = locked;
+    if (locked)
+      jref["pw_attempts_exceeded"] = !!(state & 0x0010);
+  }
 }
 
 static void print_standby_timer(const char * msg, int timer, const ata_identify_device & drive)
@@ -2662,7 +3310,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
     }
     if (powername) {
       if (options.powermode >= powerlimit) {
-        pout("Device is in %s mode, exit(%d)\n", powername, options.powerexit);
+        jinf("Device is in %s mode, exit(%d)\n", powername, options.powerexit);
         return options.powerexit;
       }
       powerchg = (powermode != 0xff); // SMART tests will spin up drives
@@ -2857,8 +3505,10 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
   if (options.get_aam) {
     if ((drive.command_set_2 & 0xc200) != 0x4200) // word083
       pout("AAM feature is:   Unavailable\n");
-    else if (!(drive.word086 & 0x0200))
-      pout("AAM feature is:   Disabled\n");
+    else if (!(drive.word086 & 0x0200)) {
+      jout("AAM feature is:   Disabled\n");
+      jglb["ata_aam"]["enabled"] = false;
+    }
     else
       print_aam_level("AAM level is:     ", drive.words088_255[94-88] & 0xff,
         drive.words088_255[94-88] >> 8);
@@ -2868,26 +3518,36 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
   if (options.get_apm) {
     if ((drive.command_set_2 & 0xc008) != 0x4008) // word083
       pout("APM feature is:   Unavailable\n");
-    else if (!(drive.word086 & 0x0008))
-      pout("APM feature is:   Disabled\n");
+    else if (!(drive.word086 & 0x0008)) {
+      jout("APM feature is:   Disabled\n");
+      jglb["ata_apm"]["enabled"] = false;
+    }
     else
       print_apm_level("APM level is:     ", drive.words088_255[91-88] & 0xff);
   }
 
   // Print read look-ahead status
   if (options.get_lookahead) {
-    pout("Rd look-ahead is: %s\n",
-      (   (drive.command_set_2 & 0xc000) != 0x4000 // word083
-       || !(drive.command_set_1 & 0x0040)) ? "Unavailable" : // word082
-       !(drive.cfs_enable_1 & 0x0040) ? "Disabled" : "Enabled"); // word085
+    if (   (drive.command_set_2 & 0xc000) != 0x4000 // word083
+        || !(drive.command_set_1 & 0x0040)         ) // word082
+      pout("Rd look-ahead is: Unavailable\n");
+    else {
+      bool enabled = !!(drive.cfs_enable_1 & 0x0040); // word085
+      jout("Rd look-ahead is: %sabled\n", (enabled ? "En" : "Dis"));
+      jglb["read_lookahead"]["enabled"] = enabled;
+    }
   }
 
   // Print write cache status
   if (options.get_wcache) {
-    pout("Write cache is:   %s\n",
-      (   (drive.command_set_2 & 0xc000) != 0x4000 // word083
-       || !(drive.command_set_1 & 0x0020)) ? "Unavailable" : // word082
-       !(drive.cfs_enable_1 & 0x0020) ? "Disabled" : "Enabled"); // word085
+    if (   (drive.command_set_2 & 0xc000) != 0x4000 // word083
+        || !(drive.command_set_1 & 0x0020)         ) // word082
+      pout("Write cache is:   Unavailable\n");
+    else {
+      bool enabled = !!(drive.cfs_enable_1 & 0x0020); // word085
+      jout("Write cache is:   %sabled\n", (enabled ? "En" : "Dis"));
+      jglb["write_cache"]["enabled"] = enabled;
+    }
   }
 
   // Print DSN status
@@ -2898,10 +3558,11 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
        || ((word119 & 0xc200) != 0x4200) // word119
        || ((word120 & 0xc000) != 0x4000)) // word120
       pout("DSN feature is:   Unavailable\n");
-    else if (word120 & 0x200) // word120
-      pout("DSN feature is:   Enabled\n");
-    else
-      pout("DSN feature is:   Disabled\n");
+    else {
+      bool enabled = !!(word120 & 0x200);
+      jout("DSN feature is:   %sabled\n", (enabled ? "En" : "Dis"));
+      jglb["ata_dsn"]["enabled"] = enabled;
+    }
   }
 
   // Check for ATA Security LOCK
@@ -3238,7 +3899,8 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
 
     case 0:
       // The case where the disk health is OK
-      pout("SMART overall-health self-assessment test result: PASSED\n");
+      jout("SMART overall-health self-assessment test result: PASSED\n");
+      jglb["smart_status"]["passed"] = true;
       if (smart_thres_ok && find_failed_attr(&smartval, &smartthres, attribute_defs, 0)) {
         if (options.smart_vendor_attrib)
           pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
@@ -3256,8 +3918,9 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
     case 1:
       // The case where the disk health is NOT OK
       print_on();
-      pout("SMART overall-health self-assessment test result: FAILED!\n"
+      jout("SMART overall-health self-assessment test result: FAILED!\n"
            "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
+      jglb["smart_status"]["passed"] = false;
       print_off();
       if (smart_thres_ok && find_failed_attr(&smartval, &smartthres, attribute_defs, 1)) {
         returnval|=FAILATTR;
@@ -3293,9 +3956,10 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
       }
       else if (find_failed_attr(&smartval, &smartthres, attribute_defs, 1)) {
         print_on();
-        pout("SMART overall-health self-assessment test result: FAILED!\n"
+        jout("SMART overall-health self-assessment test result: FAILED!\n"
              "Drive failure expected in less than 24 hours. SAVE ALL DATA.\n");
-        pout("Warning: This result is based on an Attribute check.\n");
+        jwrn("Warning: This result is based on an Attribute check.\n");
+        jglb["smart_status"]["passed"] = false;
         print_off();
         returnval|=FAILATTR;
         returnval|=FAILSTATUS;
@@ -3308,8 +3972,9 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
         }
       }
       else {
-        pout("SMART overall-health self-assessment test result: PASSED\n");
-        pout("Warning: This result is based on an Attribute check.\n");
+        jout("SMART overall-health self-assessment test result: PASSED\n");
+        jwrn("Warning: This result is based on an Attribute check.\n");
+        jglb["smart_status"]["passed"] = true;
         if (find_failed_attr(&smartval, &smartthres, attribute_defs, 0)) {
           if (options.smart_vendor_attrib)
             pout("See vendor-specific Attribute list for marginal Attributes.\n\n");
@@ -3344,7 +4009,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
 
   // If GP Log is supported use smart log directory for
   // error and selftest log support check.
-  bool gp_log_supported = !!isGeneralPurposeLoggingCapable(&drive);
+  bool gp_log_supported = isGeneralPurposeLoggingCapable(&drive);
   if (   gp_log_supported
       && (   options.smart_error_log || options.smart_selftest_log
           || options.retry_error_log || options.retry_selftest_log))
@@ -3687,7 +4352,7 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
     bool use_gplog = true;
     unsigned nsectors = 0;
     if (gplogdir) 
-      nsectors = GetNumLogSectors(gplogdir, 0x04, false);
+      nsectors = GetNumLogSectors(gplogdir, 0x04, true);
     else if (smartlogdir){ // for systems without ATA_READ_LOG_EXT
       nsectors = GetNumLogSectors(smartlogdir, 0x04, false);
       use_gplog = false;
@@ -3700,12 +4365,10 @@ int ataPrintMain (ata_device * device, const ata_print_options & options)
   }
 
   // Print Pending Defects log
-  if (options.pending_defects_log || options.pending_defects_info) {
+  if (options.pending_defects_log) {
     unsigned nsectors = GetNumLogSectors(gplogdir, 0x0c, true);
     if (!nsectors)
       pout("Pending Defects log (GP Log 0x0c) not supported\n\n");
-    else if (!options.pending_defects_log) // TODO: Remove when no longer EXPERIMENTAL
-      pout("Pending Defects log (GP Log 0x0c) supported [please try: '-l defects']\n\n");
     else if (!print_pending_defects_log(device, nsectors, options.pending_defects_log))
       failuretest(OPTIONAL_CMD, returnval|=FAILSMART);
   }
index d7abd131f97571a90a72dd5ab6c56d198fd270b9..6c296b3afa59584664dd9e8f982c10cac7a008f1 100644 (file)
@@ -4,28 +4,16 @@
  * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-09 Bruce Allen
- * Copyright (C) 2008-17 Christian Franke
+ * Copyright (C) 2008-18 Christian Franke
  * 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, see <http://www.gnu.org/licenses/>.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef ATAPRINT_H_
 #define ATAPRINT_H_
 
-#define ATAPRINT_H_CVSID "$Id: ataprint.h 4572 2017-10-29 14:36:00Z chrfranke $\n"
+#define ATAPRINT_H_CVSID "$Id: ataprint.h 4826 2018-11-02 20:09:12Z chrfranke $\n"
 
 #include <vector>
 
@@ -64,7 +52,6 @@ struct ata_print_options
   bool devstat_all_pages, devstat_ssd_page;
   std::vector<int> devstat_pages;
 
-  bool pending_defects_info;
   unsigned pending_defects_log;
 
   bool sct_temp_sts, sct_temp_hist;
@@ -133,7 +120,7 @@ struct ata_print_options
       smart_ext_selftest_log(0),
       retry_error_log(false), retry_selftest_log(false),
       devstat_all_pages(false), devstat_ssd_page(false),
-      pending_defects_info(false), pending_defects_log(0),
+      pending_defects_log(0),
       sct_temp_sts(false), sct_temp_hist(false),
       sct_erc_get(false),
       sct_erc_set(false),
index db212b852c069999ad0f863db6c4d6ea1d241ea0..151e0944cad2f9337f97229ff6d9aab338aea7c9 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/sh
-# $Id: autogen.sh 4434 2017-09-14 18:05:12Z samm2 $
+# $Id: autogen.sh 4838 2018-11-27 17:27:02Z chrfranke $
 #
 # Generate ./configure from configure.ac and Makefile.in from Makefile.am.
 # This also adds files like missing,depcomp,install-sh to the source
@@ -28,7 +28,7 @@ if [ -n "$AUTOMAKE" ]; then
   ver=$("$AUTOMAKE" --version) || exit 1
 else
   maxver=
-  for v in 1.15 1.14 1.13 1.12 1.11 1.10; do
+  for v in 1.16 1.15 1.14 1.13 1.12 1.11 1.10; do
     minver=$v; test -n "$maxver" || maxver=$v
     ver=$(automake-$v --version 2>/dev/null) || continue
     AUTOMAKE="automake-$v"
@@ -60,7 +60,7 @@ case "$ver" in
     # OK
     ;;
 
-  1.14|1.14.1|1.15|1.15.1)
+  1.14|1.14.1|1.15|1.15.1|1.16|1.16.1)
     # TODO: Enable 'subdir-objects' in configure.ac
     # For now, suppress 'subdir-objects' forward-incompatibility warning
     test -n "$warnings" || amwarnings="--warnings=no-unsupported"
index f5871dd75786f959bfdf9457cce6644fb0e18c0a..1d207a6b3b9240df9d17d703cba0ac16653f3eaf 100644 (file)
--- a/cciss.cpp
+++ b/cciss.cpp
@@ -1,3 +1,13 @@
+/*
+ * cciss.cpp
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2007 Sergey Svishchev
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
 #include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
 #  ifndef be32toh
 #    define be32toh __be32_to_cpu
 #  endif
-#elif defined(__FreeBSD__)
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 #  include <sys/endian.h>
 #  include CISS_LOCATION
 #  define _HAVE_CCISS
-#elif defined(__FreeBSD_kernel__)
-#  include <endian.h>
-#  ifdef __GLIBC__
-#  include <bsd/sys/cdefs.h>
-#  include <stdint.h>
-#  endif
-#  include CISS_LOCATION
-#  define _HAVE_CCISS
 #endif
 
 #ifdef _HAVE_CCISS
 #include "cciss.h"
-#include "int64.h"
 #include "scsicmds.h"
 #include "utility.h"
 
-const char * cciss_cpp_cvsid = "$Id: cciss.cpp 4156 2015-10-18 12:20:40Z samm2 $"
+const char * cciss_cpp_cvsid = "$Id: cciss.cpp 4858 2018-12-16 17:59:59Z chrfranke $"
   CCISS_H_CVSID;
 
 typedef struct _ReportLUNdata_struct
@@ -98,7 +99,7 @@ int cciss_io_interface(int device, int target, struct scsi_cmnd_io * iop, int re
                  int trunc = (iop->dxfer_len > 256) ? 1 : 0;
                  printf("  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);
+                 dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
              }
          }
          return 0;
@@ -116,7 +117,7 @@ int cciss_io_interface(int device, int target, struct scsi_cmnd_io * iop, int re
          if (report > 1)
          {
              printf("  >>> Sense buffer, len=%d:\n", (int)len);
-             dStrHex((const char *)pBuf, len , 1);
+             dStrHex((const uint8_t *)pBuf, len , 1);
          }
      }
      if (report)
diff --git a/cciss.h b/cciss.h
index 99452be6643265d4abb3c0c01c1b48ad69a15381..4895ed9010f4f2ab4e789476d129adb70a8801ec 100644 (file)
--- a/cciss.h
+++ b/cciss.h
@@ -1,7 +1,17 @@
+/*
+ * cciss.h
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2007 Sergey Svishchev
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
 #ifndef CCISS_H_
 #define CCISS_H_
 
-#define CCISS_H_CVSID "$Id: cciss.h,v 1.1 2007/04/01 16:49:46 shattered Exp $\n"
+#define CCISS_H_CVSID "$Id: cciss.h 4761 2018-08-20 19:33:04Z chrfranke $"
 
 int cciss_io_interface(int device, int target,
                              struct scsi_cmnd_io * iop, int report);
index 67c2dd1f53e2fcacd7bc006ae2ad30471c7f7295..6ae01a4a4c3b520039f0d8cbe6a852f37db392de 100644 (file)
 /* Define to 1 if C++ compiler supports __attribute__((packed)) */
 #undef HAVE_ATTR_PACKED
 
+/* Define to 1 if you have the <byteswap.h> header file. */
+#undef HAVE_BYTESWAP_H
+
+/* Define to 1 if you have the <cap-ng.h> header file. */
+#undef HAVE_CAP_NG_H
+
 /* Define to 1 if you have the `clock_gettime' function. */
 #undef HAVE_CLOCK_GETTIME
 
@@ -33,9 +39,6 @@
 /* Define to 1 if you have the `gettimeofday' function. */
 #undef HAVE_GETTIMEOFDAY
 
-/* 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
 
@@ -45,6 +48,9 @@
 /* Define to 1 if you have the `selinux' library (-lselinux). */
 #undef HAVE_LIBSELINUX
 
+/* Define to 1 if you have the `systemd' library (-lsystemd). */
+#undef HAVE_LIBSYSTEMD
+
 /* Define to 1 if you have the `usb' library (-lusb). */
 #undef HAVE_LIBUSB
 
 /* Define to 1 if you have the <locale.h> header file. */
 #undef HAVE_LOCALE_H
 
+/* Define to 1 if the type `long double' works and has more range or precision
+   than `double'. */
+#undef HAVE_LONG_DOUBLE_WIDER
+
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
 
@@ -69,6 +79,9 @@
 /* Define to 1 if you have the <selinux/selinux.h> header file. */
 #undef HAVE_SELINUX_SELINUX_H
 
+/* Define to 1 if you have the `sigaction' function. */
+#undef HAVE_SIGACTION
+
 /* Define to 1 if you have the `sigset' function. */
 #undef HAVE_SIGSET
 
 /* 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 <systemd/sd-daemon.h> header file. */
+#undef HAVE_SYSTEMD_SD_DAEMON_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/sysmacros.h> header file. */
+#undef HAVE_SYS_SYSMACROS_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/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 you have the <wbemcli.h> header file. */
-#undef HAVE_WBEMCLI_H
-
 /* Define to 1 if the `snprintf' function is sane. */
 #undef HAVE_WORKING_SNPRINTF
 
+/* Define to 1 if the system has the type `__int128'. */
+#undef HAVE___INT128
+
+/* Define to 1 to use generic LE/BE code instead */
+#undef IGNORE_FAST_LEBE
+
 /* Define to 1 if os_*.cpp still uses the old interface */
 #undef OLD_INTERFACE
 
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 
+/* Define to 1 to enable check on each SCSI cdb */
+#undef SCSI_CDB_CHECK
+
 /* smartmontools Build Host */
 #undef SMARTMONTOOLS_BUILD_HOST
 
 /* Version number of package */
 #undef VERSION
 
+/* Define to 1 to use C++11 std::regex instead of POSIX regex(3) */
+#undef WITH_CXX11_REGEX
+
 /* Define to 1 to include NVMe devices in smartd DEVICESCAN. */
 #undef WITH_NVME_DEVICESCAN
 
-/* Define to 1 if SELinux support is enabled */
-#undef WITH_SELINUX
-
 /* Define to 1 to enable legacy ATA support on Solaris SPARC. */
 #undef WITH_SOLARIS_SPARC_ATA
 
index bb1c3ca27e2883a5822b8b245a58486323e74e4f..1ae0924d2a071d6e89bced46b7c018e632c41d20 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for smartmontools 6.6.
+# Generated by GNU Autoconf 2.69 for smartmontools 7.0.
 #
 # Report bugs to <smartmontools-support@listi.jpberlin.de>.
 #
@@ -580,8 +580,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='smartmontools'
 PACKAGE_TARNAME='smartmontools'
-PACKAGE_VERSION='6.6'
-PACKAGE_STRING='smartmontools 6.6'
+PACKAGE_VERSION='7.0'
+PACKAGE_STRING='smartmontools 7.0'
 PACKAGE_BUGREPORT='smartmontools-support@listi.jpberlin.de'
 PACKAGE_URL=''
 
@@ -648,12 +648,18 @@ os_mailer
 os_dltools
 os_libs
 os_deps
+with_nvme_devicescan
 smartmontools_release_time
 smartmontools_release_date
 releaseversion
-with_nvme_devicescan
+NEED_REGEX_FALSE
+NEED_REGEX_TRUE
+systemdenvfile
+INSTALL_SYSTEMDUNIT_FALSE
+INSTALL_SYSTEMDUNIT_TRUE
+systemdsystemunitdir
+SYSTEMD_LDADD
 CAPNG_LDADD
-with_selinux
 smartd_suffix
 ENABLE_ATTRIBUTELOG_FALSE
 ENABLE_ATTRIBUTELOG_TRUE
@@ -680,14 +686,8 @@ initdfile
 INSTALL_INITSCRIPT_FALSE
 INSTALL_INITSCRIPT_TRUE
 initddir
-systemdenvfile
-INSTALL_SYSTEMDUNIT_FALSE
-INSTALL_SYSTEMDUNIT_TRUE
-systemdsystemunitdir
 ASFLAGS
 gcc_have_attr_packed
-NEED_REGEX_FALSE
-NEED_REGEX_TRUE
 NEED_GETOPT_LONG_FALSE
 NEED_GETOPT_LONG_TRUE
 EGREP
@@ -812,8 +812,6 @@ enable_option_checking
 enable_silent_rules
 enable_maintainer_mode
 enable_dependency_tracking
-with_systemdsystemunitdir
-with_systemdenvfile
 with_initscriptdir
 with_exampledir
 with_drivedbdir
@@ -825,14 +823,21 @@ with_scriptpath
 with_savestates
 with_attributelog
 enable_sample
+enable_scsi_cdb_check
+enable_fast_lebe
 with_os_deps
 with_selinux
 with_libcap_ng
+with_libsystemd
+with_systemdsystemunitdir
+with_systemdenvfile
 with_nvme_devicescan
 with_solaris_sparc_ata
+with_signal_func
 with_working_snprintf
 with_mingw_aslr
 with_cxx11_option
+with_cxx11_regex
 '
       ac_precious_vars='build_alias
 host_alias
@@ -1394,7 +1399,7 @@ 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 6.6 to adapt to many kinds of systems.
+\`configure' configures smartmontools 7.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1464,7 +1469,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of smartmontools 6.6:";;
+     short | recursive ) echo "Configuration of smartmontools 7.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1483,16 +1488,14 @@ Optional Features:
                           speeds up one-time build
   --enable-sample         Enables appending .sample to the installed smartd rc
                           script and configuration file
+  --enable-scsi-cdb-check do sanity check on each SCSI cdb
+  --disable-fast-lebe     use generic little-endian/big-endian code instead
 
 Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
-  --with-systemdsystemunitdir[=DIR|auto|yes|no]
-                          Location of systemd service files [auto]
-  --with-systemdenvfile[=FILE|auto|yes|no]
-                          Path of systemd EnvironmentFile [auto]
-  --with-initscriptdir[=DIR|auto|yes|no]
-                          Location of init scripts [auto]
+  --with-initscriptdir=[DIR|no]
+                          Location of init scripts [no]
   --with-exampledir=DIR   Location of example scripts [DOCDIR/examplescripts]
   --with-drivedbdir[=DIR|yes|no]
                           Location of drive database file
@@ -1508,7 +1511,8 @@ Optional Packages:
                           Location of smartd_warning.sh plugin scripts
                           [SMARTDSCRIPTDIR/smartd_warning.d]
   --with-scriptpath=[PATH|no]
-                          PATH variable set within scripts [/usr/bin:/bin]
+                          PATH variable set within scripts
+                          [/usr/local/bin:/usr/bin:/bin]
   --with-savestates[=PREFIX|yes|no]
                           Enable default smartd state files [no]
                           (yes=LOCALSTATEDIR/lib/smartmontools/smartd.)
@@ -1520,20 +1524,31 @@ Optional Packages:
   --with-selinux[=yes|no] Enables SELinux support [no]
   --with-libcap-ng[=auto|yes|no]
                           Add Libcap-ng support to smartd [auto]
+  --with-libsystemd[=auto|yes|no]
+                          Add systemd 'Type=notify' support to smartd [auto]
+  --with-systemdsystemunitdir[=DIR|auto|yes|no]
+                          Location of systemd service files [auto]
+  --with-systemdenvfile[=FILE|auto|yes|no]
+                          Path of systemd EnvironmentFile [auto]
   --with-nvme-devicescan[=yes|no]
-                          Include NVMe devices in smartd DEVICESCAN [no]
+                          Include NVMe devices in smartd DEVICESCAN
+                          [Linux,Windows:yes;Others:no]
   --with-solaris-sparc-ata[=yes|no]
                           Enable legacy ATA support on Solaris SPARC (requires
                           os_solaris_ata.s from SVN repository) [no]
+  --with-signal-func=[sigaction|sigset|signal]
+                          Function to set signal(2) action [sigaction]
   --with-working-snprintf[=yes|no]
                           Function snprintf() handles output truncation as
-                          specified by C99 [MinGW:guessed,others:yes]
+                          specified by C99 [yes]
   --with-mingw-aslr[=auto|yes|low|no]
                           Enable ASLR for MinGW executables [auto]
-  --with-cxx11-option[=OPTION|auto|no]
+  --with-cxx11-option=[OPTION|auto|no]
                           Compiler option to enable C++11 support for future
                           versions of smartmontools, 'no' if unsupported
                           [auto]
+  --with-cxx11-regex[=yes|no]
+                          Use C++11 std::regex instead of POSIX regex(3) [no]
 
 Some influential environment variables:
   CXX         C++ compiler command
@@ -1623,7 +1638,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-smartmontools configure 6.6
+smartmontools configure 7.0
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2084,7 +2099,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by smartmontools $as_me 6.6, which was
+It was created by smartmontools $as_me 7.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2947,7 +2962,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='smartmontools'
- VERSION='6.6'
+ VERSION='7.0'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -3041,9 +3056,12 @@ END
 fi
 
 
-smartmontools_cvs_tag=`echo '$Id: configure.ac 4594 2017-11-05 15:21:35Z chrfranke $'`
-smartmontools_release_date=2017-11-05
-smartmontools_release_time="15:20:58 UTC"
+# Version of drive database branch
+smartmontools_drivedb_version=7.0
+
+smartmontools_cvs_tag=`echo '$Id: configure.ac 4883 2018-12-30 14:48:54Z chrfranke $'`
+smartmontools_release_date=2018-12-30
+smartmontools_release_time="14:47:55 UTC"
 
 
 cat >>confdefs.h <<_ACEOF
@@ -3067,7 +3085,7 @@ _ACEOF
 
 
 cat >>confdefs.h <<_ACEOF
-#define PACKAGE_HOMEPAGE "http://www.smartmontools.org/"
+#define PACKAGE_HOMEPAGE "https://www.smartmontools.org/"
 _ACEOF
 
 
@@ -5095,9 +5113,10 @@ fi
 $as_echo "$is_svn_build" >&6; }
 
 # Note: On Linux, clock_gettime() requires -lrt which implies -lpthreads
-# Check ommitted for now, gettimeofday() provides reasonable precision
+# Check omitted for now, gettimeofday() provides reasonable precision
 # AC_SEARCH_LIBS(clock_gettime, rt)
 
+# Checks for header files.
 
 ac_ext=cpp
 ac_cpp='$CXXCPP $CPPFLAGS'
 
 done
 
-for ac_header in dev/ata/atavar.h
+for ac_header in byteswap.h
 do :
-  ac_fn_cxx_check_header_mongrel "$LINENO" "dev/ata/atavar.h" "ac_cv_header_dev_ata_atavar_h" "$ac_includes_default"
-if test "x$ac_cv_header_dev_ata_atavar_h" = xyes; then :
+  ac_fn_cxx_check_header_mongrel "$LINENO" "byteswap.h" "ac_cv_header_byteswap_h" "$ac_includes_default"
+if test "x$ac_cv_header_byteswap_h" = xyes; then :
   cat >>confdefs.h <<_ACEOF
-#define HAVE_DEV_ATA_ATAVAR_H 1
+#define HAVE_BYTESWAP_H 1
 _ACEOF
 
 fi
 
 done
 
-for ac_header in inttypes.h
-do :
-  ac_fn_cxx_check_header_mongrel "$LINENO" "inttypes.h" "ac_cv_header_inttypes_h" "$ac_includes_default"
-if test "x$ac_cv_header_inttypes_h" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_INTTYPES_H 1
-_ACEOF
-
-fi
-
-done
-               for ac_header in stdint.h
-do :
-  ac_fn_cxx_check_header_mongrel "$LINENO" "stdint.h" "ac_cv_header_stdint_h" "$ac_includes_default"
-if test "x$ac_cv_header_stdint_h" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_STDINT_H 1
-_ACEOF
-
-fi
-
-done
-               for ac_header in sys/inttypes.h
-do :
-  ac_fn_cxx_check_header_mongrel "$LINENO" "sys/inttypes.h" "ac_cv_header_sys_inttypes_h" "$ac_includes_default"
-if test "x$ac_cv_header_sys_inttypes_h" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_SYS_INTTYPES_H 1
-_ACEOF
 
-fi
-
-done
-       for ac_header in sys/int_types.h
-do :
-  ac_fn_cxx_check_header_mongrel "$LINENO" "sys/int_types.h" "ac_cv_header_sys_int_types_h" "$ac_includes_default"
-if test "x$ac_cv_header_sys_int_types_h" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_SYS_INT_TYPES_H 1
-_ACEOF
-
-fi
-
-done
-       for ac_header in sys/tweio.h
-do :
-  ac_fn_cxx_check_header_mongrel "$LINENO" "sys/tweio.h" "ac_cv_header_sys_tweio_h" "$ac_includes_default"
-if test "x$ac_cv_header_sys_tweio_h" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_SYS_TWEIO_H 1
-_ACEOF
-
-fi
-
-done
-
-for ac_header in sys/twereg.h
+case "$host" in
+  *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*)
+    # Check for FreeBSD twe and twa include files
+    for ac_header in sys/tweio.h sys/twereg.h sys/tw_osl_ioctl.h
 do :
-  ac_fn_cxx_check_header_mongrel "$LINENO" "sys/twereg.h" "ac_cv_header_sys_twereg_h" "$ac_includes_default"
-if test "x$ac_cv_header_sys_twereg_h" = xyes; then :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
   cat >>confdefs.h <<_ACEOF
-#define HAVE_SYS_TWEREG_H 1
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
 _ACEOF
 
 fi
 
 done
 
-for ac_header in sys/tw_osl_ioctl.h
+    # Check for the FreeBSD CCISS system header and use internal one if not found
+    for ac_header in dev/ciss/cissio.h
 do :
-  ac_fn_cxx_check_header_mongrel "$LINENO" "sys/tw_osl_ioctl.h" "ac_cv_header_sys_tw_osl_ioctl_h" "$ac_includes_default"
-if test "x$ac_cv_header_sys_tw_osl_ioctl_h" = xyes; then :
+  ac_fn_cxx_check_header_mongrel "$LINENO" "dev/ciss/cissio.h" "ac_cv_header_dev_ciss_cissio_h" "$ac_includes_default"
+if test "x$ac_cv_header_dev_ciss_cissio_h" = xyes; then :
   cat >>confdefs.h <<_ACEOF
-#define HAVE_SYS_TW_OSL_IOCTL_H 1
+#define HAVE_DEV_CISS_CISSIO_H 1
 _ACEOF
 
-fi
+$as_echo "#define CISS_LOCATION <dev/ciss/cissio.h>" >>confdefs.h
 
-done
+else
 
-for ac_header in linux/compiler.h
-do :
-  ac_fn_cxx_check_header_mongrel "$LINENO" "linux/compiler.h" "ac_cv_header_linux_compiler_h" "$ac_includes_default"
-if test "x$ac_cv_header_linux_compiler_h" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_LINUX_COMPILER_H 1
-_ACEOF
+$as_echo "#define CISS_LOCATION \"cissio_freebsd.h\"" >>confdefs.h
 
 fi
 
 done
 
-for ac_header in dev/ciss/cissio.h
+    ;;
+  *-*-linux*)
+    # <linux/compiler.h> is needed for cciss_ioctl.h at least on SuSE LINUX
+    for ac_header in sys/sysmacros.h linux/compiler.h
 do :
-  ac_fn_cxx_check_header_mongrel "$LINENO" "dev/ciss/cissio.h" "ac_cv_header_dev_ciss_cissio_h" "$ac_includes_default"
-if test "x$ac_cv_header_dev_ciss_cissio_h" = xyes; then :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
   cat >>confdefs.h <<_ACEOF
-#define HAVE_DEV_CISS_CISSIO_H 1
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
 _ACEOF
 
-$as_echo "#define CISS_LOCATION <dev/ciss/cissio.h>" >>confdefs.h
-
-else
-
-$as_echo "#define CISS_LOCATION \"cissio_freebsd.h\"" >>confdefs.h
-
-
 fi
 
 done
 
-for ac_header in linux/cciss_ioctl.h
+    # Check for Linux CCISS include file
+    for ac_header in linux/cciss_ioctl.h
 do :
   ac_fn_cxx_check_header_compile "$LINENO" "linux/cciss_ioctl.h" "ac_cv_header_linux_cciss_ioctl_h" "$ac_includes_default
 #ifdef HAVE_LINUX_COMPILER_H
 
 done
 
-for ac_header in ntdddisk.h ddk/ntdddisk.h
+    ;;
+  *-*-netbsd*|*-*-openbsd*)
+    for ac_header in dev/ata/atavar.h
 do :
-  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
-ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
-#include <windows.h>
-
-"
-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
+  ac_fn_cxx_check_header_mongrel "$LINENO" "dev/ata/atavar.h" "ac_cv_header_dev_ata_atavar_h" "$ac_includes_default"
+if test "x$ac_cv_header_dev_ata_atavar_h" = xyes; then :
   cat >>confdefs.h <<_ACEOF
-#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
+#define HAVE_DEV_ATA_ATAVAR_H 1
 _ACEOF
 
 fi
 
 done
 
-for ac_header in wbemcli.h
+    ;;
+  *-*-cygwin*|*-*-mingw*)
+    # Check for Windows DDK header files
+    for ac_header in ntdddisk.h ddk/ntdddisk.h
 do :
-  ac_fn_cxx_check_header_mongrel "$LINENO" "wbemcli.h" "ac_cv_header_wbemcli_h" "$ac_includes_default"
-if test "x$ac_cv_header_wbemcli_h" = xyes; then :
+  as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
+ac_fn_cxx_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
+#include <windows.h>
+
+"
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
   cat >>confdefs.h <<_ACEOF
-#define HAVE_WBEMCLI_H 1
+#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
 _ACEOF
 
 fi
 
 done
 
+    ;;
+esac
 
-ac_fn_cxx_check_type "$LINENO" "int64_t" "ac_cv_type_int64_t" "$ac_includes_default"
-if test "x$ac_cv_type_int64_t" = xyes; then :
+# Checks for typedefs, and compiler characteristics.
+ac_fn_cxx_check_type "$LINENO" "__int128" "ac_cv_type___int128" "$ac_includes_default"
+if test "x$ac_cv_type___int128" = xyes; then :
 
 cat >>confdefs.h <<_ACEOF
-#define HAVE_INT64_T 1
+#define HAVE___INT128 1
 _ACEOF
 
 
 fi
-ac_fn_cxx_check_type "$LINENO" "uint64_t" "ac_cv_type_uint64_t" "$ac_includes_default"
-if test "x$ac_cv_type_uint64_t" = xyes; then :
 
-cat >>confdefs.h <<_ACEOF
-#define HAVE_UINT64_T 1
-_ACEOF
 
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for long double with more range or precision than double" >&5
+$as_echo_n "checking for long double with more range or precision than double... " >&6; }
+if ${ac_cv_type_long_double_wider+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <float.h>
+           long double const a[] =
+             {
+                0.0L, DBL_MIN, DBL_MAX, DBL_EPSILON,
+                LDBL_MIN, LDBL_MAX, LDBL_EPSILON
+             };
+           long double
+           f (long double x)
+           {
+              return ((x + (unsigned long int) 10) * (-1 / x) + a[0]
+                       + (x ? f (x) : 'c'));
+           }
+
+int
+main ()
+{
+static int test_array [1 - 2 * !((0 < ((DBL_MAX_EXP < LDBL_MAX_EXP)
+                  + (DBL_MANT_DIG < LDBL_MANT_DIG)
+                  - (LDBL_MAX_EXP < DBL_MAX_EXP)
+                  - (LDBL_MANT_DIG < DBL_MANT_DIG)))
+           && (int) LDBL_EPSILON == 0
+         )];
+test_array [0] = 0;
+return test_array [0];
 
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ac_cv_type_long_double_wider=yes
+else
+  ac_cv_type_long_double_wider=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_double_wider" >&5
+$as_echo "$ac_cv_type_long_double_wider" >&6; }
+  if test $ac_cv_type_long_double_wider = yes; then
 
+$as_echo "#define HAVE_LONG_DOUBLE_WIDER 1" >>confdefs.h
 
+  fi
+
+
+# Checks for library functions.
 for ac_func in getopt_long
 do :
   ac_fn_cxx_check_func "$LINENO" "getopt_long" "ac_cv_func_getopt_long"
@@ -5714,60 +5727,6 @@ else
   NEED_GETOPT_LONG_FALSE=
 fi
 
-for ac_func in regcomp
-do :
-  ac_fn_cxx_check_func "$LINENO" "regcomp" "ac_cv_func_regcomp"
-if test "x$ac_cv_func_regcomp" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_REGCOMP 1
-_ACEOF
- need_regex=no
-else
-  need_regex=yes
-fi
-done
-
- if test "$need_regex" = "yes"; then
-  NEED_REGEX_TRUE=
-  NEED_REGEX_FALSE='#'
-else
-  NEED_REGEX_TRUE='#'
-  NEED_REGEX_FALSE=
-fi
-
-
-for ac_func in sigset
-do :
-  ac_fn_cxx_check_func "$LINENO" "sigset" "ac_cv_func_sigset"
-if test "x$ac_cv_func_sigset" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_SIGSET 1
-_ACEOF
-
-fi
-done
-
-for ac_func in strtoull
-do :
-  ac_fn_cxx_check_func "$LINENO" "strtoull" "ac_cv_func_strtoull"
-if test "x$ac_cv_func_strtoull" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_STRTOULL 1
-_ACEOF
-
-fi
-done
-
-for ac_func in uname
-do :
-  ac_fn_cxx_check_func "$LINENO" "uname" "ac_cv_func_uname"
-if test "x$ac_cv_func_uname" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_UNAME 1
-_ACEOF
-
-fi
-done
 
 for ac_func in clock_gettime ftime gettimeofday
 do :
@@ -6045,102 +6004,18 @@ $as_echo "$gcc_have_attr_packed" >&6; }
 
 
 
-
-# Check whether --with-systemdsystemunitdir was given.
-if test "${with_systemdsystemunitdir+set}" = set; then :
-  withval=$with_systemdsystemunitdir;
-else
-  with_systemdsystemunitdir=auto
-fi
-
-
-systemdsystemunitdir=
-case "$with_systemdsystemunitdir" in
- auto|yes)
-   if test -n "$PKG_CONFIG"; then
-     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for systemdsystemunitdir" >&5
-$as_echo_n "checking for systemdsystemunitdir... " >&6; }
-     systemdsystemunitdir=`$PKG_CONFIG --variable=systemdsystemunitdir systemd 2>/dev/null`
-     { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${systemdsystemunitdir:-no}" >&5
-$as_echo "${systemdsystemunitdir:-no}" >&6; }
-   fi
-   case "$with_systemdsystemunitdir:$sysconfdir:$systemdsystemunitdir" in
-     yes:*:) as_fn_error $? "Location of systemd service files not found" "$LINENO" 5 ;;
-     yes:*:*|auto:*:|auto:/etc:*) ;;
-     *) systemdsystemunitdir='${prefix}'$systemdsystemunitdir ;;
-   esac ;;
- no) ;;
- *) systemdsystemunitdir="$with_systemdsystemunitdir" ;;
-esac
-
- if test -n "$systemdsystemunitdir"; then
-  INSTALL_SYSTEMDUNIT_TRUE=
-  INSTALL_SYSTEMDUNIT_FALSE='#'
-else
-  INSTALL_SYSTEMDUNIT_TRUE='#'
-  INSTALL_SYSTEMDUNIT_FALSE=
-fi
-
-
-
-# Check whether --with-systemdenvfile was given.
-if test "${with_systemdenvfile+set}" = set; then :
-  withval=$with_systemdenvfile;
-else
-  with_systemdenvfile=auto
-fi
-
-
-systemdenvfile=
-case "$with_systemdenvfile:$cross_compiling:$systemdsystemunitdir" in
-  auto:no:?*|yes:*:?*)
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for path of systemd EnvironmentFile" >&5
-$as_echo_n "checking for path of systemd EnvironmentFile... " >&6; }
-    for dir in sysconfig default; do
-      if test -d /etc/$dir; then
-        systemdenvfile='${sysconfdir}'/$dir/smartmontools
-        break
-      fi
-    done
-    { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${systemdenvfile:-no}" >&5
-$as_echo "${systemdenvfile:-no}" >&6; }
-    case "$with_systemdenvfile:$systemdenvfile" in
-      yes:) as_fn_error $? "Path of systemd EnvironmentFile not found" "$LINENO" 5 ;;
-    esac ;;
-  auto:*|no:*) ;;
-  *:*:) as_fn_error $? "Location of systemd service files not found" "$LINENO" 5 ;;
-  *) systemdenvfile="$with_systemdenvfile"
-esac
-
-
+initddir=
 
 # Check whether --with-initscriptdir was given.
 if test "${with_initscriptdir+set}" = set; then :
-  withval=$with_initscriptdir;
-else
-  with_initscriptdir=auto
-fi
+  withval=$with_initscriptdir;  case "$withval" in
+      auto|yes) as_fn_error $? "'--with-initscriptdir=$withval' is no longer supported" "$LINENO" 5 ;;
+      no) ;;
+      *) initddir="$withval" ;;
+    esac
 
+fi
 
-initddir=
-case "$with_initscriptdir:$cross_compiling:$systemdsystemunitdir" in
-  auto:no:|yes:*)
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for init (rc) directory" >&5
-$as_echo_n "checking for init (rc) directory... " >&6; }
-    for dir in rc.d/init.d init.d rc.d; do
-      if test -d /etc/$dir; then
-        initddir='${sysconfdir}'/$dir
-        break
-      fi
-    done
-    { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${initddir:-no}" >&5
-$as_echo "${initddir:-no}" >&6; }
-    case "$with_initscriptdir:$initddir" in
-      yes:) as_fn_error $? "Location of init scripts not found" "$LINENO" 5 ;;
-    esac ;;
-  auto:*|no:*) ;;
-  *) initddir="$with_initscriptdir" ;;
-esac
 
  if test -n "$initddir"; then
   INSTALL_INITSCRIPT_TRUE=
@@ -6159,6 +6034,9 @@ case "${host}" in
   *-apple-darwin*)
     initdfile="com.smartmontools.smartd.plist"
     ;;
+  *-*-cygwin*)
+    initdfile="smartd.cygwin.initd"
+    ;;
   *)
     initdfile="smartd.initd"
     ;;
@@ -6192,7 +6070,7 @@ else
 fi
 
 
-drivedb_version=$VERSION
+drivedb_version=$smartmontools_drivedb_version
 
 # Check whether --with-update-smart_drivedb was given.
 if test "${with_update_smart_drivedb+set}" = set; then :
@@ -6260,7 +6138,7 @@ fi
 if test "${with_scriptpath+set}" = set; then :
   withval=$with_scriptpath; scriptpath=; test "$withval" != "no" && scriptpath="$withval"
 else
-  scriptpath="/usr/bin:/bin"
+  scriptpath="/usr/local/bin:/usr/bin:/bin"
 fi
 
 
@@ -6322,6 +6200,28 @@ fi
 
 
 
+# Check whether --enable-scsi-cdb-check was given.
+if test "${enable_scsi_cdb_check+set}" = set; then :
+  enableval=$enable_scsi_cdb_check;  if test "$enableval" = "yes"; then
+
+$as_echo "#define SCSI_CDB_CHECK 1" >>confdefs.h
+
+    fi
+
+fi
+
+
+# Check whether --enable-fast-lebe was given.
+if test "${enable_fast_lebe+set}" = set; then :
+  enableval=$enable_fast_lebe;  if test "$enableval" = "no"; then
+
+$as_echo "#define IGNORE_FAST_LEBE 1" >>confdefs.h
+
+    fi
+
+fi
+
+
 
 # Check whether --with-os-deps was given.
 if test "${with_os_deps+set}" = set; then :
@@ -6405,12 +6305,6 @@ fi
 fi
 
 
-if test "$with_selinux" = "yes"; then
-
-$as_echo "#define WITH_SELINUX 1" >>confdefs.h
-
-fi
-
 
 # Check whether --with-libcap-ng was given.
 if test "${with_libcap_ng+set}" = set; then :
@@ -6421,8 +6315,16 @@ fi
 
 
 use_libcap_ng=no
-if test "$with_libcap_ng" != "no"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for capng_clear in -lcap-ng" >&5
+case "$with_libcap_ng:$host_os" in
+  auto:linux*|yes:*)
+    for ac_header in cap-ng.h
+do :
+  ac_fn_cxx_check_header_mongrel "$LINENO" "cap-ng.h" "ac_cv_header_cap_ng_h" "$ac_includes_default"
+if test "x$ac_cv_header_cap_ng_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_CAP_NG_H 1
+_ACEOF
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for capng_clear in -lcap-ng" >&5
 $as_echo_n "checking for capng_clear in -lcap-ng... " >&6; }
 if ${ac_cv_lib_cap_ng_capng_clear+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -6461,45 +6363,172 @@ $as_echo "$ac_cv_lib_cap_ng_capng_clear" >&6; }
 if test "x$ac_cv_lib_cap_ng_capng_clear" = xyes; then :
 
 $as_echo "#define HAVE_LIBCAP_NG 1" >>confdefs.h
-      CAPNG_LDADD="-lcap-ng"; use_libcap_ng=yes
+        CAPNG_LDADD="-lcap-ng"; use_libcap_ng=yes
+else
+  as_fn_error $? "libcap-ng headers found but library is missing" "$LINENO" 5
 fi
 
+else
+  test "$with_libcap_ng" != "yes" || as_fn_error $? "Missing libcap-ng header files" "$LINENO" 5
+fi
 
-  if test "$use_libcap_ng" = "yes"; then
-    ac_fn_cxx_check_header_mongrel "$LINENO" "cap-ng.h" "ac_cv_header_cap_ng_h" "$ac_includes_default"
-if test "x$ac_cv_header_cap_ng_h" = xyes; then :
+done
 
+    ;;
+esac
+
+
+
+# Check whether --with-libsystemd was given.
+if test "${with_libsystemd+set}" = set; then :
+  withval=$with_libsystemd;
 else
-  as_fn_error $? "libcap-ng libraries found but headers are missing" "$LINENO" 5
+  with_libsystemd=auto
 fi
 
 
-  elif test "$with_libcap_ng" = "yes"; then
-    as_fn_error $? "libcap-ng support was requested but the library was not found" "$LINENO" 5
-  fi
+use_libsystemd=no
+case "$with_libsystemd:$host_os" in
+  auto:linux*|yes:*)
+    for ac_header in systemd/sd-daemon.h
+do :
+  ac_fn_cxx_check_header_mongrel "$LINENO" "systemd/sd-daemon.h" "ac_cv_header_systemd_sd_daemon_h" "$ac_includes_default"
+if test "x$ac_cv_header_systemd_sd_daemon_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_SYSTEMD_SD_DAEMON_H 1
+_ACEOF
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sd_notify in -lsystemd" >&5
+$as_echo_n "checking for sd_notify in -lsystemd... " >&6; }
+if ${ac_cv_lib_systemd_sd_notify+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lsystemd  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char sd_notify ();
+int
+main ()
+{
+return sd_notify ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+  ac_cv_lib_systemd_sd_notify=yes
+else
+  ac_cv_lib_systemd_sd_notify=no
 fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_systemd_sd_notify" >&5
+$as_echo "$ac_cv_lib_systemd_sd_notify" >&6; }
+if test "x$ac_cv_lib_systemd_sd_notify" = xyes; then :
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use libcap-ng" >&5
-$as_echo_n "checking whether to use libcap-ng... " >&6; }
+$as_echo "#define HAVE_LIBSYSTEMD 1" >>confdefs.h
+        SYSTEMD_LDADD="-lsystemd"; use_libsystemd=yes
+else
+  as_fn_error $? "libsystemd headers found but library is missing" "$LINENO" 5
+fi
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $use_libcap_ng" >&5
-$as_echo "$use_libcap_ng" >&6; }
+else
+  test "$with_libsystemd" != "yes" || as_fn_error $? "Missing libsystemd header files" "$LINENO" 5
+fi
 
-# TODO: Remove when NVMe support is no longer EXPERIMENTAL
+done
 
-# Check whether --with-nvme-devicescan was given.
-if test "${with_nvme_devicescan+set}" = set; then :
-  withval=$with_nvme_devicescan;
+    ;;
+esac
+
+
+
+# Check whether --with-systemdsystemunitdir was given.
+if test "${with_systemdsystemunitdir+set}" = set; then :
+  withval=$with_systemdsystemunitdir;
+else
+  with_systemdsystemunitdir=auto
 fi
 
 
-if test "$with_nvme_devicescan" = "yes"; then
+systemdsystemunitdir=
+case "$with_systemdsystemunitdir:$use_libsystemd" in
+  auto:yes|yes:yes)
+    if test -n "$PKG_CONFIG"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for systemdsystemunitdir" >&5
+$as_echo_n "checking for systemdsystemunitdir... " >&6; }
+      systemdsystemunitdir=`$PKG_CONFIG --variable=systemdsystemunitdir systemd 2>/dev/null`
+      { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${systemdsystemunitdir:-no}" >&5
+$as_echo "${systemdsystemunitdir:-no}" >&6; }
+    fi
+    case "$with_systemdsystemunitdir:$sysconfdir:$systemdsystemunitdir" in
+      yes:*:) as_fn_error $? "Location of systemd service files not found" "$LINENO" 5 ;;
+      yes:*:*|auto:*:|auto:/etc:*) ;;
+      *) systemdsystemunitdir='${prefix}'$systemdsystemunitdir ;;
+    esac ;;
+  auto:*|no:*) ;;
+  *:yes) systemdsystemunitdir="$with_systemdsystemunitdir" ;;
+  *) as_fn_error $? "'--with-systemdsystemunitdir=$with_systemdsystemunitdir' now requires '--with-libsystemd'" "$LINENO" 5 ;;
+esac
 
-$as_echo "#define WITH_NVME_DEVICESCAN 1" >>confdefs.h
+ if test -n "$systemdsystemunitdir"; then
+  INSTALL_SYSTEMDUNIT_TRUE=
+  INSTALL_SYSTEMDUNIT_FALSE='#'
+else
+  INSTALL_SYSTEMDUNIT_TRUE='#'
+  INSTALL_SYSTEMDUNIT_FALSE=
+fi
+
+
+
+# Check whether --with-systemdenvfile was given.
+if test "${with_systemdenvfile+set}" = set; then :
+  withval=$with_systemdenvfile;
+else
+  with_systemdenvfile=auto
+fi
+
+
+systemdenvfile=
+case "$with_systemdenvfile:$cross_compiling:$systemdsystemunitdir" in
+  auto:no:?*|yes:*:?*)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for path of systemd EnvironmentFile" >&5
+$as_echo_n "checking for path of systemd EnvironmentFile... " >&6; }
+    for dir in sysconfig default; do
+      if test -d /etc/$dir; then
+        systemdenvfile='${sysconfdir}'/$dir/smartmontools
+        break
+      fi
+    done
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${systemdenvfile:-no}" >&5
+$as_echo "${systemdenvfile:-no}" >&6; }
+    case "$with_systemdenvfile:$systemdenvfile" in
+      yes:) as_fn_error $? "Path of systemd EnvironmentFile not found" "$LINENO" 5 ;;
+    esac ;;
+  auto:*|no:*) ;;
+  *:*:) as_fn_error $? "Location of systemd service files not found" "$LINENO" 5 ;;
+  *) systemdenvfile="$with_systemdenvfile"
+esac
+
+
+# TODO: Remove when NVMe support is no longer EXPERIMENTAL
 
+# Check whether --with-nvme-devicescan was given.
+if test "${with_nvme_devicescan+set}" = set; then :
+  withval=$with_nvme_devicescan;
 fi
 
 
+
 # Check whether --with-solaris-sparc-ata was given.
 if test "${with_solaris_sparc_ata+set}" = set; then :
   withval=$with_solaris_sparc_ata;
@@ -6519,52 +6548,107 @@ $as_echo "#define WITH_SOLARIS_SPARC_ATA 1" >>confdefs.h
     ;;
 esac
 
-# Assume broken snprintf only on Windows with MSVCRT (MinGW without ANSI stdio support)
-libc_have_working_snprintf=yes
 
-case "$host" in
-  *-*-mingw*)
-    case " $CPPFLAGS $CXXFLAGS" in
-      *\ -[DU]__USE_MINGW_ANSI_STDIO*)
-        ;;
-      *)
-        # Older MinGW do not properly define PRI?64 if __USE_MINGW_ANSI_STDIO is set
-        # Newer MinGW set __USE_MINGW_ANSI_STDIO in first C++ include which may be too late
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __USE_MINGW_ANSI_STDIO is defined by C++ includes" >&5
-$as_echo_n "checking whether __USE_MINGW_ANSI_STDIO is defined by C++ includes... " >&6; }
-        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
+# Check whether --with-signal-func was given.
+if test "${with_signal_func+set}" = set; then :
+  withval=$with_signal_func;
+else
+  with_signal_func=sigaction
+fi
+
 
-          #undef __USE_MINGW_ANSI_STDIO
-          #include <iostream>
-          #ifndef __USE_MINGW_ANSI_STDIO
-            #error false
-          #endif
+case "$host:$with_signal_func" in
+  *-*-mingw*:*) ;;
+  *:sigaction)
+    for ac_func in sigaction
+do :
+  ac_fn_cxx_check_func "$LINENO" "sigaction" "ac_cv_func_sigaction"
+if test "x$ac_cv_func_sigaction" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_SIGACTION 1
 _ACEOF
-if ac_fn_cxx_try_cpp "$LINENO"; then :
-  CXXFLAGS="-D__USE_MINGW_ANSI_STDIO $CXXFLAGS"
+
 else
-  libc_have_working_snprintf=no
+  as_fn_error $? "Missing function 'sigaction()'.
+Try '--with-signal-func=sigset' or '--with-signal-func=signal'.
+Please send info about your system to $PACKAGE_BUGREPORT." "$LINENO" 5
 fi
-rm -f conftest.err conftest.i conftest.$ac_ext
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $libc_have_working_snprintf" >&5
-$as_echo "$libc_have_working_snprintf" >&6; }
-        ;;
-    esac ;;
+done
+ ;;
+  *:sigset)
+    for ac_func in sigset
+do :
+  ac_fn_cxx_check_func "$LINENO" "sigset" "ac_cv_func_sigset"
+if test "x$ac_cv_func_sigset" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_SIGSET 1
+_ACEOF
+
+else
+  as_fn_error $? "Missing function 'sigset()'" "$LINENO" 5
+fi
+done
+ ;;
+  *:signal) ;;
+  *) as_fn_error $? "Invalid option '--with-signal-func=$with_signal_func'" "$LINENO" 5 ;;
 esac
 
+# TODO: Remove after smartmontools 6.7
 
 # Check whether --with-working-snprintf was given.
 if test "${with_working_snprintf+set}" = set; then :
-  withval=$with_working_snprintf; libc_have_working_snprintf=$withval
+  withval=$with_working_snprintf;
+else
+  with_working_snprintf=yes
 fi
 
 
-if test "$libc_have_working_snprintf" = "yes"; then
+if test "$with_working_snprintf" = "yes"; then
 
 $as_echo "#define HAVE_WORKING_SNPRINTF 1" >>confdefs.h
  fi
 
+case "$with_working_snprintf:$host_os: $CPPFLAGS $CXXFLAGS" in
+  yes:mingw*:*\ -[DU]__USE_MINGW_ANSI_STDIO*)
+    ;;
+  yes:mingw*:*)
+    # Older MinGW (4.6.3) do not properly define PRI?64 if __USE_MINGW_ANSI_STDIO is set.
+    # Newer MinGW (4.9.1) set __USE_MINGW_ANSI_STDIO in first C++ include which may be too late.
+    # Set __USE_MINGW_ANSI_STDIO always and fail if not fully supported.
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports __USE_MINGW_ANSI_STDIO" >&5
+$as_echo_n "checking whether $CXX supports __USE_MINGW_ANSI_STDIO... " >&6; }
+    save_CXXFLAGS=$CXXFLAGS
+    CXXFLAGS="-Wformat -Werror -D__USE_MINGW_ANSI_STDIO"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+      #define __STDC_FORMAT_MACROS 1
+      #include <inttypes.h>
+      #include <stdio.h>
+      void f(char * buf1, char * buf2, size_t size) {
+        snprintf(buf1, size, "%lld", 42LL);
+        snprintf(buf2, size, "%" PRId64, (int64_t)42);
+      }
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  result=yes
+else
+  result=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $result" >&5
+$as_echo "$result" >&6; }
+    if test "$result" != "yes"; then
+      as_fn_error $? "
+This version of $CXX does not support __USE_MINGW_ANSI_STDIO.
+Use option '--without-working-snprintf' to skip this check.
+Please send info about your system to $PACKAGE_BUGREPORT.
+" "$LINENO" 5
+    fi
+    CXXFLAGS="-D__USE_MINGW_ANSI_STDIO $save_CXXFLAGS"
+    ;;
+esac
+
 
 # Check whether --with-mingw-aslr was given.
 if test "${with_mingw_aslr+set}" = set; then :
@@ -6720,7 +6804,7 @@ This version of smartmontools does not use C++11 features, but future
 versions possibly will.
 This script was unable to determine a compiler option to enable C++11.
 Use option '--with-cxx11-option=OPTION' to specify the compiler option
-(it will be checked, but not used in the actual build).
+(it will be used in the actual build only if '--with-cxx11-regex' is set).
 Use option '--without-cxx11-option' to suppress this error message if the
 compiler lacks C++11 support.
 In both cases, please send info about compiler and platform to
@@ -6737,6 +6821,46 @@ $as_echo "$res" >&6; }
 esac
 
 
+# Check whether --with-cxx11-regex was given.
+if test "${with_cxx11_regex+set}" = set; then :
+  withval=$with_cxx11_regex;
+fi
+
+
+need_regex=no
+if test "$with_cxx11_regex" = "yes"; then
+
+$as_echo "#define WITH_CXX11_REGEX 1" >>confdefs.h
+
+  case "$with_cxx11_option: $CXXFLAGS " in
+    no:*) as_fn_error $? "'--with-cxx11-regex' requires C++11 support" "$LINENO" 5 ;;
+    ?*:*\ $with_cxx11_option\ *) ;;
+    ?*:*) CXXFLAGS="$CXXFLAGS $with_cxx11_option" ;;
+  esac
+else
+  for ac_func in regcomp
+do :
+  ac_fn_cxx_check_func "$LINENO" "regcomp" "ac_cv_func_regcomp"
+if test "x$ac_cv_func_regcomp" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_REGCOMP 1
+_ACEOF
+
+else
+  need_regex=yes
+fi
+done
+
+fi
+ if test "$need_regex" = "yes"; then
+  NEED_REGEX_TRUE=
+  NEED_REGEX_FALSE='#'
+else
+  NEED_REGEX_TRUE='#'
+  NEED_REGEX_FALSE=
+fi
+
+
 releaseversion='${PACKAGE}-${VERSION}'
 
 
@@ -6755,16 +6879,18 @@ os_win32=no
 os_win32_mingw=no
 os_win64=no
 os_man_filter=
+os_nvme_devicescan=
 case "${host}" in
   *-*-linux*)
     os_deps='os_linux.o cciss.o dev_areca.o'
     os_dnsdomainname="'dnsdomainname' 'hostname -d'"
     os_nisdomainname="'nisdomainname' 'hostname -y' 'domainname'"
     os_man_filter=Linux
+    os_nvme_devicescan=yes
     ;;
   *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*)
     os_deps='os_freebsd.o cciss.o dev_areca.o'
-    os_libs='-lcam'
+    os_libs='-lcam -lsbuf'
     os_dltools='curl wget lynx fetch svn'
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libusb20_dev_get_device_desc in -lusb" >&5
 $as_echo_n "checking for libusb20_dev_get_device_desc in -lusb... " >&6; }
@@ -6812,6 +6938,7 @@ _ACEOF
 fi
 
     os_man_filter=FreeBSD
+    os_nvme_devicescan=no
     ;;
   sparc-*-solaris*)
     os_deps='os_solaris.o'
@@ -6831,6 +6958,7 @@ fi
     os_deps='os_netbsd.o'
     os_libs='-lutil'
     os_man_filter=NetBSD
+    os_nvme_devicescan=no
     ;;
   *-*-openbsd*)
     os_deps='os_openbsd.o'
@@ -6846,6 +6974,7 @@ fi
     os_nisdomainname=
     os_win32=yes
     os_man_filter=Cygwin
+    os_nvme_devicescan=yes
     ;;
   x86_64-*-mingw*)
     os_deps='os_win32.o dev_areca.o'
@@ -6853,18 +6982,21 @@ fi
     os_win32_mingw=yes
     os_win64=yes
     os_man_filter=Windows
+    os_nvme_devicescan=yes
     ;;
   *-*-mingw*)
     os_deps='os_win32.o dev_areca.o'
     os_win32=yes
     os_win32_mingw=yes
     os_man_filter=Windows
+    os_nvme_devicescan=yes
     ;;
   *-*-darwin*)
     os_deps='os_darwin.o'
     os_libs='-framework CoreFoundation -framework IOKit'
     os_darwin=yes
     os_man_filter=Darwin
+    os_nvme_devicescan=no
     ;;
   *-*-nto-qnx*)
     os_deps='os_qnxnto.o'
@@ -6896,6 +7028,23 @@ fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $os_new_interface" >&5
 $as_echo "$os_new_interface" >&6; }
 
+# TODO: Remove when NVMe support is no longer EXPERIMENTAL
+case "$os_nvme_devicescan:${with_nvme_devicescan+set}" in
+  no:|yes:)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether NVMe device scanning could be safely enabled" >&5
+$as_echo_n "checking whether NVMe device scanning could be safely enabled... " >&6; }
+    with_nvme_devicescan=$os_nvme_devicescan
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $os_nvme_devicescan" >&5
+$as_echo "$os_nvme_devicescan" >&6; }
+    os_nvme_devicescan=used ;;
+esac
+
+if test "$with_nvme_devicescan" = "yes"; then
+
+$as_echo "#define WITH_NVME_DEVICESCAN 1" >>confdefs.h
+
+fi
+
 
 
 
@@ -6965,6 +7114,7 @@ fi
 
 
 if test "$GXX" = "yes"; then
+  orig_CXXFLAGS=$CXXFLAGS
   # Add -Wall and -W[extra] if its not already specified
   case " $CXXFLAGS " in
     *\ -Wall\ *) ;;
@@ -6975,16 +7125,15 @@ if test "$GXX" = "yes"; then
     *) CXXFLAGS="$CXXFLAGS -W" ;;
   esac
   # Add -Wformat=2 (GCC 3.0) -fstack-protector[-strong] (GCC 4.1[4.9]) if supported
-  # and CXXFLAGS was not set in configure cmdline (TODO: -Wformat-signedness)
-  case "${ac_test_CXXFLAGS+set}:$ac_test_CXXFLAGS" in
-    set:)
-      for option in "-Wformat=2" "-fstack-protector-strong" "-fstack-protector"; do
-        case " $CXXFLAGS:$option" in *\ -fstack-p*:-fstack-p*) continue ;; esac
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports $option" >&5
+  # and no -W or -f option was set in configure cmdline (TODO: -Wformat-signedness)
+  for option in "-Wformat=2" "-fstack-protector-strong" "-fstack-protector"; do
+    case " $orig_CXXFLAGS:$option" in *\ -W*:-W*|*\ -f*:-f*) continue ;; esac
+    case " $CXXFLAGS:$option" in *\ -fstack-p*:-fstack-p*) continue ;; esac
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports $option" >&5
 $as_echo_n "checking whether $CXX supports $option... " >&6; }
-        save_CXXFLAGS=$CXXFLAGS
-        CXXFLAGS="$CXXFLAGS $option"
-        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+    save_CXXFLAGS=$CXXFLAGS
+    CXXFLAGS="$CXXFLAGS $option"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
 int
@@ -7001,11 +7150,9 @@ else
   res=no; CXXFLAGS=$save_CXXFLAGS
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $res" >&5
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $res" >&5
 $as_echo "$res" >&6; }
-      done
-      ;;
-  esac
+  done
 else
   # We are NOT using gcc, so enable host-specific compiler flags
   case "${host}" in
@@ -7194,15 +7341,7 @@ if test -z "${NEED_GETOPT_LONG_TRUE}" && test -z "${NEED_GETOPT_LONG_FALSE}"; th
   as_fn_error $? "conditional \"NEED_GETOPT_LONG\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
-if test -z "${NEED_REGEX_TRUE}" && test -z "${NEED_REGEX_FALSE}"; then
-  as_fn_error $? "conditional \"NEED_REGEX\" was never defined.
-Usually this means the macro was only invoked conditionally." "$LINENO" 5
-fi
 
-if test -z "${INSTALL_SYSTEMDUNIT_TRUE}" && test -z "${INSTALL_SYSTEMDUNIT_FALSE}"; then
-  as_fn_error $? "conditional \"INSTALL_SYSTEMDUNIT\" was never defined.
-Usually this means the macro was only invoked conditionally." "$LINENO" 5
-fi
 if test -z "${INSTALL_INITSCRIPT_TRUE}" && test -z "${INSTALL_INITSCRIPT_FALSE}"; then
   as_fn_error $? "conditional \"INSTALL_INITSCRIPT\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -7227,6 +7366,14 @@ if test -z "${ENABLE_ATTRIBUTELOG_TRUE}" && test -z "${ENABLE_ATTRIBUTELOG_FALSE
   as_fn_error $? "conditional \"ENABLE_ATTRIBUTELOG\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
 fi
+if test -z "${INSTALL_SYSTEMDUNIT_TRUE}" && test -z "${INSTALL_SYSTEMDUNIT_FALSE}"; then
+  as_fn_error $? "conditional \"INSTALL_SYSTEMDUNIT\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
+if test -z "${NEED_REGEX_TRUE}" && test -z "${NEED_REGEX_FALSE}"; then
+  as_fn_error $? "conditional \"NEED_REGEX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
 if test -z "${OS_DARWIN_TRUE}" && test -z "${OS_DARWIN_FALSE}"; then
   as_fn_error $? "conditional \"OS_DARWIN\" was never defined.
 Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -7648,7 +7795,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by smartmontools $as_me 6.6, which was
+This file was extended by smartmontools $as_me 7.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -7714,7 +7861,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-smartmontools config.status 6.6
+smartmontools config.status 7.0
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
@@ -8649,7 +8796,9 @@ info=`
         echo "drive database file:    \`eval eval eval echo $drivedbdir\`/drivedb.h"
         if test "$with_update_smart_drivedb" = "yes"; then
           echo "database update script: \`eval eval eval echo $sbindir\`/update-smart-drivedb"
-          if test "$drivedb_version" != "$VERSION"; then
+          if test "$drivedb_version" = "$smartmontools_drivedb_version"; then
+            echo "database update branch: branches/$DRIVEDB_BRANCH"
+          else
             echo "... backported to:      branches/$DRIVEDB_BRANCH"
           fi
           echo "download tools:         \`eval eval eval echo $os_dltools\`"
@@ -8678,12 +8827,12 @@ info=`
         echo "PATH within scripts:    [inherited]"
       fi
       if test -n "$initddir"; then
-        echo "smartd initd script:    \`eval eval eval echo $initddir\`/${initdfile}"
+        echo "smartd initd script:    \`eval eval eval echo $initddir\`/smartd"
       elif test -z "$systemdsystemunitdir"; then
         echo "smartd initd script:    [disabled]"
       fi
       if test -n "$systemdsystemunitdir"; then
-        echo "smartd systemd file:    \`eval eval eval echo $systemdsystemunitdir\`/smartd.service"
+        echo "smartd service file:    \`eval eval eval echo $systemdsystemunitdir\`/smartd.service"
         if test -n "$systemdenvfile"; then
           echo "smartd environ file:    \`eval eval eval echo $systemdenvfile\`"
         else
@@ -8700,14 +8849,13 @@ info=`
       else
         echo "smartd attribute logs:  [disabled]"
       fi
-      echo "libcap-ng support:      $use_libcap_ng"
-      case "$host_os" in
-        linux*) echo "SELinux support:        ${with_selinux-no}" ;;
-      esac
       case "$host_os" in
-        linux*|darwin*|netbsd*|cygwin*)
-          echo "NVMe DEVICESCAN:        ${with_nvme_devicescan-no}" ;;
+        linux*)
+          echo "SELinux support:        ${with_selinux-no}"
+          echo "libcap-ng support:      $use_libcap_ng"
+          echo "systemd notify support: $use_libsystemd" ;;
       esac
+      echo "NVMe DEVICESCAN:        ${with_nvme_devicescan-[not implemented]}"
       ;;
   esac
   echo "-----------------------------------------------------------------------------"
@@ -8721,9 +8869,22 @@ $info
 " >&6;}
 
 # TODO: Remove when NVMe support is no longer EXPERIMENTAL
-case "$host_os:${with_nvme_devicescan+set}" in
-  linux*:|darwin*:|netbsd*:|cygwin*:|mingw*:)
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+case "$os_nvme_devicescan:$with_nvme_devicescan" in
+  used:yes) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+The default for the inclusion of NVME devices in smartd.conf
+'DEVICESCAN' and 'smartctl --scan' has been changed to 'yes' on
+this platform.  If '--without-nvme-devicescan' is still needed,
+please inform $PACKAGE_BUGREPORT.
+Use option '--with-nvme-devicescan' to suppress this warning.
+" >&5
+$as_echo "$as_me: WARNING:
+The default for the inclusion of NVME devices in smartd.conf
+'DEVICESCAN' and 'smartctl --scan' has been changed to 'yes' on
+this platform.  If '--without-nvme-devicescan' is still needed,
+please inform $PACKAGE_BUGREPORT.
+Use option '--with-nvme-devicescan' to suppress this warning.
+" >&2;}  ;;
+  used:no) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
 This version of smartmontools provides NVMe support which is still
 EXPERIMENTAL.  NVMe devices are not yet included in smartd.conf
 'DEVICESCAN' and 'smartctl --scan' unless '-d nvme' is specified.
@@ -8736,26 +8897,35 @@ EXPERIMENTAL.  NVMe devices are not yet included in smartd.conf
 'DEVICESCAN' and 'smartctl --scan' unless '-d nvme' is specified.
 Use option '--with-nvme-devicescan' to include NVMe devices.
 Use option '--without-nvme-devicescan' to suppress this warning.
-" >&2;}
-    ;;
+" >&2;}  ;;
 esac
 
-# TODO: Remove after smartmontools 6.6
-case "$host:${with_solaris_sparc_ata+set}" in
-  sparc-*-solaris*:)
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
-Legacy ATA support is no longer enabled by default on Solaris SPARC.
-The required source file 'os_solaris_ata.s' is no longer included in
-the source tarball but still available in the SVN repository.
-Use option '--with-solaris-sparc-ata' to enable legacy ATA support.
-Use option '--without-solaris-sparc-ata' to suppress this warning.
+# TODO: Remove after smartmontools 6.7
+if test "$with_working_snprintf" != "yes"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+The option '--without-working-snprintf' is deprecated and will be removed
+in a future version of smartmontools.  If you still need support for
+pre-C99 snprintf(), please inform $PACKAGE_BUGREPORT.
 " >&5
 $as_echo "$as_me: WARNING:
-Legacy ATA support is no longer enabled by default on Solaris SPARC.
-The required source file 'os_solaris_ata.s' is no longer included in
-the source tarball but still available in the SVN repository.
-Use option '--with-solaris-sparc-ata' to enable legacy ATA support.
-Use option '--without-solaris-sparc-ata' to suppress this warning.
+The option '--without-working-snprintf' is deprecated and will be removed
+in a future version of smartmontools.  If you still need support for
+pre-C99 snprintf(), please inform $PACKAGE_BUGREPORT.
 " >&2;}
-    ;;
+fi
+
+case "$host_os:$with_libsystemd:$use_libsystemd:$PKG_CONFIG" in
+  linux*:auto:no:?*)
+    if $PKG_CONFIG systemd >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING:
+systemd(1) is used on this system but smartd systemd notify support will
+not be available because libsystemd-dev[el] package is not installed.
+Use option '--without-libsystemd' to suppress this warning.
+" >&5
+$as_echo "$as_me: WARNING:
+systemd(1) is used on this system but smartd systemd notify support will
+not be available because libsystemd-dev[el] package is not installed.
+Use option '--without-libsystemd' to suppress this warning.
+" >&2;}
+    fi ;;
 esac
index 91cf6d7aafb532a3da280826ed971502f887bee1..e3eb2b7adf86d71491c93ae8546ae9975fdb0e89 100644 (file)
@@ -1,20 +1,23 @@
 #
-# $Id: configure.ac 4594 2017-11-05 15:21:35Z chrfranke $
+# $Id: configure.ac 4883 2018-12-30 14:48:54Z chrfranke $
 #
 dnl Process this file with autoconf to produce a configure script.
 AC_PREREQ([2.60])
-AC_INIT(smartmontools, 6.6, smartmontools-support@listi.jpberlin.de)
+AC_INIT([smartmontools], [7.0], [smartmontools-support@listi.jpberlin.de])
 AM_INIT_AUTOMAKE([1.10 foreign])
 
-smartmontools_cvs_tag=`echo '$Id: configure.ac 4594 2017-11-05 15:21:35Z chrfranke $'`
-smartmontools_release_date=2017-11-05
-smartmontools_release_time="15:20:58 UTC"
+# Version of drive database branch
+smartmontools_drivedb_version=7.0
 
-AC_DEFINE_UNQUOTED(SMARTMONTOOLS_CONFIGURE_ARGS, "$ac_configure_args",            [smartmontools Configure Arguments])
-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://www.smartmontools.org/", [smartmontools Home Page])
+smartmontools_cvs_tag=`echo '$Id: configure.ac 4883 2018-12-30 14:48:54Z chrfranke $'`
+smartmontools_release_date=2018-12-30
+smartmontools_release_time="14:47:55 UTC"
+
+AC_DEFINE_UNQUOTED(SMARTMONTOOLS_CONFIGURE_ARGS, "$ac_configure_args",             [smartmontools Configure Arguments])
+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,             "https://www.smartmontools.org/", [smartmontools Home Page])
 
 AC_CONFIG_SRCDIR([smartctl.cpp])
 AC_CONFIG_HEADER([config.h])
@@ -77,53 +80,51 @@ AM_CONDITIONAL(IS_SVN_BUILD, [test "$is_svn_build" = "yes"])
 AC_MSG_RESULT([$is_svn_build])
 
 # Note: On Linux, clock_gettime() requires -lrt which implies -lpthreads
-# Check ommitted for now, gettimeofday() provides reasonable precision
+# Check omitted for now, gettimeofday() provides reasonable precision
 # AC_SEARCH_LIBS(clock_gettime, rt)
 
-dnl Checks for header files.
+# Checks for header files.
 AC_CHECK_HEADERS([locale.h])
-AC_CHECK_HEADERS([dev/ata/atavar.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 This header file is needed for cciss_ioctl.h at least on SuSE LINUX
-AC_CHECK_HEADERS([linux/compiler.h])
-dnl Check for the FreeBSD CCISS system header and use internal one if not found
-AC_CHECK_HEADERS([dev/ciss/cissio.h],
-    [AC_DEFINE([CISS_LOCATION],[<dev/ciss/cissio.h>],[freebsd ciss header location])],
-    [AC_DEFINE([CISS_LOCATION],["cissio_freebsd.h"],[freebsd ciss header location])]
-    )
-dnl Check for Linux CCISS include file
-AC_CHECK_HEADERS([linux/cciss_ioctl.h], [], [], [AC_INCLUDES_DEFAULT
+AC_CHECK_HEADERS([byteswap.h], [], [], [])
+
+case "$host" in
+  *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*)
+    # Check for FreeBSD twe and twa include files
+    AC_CHECK_HEADERS([sys/tweio.h sys/twereg.h sys/tw_osl_ioctl.h])
+    # Check for the FreeBSD CCISS system header and use internal one if not found
+    AC_CHECK_HEADERS([dev/ciss/cissio.h],
+      [AC_DEFINE([CISS_LOCATION],[<dev/ciss/cissio.h>],[freebsd ciss header location])],
+      [AC_DEFINE([CISS_LOCATION],["cissio_freebsd.h"],[freebsd ciss header location])])
+    ;;
+  *-*-linux*)
+    # <linux/compiler.h> is needed for cciss_ioctl.h at least on SuSE LINUX
+    AC_CHECK_HEADERS([sys/sysmacros.h linux/compiler.h])
+    # Check for Linux CCISS include file
+    AC_CHECK_HEADERS([linux/cciss_ioctl.h], [], [], [AC_INCLUDES_DEFAULT
 #ifdef HAVE_LINUX_COMPILER_H
 # include <linux/compiler.h>
 #endif
 ])
-dnl Check for Windows DDK and WMI header files
-AC_CHECK_HEADERS([ntdddisk.h ddk/ntdddisk.h], [], [], [AC_INCLUDES_DEFAULT
+    ;;
+  *-*-netbsd*|*-*-openbsd*)
+    AC_CHECK_HEADERS([dev/ata/atavar.h])
+    ;;
+  *-*-cygwin*|*-*-mingw*)
+    # Check for Windows DDK header files
+    AC_CHECK_HEADERS([ntdddisk.h ddk/ntdddisk.h], [], [], [AC_INCLUDES_DEFAULT
 #include <windows.h>
 ])
-AC_CHECK_HEADERS([wbemcli.h])
+    ;;
+esac
 
-dnl Checks for typedefs, structures, and compiler characteristics.
-AC_CHECK_TYPES([int64_t, uint64_t])
+# Checks for typedefs, and compiler characteristics.
+AC_CHECK_TYPES([__int128])
+AC_TYPE_LONG_DOUBLE_WIDER
 
-dnl Checks for library functions.
+# Checks for library functions.
 AC_CHECK_FUNCS([getopt_long], [need_getopt_long=no], [need_getopt_long=yes])
 AM_CONDITIONAL(NEED_GETOPT_LONG, [test "$need_getopt_long" = "yes"])
-AC_CHECK_FUNCS([regcomp], [need_regex=no], [need_regex=yes])
-AM_CONDITIONAL(NEED_REGEX, [test "$need_regex" = "yes"])
 
-AC_CHECK_FUNCS([sigset])
-AC_CHECK_FUNCS([strtoull])
-AC_CHECK_FUNCS([uname])
 AC_CHECK_FUNCS([clock_gettime ftime gettimeofday])
 
 # Check byte ordering (defines WORDS_BIGENDIAN)
@@ -147,74 +148,15 @@ AC_SUBST(CPPFLAGS)
 AC_SUBST(LDFLAGS)
 AC_SUBST(ASFLAGS)
 
-AC_ARG_WITH(systemdsystemunitdir,
-  [AS_HELP_STRING([--with-systemdsystemunitdir@<:@=DIR|auto|yes|no@:>@], [Location of systemd service files [auto]])],
-  [], [with_systemdsystemunitdir=auto])
-
-systemdsystemunitdir=
-case "$with_systemdsystemunitdir" in
- auto|yes)
-   if test -n "$PKG_CONFIG"; then
-     AC_MSG_CHECKING([for systemdsystemunitdir])
-     systemdsystemunitdir=`$PKG_CONFIG --variable=systemdsystemunitdir systemd 2>/dev/null`
-     AC_MSG_RESULT([${systemdsystemunitdir:-no}])
-   fi
-   case "$with_systemdsystemunitdir:$sysconfdir:$systemdsystemunitdir" in
-     yes:*:) AC_MSG_ERROR([Location of systemd service files not found]) ;;
-     yes:*:*|auto:*:|auto:/etc:*) ;;
-     *) systemdsystemunitdir='${prefix}'$systemdsystemunitdir ;;
-   esac ;;
- no) ;;
- *) systemdsystemunitdir="$with_systemdsystemunitdir" ;;
-esac
-AC_SUBST(systemdsystemunitdir)
-AM_CONDITIONAL(INSTALL_SYSTEMDUNIT, [test -n "$systemdsystemunitdir"])
-
-AC_ARG_WITH(systemdenvfile,
-  [AS_HELP_STRING([--with-systemdenvfile@<:@=FILE|auto|yes|no@:>@], [Path of systemd EnvironmentFile [auto]])],
-  [], [with_systemdenvfile=auto])
-
-systemdenvfile=
-case "$with_systemdenvfile:$cross_compiling:$systemdsystemunitdir" in
-  auto:no:?*|yes:*:?*)
-    AC_MSG_CHECKING([for path of systemd EnvironmentFile])
-    for dir in sysconfig default; do
-      if test -d /etc/$dir; then
-        systemdenvfile='${sysconfdir}'/$dir/smartmontools
-        break
-      fi
-    done
-    AC_MSG_RESULT([${systemdenvfile:-no}])
-    case "$with_systemdenvfile:$systemdenvfile" in
-      yes:) AC_MSG_ERROR([Path of systemd EnvironmentFile not found]) ;;
-    esac ;;
-  auto:*|no:*) ;;
-  *:*:) AC_MSG_ERROR([Location of systemd service files not found]) ;;
-  *) systemdenvfile="$with_systemdenvfile"
-esac
-AC_SUBST(systemdenvfile)
-
-AC_ARG_WITH(initscriptdir,
-  [AS_HELP_STRING([--with-initscriptdir@<:@=DIR|auto|yes|no@:>@], [Location of init scripts [auto]])],
-  [], [with_initscriptdir=auto])
-
 initddir=
-case "$with_initscriptdir:$cross_compiling:$systemdsystemunitdir" in
-  auto:no:|yes:*)
-    AC_MSG_CHECKING([for init (rc) directory])
-    for dir in rc.d/init.d init.d rc.d; do
-      if test -d /etc/$dir; then
-        initddir='${sysconfdir}'/$dir
-        break
-      fi
-    done
-    AC_MSG_RESULT([${initddir:-no}])
-    case "$with_initscriptdir:$initddir" in
-      yes:) AC_MSG_ERROR([Location of init scripts not found]) ;;
-    esac ;;
-  auto:*|no:*) ;;
-  *) initddir="$with_initscriptdir" ;;
-esac
+AC_ARG_WITH(initscriptdir,
+  [AS_HELP_STRING([--with-initscriptdir=@<:@DIR|no@:>@], [Location of init scripts [no]])],
+  [ case "$withval" in
+      auto|yes) AC_MSG_ERROR(['--with-initscriptdir=$withval' is no longer supported]) ;;
+      no) ;;
+      *) initddir="$withval" ;;
+    esac
+  ])
 AC_SUBST(initddir)
 AM_CONDITIONAL(INSTALL_INITSCRIPT, [test -n "$initddir"])
 
@@ -226,6 +168,9 @@ case "${host}" in
   *-apple-darwin*)
     initdfile="com.smartmontools.smartd.plist"
     ;;
+  *-*-cygwin*)
+    initdfile="smartd.cygwin.initd"
+    ;;
   *)
     initdfile="smartd.initd"
     ;;
@@ -244,7 +189,7 @@ AC_ARG_WITH(drivedbdir,
 AC_SUBST(drivedbdir)
 AM_CONDITIONAL(ENABLE_DRIVEDB, [test -n "$drivedbdir"])
 
-drivedb_version=$VERSION
+drivedb_version=$smartmontools_drivedb_version
 AC_ARG_WITH(update-smart_drivedb,
   [AS_HELP_STRING([--with-update-smart-drivedb@<:@=yes|no|X.Y@:>@],
     [Install update-smart-drivedb script (and backport it to branches/RELEASE_X_Y_DRIVEDB) [yes]])],
@@ -288,9 +233,9 @@ AC_SUBST(smartdplugindir)
 
 AC_ARG_WITH(scriptpath,
   [AS_HELP_STRING([--with-scriptpath=@<:@PATH|no@:>@],
-    [PATH variable set within scripts [/usr/bin:/bin]])],
+    [PATH variable set within scripts [/usr/local/bin:/usr/bin:/bin]])],
   [scriptpath=; test "$withval" != "no" && scriptpath="$withval"],
-  [scriptpath="/usr/bin:/bin"])
+  [scriptpath="/usr/local/bin:/usr/bin:/bin"])
 AC_SUBST(scriptpath)
 AM_CONDITIONAL(ENABLE_SCRIPTPATH, [test -n "$scriptpath"])
 
@@ -322,6 +267,20 @@ AC_ARG_ENABLE(sample,
   [smartd_suffix=;])
 AC_SUBST(smartd_suffix)
 
+AC_ARG_ENABLE([scsi-cdb-check],
+  [AS_HELP_STRING([--enable-scsi-cdb-check], [do sanity check on each SCSI cdb])],
+  [ if test "$enableval" = "yes"; then
+      AC_DEFINE(SCSI_CDB_CHECK, 1, [Define to 1 to enable check on each SCSI cdb])
+    fi
+  ],[])
+
+AC_ARG_ENABLE([fast-lebe],
+  [AS_HELP_STRING([--disable-fast-lebe], [use generic little-endian/big-endian code instead])],
+  [ if test "$enableval" = "no"; then
+      AC_DEFINE(IGNORE_FAST_LEBE, 1, [Define to 1 to use generic LE/BE code instead])
+    fi
+  ],[])
+
 AC_ARG_WITH(os-deps,
   [AS_HELP_STRING([--with-os-deps='os_module.o ...'], [Specify OS dependent module(s) [guessed]])],
   [ for x in $with_os_deps; do
@@ -339,40 +298,94 @@ AC_ARG_WITH(selinux,
       AC_CHECK_LIB(selinux, matchpathcon, [], [AC_MSG_ERROR([Missing or incorrect SELinux library files])])
     fi
   ],[])
-AC_SUBST(with_selinux)
-if test "$with_selinux" = "yes"; then
-  AC_DEFINE(WITH_SELINUX, 1, [Define to 1 if SELinux support is enabled])
-fi
 
 AC_ARG_WITH(libcap-ng,
   [AS_HELP_STRING([--with-libcap-ng@<:@=auto|yes|no@:>@], [Add Libcap-ng support to smartd [auto]])],
   [], [with_libcap_ng=auto])
 
 use_libcap_ng=no
-if test "$with_libcap_ng" != "no"; then
-  AC_CHECK_LIB(cap-ng, capng_clear,
-    [AC_DEFINE(HAVE_LIBCAP_NG, 1, [Define to 1 if you have the `cap-ng' library (-lcap-ng).]) dnl `vim syntax
-     CAPNG_LDADD="-lcap-ng"; use_libcap_ng=yes])
-
-  if test "$use_libcap_ng" = "yes"; then
-    AC_CHECK_HEADER(cap-ng.h, [], [AC_MSG_ERROR([libcap-ng libraries found but headers are missing])])
-  elif test "$with_libcap_ng" = "yes"; then
-    AC_MSG_ERROR([libcap-ng support was requested but the library was not found])
-  fi
-fi
-
-AC_MSG_CHECKING([whether to use libcap-ng])
+case "$with_libcap_ng:$host_os" in
+  auto:linux*|yes:*)
+    AC_CHECK_HEADERS([cap-ng.h], [AC_CHECK_LIB([cap-ng], [capng_clear],
+      [AC_DEFINE(HAVE_LIBCAP_NG, 1,
+        [Define to 1 if you have the `cap-ng' library (-lcap-ng).]) dnl `vim syntax
+       CAPNG_LDADD="-lcap-ng"; use_libcap_ng=yes],
+      [AC_MSG_ERROR([libcap-ng headers found but library is missing])])],
+      [test "$with_libcap_ng" != "yes" || AC_MSG_ERROR([Missing libcap-ng header files])])
+    ;;
+esac
 AC_SUBST(CAPNG_LDADD)
-AC_MSG_RESULT([$use_libcap_ng])
+
+AC_ARG_WITH(libsystemd,
+  [AS_HELP_STRING([--with-libsystemd@<:@=auto|yes|no@:>@],
+    [Add systemd 'Type=notify' support to smartd [auto]])],
+  [], [with_libsystemd=auto])
+
+use_libsystemd=no
+case "$with_libsystemd:$host_os" in
+  auto:linux*|yes:*)
+    AC_CHECK_HEADERS([systemd/sd-daemon.h], [AC_CHECK_LIB([systemd], [sd_notify],
+      [AC_DEFINE(HAVE_LIBSYSTEMD, 1,
+        [Define to 1 if you have the `systemd' library (-lsystemd).]) dnl `vim syntax
+       SYSTEMD_LDADD="-lsystemd"; use_libsystemd=yes],
+      [AC_MSG_ERROR([libsystemd headers found but library is missing])])],
+      [test "$with_libsystemd" != "yes" || AC_MSG_ERROR([Missing libsystemd header files])])
+    ;;
+esac
+AC_SUBST(SYSTEMD_LDADD)
+
+AC_ARG_WITH(systemdsystemunitdir,
+  [AS_HELP_STRING([--with-systemdsystemunitdir@<:@=DIR|auto|yes|no@:>@], [Location of systemd service files [auto]])],
+  [], [with_systemdsystemunitdir=auto])
+
+systemdsystemunitdir=
+case "$with_systemdsystemunitdir:$use_libsystemd" in
+  auto:yes|yes:yes)
+    if test -n "$PKG_CONFIG"; then
+      AC_MSG_CHECKING([for systemdsystemunitdir])
+      systemdsystemunitdir=`$PKG_CONFIG --variable=systemdsystemunitdir systemd 2>/dev/null`
+      AC_MSG_RESULT([${systemdsystemunitdir:-no}])
+    fi
+    case "$with_systemdsystemunitdir:$sysconfdir:$systemdsystemunitdir" in
+      yes:*:) AC_MSG_ERROR([Location of systemd service files not found]) ;;
+      yes:*:*|auto:*:|auto:/etc:*) ;;
+      *) systemdsystemunitdir='${prefix}'$systemdsystemunitdir ;;
+    esac ;;
+  auto:*|no:*) ;;
+  *:yes) systemdsystemunitdir="$with_systemdsystemunitdir" ;;
+  *) AC_MSG_ERROR(['--with-systemdsystemunitdir=$with_systemdsystemunitdir' now requires '--with-libsystemd']) ;;
+esac
+AC_SUBST(systemdsystemunitdir)
+AM_CONDITIONAL(INSTALL_SYSTEMDUNIT, [test -n "$systemdsystemunitdir"])
+
+AC_ARG_WITH(systemdenvfile,
+  [AS_HELP_STRING([--with-systemdenvfile@<:@=FILE|auto|yes|no@:>@], [Path of systemd EnvironmentFile [auto]])],
+  [], [with_systemdenvfile=auto])
+
+systemdenvfile=
+case "$with_systemdenvfile:$cross_compiling:$systemdsystemunitdir" in
+  auto:no:?*|yes:*:?*)
+    AC_MSG_CHECKING([for path of systemd EnvironmentFile])
+    for dir in sysconfig default; do
+      if test -d /etc/$dir; then
+        systemdenvfile='${sysconfdir}'/$dir/smartmontools
+        break
+      fi
+    done
+    AC_MSG_RESULT([${systemdenvfile:-no}])
+    case "$with_systemdenvfile:$systemdenvfile" in
+      yes:) AC_MSG_ERROR([Path of systemd EnvironmentFile not found]) ;;
+    esac ;;
+  auto:*|no:*) ;;
+  *:*:) AC_MSG_ERROR([Location of systemd service files not found]) ;;
+  *) systemdenvfile="$with_systemdenvfile"
+esac
+AC_SUBST(systemdenvfile)
 
 # TODO: Remove when NVMe support is no longer EXPERIMENTAL
 AC_ARG_WITH(nvme-devicescan,
   [AS_HELP_STRING([--with-nvme-devicescan@<:@=yes|no@:>@],
-    [Include NVMe devices in smartd DEVICESCAN [no]])])
-AC_SUBST(with_nvme_devicescan)
-if test "$with_nvme_devicescan" = "yes"; then
-  AC_DEFINE(WITH_NVME_DEVICESCAN, 1, [Define to 1 to include NVMe devices in smartd DEVICESCAN.])
-fi
+    [Include NVMe devices in smartd DEVICESCAN [Linux,Windows:yes;Others:no]])])
 
 AC_ARG_WITH(solaris-sparc-ata,
   [AS_HELP_STRING([--with-solaris-sparc-ata@<:@=yes|no@:>@],
@@ -389,40 +402,64 @@ available in the SVN repository.])
     ;;
 esac
 
-# Assume broken snprintf only on Windows with MSVCRT (MinGW without ANSI stdio support)
-libc_have_working_snprintf=yes
-
-case "$host" in
-  *-*-mingw*)
-    case " $CPPFLAGS $CXXFLAGS" in
-      *\ -[[DU]]__USE_MINGW_ANSI_STDIO*)
-        ;;
-      *)
-        # Older MinGW do not properly define PRI?64 if __USE_MINGW_ANSI_STDIO is set
-        # Newer MinGW set __USE_MINGW_ANSI_STDIO in first C++ include which may be too late
-        AC_MSG_CHECKING([whether __USE_MINGW_ANSI_STDIO is defined by C++ includes])
-        AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
-          #undef __USE_MINGW_ANSI_STDIO
-          #include <iostream>
-          #ifndef __USE_MINGW_ANSI_STDIO
-            #error false
-          #endif]])],
-        [CXXFLAGS="-D__USE_MINGW_ANSI_STDIO $CXXFLAGS"],
-        [libc_have_working_snprintf=no])
-        AC_MSG_RESULT([$libc_have_working_snprintf])
-        ;;
-    esac ;;
+AC_ARG_WITH(signal-func,
+  [AS_HELP_STRING([--with-signal-func=@<:@sigaction|sigset|signal@:>@],
+    [Function to set signal(2) action [sigaction]])],
+  [], [with_signal_func=sigaction])
+
+case "$host:$with_signal_func" in
+  *-*-mingw*:*) ;;
+  *:sigaction)
+    AC_CHECK_FUNCS([sigaction], [], AC_MSG_ERROR([Missing function 'sigaction()'.
+Try '--with-signal-func=sigset' or '--with-signal-func=signal'.
+Please send info about your system to $PACKAGE_BUGREPORT.])) ;;
+  *:sigset)
+    AC_CHECK_FUNCS([sigset], [], AC_MSG_ERROR([Missing function 'sigset()'])) ;;
+  *:signal) ;;
+  *) AC_MSG_ERROR([Invalid option '--with-signal-func=$with_signal_func']) ;;
 esac
 
+# TODO: Remove after smartmontools 6.7
 AC_ARG_WITH(working-snprintf,
   [AS_HELP_STRING([--with-working-snprintf@<:@=yes|no@:>@],
-    [Function snprintf() handles output truncation as specified by C99 [MinGW:guessed,others:yes]])],
-  [libc_have_working_snprintf=$withval])
+    [Function snprintf() handles output truncation as specified by C99 [yes]])],
+  [], [with_working_snprintf=yes])
 
-if test "$libc_have_working_snprintf" = "yes"; then
+if test "$with_working_snprintf" = "yes"; then
   AC_DEFINE(HAVE_WORKING_SNPRINTF, 1, [Define to 1 if the `snprintf' function is sane.]) dnl `vim syntax
 fi
 
+case "$with_working_snprintf:$host_os: $CPPFLAGS $CXXFLAGS" in
+  yes:mingw*:*\ -[[DU]]__USE_MINGW_ANSI_STDIO*)
+    ;;
+  yes:mingw*:*)
+    # Older MinGW (4.6.3) do not properly define PRI?64 if __USE_MINGW_ANSI_STDIO is set.
+    # Newer MinGW (4.9.1) set __USE_MINGW_ANSI_STDIO in first C++ include which may be too late.
+    # Set __USE_MINGW_ANSI_STDIO always and fail if not fully supported.
+    AC_MSG_CHECKING([whether $CXX supports __USE_MINGW_ANSI_STDIO])
+    save_CXXFLAGS=$CXXFLAGS
+    CXXFLAGS="-Wformat -Werror -D__USE_MINGW_ANSI_STDIO"
+    AC_COMPILE_IFELSE([AC_LANG_SOURCE([[
+      #define __STDC_FORMAT_MACROS 1
+      #include <inttypes.h>
+      #include <stdio.h>
+      void f(char * buf1, char * buf2, size_t size) {
+        snprintf(buf1, size, "%lld", 42LL);
+        snprintf(buf2, size, "%" PRId64, (int64_t)42);
+      }]])],
+      [result=yes], [result=no])
+    AC_MSG_RESULT([$result])
+    if test "$result" != "yes"; then
+      AC_MSG_ERROR([
+This version of $CXX does not support __USE_MINGW_ANSI_STDIO.
+Use option '--without-working-snprintf' to skip this check.
+Please send info about your system to $PACKAGE_BUGREPORT.
+])
+    fi
+    CXXFLAGS="-D__USE_MINGW_ANSI_STDIO $save_CXXFLAGS"
+    ;;
+esac
+
 AC_ARG_WITH(mingw-aslr,
   [AS_HELP_STRING([--with-mingw-aslr@<:@=auto|yes|low|no@:>@], [Enable ASLR for MinGW executables [auto]])],
   [], [with_mingw_aslr=auto])
@@ -478,7 +515,7 @@ case "$host" in
 esac
 
 AC_ARG_WITH(cxx11-option,
-  [AS_HELP_STRING([--with-cxx11-option@<:@=OPTION|auto|no@:>@],
+  [AS_HELP_STRING([--with-cxx11-option=@<:@OPTION|auto|no@:>@],
     [Compiler option to enable C++11 support for future versions of smartmontools, 'no' if unsupported [auto]])],
   [], [with_cxx11_option=auto])
 
@@ -516,7 +553,7 @@ This version of smartmontools does not use C++11 features, but future
 versions possibly will.
 This script was unable to determine a compiler option to enable C++11.
 Use option '--with-cxx11-option=OPTION' to specify the compiler option
-(it will be checked, but not used in the actual build).
+(it will be used in the actual build only if '--with-cxx11-regex' is set).
 Use option '--without-cxx11-option' to suppress this error message if the
 compiler lacks C++11 support.
 In both cases, please send info about compiler and platform to
@@ -530,6 +567,22 @@ $PACKAGE_BUGREPORT - Thanks!])
     ;;
 esac
 
+AC_ARG_WITH(cxx11-regex,
+  [AS_HELP_STRING([--with-cxx11-regex@<:@=yes|no@:>@],
+    [Use C++11 std::regex instead of POSIX regex(3) [no]])])
+
+need_regex=no
+if test "$with_cxx11_regex" = "yes"; then
+  AC_DEFINE(WITH_CXX11_REGEX, 1, [Define to 1 to use C++11 std::regex instead of POSIX regex(3)])
+  case "$with_cxx11_option: $CXXFLAGS " in
+    no:*) AC_MSG_ERROR(['--with-cxx11-regex' requires C++11 support]) ;;
+    ?*:*\ $with_cxx11_option\ *) ;;
+    ?*:*) CXXFLAGS="$CXXFLAGS $with_cxx11_option" ;;
+  esac
+else
+  AC_CHECK_FUNCS([regcomp], [], [need_regex=yes])
+fi
+AM_CONDITIONAL(NEED_REGEX, [test "$need_regex" = "yes"])
 
 AC_SUBST(releaseversion,['${PACKAGE}-${VERSION}'])
 AC_SUBST(smartmontools_release_date)
@@ -548,19 +601,22 @@ os_win32=no
 os_win32_mingw=no
 os_win64=no
 os_man_filter=
+os_nvme_devicescan=
 case "${host}" in
   *-*-linux*)
     os_deps='os_linux.o cciss.o dev_areca.o'
     os_dnsdomainname="'dnsdomainname' 'hostname -d'"
     os_nisdomainname="'nisdomainname' 'hostname -y' 'domainname'"
     os_man_filter=Linux
+    os_nvme_devicescan=yes
     ;;
   *-*-freebsd*|*-*-dragonfly*|*-*-kfreebsd*-gnu*)
     os_deps='os_freebsd.o cciss.o dev_areca.o'
-    os_libs='-lcam'
+    os_libs='-lcam -lsbuf'
     os_dltools='curl wget lynx fetch svn'
     AC_CHECK_LIB(usb, libusb20_dev_get_device_desc)
     os_man_filter=FreeBSD
+    os_nvme_devicescan=no
     ;;
   sparc-*-solaris*)
     os_deps='os_solaris.o'
@@ -580,6 +636,7 @@ case "${host}" in
     os_deps='os_netbsd.o'
     os_libs='-lutil'
     os_man_filter=NetBSD
+    os_nvme_devicescan=no
     ;;
   *-*-openbsd*)
     os_deps='os_openbsd.o'
@@ -595,6 +652,7 @@ case "${host}" in
     os_nisdomainname=
     os_win32=yes
     os_man_filter=Cygwin
+    os_nvme_devicescan=yes
     ;;
   x86_64-*-mingw*)
     os_deps='os_win32.o dev_areca.o'
@@ -602,18 +660,21 @@ case "${host}" in
     os_win32_mingw=yes
     os_win64=yes
     os_man_filter=Windows
+    os_nvme_devicescan=yes
     ;;
   *-*-mingw*)
     os_deps='os_win32.o dev_areca.o'
     os_win32=yes
     os_win32_mingw=yes
     os_man_filter=Windows
+    os_nvme_devicescan=yes
     ;;
   *-*-darwin*)
     os_deps='os_darwin.o'
     os_libs='-framework CoreFoundation -framework IOKit'
     os_darwin=yes
     os_man_filter=Darwin
+    os_nvme_devicescan=no
     ;;
   *-*-nto-qnx*)
     os_deps='os_qnxnto.o'
@@ -641,6 +702,19 @@ else
 fi
 AC_MSG_RESULT([$os_new_interface])
 
+# TODO: Remove when NVMe support is no longer EXPERIMENTAL
+case "$os_nvme_devicescan:${with_nvme_devicescan+set}" in
+  no:|yes:)
+    AC_MSG_CHECKING([whether NVMe device scanning could be safely enabled])
+    with_nvme_devicescan=$os_nvme_devicescan
+    AC_MSG_RESULT([$os_nvme_devicescan])
+    os_nvme_devicescan=used ;;
+esac
+AC_SUBST(with_nvme_devicescan)
+if test "$with_nvme_devicescan" = "yes"; then
+  AC_DEFINE(WITH_NVME_DEVICESCAN, 1, [Define to 1 to include NVMe devices in smartd DEVICESCAN.])
+fi
+
 AC_SUBST([os_deps])
 AC_SUBST([os_libs])
 AC_SUBST([os_dltools])
@@ -668,6 +742,7 @@ AM_CONDITIONAL(OS_WIN32_NSIS, [test -n "$MAKENSIS"])
 AM_CONDITIONAL(OS_WIN64, [test "$os_win64" = "yes"])
 
 if test "$GXX" = "yes"; then
+  orig_CXXFLAGS=$CXXFLAGS
   # Add -Wall and -W[extra] if its not already specified
   case " $CXXFLAGS " in
     *\ -Wall\ *) ;;
@@ -678,19 +753,16 @@ if test "$GXX" = "yes"; then
     *) CXXFLAGS="$CXXFLAGS -W" ;;
   esac
   # Add -Wformat=2 (GCC 3.0) -fstack-protector[-strong] (GCC 4.1[4.9]) if supported
-  # and CXXFLAGS was not set in configure cmdline (TODO: -Wformat-signedness)
-  case "${ac_test_CXXFLAGS+set}:$ac_test_CXXFLAGS" in
-    set:)
-      for option in "-Wformat=2" "-fstack-protector-strong" "-fstack-protector"; do
-        case " $CXXFLAGS:$option" in *\ -fstack-p*:-fstack-p*) continue ;; esac
-        AC_MSG_CHECKING([whether $CXX supports $option])
-        save_CXXFLAGS=$CXXFLAGS
-        CXXFLAGS="$CXXFLAGS $option"
-        AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [res=yes], [res=no; CXXFLAGS=$save_CXXFLAGS])
-        AC_MSG_RESULT([$res])
-      done
-      ;;
-  esac
+  # and no -W or -f option was set in configure cmdline (TODO: -Wformat-signedness)
+  for option in "-Wformat=2" "-fstack-protector-strong" "-fstack-protector"; do
+    case " $orig_CXXFLAGS:$option" in *\ -W*:-W*|*\ -f*:-f*) continue ;; esac
+    case " $CXXFLAGS:$option" in *\ -fstack-p*:-fstack-p*) continue ;; esac
+    AC_MSG_CHECKING([whether $CXX supports $option])
+    save_CXXFLAGS=$CXXFLAGS
+    CXXFLAGS="$CXXFLAGS $option"
+    AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [res=yes], [res=no; CXXFLAGS=$save_CXXFLAGS])
+    AC_MSG_RESULT([$res])
+  done
 else
   # We are NOT using gcc, so enable host-specific compiler flags
   case "${host}" in
@@ -769,7 +841,9 @@ info=`
         echo "drive database file:    \`eval eval eval echo $drivedbdir\`/drivedb.h"
         if test "$with_update_smart_drivedb" = "yes"; then
           echo "database update script: \`eval eval eval echo $sbindir\`/update-smart-drivedb"
-          if test "$drivedb_version" != "$VERSION"; then
+          if test "$drivedb_version" = "$smartmontools_drivedb_version"; then
+            echo "database update branch: branches/$DRIVEDB_BRANCH"
+          else
             echo "... backported to:      branches/$DRIVEDB_BRANCH"
           fi
           echo "download tools:         \`eval eval eval echo $os_dltools\`"
@@ -798,12 +872,12 @@ info=`
         echo "PATH within scripts:    [[inherited]]"
       fi
       if test -n "$initddir"; then
-        echo "smartd initd script:    \`eval eval eval echo $initddir\`/${initdfile}"
+        echo "smartd initd script:    \`eval eval eval echo $initddir\`/smartd"
       elif test -z "$systemdsystemunitdir"; then
         echo "smartd initd script:    [[disabled]]"
       fi
       if test -n "$systemdsystemunitdir"; then
-        echo "smartd systemd file:    \`eval eval eval echo $systemdsystemunitdir\`/smartd.service"
+        echo "smartd service file:    \`eval eval eval echo $systemdsystemunitdir\`/smartd.service"
         if test -n "$systemdenvfile"; then
           echo "smartd environ file:    \`eval eval eval echo $systemdenvfile\`"
         else
@@ -820,14 +894,13 @@ info=`
       else
         echo "smartd attribute logs:  [[disabled]]"
       fi
-      echo "libcap-ng support:      $use_libcap_ng"
-      case "$host_os" in
-        linux*) echo "SELinux support:        ${with_selinux-no}" ;;
-      esac
       case "$host_os" in
-        linux*|darwin*|netbsd*|cygwin*)
-          echo "NVMe DEVICESCAN:        ${with_nvme_devicescan-no}" ;;
+        linux*)
+          echo "SELinux support:        ${with_selinux-no}"
+          echo "libcap-ng support:      $use_libcap_ng"
+          echo "systemd notify support: $use_libsystemd" ;;
       esac
+      echo "NVMe DEVICESCAN:        ${with_nvme_devicescan-[[not implemented]]}"
       ;;
   esac
   echo "-----------------------------------------------------------------------------"
@@ -838,27 +911,39 @@ $info
 ])
 
 # TODO: Remove when NVMe support is no longer EXPERIMENTAL
-case "$host_os:${with_nvme_devicescan+set}" in
-  linux*:|darwin*:|netbsd*:|cygwin*:|mingw*:)
-    AC_MSG_WARN([
+case "$os_nvme_devicescan:$with_nvme_devicescan" in
+  used:yes) AC_MSG_WARN([
+The default for the inclusion of NVME devices in smartd.conf
+'DEVICESCAN' and 'smartctl --scan' has been changed to 'yes' on
+this platform.  If '--without-nvme-devicescan' is still needed,
+please inform $PACKAGE_BUGREPORT.
+Use option '--with-nvme-devicescan' to suppress this warning.
+])  ;;
+  used:no) AC_MSG_WARN([
 This version of smartmontools provides NVMe support which is still
 EXPERIMENTAL.  NVMe devices are not yet included in smartd.conf
 'DEVICESCAN' and 'smartctl --scan' unless '-d nvme' is specified.
 Use option '--with-nvme-devicescan' to include NVMe devices.
 Use option '--without-nvme-devicescan' to suppress this warning.
-])
-    ;;
+])  ;;
 esac
 
-# TODO: Remove after smartmontools 6.6
-case "$host:${with_solaris_sparc_ata+set}" in
-  sparc-*-solaris*:)
-    AC_MSG_WARN([
-Legacy ATA support is no longer enabled by default on Solaris SPARC.
-The required source file 'os_solaris_ata.s' is no longer included in
-the source tarball but still available in the SVN repository.
-Use option '--with-solaris-sparc-ata' to enable legacy ATA support.
-Use option '--without-solaris-sparc-ata' to suppress this warning.
+# TODO: Remove after smartmontools 6.7
+if test "$with_working_snprintf" != "yes"; then
+  AC_MSG_WARN([
+The option '--without-working-snprintf' is deprecated and will be removed
+in a future version of smartmontools.  If you still need support for
+pre-C99 snprintf(), please inform $PACKAGE_BUGREPORT.
 ])
-    ;;
+fi
+
+case "$host_os:$with_libsystemd:$use_libsystemd:$PKG_CONFIG" in
+  linux*:auto:no:?*)
+    if $PKG_CONFIG systemd >/dev/null 2>&1; then
+      AC_MSG_WARN([
+systemd(1) is used on this system but smartd systemd notify support will
+not be available because libsystemd-dev[[el]] package is not installed.
+Use option '--without-libsystemd' to suppress this warning.
+])
+    fi ;;
 esac
index f3bb7cd8c33c9ae53eee1ada09701f7660ba07dc..974754830b9baf19268587df52d36fc9998009f8 100644 (file)
@@ -5,23 +5,15 @@
  *
  * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
-#include "int64.h"
 
 #include "dev_interface.h"
 #include "dev_areca.h"
 
-const char * dev_areca_cpp_cvsid = "$Id: dev_areca.cpp 4582 2017-11-03 20:54:56Z chrfranke $"
+const char * dev_areca_cpp_cvsid = "$Id: dev_areca.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
   DEV_ARECA_H_CVSID;
 
 #include "atacmds.h"
@@ -131,8 +123,8 @@ int generic_areca_device::arcmsr_command_handler(unsigned long arcmsr_cmd, unsig
   struct scsi_cmnd_io iop;
   int dir = DXFER_TO_DEVICE;
 
-  UINT8 cdb[10]={0};
-  UINT8 sense[32]={0};
+  uint8_t cdb[10]={0};
+  uint8_t sense[32]={0};
 
   unsigned char *areca_return_packet;
   int total = 0;
index 0297be97e4e410a75ecf04396a2ff47b5dfa5846..539ecd898cf9fd987b0f66bc338c15e31f2b0040 100644 (file)
@@ -5,20 +5,13 @@
  *
  * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef DEV_ARECA_H
 #define DEV_ARECA_H
 
-#define DEV_ARECA_H_CVSID "$Id: dev_areca.h 4146 2015-10-17 12:12:49Z chrfranke $"
+#define DEV_ARECA_H_CVSID "$Id: dev_areca.h 4760 2018-08-19 18:45:53Z chrfranke $"
 
 /////////////////////////////////////////////////////////////////////////////
 /// Areca RAID support
index 1e08cdd282ce6cd8afa9b3edf336b88d0ffa51c3..117e5d5850cb96e0532a4fac23829f10529c5e36 100644 (file)
@@ -3,26 +3,19 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2008-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
-#include "int64.h"
+
 #include "atacmds.h"
 #include "dev_ata_cmd_set.h"
 
 #include <errno.h>
 
-const char * dev_ata_cmd_set_cpp_cvsid = "$Id: dev_ata_cmd_set.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
+const char * dev_ata_cmd_set_cpp_cvsid = "$Id: dev_ata_cmd_set.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
   DEV_ATA_CMD_SET_H_CVSID;
 
 
index 2f98901accf770460db4b2c80876009f0498676a..eaec371b457c881f04e1f9f0676954610e2f7a47 100644 (file)
@@ -5,20 +5,13 @@
  *
  * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef DEV_ATA_CMD_SET_H
 #define DEV_ATA_CMD_SET_H
 
-#define DEV_ATA_CMD_SET_H_CVSID "$Id: dev_ata_cmd_set.h 4431 2017-08-08 19:38:15Z chrfranke $"
+#define DEV_ATA_CMD_SET_H_CVSID "$Id: dev_ata_cmd_set.h 4760 2018-08-19 18:45:53Z chrfranke $"
 
 #include "atacmds.h" // smart_command_set
 #include "dev_interface.h"
index b0ab275a1dfa5de30c89be2fbb56e33533dbad10..2ab6dec8a5d44c2eacd3219380ba11873a493e22 100644 (file)
@@ -5,25 +5,18 @@
  *
  * Copyright (C) 2016 Casey Biemiller  <cbiemiller@intelliprop.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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
-#include "int64.h"
+
 #include "atacmds.h" //ATTR_PACKED and ASSERT_SIZEOF_STRUCT
 #include "dev_interface.h"
 #include "dev_intelliprop.h"
 #include "dev_tunnelled.h"
 #include <errno.h>
 
-const char * dev_intelliprop_cpp_cvsid = "$Id: dev_intelliprop.cpp 4370 2017-01-11 20:35:38Z chrfranke $"
+const char * dev_intelliprop_cpp_cvsid = "$Id: dev_intelliprop.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
   DEV_INTELLIPROP_H_CVSID;
 
 //Vendor Specific log addresses
index 5d8665e3e0bd7f7c006f8f80757d6a8d86585ee7..4efca328c838bc597fa945fa00ace642610af0cc 100644 (file)
@@ -3,22 +3,15 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2012 Hank Wu <hank@areca.com.tw>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2016 Casey Biemiller <cbiemiller@intelliprop.com>
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef DEV_INTELLIPROP_H
 #define DEV_INTELLIPROP_H
 
-#define DEV_INTELLIPROP_H_CVSID "$Id: dev_intelliprop.h 4370 2017-01-11 20:35:38Z chrfranke $"
+#define DEV_INTELLIPROP_H_CVSID "$Id: dev_intelliprop.h 4763 2018-09-02 13:11:48Z chrfranke $"
 
 ata_device * get_intelliprop_device(smart_interface * intf, unsigned phydrive, ata_device * atadev);
 
index 1f5b23fcc2a23508e9ae5f6ab9ae873e1bb1610e..a09799038aba2c142cce09343cc3d8b96ef83122 100644 (file)
@@ -3,24 +3,18 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2008-16 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2008-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
-#include "int64.h"
+
 #include "dev_interface.h"
 #include "dev_intelliprop.h"
 #include "dev_tunnelled.h"
 #include "atacmds.h" // ATA_SMART_CMD/STATUS
+#include "scsicmds.h" // scsi_cmnd_io
 #include "utility.h"
 
 #include <errno.h>
@@ -33,7 +27,7 @@
 #include <sys/timeb.h>
 #endif
 
-const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
+const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 4848 2018-12-05 18:30:46Z chrfranke $"
   DEV_INTERFACE_H_CVSID;
 
 /////////////////////////////////////////////////////////////////////////////
@@ -203,6 +197,38 @@ bool ata_device::ata_identify_is_cached() const
   return false;
 }
 
+/////////////////////////////////////////////////////////////////////////////
+// scsi_device
+
+bool scsi_device::scsi_pass_through_and_check(scsi_cmnd_io * iop,
+                                              const char * msg)
+{
+  // Provide sense buffer
+  unsigned char sense[32] = {0, };
+  iop->sensep = sense;
+  iop->max_sense_len = sizeof(sense);
+  iop->timeout = SCSI_TIMEOUT_DEFAULT;
+
+  // Run cmd
+  if (!scsi_pass_through(iop)) {
+    if (scsi_debugmode > 0)
+      pout("%sscsi_pass_through() failed, errno=%d [%s]\n",
+           msg, get_errno(), get_errmsg());
+    return false;
+  }
+
+  // Check sense
+  scsi_sense_disect sinfo;
+  scsi_do_sense_disect(iop, &sinfo);
+  int err = scsiSimpleSenseFilter(&sinfo);
+  if (err) {
+    if (scsi_debugmode > 0)
+      pout("%sscsi error: %s\n", msg, scsiErrString(err));
+    return set_err(EIO, "scsi error %s", scsiErrString(err));
+  }
+
+  return true;
+}
 
 /////////////////////////////////////////////////////////////////////////////
 // nvme_device
@@ -282,8 +308,9 @@ std::string smart_interface::get_valid_dev_types_str()
 {
   // default
   std::string s =
-    "ata, scsi, nvme[,NSID], sat[,auto][,N][+TYPE], usbcypress[,X], "
-    "usbjmicron[,p][,x][,N], usbprolific, usbsunplus, intelliprop,N[+TYPE]";
+    "ata, scsi[+TYPE], nvme[,NSID], sat[,auto][,N][+TYPE], usbcypress[,X], "
+    "usbjmicron[,p][,x][,N], usbprolific, usbsunplus, sntjmicron[,NSID], "
+    "intelliprop,N[+TYPE]";
   // append custom
   std::string s2 = get_valid_custom_dev_types_str();
   if (!s2.empty()) {
@@ -400,8 +427,9 @@ smart_device * smart_interface::get_smart_device(const char * name, const char *
     dev = get_nvme_device(name, type, nsid);
   }
 
-  else if (  ((!strncmp(type, "sat", 3) && (!type[3] || strchr(",+", type[3])))
-           || (!strncmp(type, "usb", 3)))) {
+  else if (  (str_starts_with(type, "sat") && (!type[3] || strchr(",+", type[3])))
+           || str_starts_with(type, "scsi+")
+           || str_starts_with(type, "usb")                                        ) {
     // Split "sat...+base..." -> ("sat...", "base...")
     unsigned satlen = strcspn(type, "+");
     std::string sattype(type, satlen);
@@ -423,6 +451,16 @@ smart_device * smart_interface::get_smart_device(const char * name, const char *
     return get_sat_device(sattype.c_str(), basedev.release()->to_scsi());
   }
 
+  else if (str_starts_with(type, "snt")) {
+    smart_device_auto_ptr basedev( get_smart_device(name, "scsi") );
+    if (!basedev) {
+      set_err(EINVAL, "Type '%s': %s", type, get_errmsg());
+      return 0;
+    }
+
+    return get_snt_device(type, basedev.release()->to_scsi());
+  }
+
   else if (str_starts_with(type, "intelliprop")) {
     // Parse "intelliprop,N[+base...]"
     unsigned phydrive = ~0; int n = -1; char c = 0;
@@ -457,6 +495,12 @@ smart_device * smart_interface::get_smart_device(const char * name, const char *
   return dev;
 }
 
+bool smart_interface::scan_smart_devices(smart_device_list & /*devlist*/,
+  const char * /*type*/, const char * /*pattern*/ /* = 0 */)
+{
+  return set_err(ENOSYS);
+}
+
 bool smart_interface::scan_smart_devices(smart_device_list & devlist,
   const smart_devtype_list & types, const char * pattern /* = 0 */)
 {
@@ -491,3 +535,12 @@ std::string smart_interface::get_valid_custom_dev_types_str()
 {
   return "";
 }
+
+smart_device * smart_interface::get_scsi_passthrough_device(const char * type, scsi_device * scsidev)
+{
+  if (!strncmp(type, "snt", 3)) {
+    return get_snt_device(type, scsidev);
+  }
+
+  return get_sat_device(type, scsidev);
+}
index 44368507135b5ae28d438bd56262783c0fc8c504..4d1aee14ba8f3e28b01531547c28034deace2bc4 100644 (file)
@@ -3,22 +3,15 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2008-16 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2008-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef DEV_INTERFACE_H
 #define DEV_INTERFACE_H
 
-#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h 4283 2016-04-10 12:55:59Z chrfranke $\n"
+#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h 4848 2018-12-05 18:30:46Z chrfranke $\n"
 
 #include "utility.h"
 
@@ -252,7 +245,7 @@ private:
   // Number of objects.
   static int s_num_objects;
 
-  // Prevent copy/assigment
+  // Prevent copy/assignment
   smart_device(const smart_device &);
   void operator=(const smart_device &);
 };
@@ -591,6 +584,18 @@ public:
   /// Returns false on error.
   virtual bool scsi_pass_through(scsi_cmnd_io * iop) = 0;
 
+  // Call scsi_pass_through and check sense.
+  bool scsi_pass_through_and_check(scsi_cmnd_io * iop,
+                                   const char * msg = "");
+
+  /// Always try READ CAPACITY(10) (rcap10) first but once we know
+  /// rcap16 is needed, use it instead.
+  void set_rcap16_first()
+    { rcap16_first = true; }
+
+  bool use_rcap16() const
+    { return rcap16_first; }
+
 protected:
   /// Hide/unhide SCSI interface.
   void hide_scsi(bool hide = true)
@@ -598,8 +603,12 @@ protected:
 
   /// Default constructor, registers device as SCSI.
   scsi_device()
-    : smart_device(never_called)
+    : smart_device(never_called),
+      rcap16_first(false)
     { hide_scsi(false); }
+
+private:
+  bool rcap16_first;
 };
 
 
@@ -848,7 +857,7 @@ public:
 private:
   std::vector<smart_device *> m_list;
 
-  // Prevent copy/assigment
+  // Prevent copy/assignment
   smart_device_list(const smart_device_list &);
   void operator=(const smart_device_list &);
 };
@@ -956,8 +965,9 @@ public:
   /// specified by some optional 'pattern'.
   /// Use platform specific default if 'type' is empty or 0.
   /// Return false on error.
+  /// Default implementation returns false;
   virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
-    const char * pattern = 0) = 0;
+    const char * pattern = 0);
 
   /// Fill 'devlist' with devices of all 'types' with device names
   /// specified by some optional 'pattern'.
@@ -992,6 +1002,11 @@ protected:
   /// Default implementation returns empty string.
   virtual std::string get_valid_custom_dev_types_str();
 
+  /// Return ATA->SCSI of NVMe->SCSI filter for a SAT, SNT or USB 'type'.
+  /// Uses get_sat_device and get_snt_device.
+  /// Return 0 and delete 'scsidev' on error.
+  virtual smart_device * get_scsi_passthrough_device(const char * type, scsi_device * scsidev);
+
   /// Return ATA->SCSI filter for a SAT or USB 'type'.
   /// Device 'scsidev' is used for SCSI access.
   /// Return 0 and delete 'scsidev' on error.
@@ -999,6 +1014,13 @@ protected:
   virtual ata_device * get_sat_device(const char * type, scsi_device * scsidev);
   //{ implemented in scsiata.cpp }
 
+  /// Return NVMe->SCSI filter for a SNT or USB 'type'.
+  /// Device 'scsidev' is used for SCSI access.
+  /// Return 0 and delete 'scsidev' on error.
+  /// Override only if platform needs special handling.
+  virtual nvme_device * get_snt_device(const char * type, scsi_device * scsidev);
+  //{ implemented in scsinvme.cpp }
+
 public:
   /// Try to detect a SAT device behind a SCSI interface.
   /// Inquiry data can be passed if available.
@@ -1026,7 +1048,7 @@ private:
   friend smart_interface * smi(); // below
   static smart_interface * s_instance; ///< Pointer to the interface object.
 
-  // Prevent copy/assigment
+  // Prevent copy/assignment
   smart_interface(const smart_interface &);
   void operator=(const smart_interface &);
 };
index 1666122409c9e29a7a22b1e4eb7434b469c10649..e760c64174f6c757ef06b7eabef712aa03acd2a0 100644 (file)
@@ -3,20 +3,13 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2008-16 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2008-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
-#include "int64.h"
+
 #include "utility.h"
 #include "atacmds.h"
 #include "scsicmds.h"
@@ -25,7 +18,7 @@
 
 #include <errno.h>
 
-const char * dev_legacy_cpp_cvsid = "$Id: dev_legacy.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
+const char * dev_legacy_cpp_cvsid = "$Id: dev_legacy.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
   DEV_INTERFACE_H_CVSID;
 
 /////////////////////////////////////////////////////////////////////////////
index 23ac55d2161d9f58489c303afb3b0402a1fa0eb1..d6388bbd767b1b316e7c5a5207443cdeb1408a82 100644 (file)
@@ -5,20 +5,13 @@
  *
  * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef DEV_TUNNELLED_H
 #define DEV_TUNNELLED_H
 
-#define DEV_TUNNELLED_H_CVSID "$Id: dev_tunnelled.h 4431 2017-08-08 19:38:15Z chrfranke $"
+#define DEV_TUNNELLED_H_CVSID "$Id: dev_tunnelled.h 4848 2018-12-05 18:30:46Z chrfranke $"
 
 #include "dev_interface.h"
 
@@ -71,6 +64,14 @@ protected:
       m_tunnel_dev(tunnel_dev)
     { }
 
+  // For nvme_device
+  explicit tunnelled_device(tunnel_device_type * tunnel_dev, unsigned nsid)
+    : smart_device(smart_device::never_called),
+      BaseDev(nsid),
+      tunnelled_device_base(tunnel_dev),
+      m_tunnel_dev(tunnel_dev)
+    { }
+
 public:
   virtual void release(const smart_device * dev)
     {
index cf444c86a4be3e033f197f8ae21076b0adcbb184..a7cea092dff5f9e06c12301da8871d8183106d19 100644 (file)
--- a/drivedb.h
+++ b/drivedb.h
@@ -4,16 +4,9 @@
  * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2003-11 Philip Williams, Bruce Allen
- * Copyright (C) 2008-17 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2008-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 /*
@@ -75,7 +68,7 @@
 /*
 const drive_settings builtin_knowndrives[] = {
  */
-  { "$Id: drivedb.h 4591 2017-11-04 22:40:53Z chrfranke $",
+  { "$Id: drivedb.h 4868 2018-12-27 15:58:07Z chrfranke $",
     "-", "-",
     "This is a dummy entry to hold the SVN-Id of drivedb.h",
     ""
@@ -155,6 +148,19 @@ const drive_settings builtin_knowndrives[] = {
     //  251-253 Unknown_Attribute
     "-v 254,raw48,Free_Fall_Sensor,HDD"
   },
+  { "Swissbit C440 Industrial CompactFlash Card",
+    // spec v1.23 found at http://www.farnell.com/datasheets/1821167.pdf
+    // tested with SFCF4096H2BU4TO-I-MS-527-STD
+    "SFCF(2048|4096|8192|16GB|32GB|64GB)H[0-9]BU[24]TO-(C|I)-(MS|QT|NU)-5[0-9]7-STD",
+    "", "",
+    "-v 196,raw24/raw24,Spare_Blocks "
+    "-v 213,raw24/raw24,Spare_Blocks_Worst_Chip "
+    "-v 229,raw48,Erase_Count "
+    "-v 203,raw48,Total_ECC_Errors "
+    "-v 232,raw48,Total_Number_of_Reads "
+    "-v 214,raw48,Reserved_Attribute " // Spec says "to be determined"
+    "-v 215,raw48,Current_TRIM_Percent "
+  },
   { "Apacer SSD",
     "(2|4|8|16|32)GB SATA Flash Drive", // tested with APSDM002G15AN-CT/SFDDA01C and SFI2101D, APSDM004G13AN-AT/SFDE001A
     "SF(DDA01C|I2101D|DE001A)", "", // spec found at http://wfcache.advantech.com/www/certified-peripherals/documents/96fmcff-04g-cs-ap_Datasheet.pdf
@@ -264,10 +270,9 @@ const drive_settings builtin_knowndrives[] = {
       // M4-CT064M4SSD2/0009, M4-CT256M4SSD3/000F
     "",
     "This drive may hang after 5184 hours of power-on time:\n"
-    "http://www.tomshardware.com/news/Crucial-m4-Firmware-BSOD,14544.html\n"
-    "See the following web pages for firmware updates:\n"
-    "http://www.crucial.com/support/firmware.aspx\n"
-    "http://www.micron.com/products/solid-state-storage/client-ssd#software",
+    "https://www.tomshardware.com/news/Crucial-m4-Firmware-BSOD,14544.html\n"
+    "See the following web page for firmware updates:\n"
+    "http://www.crucial.com/usa/en/support-ssd",
     "-v 170,raw48,Grown_Failing_Block_Ct "
     "-v 171,raw48,Program_Fail_Count "
     "-v 172,raw48,Erase_Fail_Count "
@@ -279,26 +284,35 @@ const drive_settings builtin_knowndrives[] = {
     "-v 202,raw48,Perc_Rated_Life_Used "
     "-v 206,raw48,Write_Error_Rate"
   },
-  { "Crucial/Micron MX1/2/300, M5/600, 1100 Client SSDs",
+  { "Crucial/Micron BX/MX1/2/3/500, M5/600, 1100 SSDs",
     "Crucial_CT(128|256|512)MX100SSD1|"// Marvell 88SS9189, tested with Crucial_CT256MX100SSD1/MU01
     "Crucial_CT(200|250|256|500|512|1000|1024)MX200SSD[1346]|" // Marvell 88SS9189, tested with
       // Crucial_CT500MX200SSD1/MU01, Crucial_CT1024MX200SSD1/MU01, Crucial_CT250MX200SSD3/MU01,
       // Crucial_CT250MX200SSD1/MU03
-    "Crucial_CT(275|525|1050|2050)MX300SSD[14]|" // Marvell 88SS1074, tested with
-      // Crucial_CT525MX300SSD1/M0CR021, Crucial_CT2050MX300SSD1/M0CR031, Crucial_CT275MX300SSD1/M0CR040
+    "Crucial_CT(275|525|750|1050|2050)MX300SSD[14]|" // Marvell 88SS1074, tested with
+      // Crucial_CT275MX300SSD1/M0CR040, Crucial_CT525MX300SSD1/M0CR021, Crucial_CT750MX300SSD1/M0CR011,
+      // Crucial_CT2050MX300SSD1/M0CR031
     "Crucial_CT(120|240|480|960)M500SSD[134]|" // Marvell 88SS9187, tested with
       // Crucial_CT120M500SSD1/MU02, Crucial_CT120M500SSD3/MU02, Crucial_CT240M500SSD1/MU03,
       // Crucial_CT480M500SSD1/MU03, Crucial_CT960M500SSD1/MU03, Crucial_CT240M500SSD4/MU05
     "Crucial_CT(128|256|512|1024)M550SSD[134]|" // tested with Crucial_CT512M550SSD3/MU01,
       // Crucial_CT1024M550SSD1/MU01, Crucial_CT128M550SSD4/MU02
+    "CT(120|240|480)BX300SSD1|" // Silicon Motion SM2258, same attributes as Marvell-based Crucial SSDs,
+      // tested with CT240BX300SSD1/M2CR010
+    "CT(120|240|480|960)BX500SSD1|" // Silicon Motion SM2258XT, tested with CT120BX500SSD1/M6CR013
+    "CT(250|500|1000|2000)MX500SSD[14]|" // Silicon Motion SM2258, tested with CT250MX500SSD1/M3CR010
+      // CT500MX500SSD1/M3CR010, CT1000MX500SSD1/M3CR010, CT2000MX500SSD1/M3CR010, CT250MX500SSD4/M3CR022
     "Micron_M500_MTFDDA[KTV](120|240|480|960)MAV|"// tested with Micron_M500_MTFDDAK960MAV/MU05
+    "Micron_M500DC_(EE|MT)FDDA[AK](120|240|480|800)MBB|" // tested with Micron_M500DC_EEFDDAA120MBB/129,
+      // Micron_M500DC_MTFDDAK800MBB/0129
+    "(Micron[_ ])?M500IT[_ ]MTFDDA[KTY](032|050|060|064|120|128|240|256)[MS]BD|" // tested with M500IT_MTFDDAK240MBD/MG02
     "(Micron_)?M510[_-]MTFDDA[KTV](128|256)MAZ|" // tested with M510-MTFDDAK256MAZ/MU01
+    "MICRON_M510DC_(EE|MT)FDDAK(120|240|480|800|960)MBP|" // tested with Micron_M510DC_MTFDDAK240MBP/0005
     "(Micron_)?M550[_-]MTFDDA[KTV](064|128|256|512|1T0)MAY|" // tested with M550-MTFDDAK256MAY/MU01
     "Micron_M600_(EE|MT)FDDA[KTV](128|256|512|1T0)MBF[25Z]?|" // tested with Micron_M600_MTFDDAK1T0MBF/MU01
-    "Micron_M500DC_(EE|MT)FDDA[AK](120|240|480|800)MBB|" // tested with Micron_M500DC_EEFDDAA120MBB/129, Micron_M500DC_MTFDDAK800MBB/0129
-    "Micron_1100_MTFDDA[KV](256|512|1T0|2T0)TBN|" // Marvell 88SS1074, tested Micron_1100_MTFDDAK256TBN/M0MU020
-    "MICRON_M510DC_(EE|MT)FDDAK(120|240|480|800|960)MBP|" // Micron_M510DC_MTFDDAK240MBP/0005
-    "Micron_5100_(EE|MT)FDDA[KV](240|480|960|1T9|3T8|7T6)T(BY|CB|CC)", // tested with Micron_5100_MTFDDAK1T9TBY
+    "(Micron_1100_)?MTFDDA[KV](256|512|1T0|2T0)TBN|" // Marvell 88SS1074, tested with
+      // Micron_1100_MTFDDAK256TBN/M0MU020, MTFDDAK256TBN/M0MA020 (OEM)
+    "Micron 1100 SATA (256G|512G|1T|2T)B", // tested with Micron 1100 SATA 256GB/M0DL022
     "", "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
     "-v 5,raw48,Reallocate_NAND_Blk_Cnt "
@@ -312,18 +326,52 @@ const drive_settings builtin_knowndrives[] = {
     "-v 180,raw48,Unused_Reserve_NAND_Blk "
     "-v 183,raw48,SATA_Interfac_Downshift "
     "-v 184,raw48,Error_Correction_Count "
-    "-v 195,raw48,Cumulativ_Corrected_ECC "
   //"-v 187,raw48,Reported_Uncorrect "
   //"-v 194,tempminmax,Temperature_Celsius "
+    "-v 195,raw48,Cumulativ_Corrected_ECC "
   //"-v 196,raw16(raw16),Reallocated_Event_Count "
   //"-v 197,raw48,Current_Pending_Sector "
   //"-v 198,raw48,Offline_Uncorrectable "
   //"-v 199,raw48,UDMA_CRC_Error_Count "
-    "-v 202,raw48,Percent_Lifetime_Used "
+    "-v 202,raw48,Percent_Lifetime_Remain "
     "-v 206,raw48,Write_Error_Rate "
     "-v 210,raw48,Success_RAIN_Recov_Cnt "
     "-v 246,raw48,Total_Host_Sector_Write "
     "-v 247,raw48,Host_Program_Page_Count "
+    "-v 248,raw48,FTL_Program_Page_Count"
+  },
+  // Reference: https://www.micron.com/resource-details/feec878a-265e-49a7-8086-15137c5f9011
+  // TN-FD-34: 5100 SSD SMART Implementation
+  {
+    "Micron 5100 Pro / 5200 SSDs",
+    "(Micron_5100_)?(EE|MT)FDDA[KV](240|480|960|1T9|3T8|7T6)T(BY|CB|CC)|" // Matches both stock and Dell OEM
+      // tested with Micron_5100_MTFDDAK3T8TCB/D0MU410, MTFDDAK3T8TCB/D0MU410
+    "(Micron_5200_)?MTFDDAK(480|960|1T9|3T8|7T6)TD(C|D|N)", // tested with Micron_5200_MTFDDAK3T8TDD/D1MU505
+    "", "",
+  //"-v 1,raw48,Raw_Read_Error_Rate "
+  //"-v 5,raw48,Reallocated_Block_Count "
+  //"-v 9,raw24(raw8),Power_On_Hours "  // raw24(raw8)??
+  //"-v 12,raw48,Power_Cycle_Count "
+    "-v 170,raw48,Reserved_Block_Pct " // Percentage of remaining reserved blocks available
+    "-v 171,raw48,Program_Fail_Count "
+    "-v 172,raw48,Erase_Fail_Count "
+    "-v 173,raw48,Avg_Block-Erase_Count "
+    "-v 174,raw48,Unexpect_Power_Loss_Ct "
+  //"-v 180,raw48,Reserved_Block_Count " // absolute count of remaining reserved blocks available
+    "-v 183,raw48,SATA_Int_Downshift_Ct " // SATA speed downshift count
+  //"-v 184,raw48,Error_Correction_Count "
+  //"-v 187,raw48,Reported_Uncorrect " // Number of UECC correction failures
+  //"-v 188,raw48,Command_Timeouts "
+  //"-v 194,tempminmax,Temperature_Celsius " // 100 - degrees C, wraps: 101 reported as 255
+  //"-v 195,raw48,Cumulativ_Corrected_ECC "
+  //"-v 196,raw48,Reallocation_Event_Ct "
+  //"-v 197,raw48,Current_Pending_Sector " // Use the raw value
+  //"-v 198,raw48,Offline_Uncorrectable "  // Use the raw value
+  //"-v 199,raw48,UDMA_CRC_Error_Count "   // Use the raw value
+    "-v 202,raw48,Percent_Lifetime_Remain " // Remaining endurance, trips at 10%
+    "-v 206,raw48,Write_Error_Rate "
+    "-v 210,raw48,RAIN_Success_Recovered "  // Total number of NAND pages recovered by RAIN
+    "-v 247,raw48,Host_Program_Page_Count "
     "-v 248,raw48,Bckgnd_Program_Page_Cnt"
   },
   { "Micron M500DC/M510DC Enterprise SSDs",
@@ -360,7 +408,7 @@ const drive_settings builtin_knowndrives[] = {
     "A firmware update is available for this drive.\n"
     "It is HIGHLY RECOMMENDED for drives with specific serial numbers.\n"
     "See the following web pages for details:\n"
-    "http://www.corsair.com/en-us/force-series-ls-60gb-sata-3-6gb-s-ssd\n"
+    "https://www.corsair.com/en-us/force-series-ls-60gb-sata-3-6gb-s-ssd\n"
     "https://www.smartmontools.org/ticket/628",
     "-v 1,raw24/raw32,Raw_Read_Error_Rate "
     "-v 5,raw48,Retired_Block_Count "
@@ -391,6 +439,7 @@ const drive_settings builtin_knowndrives[] = {
       // ADATA SSD SX900 512GB-DL2/5.8.2
     "ADATA XM11 (128|256)GB|" // tested with ADATA XM11 128GB/5.0.1
     "ATP Velocity MIV (60|120|240|480)GB|" // tested with ATP Velocity MIV 480GB/110719
+    "Comay BladeDrive E28 (800|1600|3200)GB|" // LSI SF-2581, tested with Comay BladeDrive E28 800GB/2.71
     "Corsair CSSD-F(40|60|80|115|120|160|240)GBP?2.*|" // Corsair Force, tested with
       // Corsair CSSD-F40GB2/1.1, Corsair CSSD-F115GB2-A/2.1a
     "Corsair Voyager GTX|" // Corsair Voyager GTX/S9FM02J6
@@ -401,7 +450,8 @@ const drive_settings builtin_knowndrives[] = {
       // FM-25S2S-240GBP2/4.2
     "FTM(06|12|24|48)CT25H|" // Supertalent TeraDrive CT, tested with
       // FTM24CT25H/STTMP2P1
-    "KINGSTON SE50S3(100|240|480)G|" // tested with SE50S3100G/KE1ABBF0
+    "KINGSTON SE50S37?(100|240|480)G|" // tested with KINGSTON SE50S3100G/KE1ABBF0,
+      // KINGSTON SE50S37100G/61AABBF0 (E50)
     "KINGSTON SH10[03]S3(90|120|240|480)G|" // HyperX (3K), SF-2281, tested with
       // SH100S3240G/320ABBF0, SH103S3120G/505ABBF0
     "KINGSTON SKC(300S37A|380S3)(60|120|180|240|480)G|" // KC300, SF-2281, tested with
@@ -411,6 +461,8 @@ const drive_settings builtin_knowndrives[] = {
     "KINGSTON SMS200S3(30|60|120)G|" // mSATA, SF-2241, tested with SMS200S3120G/KC3ABBF0
     "KINGSTON SMS450S3(32|64|128)G|" // mSATA, SF-2281, tested with SMS450S3128G/503ABBF0
     "KINGSTON (SV300|SKC100|SE100)S3.*G|" // other SF-2281
+    "KINGSTON SHFS37A(120|240|480)G|" // HyperX Fury, SF-2281, tested with KINGSTON SHFS37A240G/608ABBF0
+    "KINGSTON SNS4151S316GD|" // KINGSTON SNS4151S316GD/S9FM01.6
     "MKNSSDCR(45|60|90|120|180|240|360|480)GB(-(7|DX7?|MX|G2))?|" // Mushkin Chronos (7mm/Deluxe/MX/G2),
       // SF-2281, tested with MKNSSDCR120GB, MKNSSDCR120GB-MX/560ABBF0, MKNSSDCR480GB-DX7/603ABBF0
     "MKNSSDEC(60|120|240|480|512)GB|" // Mushkin Enhanced ECO2, tested with MKNSSDEC120GB/604ABBF0
@@ -418,6 +470,7 @@ const drive_settings builtin_knowndrives[] = {
       // tested with MKNSSDAT120GB-V/540ABBF0
     "Mushkin MKNSSDCL(40|60|80|90|115|120|180|240|480)GB-DX2?|" // Mushkin Callisto deluxe,
       // SF-1200/1222, Mushkin MKNSSDCL60GB-DX/361A13F0
+    "MXSSD3MDSF-(60|120)G|" // MX-DS FUSION, tested with MXSSD3MDSF-60G/2.32
     "OCZ[ -](AGILITY2([ -]EX)?|COLOSSUS2|ONYX2|VERTEX(2|-LE))( [123]\\..*)?|" // SF-1200,
       // tested with OCZ-VERTEX2/1.11, OCZ-VERTEX2 3.5/1.11
     "OCZ-NOCTI|" // mSATA, SF-2100, tested with OCZ-NOCTI/2.15
@@ -426,10 +479,10 @@ const drive_settings builtin_knowndrives[] = {
     "OCZ-REVODRIVE350|"
     "OCZ[ -](VELO|VERTEX2[ -](EX|PRO))( [123]\\..*)?|" // SF-1500, tested with
       // OCZ VERTEX2-PRO/1.10 (Bogus thresholds for attribute 232 and 235)
-    "D2[CR]STK251...-....|" // OCZ Deneva 2 C/R, SF-22xx/25xx,
-      // tested with D2CSTK251M11-0240/2.08, D2CSTK251A10-0240/2.15
-    "OCZ-(AGILITY3|SOLID3|VERTEX3( MI)?)|"  // SF-2200, tested with OCZ-VERTEX3/2.02,
-      // OCZ-AGILITY3/2.11, OCZ-SOLID3/2.15, OCZ-VERTEX3 MI/2.15
+    "D2[CR]STK251...-....(\\.C)?|" // OCZ Deneva 2 C/R, SF-22xx/25xx,
+      // tested with D2CSTK251M11-0240/2.08, D2CSTK251A10-0240/2.15, D2RSTK251M11-0100.C/3.22
+    "OCZ-(AGILITY3|SOLID3|VERTEX3( LT| MI)?)|"  // SF-2200, tested with OCZ-VERTEX3/2.02,
+      // OCZ-AGILITY3/2.11, OCZ-SOLID3/2.15, OCZ-VERTEX3 MI/2.15, OCZ-VERTEX3 LT/2.22
     "OCZ Z-DRIVE R4 [CR]M8[48]|" // PCIe, SF-2282/2582, tested with OCZ Z-DRIVE R4 CM84/2.13
       // (Bogus attributes under Linux)
     "OCZ Z-DRIVE 4500|"
@@ -515,13 +568,20 @@ const drive_settings builtin_knowndrives[] = {
     "-v 249,raw48,Spares_Remaining_Perc " //  later then 0409 FW.
   },
   { "Phison Driven SSDs", // see MKP_521_Phison_SMART_attribute.pdf
+    "KINGSTON SEDC400S37(400|480|800|960|1600|1800)G|" // DC400, tested with
+      // KINGSTON SEDC400S37480G/SAFM02.[GH], KINGSTON SEDC400S37960G/SAFM32.I
     "KINGSTON SUV300S37A(120|240|480)G|" // UV300 SSD, tested with KINGSTON SUV300S37A120G/SAFM11.K
     "KINGSTON SKC310S3B?7A960G|" // SSDNow KC310, KINGSTON SKC310S37A960G/SAFM00.r
     "KINGSTON SKC400S37(128G|256G|512G|1T)|" // SSDNow KC400, KINGSTON SKC400S37128G
     "KINGSTON SV310S3(7A|D7|N7A|B7A)960G|" // SSDNow V310
-    "GOODRAM|" // GOODRAM/SAFM12.2 (CX200), model name is missing in the SMART!
-    "PNY CS1311 (120|240|480|960)GB SSD|" // tested with PNY CS1311 120GB SSD/CS131122
-    "KINGSTON SHSS3B?7A(120|240|480|960)G", // HyperX Savage
+    "PNY CS(13|22)11 (120|240|480|960)GB SSD|" // tested with PNY CS1311 120GB SSD/CS131122,
+      // PNY CS2211 240GB SSD/CS221016
+    "SSD Smartbuy (60|120|240)GB|" // SSD Smartbuy 240GB/SBFM91.1
+    "KINGSTON SHSS3B?7A(120|240|480|960)G|" // HyperX Savage
+    "KINGSTON  ?SA400S37(120|240|480|960)G", // Kingston A400 SSD, Phison S11 or
+      // Silicon Motion controller (see ticket #801), tested with
+      // KINGSTON SA400S37240G/SBFK10D7, KINGSTON SA400S37120G/SBFK71E0, */SBFKB1D1
+      // KINGSTON  SA400S37480G/SBFK10D7 (two spaces)
     "", "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
     "-v 2,raw48,Not_In_Use "
@@ -557,11 +617,12 @@ const drive_settings builtin_knowndrives[] = {
   },
   // this is a copy of the Phison bases record for the OEM drives with a very
   // weak information in the model. Detection is based on Firmware.
-  // Tested with FoxLine flssd240x4se (SATA SSD/SBFM10.5) and Supermicro 
-  // SSD-DM032-PHI (SATA SSD/S9FM02.1)
   { "Phison Driven OEM SSDs", // see MKP_521_Phison_SMART_attribute.pdf
-    "SATA SSD",
-    "(S9FM02\\.1|SBFM10\\.5)",
+    "GOODRAM|" // tested with GOODRAM CX200 (GOODRAM/SAFM12.2)
+    "INTENSO|" // tested with Intenso SSD SATA III Top (INTENSO/S9FM02.6, .../SAFM01.6)
+    "SATA SSD", // tested with Supermicro SSD-DM032-PHI (SATA SSD/S9FM02.1),
+      // PC Engines msata16d (SATA SSD/S9FM02.3), FoxLine flssd240x4s(SATA SSD/SBFM10.5)
+    "S9FM02\\.[136]|SAFM01\\.6|SAFM12\\.2|SBFM10\\.5|SBFM91\\.2",
     "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
     "-v 2,raw48,Not_In_Use "
@@ -723,7 +784,8 @@ const drive_settings builtin_knowndrives[] = {
   {
     "OCZ/Toshiba Trion SSDs",
     "OCZ-TRION1[05]0|" // tested with OCZ-TRION100/SAFM11.2A, TRION150/SAFZ72.2
-    "TOSHIBA-TR150", // tested with TOSHIBA-TR150/SAFZ12.3
+    "TOSHIBA-TR150|" // tested with TOSHIBA-TR150/SAFZ12.3
+    "TOSHIBA Q300( Pro\\.)?", // tested with TOSHIBA Q300 Pro./JYRA0101
     "", "",
   //"-v 9,raw24(raw8),Power_On_Hours "
   //"-v 12,raw48,Power_Cycle_Count "
@@ -735,8 +797,9 @@ const drive_settings builtin_knowndrives[] = {
   //"-v 194,tempminmax,Temperature_Celsius "
     "-v 241,raw48,Host_Writes"
   },
-  { "InnoDisk InnoLite SATADOM D150QV-L SSDs", // tested with InnoLite SATADOM D150QV-L/120319
-    "InnoLite SATADOM D150QV-L",
+  { "InnoDisk InnoLite SATADOM D150QV SSDs", // tested with InnoLite SATADOM D150QV-L/120319
+                                             // InnoLite SATADOM D150QV/120319
+    "InnoLite SATADOM D150QV.*",
     "", "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
   //"-v 2,raw48,Throughput_Performance "
@@ -744,18 +807,18 @@ const drive_settings builtin_knowndrives[] = {
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
   //"-v 7,raw48,Seek_Error_Rate " // from InnoDisk iSMART Linux tool, useless for SSD
   //"-v 8,raw48,Seek_Time_Performance "
-  //"-v 9,raw24(raw8),Power_On_Hours "
+  //"-v 9,raw48,Power_On_Hours "
   //"-v 10,raw48,Spin_Retry_Count "
   //"-v 12,raw48,Power_Cycle_Count "
     "-v 168,raw48,SATA_PHY_Error_Count "
-    "-v 170,raw48,Bad_Block_Count "
-    "-v 173,raw48,Erase_Count "
+    "-v 170,raw16,Bad_Block_Count_New/Tot "
+    "-v 173,raw16,Erase_Count_Max/Avg "
     "-v 175,raw48,Bad_Cluster_Table_Count "
     "-v 192,raw48,Unexpect_Power_Loss_Ct "
   //"-v 194,tempminmax,Temperature_Celsius "
   //"-v 197,raw48,Current_Pending_Sector "
     "-v 229,hex48,Flash_ID "
-    "-v 235,raw48,Later_Bad_Block "
+    "-v 235,raw16,Lat_Bad_Blk_Era/Wri/Rea "
     "-v 236,raw48,Unstable_Power_Count "
     "-v 240,raw48,Write_Head"
   },
@@ -954,7 +1017,7 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "This drive may require a firmware update to\n"
     "fix possible drive hangs when reading SMART self-test log:\n"
-    "http://downloadcenter.intel.com/Detail_Desc.aspx?DwnldID=18363",
+    "https://downloadcenter.intel.com/download/26491",
     "-v 192,raw48,Unsafe_Shutdown_Count "
     "-v 225,raw48,Host_Writes_32MiB "
     "-v 226,raw48,Workld_Media_Wear_Indic "
@@ -1173,8 +1236,9 @@ const drive_settings builtin_knowndrives[] = {
   { "Intel 730 and DC S35x0/3610/3700 Series SSDs", // tested with INTEL SSDSC2BP480G4, SSDSC2BB120G4/D2010355,
       // INTEL SSDSC2BB800G4T, SSDSC2BA200G3/5DV10250, SSDSC2BB080G6/G2010130,  SSDSC2BX200G4/G2010110,
       // INTEL SSDSC2BB016T6/G2010140, SSDSC2BX016T4/G2010140, SSDSC2BB150G7/N2010101
-    "INTEL SSDSC(1N|2B)[ABPX]((080|100|120|150|160|200|240|300|400|480|600|800)G[3467]T?|(012|016)T[46])",
+    "INTEL SSDSC(1N|2B)[ABPX]((080|100|120|150|160|200|240|300|400|480|600|800)G[3467][RT]?|(012|016)T[46])",
       // A = S3700, B*4 = S3500, B*6 = S3510, P = 730, X = S3610
+      // Dell ships drives with model of the form SSDSC2BB120G4R
     "", "",
   //"-v 3,raw16(avg16),Spin_Up_Time "
   //"-v 4,raw48,Start_Stop_Count "
@@ -1206,6 +1270,23 @@ const drive_settings builtin_knowndrives[] = {
     "-v 243,raw48,NAND_Writes_32MiB " // S3510/3610
     "-F xerrorlba" // tested with SSDSC2BB600G4/D2010355
   },
+  // https://www.intel.com/content/www/us/en/solid-state-drives/ssd-pro-5400s-series-spec.html
+  // https://www.intel.com/content/www/us/en/solid-state-drives/ssd-pro-5400s-series-m2-spec.html
+  { "Intel SSD Pro 5400s Series", // Tested with SSDSC2KF480H6/LSF036P
+    "INTEL SSDSC[2K]KF(120H|180H|240H|360H|480H|010X)6",
+    "", "",
+    "-v 170,raw48,Available_Reservd_Space "
+    "-v 171,raw48,Program_Fail_Count "
+    "-v 172,raw48,Erase_Fail_Count "
+    "-v 174,raw48,Unexpect_Power_Loss_Ct "
+    "-v 183,raw48,SATA_Downshift_Count "
+    "-v 187,raw48,Uncorrectable_Error_Cnt "
+    "-v 225,raw48,Host_Writes_32MiB "
+    "-v 226,raw48,Workld_Media_Wear_Indic "
+    "-v 227,raw48,Workld_Host_Reads_Perc "
+    "-v 228,raw48,Workload_Minutes "
+    "-v 249,raw48,NAND_Writes_1GiB "
+  },
   { "Intel 3710 Series SSDs", // INTEL SSDSC2BA200G4R/G201DL2B (dell)
     "INTEL SSDSC2BA(200G|400G|800G|012T)4.?",
     "", "",
@@ -1223,6 +1304,104 @@ const drive_settings builtin_knowndrives[] = {
     "-v 234,raw24/raw32:04321,Thermal_Throttle "
     "-v 243,raw48,NAND_Writes_32MiB "
   },
+  { "Intel S3520 Series SSDs", // INTEL SSDSC2BB960G7/N2010112, INTEL SSDSC2BB016T7/N2010112
+    "INTEL SSDSC(2|K)(J|B)B(240G|480G|960G|150G|760G|800G|012T|016T)7.?",
+    "", "",
+  //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
+  //"-v 9,raw24(raw8),Power_On_Hours "
+  //"-v 12,raw48,Power_Cycle_Count "
+    "-v 170,raw48,Available_Reservd_Space "
+    "-v 171,raw48,Program_Fail_Count "
+    "-v 172,raw48,Erase_Fail_Count "
+    "-v 174,raw48,Unsafe_Shutdown_Count "
+    "-v 175,raw16(raw16),Power_Loss_Cap_Test "
+    "-v 183,raw48,SATA_Downshift_Count "
+    "-v 184,raw48,End-to-End_Error_Count "
+    "-v 187,raw48,Uncorrectable_Error_Cnt "
+    "-v 190,tempminmax,Case_Temperature "
+    "-v 192,raw48,Unsafe_Shutdown_Count "
+    "-v 194,tempminmax,Drive_Temperature "
+    "-v 197,raw48,Pending_Sector_Count "
+    "-v 199,raw48,CRC_Error_Count "
+    "-v 225,raw48,Host_Writes_32MiB "
+    "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024)
+    "-v 227,raw48,Workld_Host_Reads_Perc "  // Timed Workload Host Reads Percentage
+    "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40'
+  //"-v 232,raw48,Available_Reservd_Space "
+  //"-v 233,raw48,Media_Wearout_Indicator "
+    "-v 234,raw24/raw32:04321,Thermal_Throttle_Status "
+    "-v 241,raw48,Host_Writes_32MiB "
+    "-v 242,raw48,Host_Reads_32MiB "
+    "-v 243,raw48,NAND_Writes_32MiB"
+  },
+  { "Dell Certified Intel S3520 Series SSDs",
+    "SSDSC(2|K)(J|B)B(240G|480G|960G|120G|760G|800G|012T|016T)7R.?",
+    "", "",
+    "-v 170,raw48,Available_Reservd_Space "
+    "-v 174,raw48,Unsafe_Shutdown_Count "
+    "-v 195,raw48,Uncorrectable_Error_Cnt "
+    "-v 199,raw48,CRC_Error_Count "
+    "-v 201,raw16(raw16),Power_Loss_Cap_Test "
+    "-v 202,raw48,End_of_Life "
+    "-v 225,raw48,Host_Writes_32MiB "
+    "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024)
+    "-v 227,raw48,Workld_Host_Reads_Perc "  // Timed Workload Host Reads Percentage
+    "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40'
+    "-v 233,raw48,Total_LBAs_Written "
+    "-v 234,raw24/raw32:04321,Thermal_Throttle_Status "
+    "-v 245,raw48,Percent_Life_Remaining"
+  },
+  { "Intel S4510/S4610/S4500/S4600 Series SSDs", // INTEL SSDSC2KB480G7/SCV10100,
+      // INTEL SSDSC2KB960G7/SCV10100, INTEL SSDSC2KB038T7/SCV10100,
+      // INTEL SSDSC2KB038T7/SCV10121, INTEL SSDSC2KG240G7/SCV10100
+    "INTEL SSDSC(2K|KK)(B|G)(240G|480G|960G|019T|038T)(7|8).?",
+    "", "",
+  //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
+  //"-v 9,raw24(raw8),Power_On_Hours "
+  //"-v 12,raw48,Power_Cycle_Count "
+    "-v 170,raw48,Available_Reservd_Space "
+    "-v 171,raw48,Program_Fail_Count "
+    "-v 172,raw48,Erase_Fail_Count "
+    "-v 174,raw48,Unsafe_Shutdown_Count "
+    "-v 175,raw16(raw16),Power_Loss_Cap_Test "
+    "-v 183,raw48,SATA_Downshift_Count "
+    "-v 184,raw48,End-to-End_Error_Count "
+    "-v 187,raw48,Uncorrectable_Error_Cnt "
+    "-v 190,tempminmax,Drive_Temperature "
+    "-v 192,raw48,Unsafe_Shutdown_Count "
+    "-v 197,raw48,Pending_Sector_Count "
+    "-v 199,raw48,CRC_Error_Count "
+    "-v 225,raw48,Host_Writes_32MiB "
+    "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024)
+    "-v 227,raw48,Workld_Host_Reads_Perc "  // Timed Workload Host Reads Percentage
+    "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40'
+  //"-v 232,raw48,Available_Reservd_Space "
+  //"-v 233,raw48,Media_Wearout_Indicator "
+    "-v 234,raw24/raw32:04321,Thermal_Throttle_Status "
+    "-v 235,raw16(raw16),Power_Loss_Cap_Test "
+    "-v 241,raw48,Host_Writes_32MiB "
+    "-v 242,raw48,Host_Reads_32MiB "
+    "-v 243,raw48,NAND_Writes_32MiB"
+  },
+  { "Dell Certified Intel S4x00/D3-S4x10 Series SSDs", // INTEL SSDSC2KB480G7R/SCV1DL58,
+      // INTEL SSDSC2KB960G7R/SCV1DL58, INTEL SSDSC2KB038T7R/SCV1DL58,
+      // INTEL SSDSC2KB038T7R/SCV1DL58, INTEL SSDSC2KG240G7R/SCV1DL58
+    "SSDSC(2K|KK)(B|G)(240G|480G|960G|019T|038T)(7R|8R).?",
+    "", "",
+    "-v 170,raw48,Available_Reservd_Space "
+    "-v 174,raw48,Unsafe_Shutdown_Count "
+    "-v 195,raw48,Uncorrectable_Error_Cnt "
+    "-v 199,raw48,CRC_Error_Count "
+    "-v 201,raw16(raw16),Power_Loss_Cap_Test "
+    "-v 202,raw48,End_of_Life "
+    "-v 225,raw48,Host_Writes_32MiB "
+    "-v 226,raw48,Workld_Media_Wear_Indic " // Timed Workload Media Wear Indicator (percent*1024)
+    "-v 227,raw48,Workld_Host_Reads_Perc "  // Timed Workload Host Reads Percentage
+    "-v 228,raw48,Workload_Minutes " // 226,227,228 can be reset by 'smartctl -t vendor,0x40'
+    "-v 233,raw48,Total_LBAs_Written "
+    "-v 234,raw24/raw32:04321,Thermal_Throttle_Status "
+    "-v 245,raw48,Percent_Life_Remaining"
+  },
   { "Kingston branded X25-V SSDs", // fixed firmware
     "KINGSTON SSDNow 40GB",
     "2CV102(J[89A-Z]|[K-Z].)", // >= "2CV102J8"
@@ -1250,7 +1429,7 @@ const drive_settings builtin_knowndrives[] = {
     "Kingston SSDNow V Series [0-9]*GB|" // tested with Kingston SSDNow V Series 64GB/B090522a
     "TS(2|4|8|16|32|64|128|192)GSSD(18|25)[MS]?-[MS]", // Transcend IDE and SATA, tested with
       // TS32GSSD25-M/V090331, TS32GSSD18M-M/v090331
-    "[BVv].*", // other Transcend SSD versions will be catched by subsequent entry
+    "[BVv].*", // other Transcend SSD versions will be caught by subsequent entry
     "",
   //"-v 9,raw24(raw8),Power_On_Hours " // raw value always 0?
   //"-v 12,raw48,Power_Cycle_Count "
@@ -1265,7 +1444,9 @@ const drive_settings builtin_knowndrives[] = {
     "ADATA S596 Turbo|"  // tested with ADATA S596 Turbo 256GB SATA SSD (JMicron JMF616)
     "ADATA SP600|"  // tested with ADATA SP600/2.4 (JMicron JMF661)
     "ADATA SP310|"  // Premier Pro SP310 mSATA, JMF667, tested with ADATA SP310/3.04
+    "ADATA SX930|"  // tested with ADATA SX930/6.8SE
     "APPLE SSD TS(064|128|256|512)C|"  // Toshiba?, tested with APPLE SSD TS064C/CJAA0201
+    "KingSpec KDM-SA\\.51-008GMJ|" // tested with KingSpec KDM-SA.51-008GMJ/1.092.37 (JMF605?)
     "KINGSTON SNV425S2(64|128)GB|"  // SSDNow V Series (2. Generation, JMF618),
                                     // tested with KINGSTON SNV425S264GB/C091126a
     "KINGSTON SSDNOW 30GB|" // tested with KINGSTON SSDNOW 30GB/AJXA0202
@@ -1310,7 +1491,8 @@ const drive_settings builtin_knowndrives[] = {
       // tested with PLEXTOR PX-128M3/1.01, PX-128M3P/1.04, PX-256M3/1.05, PX-128M5S/1.02, PX-256M5S/1.03,
       // PX-128M5M/1.05, PX-128M5S/1.05, PX-128M5Pro/1.05, PX-512M5Pro/1.06, PX-256M5P/1.01, PX-128M6S/1.03
       // (1.04/5 Firmware self-test log lifetime unit is bogus, possibly 1/256 hours)
-    "PLEXTOR PX-(64|128|256|512|768)M(3P?|5[MPS]|5Pro|6[MS])",
+      // PLEXTOR PX-256M6S+/1.00
+    "PLEXTOR PX-(64|128|256|512|768)M(3P?|5[MPS]|5Pro|6[MS])\\+?",
     "", "",
   //"-v 1,raw48,Raw_Read_Error_Rate "
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
@@ -1345,10 +1527,13 @@ const drive_settings builtin_knowndrives[] = {
   },
   { "Samsung based SSDs",
     "SAMSUNG SSD PM800 .*GB|"  // SAMSUNG PM800 SSDs, tested with SAMSUNG SSD PM800 TH 64GB/VBM25D1Q
-    "SAMSUNG SSD PM810 .*GB|"  // SAMSUNG PM810 (470 series) SSDs, tested with SAMSUNG SSD PM810 2.5" 128GB/AXM06D1Q
+    "SAMSUNG SSD PM810 .*GB|"  // SAMSUNG PM810 (470 series) SSDs, tested with
+      // SAMSUNG SSD PM810 2.5" 128GB/AXM06D1Q
+    "SAMSUNG SSD SM841N? (2\\.5\"? 7mm |mSATA )?(128|256|512)GB( SED)?|" // tested with
+      // SAMSUNG SSD SM841 2.5" 7mm 256GB/DXM02D0Q, SAMSUNG SSD SM841 mSATA 512GB/DXM44D0Q,
+      // SAMSUNG SSD SM841N 2.5 7mm 128GB/DXM03D0Q, SAMSUNG SSD SM841N mSATA 256GB SED/DXM45D6Q
     "SAMSUNG SSD PM851 (mSATA |M\\.2 )?(2280 )?(128|256|512)GB|" // tested with SAMSUNG SSD PM851 mSATA 128GB,
       // SAMSUNG SSD PM851 M.2 2280 256GB/EXT25D0Q
-    "SAMSUNG SSD SM841N (mSATA )?(128|256|512)GB|" // tested with SAMSUNG SSD SM841N mSATA 256GB
     "SAMSUNG 470 Series SSD|"  // tested with SAMSUNG 470 Series SSD 64GB/AXM09B1Q
     "Samsung SSD 750 EVO (120|250|500)GB|" // tested with Samsung SSD 750 EVO 250GB/MAT01B6Q
     "SAMSUNG SSD 830 Series|"  // tested with SAMSUNG SSD 830 Series 64GB/CXM03B1Q
@@ -1356,28 +1541,37 @@ const drive_settings builtin_knowndrives[] = {
     "MZ7PC(512|256|128|064)HA(GH|FU|DR)-000.*|" // probably PM830, tested with SAMSUNG MZ7PC128HAFU-000L1/CXM04L1Q
     "Samsung SSD 840 (PRO )?Series|" // tested with Samsung SSD 840 PRO Series 128GB/DXM04B0Q,
       // Samsung SSD 840 Series/DXT06B0Q
-    "Samsung SSD 8[45]0 EVO .*|" // tested with
+    "Samsung SSD 8[456]0 EVO (mSATA |M\\.2 )?((120|250|500|750)G|[12]T)B|" // tested with
       // Samsung SSD 840 EVO (120|250|500|750)GB/EXT0AB0Q,
       // Samsung SSD 840 EVO (120|250)GB/EXT0BB6Q, 1TB/EXT0BB0Q, 120GB mSATA/EXT41B6Q,
-      // Samsung SSD 850 EVO 250GB/EMT01B6Q
-      // Samsung SSD 850 EVO M.2 250GB/EMT21B6Q
-      // Samsung SSD 850 EVO mSATA 120GB/EMT41B6Q
-      // Samsung SSD 850 EVO 2TB/EMT02B6Q
-    "Samsung SSD 850 PRO ((128|256|512)G|1T)B|" // tested with Samsung SSD 850 PRO 128GB/EXM01B6Q,
-      // Samsung SSD 850 PRO 1TB/EXM01B6Q
+      // Samsung SSD 850 EVO 250GB/EMT01B6Q, Samsung SSD 850 EVO M.2 250GB/EMT21B6Q,
+      // Samsung SSD 850 EVO mSATA 120GB/EMT41B6Q, Samsung SSD 850 EVO 2TB/EMT02B6Q,
+      // Samsung SSD 860 EVO 250GB/RVT01B6Q, Samsung SSD 860 EVO mSATA 250GB/RVT41B6Q,
+      // Samsung SSD 860 EVO 500GB/RVT01B6Q, Samsung SSD 860 EVO mSATA 500GB/RVT41B6Q,
+      // Samsung SSD 860 EVO mSATA 1TB/RVT41B6Q, Samsung SSD 860 EVO 2TB/RVT01B6Q
+    "Samsung SSD 8[56]0 PRO ((128|256|512)G|1T)B|" // tested with Samsung SSD 850 PRO 128GB/EXM01B6Q,
+      // Samsung SSD 850 PRO 1TB/EXM01B6Q, Samsung SSD 860 PRO 256GB/RVM01B6Q,
+      // Samsung SSD 860 PRO 512GB/RVM01B6Q, Samsung SSD 860 PRO 1TB/RVM01B6Q
     "SAMSUNG MZ7PA256HMDR-.*|" // PM810 (470 Series), tested with SAMSUNG MZ7PA256HMDR-010H1/AXM07H1Q
     "Samsung SSD 845DC EVO .*|" // Samsung SSD 845DC EVO 960GB/EXT03X3Q
     "SAMSUNG MZ[7M]PC(032|064|128|256|512)HBCD-.*|" // PM830, tested with SAMSUNG MZMPC032HBCD-000L1/CXM12L1Q
     "SAMSUNG MZ7TD(128|256)HAFV-.*|" // 840 Series, tested with SAMSUNG MZ7TD256HAFV-000L7/DXT06L6Q
+    "SAMSUNG MZMTD(128|256|512)HAGL-.*|" // PM841, tested with SAMSUNG MZMTD512HAGL-00000/DXT4200Q
     "SAMSUNG MZ7WD((120|240)H[AC]FV|480HAGM|960HAGP)-00003|" // SM843T Series, tested with
       // SAMSUNG MZ7WD120HAFV-00003/DXM85W3Q, SAMSUNG MZ7WD120HCFV-00003/DXM9203Q
     "SAMSUNG MZ[7N]TE(128|256|512)HMHP-.*|" // PM851, tested with SAMSUNG MZ7TE256HMHP-000L7/EXT09L6Q,
       // SAMSUNG MZNTE256HMHP-000H1/EXT22H0Q
+    "SAMSUNG MZMPF(032|064)HCFV-.*|" // CM851 mSATA, tested with SAMSUNG MZMPF032HCFV-000H1/FXM42H2Q
     "SAMSUNG MZ7GE(240HMGR|(480|960)HMHP)-00003|" // SM853T Series, tested with
       // SAMSUNG MZ7GE240HMGR-00003/EXT0303Q
     "SAMSUNG MZ7LM(120|240|480|960|1T9|3T8)HC(JM|HP|GR|FD)-.*|" // PM863 Series, tested with
       // SAMSUNG MZ7LM960HCHP-0E003/GXT3003Q
-    "SAMSUNG MZ7KM(120|240|480|960|1T9)HA(JM|HP|GR|FD|JM)-.*|" // SM863, tested with MZ7KM480HAHP-0E005/GXM1003Q
+    "SAMSUNG MZ7LM(240|480|960|1T9|3T8)HM(JP|HQ|LP)-.*|" // PM863a Series, tested with
+      // SAMSUNG MZ7LM3T8HMLP-00005/GXT5104Q
+    "SAMSUNG MZ7KM(120|240|480|960|1T9)H[AM](FD|GR|HP|HQ|JM)-.*|" // SM863(a), tested with
+      // SAMSUNG MZ7KM480HAHP-0E005/GXM1003Q, SAMSUNG MZ7KM480HMHQ-00005/GXM5104Q
+    "SAMSUNG MZ7LH(240|480|960|1T9|3T8|7T6)H[AM](HQ|JR|LT|LA)-.*|" //PM883, tested with SAMSUNG MZ7LH960HAJR-00005
+    "SAMSUNG MZ7KH(240|480|960|1T9|3T8)HA(HQ|JR|LS)-.*|" //SM883
     "SAMSUNG MZN(LF|TY)(128|256)H[CD]HP-.*|" // CM871/871a, tested with SAMSUNG MZNLF128HCHP-000H1/FXT21H1Q,
       // SAMSUNG MZNTY256HDHP-000/MAT21K0Q
     "SAMSUNG MZ[7N]LN(128|256|512|1T0)H[ACM](GR|HP|HQ|J[HPQ]|LR)-.*|" // PM871/871a/b, tested with
@@ -1385,7 +1579,9 @@ const drive_settings builtin_knowndrives[] = {
       // SAMSUNG MZNLN256HMHQ-000H1/MAV21H3Q
     "SAMSUNG SSD PM871 .*|" // SAMSUNG SSD PM871 2.5 7mm 256GB/EMT02D0Q
       // SAMSUNG MZ7LN256HMJP-00000/MAV0100Q, SAMSUNG MZ7LN512HMJP-00000/MAV0100Q
-    "SAMSUNG MZHPV(128|256|512)HDGL-.*", // SM951, tested with SAMSUNG MZHPV512HDGL-00000/BXW2500Q
+    "SAMSUNG MZHPV(128|256|512)HDG(L|M)-.*|" // SM951, tested with SAMSUNG MZHPV512HDGL-00000/BXW2500Q,
+      // SAMSUNG MZHPV128HDGM-00000 (BXW2500Q)
+    "Samsung Portable SSD T5", // tested with Samsung Portable SSD T5 (0x04e8:0x61f5)
     "", "",
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
   //"-v 9,raw24(raw8),Power_On_Hours "
@@ -1438,12 +1634,14 @@ const drive_settings builtin_knowndrives[] = {
       // SanDisk SD6SB1M256G1022I/X231600, SanDisk SD6SF1M128G1022/X231200, SanDisk SD6SB2M512G1022I/X210400
     "SanDisk SD7S[BN]6S-?(128|256|512)G(1122|-1006)|" // X300 (88SS9189?), tested with
       // SanDisk SD7SB6S128G1122/X3310000, SanDisk SD7SN6S-512G-1006/X3511006
-    "SanDisk SD8SB8U((128|256|512)G|1T00)1122|" // X400 (88SS1074), tested with SanDisk SD8SB8U128G1122/X4120000
+    "SanDisk SD8S[BN]8U-?((128|256|512)G|1T00)(1122|-1006)|" // X400 (88SS1074), tested with SanDisk SD8SB8U128G1122/X4120000
     "SanDisk SDSSDHP[0-9]*G|" // Ultra Plus (88SS9175), tested with SanDisk SDSSDHP128G/X23[01]6RL
     "SanDisk (SDSSDHII|Ultra II )[0-9]*GB?|" // Ultra II (88SS9190/88SS9189), tested with
       // SanDisk SDSSDHII120G/X31200RL, SanDisk Ultra II 960GB/X41100RL
-    "SanDisk SDSSDXPS?[0-9]*G", // Extreme II/Pro (88SS9187), tested with SanDisk SDSSDXP480G/R1311,
+    "SanDisk SDSSDH2(128|256)G|" // SanDisk SDSSDH2128G/X211200
+    "SanDisk SDSSDXPS?[0-9]*G|" // Extreme II/Pro (88SS9187), tested with SanDisk SDSSDXP480G/R1311,
       // SanDisk SDSSDXPS480G/X21200RL
+    "SSD SATAIII 16GB", // SSD SATAIII 16GB/i221100 (see #923)
     "", "",
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
   //"-v 9,raw24(raw8),Power_On_Hours "
@@ -1462,6 +1660,7 @@ const drive_settings builtin_knowndrives[] = {
   //"-v 188,raw48,Command_Timeout "
   //"-v 194,tempminmax,Temperature_Celsius "
     "-v 199,raw48,SATA_CRC_Error "
+    "-v 201,raw48,Lifetime_Remaining% "
     "-v 212,raw48,SATA_PHY_Error "
     "-v 230,raw48,Perc_Write/Erase_Count "
     "-v 232,raw48,Perc_Avail_Resrvd_Space "
@@ -1471,6 +1670,7 @@ const drive_settings builtin_knowndrives[] = {
     "-v 242,raw48,Total_Reads_GiB "
   //"-v 243,raw48,Unknown_Attribute "
     "-v 244,raw48,Thermal_Throttle "
+    "-v 249,raw48,TLC_NAND_GB_Writes"
   },
   { "SanDisk based SSDs", // see also #463 for the vendor attribute description
     "SanDisk iSSD P4 [0-9]*GB|" // tested with SanDisk iSSD P4 16GB/SSD 9.14
@@ -1480,6 +1680,7 @@ const drive_settings builtin_knowndrives[] = {
     "SanDisk SSD i100 [0-9]*GB|" // tested with SanDisk SSD i100 8GB/11.56.04, 24GB/11.56.04
     "SanDisk SSD U100 ([0-9]*GB|SMG2)|" // tested with SanDisk SSD U100 8GB/10.56.00, 256GB/10.01.02, SMG2/10.56.04
     "SanDisk SSD U110 (8|16|24|32|64|128)GB|" // tested with SanDisk SSD U110 32GB/U221000
+    "SanDisk SDSA6MM-.*|" // tested with SanDisk SDSA6MM-016G-1006/U221006
     "SanDisk SD7[SU]B[23]Q(064|128|256|512)G.*", // tested with SD7SB3Q064G1122/SD7UB3Q256G1122/SD7SB3Q128G/SD7UB2Q512G1122
     "", "",
   //"-v 5,raw16(raw16),Reallocated_Sector_Ct "
@@ -1558,16 +1759,24 @@ const drive_settings builtin_knowndrives[] = {
     "CT(120|250|500|1000)BX100SSD1|" // Crucial BX100, tested with CT250BX100SSD1/MU02,
       // CT500BX100SSD1/MU02, CT1000BX100SSD1/MU02
     "CT(240|480|960)BX200SSD1|" // Crucial BX200 Solid State Drive, tested with CT480BX200SSD1/MU02.6
-    "TS((16|32|64|128|256|512)G|1T)(SSD|MSA)(370S?|420I?)|" // Transcend SSD370/420 SATA/mSATA, TS6500,
+    "KingDian S400 (120|240|480)GB|" // SM2256EN, tested with KingDian S400 120GB/Q0607A
+    "KingSpec KSD-[PS]A25\\.[1-9]-(016|032|064|128)(MS|SJ)|" // tested with KingSpec KSD-PA25.6-064MS/20140803
+    "T60|" // KingSpec T60, tested with T60/20151120
+    "TEAML5Lite3D(120G|240G|480G|1T)|" // Team Group L5Lite 3D, tested with TEAML5Lite3D240G/R0302A0
+    "TS((16|32|64|128|256|512)G|1T)(SSD|MSA)(370S?|420[IK]?)|" // Transcend SSD370/420 SATA/mSATA, TS6500,
       // tested with TS32GMSA370/20140402, TS16GMSA370/20140516, TS64GSSD370/20140516,
-      // TS256GSSD370/N0815B, TS256GSSD370S/N1114H, TS512GSSD370S/N1114H, TS32GSSD420I/N1114H
-      // TS256GMTS400
-    "TS(16|32|64|128|512|256)GMTS400S?|"
-    "TS(120|240)GMTS420|" // Transcend MTS420, TS(120|240)GMTS420
+      // TS256GSSD370/N0815B, TS256GSSD370S/N1114H, TS512GSSD370S/N1114H, TS32GSSD420I/N1114H,
+      // TS32GSSD420K/P1225CE
+    "TS(16|32|64|128|512|256)GMTS400S?|" // TS256GMTS400
+    "TS(120|240)GMTS420|" // Transcend MTS420
     "TS(128G|256G|512G|1T)SSD230S|" // TS128GSSD230S/P1025F8
     "TS(120|240|480|960)GSSD220S|" // TS480GSSD220S/P0520AA
     "TS(16G|32G|64G|128G|256G|512G|1T)MTS800S?|" // MTS800, tested with TS1TMTS800/O1225H1
     "TS(16|32|64)GMSA630|" // MSA630 mSATA SSD, tested with TS32GMSA630/N0113E1
+    "TS(32|64|128)GPSD330|" // Transcend PSD SSD, tested with TS64GPSD330/20140121
+    "TS(16|32|64|96|128|256)GSSD630|" // Transcend 630, tested with TS16GSSD630/N0113E1
+    "TS(128G|256G|512G|1T)ESD400K|" // Transcend ESD400 Portable, tested with
+      // TS256GESD400K/R0605AS (0x2174:0x2000)
     "MKNSSDRE(1TB|2TB|512GB|500GB|256GB|250GB)|" // MKNSSDRE256GB/N1007C
     "MKNSSDTR(240|500|250|120|480|240)GB(-LT)?|" // MKNSSDTR500GB/O1126A
     "LITEON LMH-(128|256|512)V2M-.*|" // LITEON LMH-256V2M-11 MSATA 256GB/FM8110C
@@ -2006,9 +2215,14 @@ const drive_settings builtin_knowndrives[] = {
     "Buggy and fixed firmware report same version number!\n"
     "See the following web pages for details:\n"
     "http://knowledge.seagate.com/articles/en_US/FAQ/223571en\n"
-    "http://www.smartmontools.org/wiki/SamsungF4EGBadBlocks",
+    "https://www.smartmontools.org/wiki/SamsungF4EGBadBlocks",
     ""
   },
+  { "Seagate Samsung SpinPoint F4 EG (AF)", // later sold as Barracuda Green,
+       // tested with ST2000DL004 HD204UI/1AQ10001
+    "ST2000DL004 HD204UI",
+    "", "", ""
+  },
   { "SAMSUNG SpinPoint S250", // tested with HD200HJ/KF100-06
     "SAMSUNG HD(162|200|250)HJ",
     "", "", ""
@@ -2768,7 +2982,8 @@ const drive_settings builtin_knowndrives[] = {
   },
   { "HGST Deskstar NAS", // tested with HGST HDN724040ALE640/MJAOA5E0,
        // HGST HDN726050ALE610/APGNT517, HGST HDN726060ALE610/APGNT517
-    "HGST HDN72(4030|4040|6050|6060)ALE6[14]0",
+       // HGST HDN726040ALE614/APGNW7JH, HGST HDN726060ALE614/K1HE594D
+    "HGST HDN72(4030|4040|6040|6050|6060)ALE6(10|14|40|04)",
     "", "", ""
   },
   { "Hitachi Ultrastar A7K1000", // tested with
@@ -2815,6 +3030,18 @@ const drive_settings builtin_knowndrives[] = {
     "", "",
     "-v 22,raw48,Helium_Level"
   },
+  { "WDC HGST Ultrastar He10", // WD white label, tested with
+      // WDC WD80EMAZ-00WJTA0/83.H0A83, WDC WD80EZAZ-11TDBA0/83.H0A83,
+      // WDC WD100EZAZ-11TDBA0/83.H0A83
+    "WDC WD(80E[MZ]|100EZ)AZ-.*",
+    "", "",
+    "-v 22,raw48,Helium_Level"
+  },
+  { "HGST Ultrastar DC HC520 (He12)", // tested with HGST HUH721212ALE600/LEGNT3D0
+    "HGST HUH721212AL[EN]60[014]",
+    "", "",
+    "-v 22,raw48,Helium_Level"
+  },
   { "HGST MegaScale 4000", // tested with HGST HMS5C4040ALE640/MPAOA580
     "HGST HMS5C4040[AB]LE64[01]", // B = DC 4000.B
     "", "", ""
@@ -2934,6 +3161,10 @@ const drive_settings builtin_knowndrives[] = {
     "TOSHIBA MQ01UBD(050|075|100)",
     "", "", ""
   },
+  { "Toshiba 2.5\" HDD MQ04UBF... (USB 3.0)", // tested with TOSHIBA MQ04UBF100/JU000U (0x0480:0xa202)
+    "TOSHIBA MQ04UBF100",
+    "", "", ""
+  },
   { "Toshiba 2.5\" HDD MQ03ABB...", // tested with TOSHIBA MQ03ABB300
     "TOSHIBA MQ03ABB[23]00",
     "", "", ""
@@ -2987,6 +3218,14 @@ const drive_settings builtin_knowndrives[] = {
     "TOSHIBA MK6022GAX",
     "", "", ""
   },
+  { "Toshiba HK4R Series SSD", // TOSHIBA THNSN8960PCSE/8EET6101
+    "TOSHIBA THNSN8(120P|240P|480P|960P|1Q92)CSE",
+    "", "", 
+    "-v 167,raw48,SSD_Protect_Mode "
+    "-v 168,raw48,SATA_PHY_Error_Count "
+    "-v 169,raw48,Bad_Block_Count "
+    "-v 173,raw48,Erase_Count "
+  },
   { "Toshiba HG6 Series SSD", // TOSHIBA THNSNJ512GCST/JTRA0102
     // http://www.farnell.com/datasheets/1852757.pdf
     // TOSHIBA THNSFJ256GCSU/JULA1102
@@ -3040,7 +3279,8 @@ const drive_settings builtin_knowndrives[] = {
   },
   { "Seagate Momentus 5400.6",
     "ST9(80313|160(301|314)|(12|25)0315|250317|(320|500)325|500327|640320)ASG?",
-    "", "", ""
+    "", "",
+    "-F xerrorlba" // ST9500325AS/0002SDM1 (ticket #1094)
   },
   { "Seagate Momentus 5400.7",
     "ST9(160316|(250|320)310|(500|640)320)AS",
@@ -3091,8 +3331,7 @@ const drive_settings builtin_knowndrives[] = {
     "AND THIS FIRMWARE VERSION IS AFFECTED,\n"
     "see the following web pages for details:\n"
     "http://knowledge.seagate.com/articles/en_US/FAQ/215451en\n"
-    "http://forums.seagate.com/t5/Momentus-XT-Momentus-Momentus/Momentus-XT-corrupting-large-files-Linux/td-p/109008\n"
-    "http://superuser.com/questions/313447/seagate-momentus-xt-corrupting-files-linux-and-mac",
+    "https://superuser.com/questions/313447/seagate-momentus-xt-corrupting-files-linux-and-mac",
     ""
   },
   { "Seagate Momentus XT", // unknown firmware
@@ -3101,8 +3340,7 @@ const drive_settings builtin_knowndrives[] = {
     "These drives may corrupt large files,\n"
     "see the following web pages for details:\n"
     "http://knowledge.seagate.com/articles/en_US/FAQ/215451en\n"
-    "http://forums.seagate.com/t5/Momentus-XT-Momentus-Momentus/Momentus-XT-corrupting-large-files-Linux/td-p/109008\n"
-    "http://superuser.com/questions/313447/seagate-momentus-xt-corrupting-files-linux-and-mac",
+    "https://superuser.com/questions/313447/seagate-momentus-xt-corrupting-files-linux-and-mac",
     ""
   },
   { "Seagate Momentus XT (AF)", // tested with ST750LX003-1AC154/SM12
@@ -3115,7 +3353,7 @@ const drive_settings builtin_knowndrives[] = {
   },
   { "Seagate Laptop HDD", // tested with ST500LT012-9WS142/0001SDM1,
       // ST500LM021-1KJ152/0002LIM1, ST4000LM016-1N2170/0003
-    "ST((25|32|50)0LT0(12|15|25)|(32|50)0LM0(10|21)|[34]00LM016)-.*",
+    "ST((25|32|50)0LT0(12|15|25)|(32|50)0LM0(10|21)|[34]000LM016)-.*",
     "", "", ""
   },
   { "Seagate Laptop SSHD", // tested with ST500LM000-1EJ162/SM11
@@ -3198,8 +3436,9 @@ const drive_settings builtin_knowndrives[] = {
     "ST3(402111?|80[28]110?|120[28]1[0134]|160[28]1[012]|200827|250[68]24|300[68]22|(320|400)[68]33|500[68](32|41))AS?.*",
     "", "", ""
   },
-  { "Seagate Barracuda 7200.10",
-    "ST3((80|160)[28]15|200820|250[34]10|(250|300|320|400)[68]20|360320|500[68]30|750[68]40)AS?",
+  { "Seagate Barracuda 7200.10", // tested with GB0160EAFJE/HPG0
+    "ST3((80|160)[28]15|200820|250[34]10|(250|300|320|400)[68]20|360320|500[68]30|750[68]40)AS?|"
+    "GB0160EAFJE", // HP OEM
     "", "", ""
   },
   { "Seagate Barracuda 7200.11", // unaffected firmware
@@ -3225,7 +3464,7 @@ const drive_settings builtin_knowndrives[] = {
     "see the following web pages for details:\n"
     "http://knowledge.seagate.com/articles/en_US/FAQ/207931en\n"
     "http://knowledge.seagate.com/articles/en_US/FAQ/207951en\n"
-    "http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=632758",
+    "https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=632758",
     ""
   },
   { "Seagate Barracuda 7200.11", // unknown firmware
@@ -3307,6 +3546,17 @@ const drive_settings builtin_knowndrives[] = {
     "", "",
     "-v 188,raw16 -v 240,msec24hour32"
   },
+  // ST8000DM004, ST6000DM003, ST4000DM004, ST3000DM007, ST2000DM005
+  { "Seagate Barracuda Compute", // tested on ST8000DM004-2CX188/0001
+    "ST(8000DM004|6000DM003|4000DM004|3000DM007|2000DM005)-.*",
+    "", "",
+    ""
+  },
+  { "Seagate Barracuda Pro", // tested on ST8000DM004-2CX188/0001
+    "ST(8000DM005|6000DM004|4000DM006|2000DM009)-.*",
+    "", "",
+    "-v 188,raw16 -v 240,msec24hour32"
+  },
   { "Seagate Barracuda 7200.14 (AF)", // < 1TB, tested with ST250DM000-1BC141
     "ST(250|320|500|750)DM00[0-3]-.*",
     "", "",
@@ -3367,7 +3617,7 @@ const drive_settings builtin_knowndrives[] = {
     "ST3(25031|50032|75033|100034)0NS",
     "SN[01]6|"         // http://knowledge.seagate.com/articles/en_US/FAQ/207963en
     "MA(0[^7]|[^0].)", // http://dellfirmware.seagate.com/dell_firmware/DellFirmwareRequest.jsp
-    "",
+    "",                //        ^^^^^^^^^^^^ down (no DNS A record)
     "-F xerrorlba" // tested with ST31000340NS/SN06
   },
   { "Seagate Barracuda ES.2", // buggy firmware (Dell)
@@ -3375,8 +3625,7 @@ const drive_settings builtin_knowndrives[] = {
     "MA07",
     "There are known problems with these drives,\n"
     "AND THIS FIRMWARE VERSION IS AFFECTED,\n"
-    "see the following Seagate web page:\n"
-    "http://dellfirmware.seagate.com/dell_firmware/DellFirmwareRequest.jsp",
+    "contact Dell support for a firmware update.",
     ""
   },
   { "Seagate Barracuda ES.2", // unknown firmware
@@ -3411,9 +3660,9 @@ const drive_settings builtin_knowndrives[] = {
     "", "", ""
   },
   { "Seagate Constellation ES.3", // tested with ST1000NM0033-9ZM173/0001,
-      // ST4000NM0033-9ZM170/SN03, MB1000GCWCV/HPGC
+      // ST4000NM0033-9ZM170/SN03, MB1000GCWCV/HPGC, MB4000GCWDC/HPGE
     "ST[1234]000NM00[35]3-.*|"
-    "MB1000GCWCV", // HP OEM
+    "MB[14]000GCW(CV|DC)", // HP OEM
     "", "", ""
   },
   { "Seagate Constellation CS", // tested with ST3000NC000/CE02, ST3000NC002-1DY166/CN02
@@ -3435,10 +3684,27 @@ const drive_settings builtin_knowndrives[] = {
       // ST8000NM0045-1RL112/NN02
       // ST6000NM0004-1FT17Z/NN01
       // ST4000NM0035-1V4107/TNC3
-    "ST([24568]|10)000NM0[01][0-48][456]-.*", // *[069]4 = 4Kn
+      // ST1000NM0055-1V410C/TN02
+      // ST8000NM0055-1RM112/SN04
+    "ST([1234568]|10)000NM0[01][0-68][456]-.*", // *[069]4 = 4Kn
     "", "", 
     "-v 188,raw16 -v 240,msec24hour32"
   },
+  { "Seagate Enterprise Capacity 3.5 HDD", // V5.1, ms in attribute 9
+    "ST[12]000NM0008-.*", // tested with ST1000NM0008-2F2100/SN01
+    "", "",
+    "-v 9,msec24hour32 -v 188,raw16 -v 240,msec24hour32"
+  },
+  { "Seagate Exos 5E8", // tested with ST8000AS0003-2HH188/0003
+    "ST8000AS0003-.*",
+    "", "",
+    "-v 9,msec24hour32 -v 240,msec24hour32"
+  },
+  { "Seagate Exos X12", // tested with ST12000NM0007-2A1101/SN02
+    "ST12000NM00[01]7-.*", // *17 = SED
+    "", "",
+    "-v 240,msec24hour32"
+  },
   // new models: ST8000VN0002, ST6000VN0021, ST4000VN000
   //             ST8000VN0012, ST6000VN0031, ST4000VN003
   // tested with ST8000VN0002-1Z8112/ZA13YGNF
@@ -3446,12 +3712,26 @@ const drive_settings builtin_knowndrives[] = {
     "ST([234]000VN000|[468]000VN00(02|21|12|31|3))-.*",
     "", "", ""
   },
+  // ST8000NE0001, ST8000NE0011, ST6000VN0001, ST6000VN0011, ST5000VN0001,
+  // ST5000VN0011, ST4000VN0001, ST4000VN0011, ST3000VN0001, ST3000VN0011,
+  // ST2000VN0001, ST2000VN0011
+  // tested with ST8000NE0001-1WN112/PNA2
+  { "Seagate Enterprise NAS HDD",
+    "ST(8000NE|[65432]000VN)00[01]1-.*",
+    "", "", ""
+  },
   // ST10000VN0004, ST8000VN0022, ST6000VN0041, ST4000VN008, ST3000VN007,
   // ST2000VN004, ST1000VN002
-  { "Seagate IronWolf", // tested with ST6000VN0041-2EL11C/SC61
-    "ST(10|8|6|4|3|2|1)000VN00(04|22|41|8|7|2|4)-.*",
+  { "Seagate IronWolf", // tested with ST6000VN0041-2EL11C/SC61,
+      // ST12000VN0007-2GS116/SC60
+    "ST(12|10|8|6|4|3|2|1)000VN00(04|07|22|41|8|7|2|4)-.*",
+    "", "", ""
+  },
+  { "Seagate IronWolf Pro", // tested with ST4000NE0025-2EW107/EN02,
+      // ST8000NE0004-1ZF11G/EN01, ST8000NE0021-2EN112/EN02
+    "ST([24]000NE0025|6000NE0023|8000NE00(04|08|21)|(10|12|14)000NE000[478])-.*",
     "", "", ""
-  },  
+  },
   { "Seagate Archive HDD", // tested with ST8000AS0002-1NA17Z/AR13
     "ST[568]000AS00[01][12]-.*",
     "", "", ""
@@ -3520,8 +3800,25 @@ const drive_settings builtin_knowndrives[] = {
     "ST6[468]022CF",
     "", "", ""
   },
-  { "WD Blue PC SSD", // tested with WDC WDS100T1B0A-00H9H0
-    "WDC WDS...[TG]1B0[AB].*",
+  { "Seagate Nytro XF1230 SATA SSD", // tested with XF1230-1A0480/ST200354
+    "XF1230-1A(0240|0480|0960|1920)",
+    "", "", 
+    "-v 174,raw48,Unexpect_Power_Loss_Ct "
+    "-v 180,raw48,End_to_End_Err_Detect "
+    "-v 183,raw48,SATA_Downshift_Count "
+    "-v 189,raw48,SSD_Health_Flags "
+    "-v 190,raw48,SATA_Error_Ct "
+    "-v 201,raw48,Read_Error_Rate "
+    "-v 231,raw48,SSD_Life_Left_Perc "
+    "-v 234,raw48,Lifetime_Nand_Gb "
+    "-v 241,raw48,Total_Writes_GiB "
+    "-v 242,raw48,Total_Reads_GiB "
+    "-v 245,raw48,Read_Error_Rate "
+  },
+  { "WD Blue and Green SSDs", // tested with WDC WDS250G1B0A-00H9H0/X41000WD,
+      // WDC WDS250G1B0A-00H9H0/X41100WD, WDC WDS100T1B0A-00H9H0,
+      // WDC WDS120G2G0A-00JH30/UE360000, WDC WDS240G2G0A-00JH30/UF300000
+    "WDC WDS((120|240|250|480|500)G|100T)(1B|2G)0[AB]-.*", // *1B* = Blue, *2G* = Green
     "", "",
   //"-v 5,raw48,Reallocated_Sector_Ct " // Reassigned Block Count
   //"-v 9,raw48,Power_On_Hours "
@@ -3683,12 +3980,15 @@ const drive_settings builtin_knowndrives[] = {
       // WDC WD2000FYYZ-01UL1B0/01.01K01, WDC WD2000FYYZ-01UL1B1/01.01K02,
       // WDC WD4000FYYZ-01UL1B2/01.01K03, WD2000FYYX/00.0D1K2,
       // WDC WD1004FBYZ-01YCBB1/RR04
-    "WDC WD([12]004FBYZ|[1-6]00[01M]F[SXY]YZ)-.*|WD2000FYYX",
+      // WD4000FYYZ, WD4000FDYZ, WD3000FYYZ, WD3000FDYZ, WD2000FYYZ, WD2000FDYZ
+      // WD2004FBYZ, WD1004FBYZ
+    "WDC WD((1004|2004)FBYZ|([234]000)FDYZ|[234]000FYYZ|2000FYYX)-.*",
     "", "",
     "-v 16,raw48,Total_LBAs_Read" // WDC WD1004FBYZ-01YCBB1/RR04
   },
   { "Western Digital Se", // tested with WDC WD2000F9YZ-09N20L0/01.01A01
-    "WDC WD(1002|2000|3000|4000)F9YZ-.*",
+    // WD6001F9YZ, WD5001F9YZ, WD4000F9YZ, WD3000F9YZ, WD2000F9YZ, WD1002F9YZ
+    "WDC WD(1002|2000|3000|4000|5001|6001)F9YZ-.*",
     "", "", ""
   },
   { "Western Digital Caviar Green", // tested with WDC WD7500AADS-00M2B0/01.00A01,
@@ -3761,7 +4061,7 @@ const drive_settings builtin_knowndrives[] = {
     "", "", ""
   },
   { "Western Digital VelociRaptor (AF)", // tested with WDC WD1000DHTZ-04N21V0/04.06A00
-    "WDC WD(2500H|5000H|1000D)HTZ-.*",
+    "WDC WD(2500H|5000B|5000H|1000D)HTZ-.*",
     "", "", ""
   },
   { "Western Digital Scorpio EIDE",
@@ -3794,10 +4094,11 @@ const drive_settings builtin_knowndrives[] = {
     "", "", ""
   },
   { "Western Digital Red", // tested with WDC WD10EFRX-68JCSN0/01.01A01,
-      // WDC WD10JFCX-68N6GN0/01.01A01, WDC WD40EFRX-68WT0N0/80.00A80,
-      // WDC WD60EFRX-68MYMN1/82.00A82, WDC WD80EFAX-68LHPN0/83.H0A83,
-      // WDC WD80EFZX-68UW8N0/83.H0A83
-    "WDC WD(7500BFC|10JFC|[1-6]0EFR|80EF[AZ])X-.*",
+      // WDC WD10JFCX-68N6GN0/01.01A01, WDC WD30EFRX-68EUZN0/82.00A82,
+      // WDC WD40EFRX-68WT0N0/80.00A80, WDC WD60EFRX-68MYMN1/82.00A82,
+      // WDC WD80EFAX-68LHPN0/83.H0A83, WDC WD80EFZX-68UW8N0/83.H0A83,
+      // WDC WD80EZZX-11CSGA0/83.H0A03 (My Book 0x1058:0x25ee)
+    "WDC WD(7500BFC|10JFC|[1-6]0EFR|80E[FZ][AZ])X-.*",
     "", "",
     "-v 22,raw48,Helium_Level" // WD80EFZX
   },
@@ -3810,9 +4111,12 @@ const drive_settings builtin_knowndrives[] = {
     "WDC WD[123456]0PURX-.*",
     "", "", ""
   },
-  { "Western Digital Gold", // tested with WDC WD4002FYYZ-01B7CB0/01.01M02
-    "WDC WD(101KR|[68]002FR|4002FY)YZ-.*",
-    "", "", ""
+  { "Western Digital Gold", // tested with WDC WD1005FBYZ-01YCBB2/RR07,
+      // WDC WD2005FBYZ-01YCBB2/RR07, WDC WD4002FYYZ-01B7CB0/01.01M02,
+      // WDC WD8003FRYZ-01JPDB1/01.01H02, WDC WD121KRYZ-01W0RB0/01.01H01
+    "WDC WD([12]005FB|4002FY|6002FR|800[23]FR|1[02]1KR)YZ-.*",
+    "", "",
+    "-v 22,raw48,Helium_Level" // WD121KRYZ
   },
   { "Western Digital Blue Mobile", // tested with WDC WD5000LPVX-08V0TT2/03.01A03,
       // WDC WD20NPVZ-00WFZT0/01.01A01
@@ -3838,7 +4142,7 @@ const drive_settings builtin_knowndrives[] = {
       // WDC WD5000LMVW-11VEDS0/01.01A01 (0x1058:0x0816),
       // WDC WD7500BMVW-11AJGS2/01.01A01,
       // WDC WD10JMVW-11AJGS2/01.01A01 (0x1058:0x10b8),
-      // WDC WD10JMVW-11AJGS4/01.01A01 (0x1058:0x25a2),
+      // WDC WD10JMVW-11AJGS4/01.01A01 (0x1058:0x25a0/25a2),
       // WDC WD10JMVW-11S5XS1/01.01A01,
       // WDC WD10TMVW-11ZSMS5/01.01A01,
       // WDC WD20NMVW-11AV3S2/01.01A01 (0x1058:0x0822),
@@ -3952,9 +4256,9 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
-  { "USB: Buffalo MiniStation; ",
-    "0x0411:0x0(1[df]9|240|251)", // 0x01d9: HD-PCTU2 (0x0108),
-      // 0x01f9: HD-PZU3 (0x0100), 0x0240: HD-PCFU3, 0x0251: HD-PNFU3
+  { "USB: Buffalo; ",
+    "0x0411:0x0(1[df]9|1e7|240|251|27e)", // 0x01d9: HD-PCTU2 (0x0108), 0x01e7: HD-PNTU3,
+      // 0x01f9: HD-PZU3 (0x0100), 0x0240: HD-PCFU3, 0x0251: HD-PNFU3, 0x027e: HD-LC3
     "",
     "",
     "-d sat"
@@ -3983,48 +4287,15 @@ const drive_settings builtin_knowndrives[] = {
     "-d sat"
   },
   // Toshiba
-  { "USB: Toshiba Stor.E Slim USB 3.0; ", // TOSHIBA MQ01UBD100
-    "0x0480:0x0100",
-    "", // 0x0000
-    "",
-    "-d sat"
-  },
-  { "USB: Toshiba Canvio; ", // 0x0210: TOSHIBA MQ03UBB300
-    "0x0480:0x02(01|10)",
-    "",
-    "",
-    "-d sat"
-  },
   { "USB: Toshiba Canvio 500GB; SunPlus",
     "0x0480:0xa004",
     "",
     "",
     "-d usbsunplus"
   },
-  { "USB: Toshiba Canvio Basics; ",
-    "0x0480:0xa00[6d]",
-    "", // 0x0001, 0x0407
+  { "USB: Toshiba; ",
+    "0x0480:0x....",
     "",
-    "-d sat"
-  },
-  { "USB: Toshiba Stor.E; ",
-    "0x0480:0xa00[9ace]",
-    "", // 0x0000 (0xa00e)
-    "",
-    "-d sat"
-  },
-  { "USB: Toshiba Canvio; ",
-    "0x0480:0x(a(007|100|20[027c])|" // 0xa007 TOSHIBA MQ01UBD050,
-              "b207)", // 0xa100: TOSHIBA MQ01UBB200, 0xa200: TOSHIBA MQ01UBD100,
-        // 0xa202: TOSHIBA MQ01UBD050, 0xa207: TOSHIBA MQ01ABD100,
-        // 0xa20c: TOSHIBA MQ01ABB200, 0xb207: TOSHIBA MQ03UBB200
-    "", // 0x0001 (0xa007)
-    "",
-    "-d sat"
-  },
-  { "USB: Toshiba Canvio Desktop; ",
-    "0x0480:0xd0(00|10|11)",
-    "", // 0x0316 (0xd011)
     "",
     "-d sat"
   },
@@ -4128,6 +4399,12 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  { "USB: Samsung Portable SSD T5; ",
+    "0x04e8:0x61f5",
+    "", // 0x0100
+    "",
+    "-d sat"
+  },
   // Sunplus
   { "USB: ; SunPlus",
     "0x04fc:0x0c05",
@@ -4160,6 +4437,12 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbcypress"
   },
+  { "USB: Iomega MDHD500-U; JMicron",
+    "0x059b:0x0274",
+    "", // 0x0000
+    "",
+    "-d usbjmicron,0"
+  },
   { "USB: Iomega MDHD500-U; ",
     "0x059b:0x0275",
     "", // 0x0001
@@ -4282,6 +4565,12 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  { "USB: LaCie Rugged Mini HDD; ",
+    "0x059f:0x106b",
+    "",
+    "",
+    "-d sat"
+  },
   { "USB: LaCie; ", // 0x1070: ASMedia 1053 ?
     "0x059f:0x10(6f|7[05])",
     "", // 6f/70=0x0001, 75=0x0000
@@ -4374,6 +4663,12 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  { "USB: Freecom; ",
+    "0x07ab:0xfc17",
+    "", // 0x0100
+    "",
+    "-d sat"
+  },
   { "USB: Freecom Quattro 3.0; ", // USB3.0+IEEE1394+eSATA->SATA
     "0x07ab:0xfc77",
     "",
@@ -4482,135 +4777,14 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbcypress"
   },
-  { "USB: Seagate FreeAgent Go; ",
-    "0x0bc2:0x2(000|100|101)",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate FreeAgent Go FW; ",
-    "0x0bc2:0x2200",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate Expansion Portable; ",
-    "0x0bc2:0x23(00|12|1a|20|21|22)",
-    "", // 12=0x0219, 1a=0x0707, 22=0x0000
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate FreeAgent Desktop; ",
-    "0x0bc2:0x3000",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate FreeAgent Desk; ",
-    "0x0bc2:0x3001",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate FreeAgent Desk; ", // 1TB
-    "0x0bc2:0x3008",
-    "",
-    "",
-    "-d sat,12"
-  },
-  { "USB: Seagate FreeAgent XTreme; ",
-    "0x0bc2:0x3101",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate Expansion Desktop; ",
-    "0x0bc2:0x33(00|1[2a]|2[012]|3[02])", // 1a=5TB, 21=4TB
-    "", // 1a=0x0909
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate FreeAgent GoFlex USB 2.0; ",
-    "0x0bc2:0x502[01]",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate FreeAgent GoFlex USB 3.0; ",
-    "0x0bc2:0x5030",
-    "", // 0x0100
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate FreeAgent GoFlex USB 3.0; ",
-    "0x0bc2:0x5031",
-    "",
-    "",
-    "-d sat,12"
-  },
   { "USB: Seagate FreeAgent; ",
-    "0x0bc2:0x5040",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate FreeAgent GoFlex USB 3.0; ", // 2TB
-    "0x0bc2:0x507[01]",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate FreeAgent GoFlex Desk USB 3.0; ", // 3TB
-    "0x0bc2:0x50a1",
-    "",
-    "",
-    "-d sat,12" // "-d sat" does not work (ticket #151)
-  },
-  { "USB: Seagate FreeAgent GoFlex Desk USB 3.0; ", // 2TB, 4TB
-    "0x0bc2:0x50a[57]",
-    "", // 0x0100
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate FreeAgent / Maxtor D3; ",
-    // 0x6121: Seagate FreeAgent GoFlex Pro for Mac (0x0148)
-    // 0x6123: Maxtor D3 Station 3TB (0x0209)
-    // 0x6126: Maxtor D3 Station 5TB (0x0209)
-    "0x0bc2:0x612[136]",
-    "",
+    "0x0bc2:0x(3008|50(31|a1))",
     "",
-    "-d sat"
-  },
-  { "USB: Maxtor M3 Portable; ",
-    "0x0bc2:0x61b7", // Maxtor M3 Portable 4TB (0x0000)
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate Backup Plus USB 3.0; ", // 1TB
-    "0x0bc2:0xa0[01]3",
-    "", // 0x0100
     "",
-    "-d sat"
+    "-d sat,12" // 0x50a1: "-d sat" does not work (ticket #151)
   },
-  { "USB: Seagate Backup Plus Desktop USB 3.0; ",
-    "0x0bc2:0xa(0a[14]|b3[14])", // 4TB, 3TB (8 LBA/1 PBA offset), 5TB, ?
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate Slim Portable Drive; ", // SRD00F1
-    "0x0bc2:0xab00",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: Seagate Backup Plus USB 3.0; ",
-    // 0xab1e: Seagate Backup Plus 4TB
-    // 0xab24: Seagate Backup Plus Slim (0x0100) (ticket #443)
-    // 0xab25: Seagate Backup Plus for Mac (0x0100)
-    // 0xab38: Seagate Backup Plus 8TB (0x0100) (ticket #786)
-    "0x0bc2:0xab(1e|2[01458]|38)",
+  { "USB: Seagate; ",
+    "0x0bc2:0x....",
     "",
     "",
     "-d sat"
@@ -4719,28 +4893,9 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbcypress"
   },
-  { "USB: WD Elements Desktop; ",
-    "0x1058:0x10(01|03|21|7c)",
-    "", // 01=0x0104, 03=0x0175, 21=0x2002, 7c=0x1065
+  { "USB: Western Digital; ",
+    "0x1058:0x....",
     "",
-    "-d sat"
-  },
-  { "USB: WD Elements / My Passport; ", // 2.5" with integrated USB
-    "0x1058:0x(07(0[245a]|1a|30|4[0128a]|a[8e])|"
-              "08(1[06]|2[02]|3[37a])|"
-              "10(10|23|4[28]|7d|a[28]|b8)|"
-              "25(9[9df]|a[12]|e2|fa))",
-    "", // 0x0822=0x1007, 37=0x1072
-        // 0x1010=0x0105, 7d=0x1073, a8=0x1042, b8=0x1007
-        // 0x2599=0x1012, 9d/a1=0x1005, 9f/a2=0x1004
-    "",
-    "-d sat"
-  },
-  { "USB: WD My Book / Easystore; ", // 3.5"
-    "0x1058:0x(09(0[06]|10)|11(0[01245]|1[0d]|30|40)|1230|25(a3|fb))",
-    "", // 0x0906=0x0012, 10=0x0106
-        // 0x1100/01=0x0165, 02=0x1028, 10=0x1030, 1d=0x1020, 30=0x1012, 40=0x1003
-        // 0x1230=0x1065, 0x25a3=0x1013, 0x25fb=0x3004
     "",
     "-d sat"
   },
@@ -4753,9 +4908,9 @@ const drive_settings builtin_knowndrives[] = {
   },
   // ADATA
   { "USB: ADATA; ",
-    "0x125f:0xa(11|31|35|15)a", // 0xa11a: Classic CH11 1TB, 0xa31a: HV620 2TB (0x0100)
-    "", // 0xa35a: HD650 2TB (0x6503), 0xa15a: HD710 1TB
-    "",
+    "0x125f:0xa(11|13|15|31|35|75)a", // 0xa11a: Classic CH11 1TB, 0xa13a: NH13 1TB,
+    "", // 0xa15a: HD710 1TB, 0xa31a: HV620 2TB (0x0100), 0xa35a: HD650 2TB (0x6503),
+    "", // 0xa75a: HD710P 4TB
     "-d sat"
   },
   { "USB: ADATA; Cypress",
@@ -4765,66 +4920,24 @@ const drive_settings builtin_knowndrives[] = {
     "-d usbcypress"
   },
   // Initio
-  { "USB: ; Initio 316000",
-    "0x13fd:0x0540",
-    "",
-    "",
-    "" // unsupported
-  },
-  { "USB: ; Initio", // Thermaltake BlacX
-    "0x13fd:0x0840",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: ; Initio", // USB->SATA+PATA, Chieftec CEB-25I
-    "0x13fd:0x1040",
-    "", // 0x0106
-    "",
-    "" // unsupported
-  },
-  { "USB: ; Initio 6Y120L0", // CoolerMaster XCraft RX-3HU
-    "0x13fd:0x1150",
-    "",
-    "",
+  { "USB: ; Initio",
+    "0x13fd:0x(054|1(04|15))0", // 0x0540: Initio 316000
+    "", // 0x1040 (0x0106): USB->SATA+PATA, Chieftec CEB-25I
+    "", // 0x1150: Initio 6Y120L0, CoolerMaster XCraft RX-3HU
     "" // unsupported
   },
-  { "USB: ; Initio", // USB->SATA
-    "0x13fd:0x1240",
-    "", // 0x0104
-    "",
-    "-d sat"
-  },
-  { "USB: ; Initio", // USB+SATA->SATA
-    "0x13fd:0x1340",
-    "", // 0x0208
-    "",
-    "-d sat"
-  },
   { "USB: ; Initio",
     "0x13fd:0x16[45]0",
     "", // 0x1640: 0x0864, 0x1650: 0x0436
     "",
     "-d sat,12" // some SMART commands fail, see ticket #295
   },
-  { "USB: Intenso Memory Station 2,5\"; Initio",
-    "0x13fd:0x1840",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: ; Initio", // NexStar CX USB enclosure
-    "0x13fd:0x1e40",
+  { "USB: ; Initio",
+    "0x13fd:0x....",
     "",
     "",
     "-d sat"
   },
-  { "USB: ; Initio",
-    "0x13fd:0x39[124]0", // 0x3910: Seagate Expansion Portable SRD00F1 (0x0100)
-    "", // 0x3920: ezDISK EZ370 (0x0205)
-    "", // 0x3940: MS-TECH LU-275S (0x0306)
-    "-d sat"
-  },
   // Super Top
   { "USB: Super Top generic enclosure; ",
     "0x14cd:0x6116",
@@ -4842,9 +4955,9 @@ const drive_settings builtin_knowndrives[] = {
   { "USB: ; JMicron JMS539", // USB2/3->SATA (new firmware)
     "0x152d:0x0539",
     "0x020[56]|"   //  2.05, ZTC USB 3.0 enclosure (ticket #338)
-    "0x28(03|12)", // 28.03, Mediasonic ProBox HF2-SU3S2 Rev 2 (port multiplier, ticket #504)
-    "",            // 28.12, Mediasonic ProBox H82-SU3S2 (port multiplier)
-    "-d sat"
+    "0x28(01|03|12)", // 28.01, DATOptic U3eSATA (USB3.0 bridge with port multiplier)
+    "",               // 28.03, Mediasonic ProBox HF2-SU3S2 Rev 2 (port multiplier, ticket #504)
+    "-d sat"          // 28.12, Mediasonic ProBox H82-SU3S2 (port multiplier)
   },
   { "USB: ; JMicron ", // USB->SATA->4xSATA (port multiplier)
     "0x152d:0x0551",   // JMS539? (old firmware may use 0x152d:0x0539, ticket #552)
@@ -4852,6 +4965,18 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbjmicron,x"
   },
+  { "USB: ; JMicron",
+    "0x152d:0x0561",
+    "", // 0x0003, ODROID CloudShell 2
+    "",
+    "-d sat"
+  },
+  { "USB: ; JMicron JM562", // USB2/3+eSATA->2xSATA, USB2/3->3xSATA (RAID0/1)
+    "0x152d:0x0562",
+    "", // 0x0106, Fantec QB-X2US3R (ticket #966)
+    "", // only ATA IDENTIFY works, SMART commands don't work
+    "-d sat"
+  },
   { "USB: ; JMicron", // USB2/3->2xSATA
     "0x152d:0x0565",
     "", // 0x9114, Akasa DuoDock X (ticket #607)
@@ -4876,6 +5001,12 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  { "USB: ; JMicron JMS583", // USB->PCIe (NVMe)
+    "0x152d:0x0583",
+    "",
+    "",
+    "-d sntjmicron#please_try" // TODO: Remove '#please_try' when no longer EXPERIMENTAL
+  },
   { "USB: OCZ THROTTLE OCZESATATHR8G; JMicron JMF601",
     "0x152d:0x0602",
     "",
@@ -4942,6 +5073,12 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d usbjmicron,x"
   },
+  { "USB: ; JMicron JMS567", // USB3->SATA
+    "0x152d:0x2567",
+    "", // 0x0117, Chieftec CEB-7053S
+    "",
+    "-d sat"
+  },
   { "USB: ; JMicron",
     "0x152d:0x2590",
     "", // 0x0x8105 (ticket #550)
@@ -4960,6 +5097,12 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat"
   },
+  { "USB: ; JMicron",
+    "0x152d:0x578e",
+    "", // 0x1402, Intenso Memory Center
+    "",
+    "-d sat"
+  },
   { "USB: ; JMicron JMS561U", // USB3->2xSATA
     "0x152d:0x8561",
     "", // 0x0107
@@ -4967,38 +5110,12 @@ const drive_settings builtin_knowndrives[] = {
     "-d sat"
   },
   // ASMedia
-  { "USB: ; ASMedia ASM1053/1153/1351",
-    "0x174c:0x1([01]53|351)",
+  { "USB: ; ASMedia",
+    "0x174c:0x....",
     "",
     "",
     "-d sat"
   },
-  { "USB: ; ASMedia ASM1051",
-    "0x174c:0x5106", // 0x174c:0x55aa after firmware update
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: ; ASMedia AS2105", // Icy Box IB-AC603A-U3
-    "0x174c:0x5136",
-    "", // 0x0001
-    "",
-    "-d sat"
-  },
-  { "USB: ; ASMedia", // Vantec NexStar USB 3.0 & SATA dual drive dock
-    "0x174c:0x5516",
-    "",
-    "",
-    "-d sat"
-  },
-  { "USB: ; ASMedia", // MEDION HDDrive-n-GO, LaCie Rikiki USB 3.0,
-      // Silicon Power Armor A80 (ticket #237)
-      // reported as unsupported: BYTECC T-200U3, Kingwin USB 3.0 docking station
-    "0x174c:0x55aa",
-    "", // 0x0100
-    "",
-    "-d sat"
-  },
   // LucidPort
   { "USB: ; LucidPORT USB300", // RaidSonic ICY BOX IB-110StU3-B, Sharkoon SATA QuickPort H3
     "0x1759:0x500[02]", // 0x5000: USB 2.0, 0x5002: USB 3.0
@@ -5055,8 +5172,8 @@ const drive_settings builtin_knowndrives[] = {
     "",
     "-d sat,12"
   },
-  { "USB: Verbatim External Hard Drive; ", // 3TB USB 3.0
-    "0x18a5:0x0400",
+  { "USB: Verbatim External Hard Drive; ", // USB 3.0
+    "0x18a5:0x040[08]", // 0=3TB, 8=1TB
     "",
     "",
     "-d sat"
@@ -5121,6 +5238,19 @@ const drive_settings builtin_knowndrives[] = {
     "", // 0x0507, Intenso 2,5" Memory Case 2TB USB3
     "-d sat"
   },
+  { "USB: ; VIA VL715", // USB2/3->SATA
+    "0x2109:0x0715",
+    "", // 0x0336
+    "",
+    "-d sat"
+  },
+  // Transcend (?)
+  { "USB: Transcend ESD400; ",
+    "0x2174:0x2000", // TS256GESD400K
+    "", // 0x1000
+    "",
+    "-d sat"
+  },
   // 0x2537 (?)
   { "USB: ; ", // USB 3.0
     "0x2537:0x106[68]", // 0x1066: Orico 2599US3, 0x1068: Fantec ER-35U3
index b789c722ce0b6f3bf9f0f0cd103c4334478f62a0..73f304b0e2a57618f447b41d75a3e7c4883fa983 100755 (executable)
@@ -1,10 +1,28 @@
 #! /bin/sh
 
 # Send mail
-echo "$SMARTD_MESSAGE" | mail -s "$SMARTD_FAILTYPE" "$SMARTD_ADDRESS"
+if which mail >/dev/null 2>&1
+then
+  echo "$SMARTD_MESSAGE" | mail -s "$SMARTD_FAILTYPE" "$SMARTD_ADDRESS"
+fi
 
 # Notify desktop user
-MESSAGE="WARNING: Your hard drive is failing"
+MESSAGE="SMART Disk monitor:"
+case "$SMARTD_FAILTYPE" in
+    "EmailTest"|"Health"|"Temperature"|"Usage")
+        ;;
+    *)
+#       "CurrentPendingSector",       // 10
+#       "OfflineUncorrectableSector", // 11
+#       "FailedReadSmartErrorLog",    // 7
+#       "ErrorCount",                 // 4
+#       "FailedReadSmartData",        // 6
+#       "FailedHealthCheck",          // 5
+#       "FailedOpenDevice",           // 9
+#       "SelfTest",                   // 3
+#       "FailedReadSmartSelfTestLog", // 8
+      exit 0
+esac
 
 # direct write to terminals, do not use 'wall', because we don't want its ugly header
 for t in $(who | awk '{ print $2; }' | grep -e '^tty' -e '^pts/')
index baa211a46612e9d9de0af67fa9795b64b0e02262..e35f7cbf36b0dd47515776b70c0ebcc722032824 100644 (file)
@@ -1,22 +1,12 @@
 # Home page: http://www.smartmontools.org
 #
-# $Id: README 4120 2015-08-27 16:12:21Z samm2 $
+# $Id: README 4760 2018-08-19 18:45:53Z chrfranke $
 #
-# Copyright (C) 2003-8 Bruce Allen <smartmontools-support@lists.sourceforge.net>
-# Copyright (C) 2009-14 Christian Franke <smartmontools-support@lists.sourceforge.net>
+# Copyright (C) 2003-08 Bruce Allen
+# Copyright (C) 2009-18 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, see <http://www.gnu.org/licenses/>.
+# SPDX-License-Identifier: GPL-2.0-or-later
 #
-# 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 shell scripts, that are intended for
 use with the
@@ -30,7 +20,7 @@ smartd and 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>,
+please email them to <smartmontools-support@listi.jpberlin.de>,
 and include a brief description to use below.
 
 The files contained in this directory are:
index f5761430ea44cbd1613aa7d05cf33694a1e810f3..1479254c093e6b0f23270e6dc81ba8966c6bc92a 100644 (file)
@@ -148,8 +148,10 @@ struct nvme_pt_command {
 #include <dev/nvme/nvme.h>
 #endif
 
+#if __FreeBSD_version < 1200058
 #define nvme_completion_is_error(cpl)                                  \
        ((cpl)->status.sc != 0 || (cpl)->status.sct != 0)
+#endif
 
 #define NVME_CTRLR_PREFIX      "/dev/nvme"
 #define NVME_NS_PREFIX         "ns"
diff --git a/getopt/bits/getopt_core.h b/getopt/bits/getopt_core.h
new file mode 100644 (file)
index 0000000..a13838f
--- /dev/null
@@ -0,0 +1,96 @@
+/* Declarations for getopt (basic, portable features only).
+   Copyright (C) 1989-2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library and is also part of gnulib.
+   Patches to this file should be submitted to both projects.
+
+   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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _GETOPT_CORE_H
+#define _GETOPT_CORE_H 1
+
+/* This header should not be used directly; include getopt.h or
+   unistd.h instead.  Unlike most bits headers, it does not have
+   a protective #error, because the guard macro for getopt.h in
+   gnulib is not fixed.  */
+
+__BEGIN_DECLS
+
+/* For communication from 'getopt' to the caller.
+   When 'getopt' finds an option that takes an argument,
+   the argument value is returned here.
+   Also, when 'ordering' is RETURN_IN_ORDER,
+   each non-option ARGV-element is returned here.  */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+   This is used for communication to and from the caller
+   and for communication between successive calls to 'getopt'.
+
+   On entry to 'getopt', zero means this is the first call; initialize.
+
+   When 'getopt' returns -1, this is the index of the first of the
+   non-option elements that the caller should itself scan.
+
+   Otherwise, 'optind' communicates from one call to the next
+   how much of ARGV has been scanned so far.  */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message 'getopt' prints
+   for unrecognized options.  */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized.  */
+
+extern int optopt;
+
+/* Get definitions and prototypes for functions to process the
+   arguments in ARGV (ARGC of them, minus the program name) for
+   options given in OPTS.
+
+   Return the option character from OPTS just read.  Return -1 when
+   there are no more options.  For unrecognized options, or options
+   missing arguments, 'optopt' is set to the option letter, and '?' is
+   returned.
+
+   The OPTS string is a list of characters which are recognized option
+   letters, optionally followed by colons, specifying that that letter
+   takes an argument, to be placed in 'optarg'.
+
+   If a letter in OPTS is followed by two colons, its argument is
+   optional.  This behavior is specific to the GNU 'getopt'.
+
+   The argument '--' causes premature termination of argument
+   scanning, explicitly telling 'getopt' that there are no more
+   options.
+
+   If OPTS begins with '-', then non-option arguments are treated as
+   arguments to the option '\1'.  This behavior is specific to the GNU
+   'getopt'.  If OPTS begins with '+', or POSIXLY_CORRECT is set in
+   the environment, then do not permute arguments.
+
+   For standards compliance, the 'argv' argument has the type
+   char *const *, but this is inaccurate; if argument permutation is
+   enabled, the argv array (not the strings it points to) must be
+   writable.  */
+
+extern int getopt (int ___argc, char *const *___argv, const char *__shortopts)
+       __THROW __nonnull ((2, 3));
+
+__END_DECLS
+
+#endif /* getopt_core.h */
diff --git a/getopt/bits/getopt_ext.h b/getopt/bits/getopt_ext.h
new file mode 100644 (file)
index 0000000..b4c68f1
--- /dev/null
@@ -0,0 +1,77 @@
+/* Declarations for getopt (GNU extensions).
+   Copyright (C) 1989-2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library and is also part of gnulib.
+   Patches to this file should be submitted to both projects.
+
+   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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _GETOPT_EXT_H
+#define _GETOPT_EXT_H 1
+
+/* This header should not be used directly; include getopt.h instead.
+   Unlike most bits headers, it does not have a protective #error,
+   because the guard macro for getopt.h in gnulib is not fixed.  */
+
+__BEGIN_DECLS
+
+/* Describe the long-named options requested by the application.
+   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+   of 'struct option' terminated by an element containing a name which is
+   zero.
+
+   The field 'has_arg' is:
+   no_argument         (or 0) if the option does not take an argument,
+   required_argument   (or 1) if the option requires an argument,
+   optional_argument   (or 2) if the option takes an optional argument.
+
+   If the field 'flag' is not NULL, it points to a variable that is set
+   to the value given in the field 'val' when the option is found, but
+   left unchanged if the option is not found.
+
+   To have a long-named option do something other than set an 'int' to
+   a compiled-in constant, such as set a value from 'optarg', set the
+   option's 'flag' field to zero and its 'val' field to a nonzero
+   value (the equivalent single-letter option character, if there is
+   one).  For long options that have a zero 'flag' field, 'getopt'
+   returns the contents of the 'val' field.  */
+
+struct option
+{
+  const char *name;
+  /* has_arg can't be an enum because some compilers complain about
+     type mismatches in all the code that assumes it is an int.  */
+  int has_arg;
+  int *flag;
+  int val;
+};
+
+/* Names for the values of the 'has_arg' field of 'struct option'.  */
+
+#define no_argument            0
+#define required_argument      1
+#define optional_argument      2
+
+extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv,
+                       const char *__shortopts,
+                       const struct option *__longopts, int *__longind)
+       __THROW __nonnull ((2, 3));
+extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv,
+                            const char *__shortopts,
+                            const struct option *__longopts, int *__longind)
+       __THROW __nonnull ((2, 3));
+
+__END_DECLS
+
+#endif /* getopt_ext.h */
index b61ab8e014f3b5e6a274b8a0307de4b6aa4eb94e..8e798a108c38fa911bdcd0c99aafec854f06c041 100644 (file)
@@ -1,10 +1,7 @@
 /* Getopt for GNU.
-   NOTE: getopt is now part of the C library, so if you don't know what
-   "Keep this file name-space clean" means, talk to drepper@gnu.org
-   before changing it!
-   Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001,2002
-       Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
+   Copyright (C) 1987-2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library and is also part of gnulib.
+   Patches to this file should be submitted to both projects.
 
    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    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., 51 Franklin Street, Fifth Floor, Boston,
-   MA 02110-1301 USA.  */
-
-/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
-   Ditto for AIX 3.2 and <stdlib.h>.  */
-#ifndef _NO_PROTO
-# define _NO_PROTO
-#endif
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+\f
+#if !defined(_LIBC) && !defined(_GETOPT_STANDALONE)
+# include <config.h>
 #endif
 
-#if !defined __STDC__ || !__STDC__
-/* This is a separate conditional since some stdc systems
-   reject `defined (const)'.  */
-# ifndef const
-#  define const
-# endif
-#endif
+#include "getopt.h"
 
 #include <stdio.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
-   actually compiling the library itself.  This code is part of the GNU C
-   Library, but also included in many other GNU distributions.  Compiling
-   and linking in this code is a waste when using the GNU C library
-   (especially if it is a shared library).  Rather than having every GNU
-   program understand `configure --with-gnu-libc' and omit the object files,
-   it is simpler to just do this in the source for each such file.  */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
-# include <gnu-versions.h>
-# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#  define ELIDE_CODE
-# endif
+#include <stdlib.h>
+#include <string.h>
+#ifndef _GETOPT_STANDALONE
+#include <unistd.h>
 #endif
 
-#ifndef ELIDE_CODE
-
-
-/* This needs to come after some library #include
-   to get __GNU_LIBRARY__ defined.  */
-#ifdef __GNU_LIBRARY__
-/* Don't include stdlib.h for non-GNU C libraries because some of them
-   contain conflicting prototypes for getopt.  */
-# include <stdlib.h>
-# include <unistd.h>
-#endif /* GNU C library.  */
-
-#ifdef VMS
-# include <unixlib.h>
-# if HAVE_STRING_H - 0
-#  include <string.h>
-# endif
+#ifdef _LIBC
+/* When used as part of glibc, error printing must be done differently
+   for standards compliance.  getopt is not a cancellation point, so
+   it must not call functions that are, and it is specified by an
+   older standard than stdio locking, so it must not refer to
+   functions in the "user namespace" related to stdio locking.
+   Finally, it must use glibc's internal message translation so that
+   the messages are looked up in the proper text domain.  */
+# include <libintl.h>
+# define fprintf __fxprintf_nocancel
+# define flockfile(fp) _IO_flockfile (fp)
+# define funlockfile(fp) _IO_funlockfile (fp)
+#else
+#ifndef _GETOPT_STANDALONE
+# include "gettext.h"
+# define _(msgid) gettext (msgid)
+#else
+# define _(msgid) (msgid)
 #endif
-
-#ifndef _
-/* This is for other GNU distributions with internationalized messages.  */
-# if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
-#  include <libintl.h>
-#  ifndef _
-#   define _(msgid)    gettext (msgid)
-#  endif
-# else
-#  define _(msgid)     (msgid)
-# endif
-# if defined _LIBC && defined USE_IN_LIBIO
-#  include <wchar.h>
+/* When used standalone, flockfile and funlockfile might not be
+   available.  */
+# ifndef _POSIX_THREAD_SAFE_FUNCTIONS
+#  define flockfile(fp) /* nop */
+#  define funlockfile(fp) /* nop */
 # endif
-#endif
-
-#ifndef attribute_hidden
-# define attribute_hidden
-#endif
-
-/* This version of `getopt' appears to the caller like standard Unix `getopt'
-   but it behaves differently for the user, since it allows the user
-   to intersperse the options with the other arguments.
-
-   As `getopt' works, it permutes the elements of ARGV so that,
-   when it is done, all the options precede everything else.  Thus
-   all application programs are extended to handle flexible argument order.
-
-   Setting the environment variable POSIXLY_CORRECT disables permutation.
-   Then the behavior is completely standard.
-
-   GNU application programs can use a third alternative mode in which
-   they can distinguish the relative order of options and other arguments.  */
-
-#include "getopt.h"
-
-/* For communication from `getopt' to the caller.
-   When `getopt' finds an option that takes an argument,
+/* When used standalone, do not attempt to use alloca.  */
+# define __libc_use_alloca(size) 0
+# undef alloca
+# define alloca(size) (abort (), (void *)0)
+#endif
+
+/* This implementation of 'getopt' has three modes for handling
+   options interspersed with non-option arguments.  It can stop
+   scanning for options at the first non-option argument encountered,
+   as POSIX specifies.  It can continue scanning for options after the
+   first non-option argument, but permute 'argv' as it goes so that,
+   after 'getopt' is done, all the options precede all the non-option
+   arguments and 'optind' points to the first non-option argument.
+   Or, it can report non-option arguments as if they were arguments to
+   the option character '\x01'.
+
+   The default behavior of 'getopt_long' is to permute the argument list.
+   When this implementation is used standalone, the default behavior of
+   'getopt' is to stop at the first non-option argument, but when it is
+   used as part of GNU libc it also permutes the argument list.  In both
+   cases, setting the environment variable POSIXLY_CORRECT to any value
+   disables permutation.
+
+   If the first character of the OPTSTRING argument to 'getopt' or
+   'getopt_long' is '+', both functions will stop at the first
+   non-option argument.  If it is '-', both functions will report
+   non-option arguments as arguments to the option character '\x01'.  */
+
+#include "getopt_int.h"
+
+/* For communication from 'getopt' to the caller.
+   When 'getopt' finds an option that takes an argument,
    the argument value is returned here.
-   Also, when `ordering' is RETURN_IN_ORDER,
+   Also, when 'ordering' is RETURN_IN_ORDER,
    each non-option ARGV-element is returned here.  */
 
 char *optarg;
 
 /* Index in ARGV of the next element to be scanned.
    This is used for communication to and from the caller
-   and for communication between successive calls to `getopt'.
+   and for communication between successive calls to 'getopt'.
 
-   On entry to `getopt', zero means this is the first call; initialize.
+   On entry to 'getopt', zero means this is the first call; initialize.
 
-   When `getopt' returns -1, this is the index of the first of the
+   When 'getopt' returns -1, this is the index of the first of the
    non-option elements that the caller should itself scan.
 
-   Otherwise, `optind' communicates from one call to the next
+   Otherwise, 'optind' communicates from one call to the next
    how much of ARGV has been scanned so far.  */
 
 /* 1003.2 says this must be 1 before any call.  */
 int optind = 1;
 
-/* Formerly, initialization of getopt depended on optind==0, which
-   causes problems with re-calling getopt as programs generally don't
-   know that. */
-
-int __getopt_initialized attribute_hidden;
-
-/* The next char to be scanned in the option-element
-   in which the last option character we returned was found.
-   This allows us to pick up the scan where we left off.
-
-   If this is zero, or a null string, it means resume the scan
-   by advancing to the next ARGV-element.  */
-
-static char *nextchar;
-
 /* Callers store zero here to inhibit the error message
    for unrecognized options.  */
 
@@ -160,155 +119,25 @@ int opterr = 1;
 
 int optopt = '?';
 
-/* Describe how to deal with options that follow non-option ARGV-elements.
-
-   If the caller did not specify anything,
-   the default is REQUIRE_ORDER if the environment variable
-   POSIXLY_CORRECT is defined, PERMUTE otherwise.
-
-   REQUIRE_ORDER means don't recognize them as options;
-   stop option processing when the first non-option is seen.
-   This is what Unix does.
-   This mode of operation is selected by either setting the environment
-   variable POSIXLY_CORRECT, or using `+' as the first character
-   of the list of option characters.
-
-   PERMUTE is the default.  We permute the contents of ARGV as we scan,
-   so that eventually all the non-options are at the end.  This allows options
-   to be given in any order, even with programs that were not written to
-   expect this.
-
-   RETURN_IN_ORDER is an option available to programs that were written
-   to expect options and other ARGV-elements in any order and that care about
-   the ordering of the two.  We describe each non-option ARGV-element
-   as if it were the argument of an option with character code 1.
-   Using `-' as the first character of the list of option characters
-   selects this mode of operation.
-
-   The special argument `--' forces an end of option-scanning regardless
-   of the value of `ordering'.  In the case of RETURN_IN_ORDER, only
-   `--' can cause `getopt' to return -1 with `optind' != ARGC.  */
-
-static enum
-{
-  REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
-} ordering;
-
-/* Value of POSIXLY_CORRECT environment variable.  */
-static char *posixly_correct;
-\f
-#ifdef __GNU_LIBRARY__
-/* We want to avoid inclusion of string.h with non-GNU libraries
-   because there are many ways it can cause trouble.
-   On some systems, it contains special magic macros that don't work
-   in GCC.  */
-# include <string.h>
-# define my_index      strchr
-#else
-
-# if HAVE_STRING_H
-#  include <string.h>
-# else
-#  include <strings.h>
-# endif
-
-/* Avoid depending on library functions or files
-   whose names are inconsistent.  */
-
-#ifndef getenv
-extern char *getenv ();
-#endif
-
-static char *
-my_index (str, chr)
-     const char *str;
-     int chr;
-{
-  while (*str)
-    {
-      if (*str == chr)
-       return (char *) str;
-      str++;
-    }
-  return 0;
-}
+/* Keep a global copy of all internal members of getopt_data.  */
 
-/* If using GCC, we can safely declare strlen this way.
-   If not using GCC, it is ok not to declare it.  */
-#ifdef __GNUC__
-/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
-   That was relevant to code that was here before.  */
-# if (!defined __STDC__ || !__STDC__) && !defined strlen
-/* gcc with -traditional declares the built-in strlen to return int,
-   and has done so at least since version 2.4.5. -- rms.  */
-extern int strlen (const char *);
-# endif /* not __STDC__ */
-#endif /* __GNUC__ */
-
-#endif /* not __GNU_LIBRARY__ */
+static struct _getopt_data getopt_data;
 \f
-/* Handle permutation of arguments.  */
-
-/* Describe the part of ARGV that contains non-options that have
-   been skipped.  `first_nonopt' is the index in ARGV of the first of them;
-   `last_nonopt' is the index after the last of them.  */
-
-static int first_nonopt;
-static int last_nonopt;
-
-#ifdef _LIBC
-/* Stored original parameters.
-   XXX This is no good solution.  We should rather copy the args so
-   that we can compare them later.  But we must not use malloc(3).  */
-extern int __libc_argc;
-extern char **__libc_argv;
-
-/* Bash 2.0 gives us an environment variable containing flags
-   indicating ARGV elements that should not be considered arguments.  */
-
-# ifdef USE_NONOPTION_FLAGS
-/* Defined in getopt_init.c  */
-extern char *__getopt_nonoption_flags;
-
-static int nonoption_flags_max_len;
-static int nonoption_flags_len;
-# endif
-
-# ifdef USE_NONOPTION_FLAGS
-#  define SWAP_FLAGS(ch1, ch2) \
-  if (nonoption_flags_len > 0)                                               \
-    {                                                                        \
-      char __tmp = __getopt_nonoption_flags[ch1];                            \
-      __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2];         \
-      __getopt_nonoption_flags[ch2] = __tmp;                                 \
-    }
-# else
-#  define SWAP_FLAGS(ch1, ch2)
-# endif
-#else  /* !_LIBC */
-# define SWAP_FLAGS(ch1, ch2)
-#endif /* _LIBC */
-
 /* Exchange two adjacent subsequences of ARGV.
    One subsequence is elements [first_nonopt,last_nonopt)
    which contains all the non-options that have been skipped so far.
    The other is elements [last_nonopt,optind), which contains all
    the options processed since those non-options were skipped.
 
-   `first_nonopt' and `last_nonopt' are relocated so that they describe
+   'first_nonopt' and 'last_nonopt' are relocated so that they describe
    the new indices of the non-options in ARGV after they are moved.  */
 
-#if defined __STDC__ && __STDC__
-static void exchange (char **);
-#endif
-
 static void
-exchange (argv)
-     char **argv;
+exchange (char **argv, struct _getopt_data *d)
 {
-  int bottom = first_nonopt;
-  int middle = last_nonopt;
-  int top = optind;
+  int bottom = d->__first_nonopt;
+  int middle = d->__last_nonopt;
+  int top = d->optind;
   char *tem;
 
   /* Exchange the shorter segment with the far end of the longer segment.
@@ -316,35 +145,13 @@ exchange (argv)
      It leaves the longer segment in the right place overall,
      but it consists of two parts that need to be swapped next.  */
 
-#if defined _LIBC && defined USE_NONOPTION_FLAGS
-  /* First make sure the handling of the `__getopt_nonoption_flags'
-     string can work normally.  Our top argument must be in the range
-     of the string.  */
-  if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
-    {
-      /* We must extend the array.  The user plays games with us and
-        presents new arguments.  */
-      char *new_str = malloc (top + 1);
-      if (new_str == NULL)
-       nonoption_flags_len = nonoption_flags_max_len = 0;
-      else
-       {
-         memset (__mempcpy (new_str, __getopt_nonoption_flags,
-                            nonoption_flags_max_len),
-                 '\0', top + 1 - nonoption_flags_max_len);
-         nonoption_flags_max_len = top + 1;
-         __getopt_nonoption_flags = new_str;
-       }
-    }
-#endif
-
   while (top > middle && middle > bottom)
     {
       if (top - middle > middle - bottom)
        {
          /* Bottom segment is the short one.  */
          int len = middle - bottom;
-         register int i;
+         int i;
 
          /* Swap it with the top part of the top segment.  */
          for (i = 0; i < len; i++)
@@ -352,7 +159,6 @@ exchange (argv)
              tem = argv[bottom + i];
              argv[bottom + i] = argv[top - (middle - bottom) + i];
              argv[top - (middle - bottom) + i] = tem;
-             SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
            }
          /* Exclude the moved bottom segment from further swapping.  */
          top -= len;
@@ -361,7 +167,7 @@ exchange (argv)
        {
          /* Top segment is the short one.  */
          int len = top - middle;
-         register int i;
+         int i;
 
          /* Swap it with the bottom part of the bottom segment.  */
          for (i = 0; i < len; i++)
@@ -369,7 +175,6 @@ exchange (argv)
              tem = argv[bottom + i];
              argv[bottom + i] = argv[middle + i];
              argv[middle + i] = tem;
-             SWAP_FLAGS (bottom + i, middle + i);
            }
          /* Exclude the moved top segment from further swapping.  */
          bottom += len;
@@ -378,78 +183,244 @@ exchange (argv)
 
   /* Update records for the slots the non-options now occupy.  */
 
-  first_nonopt += (optind - last_nonopt);
-  last_nonopt = optind;
+  d->__first_nonopt += (d->optind - d->__last_nonopt);
+  d->__last_nonopt = d->optind;
 }
 
-/* Initialize the internal data when the first call is made.  */
+/* Process the argument starting with d->__nextchar as a long option.
+   d->optind should *not* have been advanced over this argument.
+
+   If the value returned is -1, it was not actually a long option, the
+   state is unchanged, and the argument should be processed as a set
+   of short options (this can only happen when long_only is true).
+   Otherwise, the option (and its argument, if any) have been consumed
+   and the return value is the value to return from _getopt_internal_r.  */
+static int
+process_long_option (int argc, char **argv, const char *optstring,
+                    const struct option *longopts, int *longind,
+                    int long_only, struct _getopt_data *d,
+                    int print_errors, const char *prefix)
+{
+  char *nameend;
+  size_t namelen;
+  const struct option *p;
+  const struct option *pfound = NULL;
+  int n_options;
+  int option_index;
+
+  for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++)
+    /* Do nothing.  */ ;
+  namelen = nameend - d->__nextchar;
+
+  /* First look for an exact match, counting the options as a side
+     effect.  */
+  for (p = longopts, n_options = 0; p->name; p++, n_options++)
+    if (!strncmp (p->name, d->__nextchar, namelen)
+       && namelen == strlen (p->name))
+      {
+       /* Exact match found.  */
+       pfound = p;
+       option_index = n_options;
+       break;
+      }
+
+  if (pfound == NULL)
+    {
+      /* Didn't find an exact match, so look for abbreviations.  */
+      unsigned char *ambig_set = NULL;
+      int ambig_malloced = 0;
+      int ambig_fallback = 0;
+      int indfound = -1;
 
-#if defined __STDC__ && __STDC__
-static const char *_getopt_initialize (int, char *const *, const char *);
+      for (p = longopts, option_index = 0; p->name; p++, option_index++)
+       if (!strncmp (p->name, d->__nextchar, namelen))
+         {
+           if (pfound == NULL)
+             {
+               /* First nonexact match found.  */
+               pfound = p;
+               indfound = option_index;
+             }
+           else if (long_only
+                    || pfound->has_arg != p->has_arg
+                    || pfound->flag != p->flag
+                    || pfound->val != p->val)
+             {
+               /* Second or later nonexact match found.  */
+               if (!ambig_fallback)
+                 {
+                   if (!print_errors)
+                     /* Don't waste effort tracking the ambig set if
+                        we're not going to print it anyway.  */
+                     ambig_fallback = 1;
+                   else if (!ambig_set)
+                     {
+                       if (__libc_use_alloca (n_options))
+                         ambig_set = alloca (n_options);
+                       else if ((ambig_set = malloc (n_options)) == NULL)
+                         /* Fall back to simpler error message.  */
+                         ambig_fallback = 1;
+                       else
+                         ambig_malloced = 1;
+
+                       if (ambig_set)
+                         {
+                           memset (ambig_set, 0, n_options);
+                           ambig_set[indfound] = 1;
+                         }
+                     }
+                   if (ambig_set)
+                     ambig_set[option_index] = 1;
+                 }
+             }
+         }
+
+      if (ambig_set || ambig_fallback)
+       {
+         if (print_errors)
+           {
+             if (ambig_fallback)
+               fprintf (stderr, _("%s: option '%s%s' is ambiguous\n"),
+                        argv[0], prefix, d->__nextchar);
+             else
+               {
+                 flockfile (stderr);
+                 fprintf (stderr,
+                          _("%s: option '%s%s' is ambiguous; possibilities:"),
+                          argv[0], prefix, d->__nextchar);
+
+                 for (option_index = 0; option_index < n_options; option_index++)
+                   if (ambig_set[option_index])
+                     fprintf (stderr, " '%s%s'",
+                              prefix, longopts[option_index].name);
+
+                 /* This must use 'fprintf' even though it's only
+                    printing a single character, so that it goes through
+                    __fxprintf_nocancel when compiled as part of glibc.  */
+                 fprintf (stderr, "\n");
+                 funlockfile (stderr);
+               }
+           }
+         if (ambig_malloced)
+           free (ambig_set);
+         d->__nextchar += strlen (d->__nextchar);
+         d->optind++;
+         d->optopt = 0;
+         return '?';
+       }
+
+      option_index = indfound;
+    }
+
+  if (pfound == NULL)
+    {
+      /* Can't find it as a long option.  If this is not getopt_long_only,
+        or the option starts with '--' or is not a valid short option,
+        then it's an error.  */
+      if (!long_only || argv[d->optind][1] == '-'
+         || strchr (optstring, *d->__nextchar) == NULL)
+       {
+         if (print_errors)
+           fprintf (stderr, _("%s: unrecognized option '%s%s'\n"),
+                    argv[0], prefix, d->__nextchar);
+
+         d->__nextchar = NULL;
+         d->optind++;
+         d->optopt = 0;
+         return '?';
+       }
+
+      /* Otherwise interpret it as a short option.  */
+      return -1;
+    }
+
+  /* We have found a matching long option.  Consume it.  */
+  d->optind++;
+  d->__nextchar = NULL;
+  if (*nameend)
+    {
+      /* Don't test has_arg with >, because some C compilers don't
+        allow it to be used on enums.  */
+      if (pfound->has_arg)
+       d->optarg = nameend + 1;
+      else
+       {
+         if (print_errors)
+           fprintf (stderr,
+                    _("%s: option '%s%s' doesn't allow an argument\n"),
+                    argv[0], prefix, pfound->name);
+
+         d->optopt = pfound->val;
+         return '?';
+       }
+    }
+  else if (pfound->has_arg == 1)
+    {
+      if (d->optind < argc)
+       d->optarg = argv[d->optind++];
+      else
+       {
+         if (print_errors)
+           fprintf (stderr,
+                    _("%s: option '%s%s' requires an argument\n"),
+                    argv[0], prefix, pfound->name);
+
+         d->optopt = pfound->val;
+         return optstring[0] == ':' ? ':' : '?';
+       }
+    }
+
+  if (longind != NULL)
+    *longind = option_index;
+  if (pfound->flag)
+    {
+      *(pfound->flag) = pfound->val;
+      return 0;
+    }
+  return pfound->val;
+}
+
+#ifndef _GL_UNUSED
+# ifdef __GNUC__
+#  define _GL_UNUSED __attribute__((__unused__))
+# else
+#  define _GL_UNUSED
+# endif
 #endif
+
+/* Initialize internal data upon the first call to getopt.  */
+
 static const char *
-_getopt_initialize (argc, argv, optstring)
-     int argc;
-     char *const *argv;
-     const char *optstring;
+_getopt_initialize (int argc _GL_UNUSED,
+                   char **argv _GL_UNUSED, const char *optstring,
+                   struct _getopt_data *d, int posixly_correct)
 {
   /* Start processing options with ARGV-element 1 (since ARGV-element 0
      is the program name); the sequence of previously skipped
      non-option ARGV-elements is empty.  */
+  if (d->optind == 0)
+    d->optind = 1;
 
-  first_nonopt = last_nonopt = optind;
-
-  nextchar = NULL;
-
-  posixly_correct = getenv ("POSIXLY_CORRECT");
+  d->__first_nonopt = d->__last_nonopt = d->optind;
+  d->__nextchar = NULL;
 
   /* Determine how to handle the ordering of options and nonoptions.  */
-
   if (optstring[0] == '-')
     {
-      ordering = RETURN_IN_ORDER;
+      d->__ordering = RETURN_IN_ORDER;
       ++optstring;
     }
   else if (optstring[0] == '+')
     {
-      ordering = REQUIRE_ORDER;
+      d->__ordering = REQUIRE_ORDER;
       ++optstring;
     }
-  else if (posixly_correct != NULL)
-    ordering = REQUIRE_ORDER;
-  else
-    ordering = PERMUTE;
-
-#if defined _LIBC && defined USE_NONOPTION_FLAGS
-  if (posixly_correct == NULL
-      && argc == __libc_argc && argv == __libc_argv)
-    {
-      if (nonoption_flags_max_len == 0)
-       {
-         if (__getopt_nonoption_flags == NULL
-             || __getopt_nonoption_flags[0] == '\0')
-           nonoption_flags_max_len = -1;
-         else
-           {
-             const char *orig_str = __getopt_nonoption_flags;
-             int len = nonoption_flags_max_len = strlen (orig_str);
-             if (nonoption_flags_max_len < argc)
-               nonoption_flags_max_len = argc;
-             __getopt_nonoption_flags =
-               (char *) malloc (nonoption_flags_max_len);
-             if (__getopt_nonoption_flags == NULL)
-               nonoption_flags_max_len = -1;
-             else
-               memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
-                       '\0', nonoption_flags_max_len - len);
-           }
-       }
-      nonoption_flags_len = nonoption_flags_max_len;
-    }
+  else if (posixly_correct || !!getenv ("POSIXLY_CORRECT"))
+    d->__ordering = REQUIRE_ORDER;
   else
-    nonoption_flags_len = 0;
-#endif
+    d->__ordering = PERMUTE;
 
+  d->__initialized = 1;
   return optstring;
 }
 \f
@@ -458,48 +429,48 @@ _getopt_initialize (argc, argv, optstring)
 
    If an element of ARGV starts with '-', and is not exactly "-" or "--",
    then it is an option element.  The characters of this element
-   (aside from the initial '-') are option characters.  If `getopt'
+   (aside from the initial '-') are option characters.  If 'getopt'
    is called repeatedly, it returns successively each of the option characters
    from each of the option elements.
 
-   If `getopt' finds another option character, it returns that character,
-   updating `optind' and `nextchar' so that the next call to `getopt' can
+   If 'getopt' finds another option character, it returns that character,
+   updating 'optind' and 'nextchar' so that the next call to 'getopt' can
    resume the scan with the following option character or ARGV-element.
 
-   If there are no more option characters, `getopt' returns -1.
-   Then `optind' is the index in ARGV of the first ARGV-element
+   If there are no more option characters, 'getopt' returns -1.
+   Then 'optind' is the index in ARGV of the first ARGV-element
    that is not an option.  (The ARGV-elements have been permuted
    so that those that are not options now come last.)
 
    OPTSTRING is a string containing the legitimate option characters.
    If an option character is seen that is not listed in OPTSTRING,
-   return '?' after printing an error message.  If you set `opterr' to
+   return '?' after printing an error message.  If you set 'opterr' to
    zero, the error message is suppressed but we still return '?'.
 
    If a char in OPTSTRING is followed by a colon, that means it wants an arg,
    so the following text in the same ARGV-element, or the text of the following
-   ARGV-element, is returned in `optarg'.  Two colons mean an option that
+   ARGV-element, is returned in 'optarg'.  Two colons mean an option that
    wants an optional arg; if there is text in the current ARGV-element,
-   it is returned in `optarg', otherwise `optarg' is set to zero.
+   it is returned in 'optarg', otherwise 'optarg' is set to zero.
 
-   If OPTSTRING starts with `-' or `+', it requests different methods of
+   If OPTSTRING starts with '-' or '+', it requests different methods of
    handling the non-option ARGV-elements.
    See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
 
-   Long-named options begin with `--' instead of `-'.
+   Long-named options begin with '--' instead of '-'.
    Their names may be abbreviated as long as the abbreviation is unique
    or is an exact match for some defined option.  If they have an
    argument, it follows the option name in the same ARGV-element, separated
-   from the option name by a `=', or else the in next ARGV-element.
-   When `getopt' finds a long-named option, it returns 0 if that option's
-   `flag' field is nonzero, the value of the option's `val' field
-   if the `flag' field is zero.
+   from the option name by a '=', or else the in next ARGV-element.
+   When 'getopt' finds a long-named option, it returns 0 if that option's
+   'flag' field is nonzero, the value of the option's 'val' field
+   if the 'flag' field is zero.
 
    The elements of ARGV aren't really const, because we permute them.
    But we pretend they're const in the prototype to be compatible
    with other systems.
 
-   LONGOPTS is a vector of `struct option' terminated by an
+   LONGOPTS is a vector of 'struct option' terminated by an
    element containing a name which is zero.
 
    LONGIND returns the index in LONGOPT of the long-named option found.
@@ -510,99 +481,86 @@ _getopt_initialize (argc, argv, optstring)
    long-named options.  */
 
 int
-_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
-     int argc;
-     char *const *argv;
-     const char *optstring;
-     const struct option *longopts;
-     int *longind;
-     int long_only;
+_getopt_internal_r (int argc, char **argv, const char *optstring,
+                   const struct option *longopts, int *longind,
+                   int long_only, struct _getopt_data *d, int posixly_correct)
 {
-  int print_errors = opterr;
-  if (optstring[0] == ':')
-    print_errors = 0;
+  int print_errors = d->opterr;
 
   if (argc < 1)
     return -1;
 
-  optarg = NULL;
+  d->optarg = NULL;
 
-  if (optind == 0 || !__getopt_initialized)
-    {
-      if (optind == 0)
-       optind = 1;     /* Don't scan ARGV[0], the program name.  */
-      optstring = _getopt_initialize (argc, argv, optstring);
-      __getopt_initialized = 1;
-    }
+  if (d->optind == 0 || !d->__initialized)
+    optstring = _getopt_initialize (argc, argv, optstring, d, posixly_correct);
+  else if (optstring[0] == '-' || optstring[0] == '+')
+    optstring++;
 
-  /* Test whether ARGV[optind] points to a non-option argument.
-     Either it does not have option syntax, or there is an environment flag
-     from the shell indicating it is not an option.  The later information
-     is only used when the used in the GNU libc.  */
-#if defined _LIBC && defined USE_NONOPTION_FLAGS
-# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0'              \
-                     || (optind < nonoption_flags_len                        \
-                         && __getopt_nonoption_flags[optind] == '1'))
-#else
-# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
-#endif
+  if (optstring[0] == ':')
+    print_errors = 0;
 
-  if (nextchar == NULL || *nextchar == '\0')
+  /* Test whether ARGV[optind] points to a non-option argument.  */
+#define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0')
+
+  if (d->__nextchar == NULL || *d->__nextchar == '\0')
     {
       /* Advance to the next ARGV-element.  */
 
       /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
         moved back by the user (who may also have changed the arguments).  */
-      if (last_nonopt > optind)
-       last_nonopt = optind;
-      if (first_nonopt > optind)
-       first_nonopt = optind;
+      if (d->__last_nonopt > d->optind)
+       d->__last_nonopt = d->optind;
+      if (d->__first_nonopt > d->optind)
+       d->__first_nonopt = d->optind;
 
-      if (ordering == PERMUTE)
+      if (d->__ordering == PERMUTE)
        {
          /* If we have just processed some options following some non-options,
             exchange them so that the options come first.  */
 
-         if (first_nonopt != last_nonopt && last_nonopt != optind)
-           exchange ((char **) argv);
-         else if (last_nonopt != optind)
-           first_nonopt = optind;
+         if (d->__first_nonopt != d->__last_nonopt
+             && d->__last_nonopt != d->optind)
+           exchange (argv, d);
+         else if (d->__last_nonopt != d->optind)
+           d->__first_nonopt = d->optind;
 
          /* Skip any additional non-options
             and extend the range of non-options previously skipped.  */
 
-         while (optind < argc && NONOPTION_P)
-           optind++;
-         last_nonopt = optind;
+         while (d->optind < argc && NONOPTION_P)
+           d->optind++;
+         d->__last_nonopt = d->optind;
        }
 
-      /* The special ARGV-element `--' means premature end of options.
+      /* The special ARGV-element '--' means premature end of options.
         Skip it like a null option,
         then exchange with previous non-options as if it were an option,
         then skip everything else like a non-option.  */
 
-      if (optind != argc && !strcmp (argv[optind], "--"))
+      if (d->optind != argc && !strcmp (argv[d->optind], "--"))
        {
-         optind++;
+         d->optind++;
 
-         if (first_nonopt != last_nonopt && last_nonopt != optind)
-           exchange ((char **) argv);
-         else if (first_nonopt == last_nonopt)
-           first_nonopt = optind;
-         last_nonopt = argc;
+         if (d->__first_nonopt != d->__last_nonopt
+             && d->__last_nonopt != d->optind)
+           exchange (argv, d);
+         else if (d->__first_nonopt == d->__last_nonopt)
+           d->__first_nonopt = d->optind;
+         d->__last_nonopt = argc;
 
-         optind = argc;
+         d->optind = argc;
        }
 
       /* If we have done all the ARGV-elements, stop the scan
         and back over any non-options that we skipped and permuted.  */
 
-      if (optind == argc)
+      if (d->optind == argc)
        {
          /* Set the next-arg-index to point at the non-options
             that we previously skipped, so the caller will digest them.  */
-         if (first_nonopt != last_nonopt)
-           optind = first_nonopt;
+         if (d->__first_nonopt != d->__last_nonopt)
+           d->optind = d->__first_nonopt;
          return -1;
        }
 
@@ -611,370 +569,87 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
 
       if (NONOPTION_P)
        {
-         if (ordering == REQUIRE_ORDER)
+         if (d->__ordering == REQUIRE_ORDER)
            return -1;
-         optarg = argv[optind++];
+         d->optarg = argv[d->optind++];
          return 1;
        }
 
       /* We have found another option-ARGV-element.
-        Skip the initial punctuation.  */
-
-      nextchar = (argv[optind] + 1
-                 + (longopts != NULL && argv[optind][1] == '-'));
-    }
-
-  /* Decode the current option-ARGV-element.  */
-
-  /* Check whether the ARGV-element is a long option.
-
-     If long_only and the ARGV-element has the form "-f", where f is
-     a valid short option, don't consider it an abbreviated form of
-     a long option that starts with f.  Otherwise there would be no
-     way to give the -f short option.
-
-     On the other hand, if there's a long option "fubar" and
-     the ARGV-element is "-fu", do consider that an abbreviation of
-     the long option, just like "--fu", and not "-f" with arg "u".
-
-     This distinction seems to be the most useful approach.  */
-
-  if (longopts != NULL
-      && (argv[optind][1] == '-'
-         || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
-    {
-      char *nameend;
-      const struct option *p;
-      const struct option *pfound = NULL;
-      int exact = 0;
-      int ambig = 0;
-      int indfound = -1;
-      int option_index;
-
-      for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
-       /* Do nothing.  */ ;
-
-      /* Test all long options for either exact match
-        or abbreviated matches.  */
-      for (p = longopts, option_index = 0; p->name; p++, option_index++)
-       if (!strncmp (p->name, nextchar, nameend - nextchar))
-         {
-           if ((unsigned int) (nameend - nextchar)
-               == (unsigned int) strlen (p->name))
-             {
-               /* Exact match found.  */
-               pfound = p;
-               indfound = option_index;
-               exact = 1;
-               break;
-             }
-           else if (pfound == NULL)
-             {
-               /* First nonexact match found.  */
-               pfound = p;
-               indfound = option_index;
-             }
-           else if (long_only
-                    || pfound->has_arg != p->has_arg
-                    || pfound->flag != p->flag
-                    || pfound->val != p->val)
-             /* Second or later nonexact match found.  */
-             ambig = 1;
-         }
-
-      if (ambig && !exact)
+        Check whether it might be a long option.  */
+      if (longopts)
        {
-         if (print_errors)
+         if (argv[d->optind][1] == '-')
            {
-#if defined _LIBC && defined USE_IN_LIBIO
-             char *buf;
-
-             if (__asprintf (&buf, _("%s: option `%s' is ambiguous\n"),
-                             argv[0], argv[optind]) >= 0)
-               {
-
-                 if (_IO_fwide (stderr, 0) > 0)
-                   __fwprintf (stderr, L"%s", buf);
-                 else
-                   fputs (buf, stderr);
-
-                 free (buf);
-               }
-#else
-             fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
-                      argv[0], argv[optind]);
-#endif
+             /* "--foo" is always a long option.  The special option
+                "--" was handled above.  */
+             d->__nextchar = argv[d->optind] + 2;
+             return process_long_option (argc, argv, optstring, longopts,
+                                         longind, long_only, d,
+                                         print_errors, "--");
            }
-         nextchar += strlen (nextchar);
-         optind++;
-         optopt = 0;
-         return '?';
-       }
 
-      if (pfound != NULL)
-       {
-         option_index = indfound;
-         optind++;
-         if (*nameend)
-           {
-             /* Don't test has_arg with >, because some C compilers don't
-                allow it to be used on enums.  */
-             if (pfound->has_arg)
-               optarg = nameend + 1;
-             else
-               {
-                 if (print_errors)
-                   {
-#if defined _LIBC && defined USE_IN_LIBIO
-                     char *buf;
-                     int n;
-#endif
+         /* If long_only and the ARGV-element has the form "-f",
+            where f is a valid short option, don't consider it an
+            abbreviated form of a long option that starts with f.
+            Otherwise there would be no way to give the -f short
+            option.
 
-                     if (argv[optind - 1][1] == '-')
-                       {
-                         /* --option */
-#if defined _LIBC && defined USE_IN_LIBIO
-                         n = __asprintf (&buf, _("\
-%s: option `--%s' doesn't allow an argument\n"),
-                                         argv[0], pfound->name);
-#else
-                         fprintf (stderr, _("\
-%s: option `--%s' doesn't allow an argument\n"),
-                                  argv[0], pfound->name);
-#endif
-                       }
-                     else
-                       {
-                         /* +option or -option */
-#if defined _LIBC && defined USE_IN_LIBIO
-                         n = __asprintf (&buf, _("\
-%s: option `%c%s' doesn't allow an argument\n"),
-                                         argv[0], argv[optind - 1][0],
-                                         pfound->name);
-#else
-                         fprintf (stderr, _("\
-%s: option `%c%s' doesn't allow an argument\n"),
-                                  argv[0], argv[optind - 1][0], pfound->name);
-#endif
-                       }
-
-#if defined _LIBC && defined USE_IN_LIBIO
-                     if (n >= 0)
-                       {
-                         if (_IO_fwide (stderr, 0) > 0)
-                           __fwprintf (stderr, L"%s", buf);
-                         else
-                           fputs (buf, stderr);
-
-                         free (buf);
-                       }
-#endif
-                   }
+            On the other hand, if there's a long option "fubar" and
+            the ARGV-element is "-fu", do consider that an
+            abbreviation of the long option, just like "--fu", and
+            not "-f" with arg "u".
 
-                 nextchar += strlen (nextchar);
-
-                 optopt = pfound->val;
-                 return '?';
-               }
-           }
-         else if (pfound->has_arg == 1)
+            This distinction seems to be the most useful approach.  */
+         if (long_only && (argv[d->optind][2]
+                           || !strchr (optstring, argv[d->optind][1])))
            {
-             if (optind < argc)
-               optarg = argv[optind++];
-             else
-               {
-                 if (print_errors)
-                   {
-#if defined _LIBC && defined USE_IN_LIBIO
-                     char *buf;
-
-                     if (__asprintf (&buf, _("\
-%s: option `%s' requires an argument\n"),
-                                     argv[0], argv[optind - 1]) >= 0)
-                       {
-                         if (_IO_fwide (stderr, 0) > 0)
-                           __fwprintf (stderr, L"%s", buf);
-                         else
-                           fputs (buf, stderr);
-
-                         free (buf);
-                       }
-#else
-                     fprintf (stderr,
-                              _("%s: option `%s' requires an argument\n"),
-                              argv[0], argv[optind - 1]);
-#endif
-                   }
-                 nextchar += strlen (nextchar);
-                 optopt = pfound->val;
-                 return optstring[0] == ':' ? ':' : '?';
-               }
+             int code;
+             d->__nextchar = argv[d->optind] + 1;
+             code = process_long_option (argc, argv, optstring, longopts,
+                                         longind, long_only, d,
+                                         print_errors, "-");
+             if (code != -1)
+               return code;
            }
-         nextchar += strlen (nextchar);
-         if (longind != NULL)
-           *longind = option_index;
-         if (pfound->flag)
-           {
-             *(pfound->flag) = pfound->val;
-             return 0;
-           }
-         return pfound->val;
        }
 
-      /* Can't find it as a long option.  If this is not getopt_long_only,
-        or the option starts with '--' or is not a valid short
-        option, then it's an error.
-        Otherwise interpret it as a short option.  */
-      if (!long_only || argv[optind][1] == '-'
-         || my_index (optstring, *nextchar) == NULL)
-       {
-         if (print_errors)
-           {
-#if defined _LIBC && defined USE_IN_LIBIO
-             char *buf;
-             int n;
-#endif
-
-             if (argv[optind][1] == '-')
-               {
-                 /* --option */
-#if defined _LIBC && defined USE_IN_LIBIO
-                 n = __asprintf (&buf, _("%s: unrecognized option `--%s'\n"),
-                                 argv[0], nextchar);
-#else
-                 fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
-                          argv[0], nextchar);
-#endif
-               }
-             else
-               {
-                 /* +option or -option */
-#if defined _LIBC && defined USE_IN_LIBIO
-                 n = __asprintf (&buf, _("%s: unrecognized option `%c%s'\n"),
-                                 argv[0], argv[optind][0], nextchar);
-#else
-                 fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
-                          argv[0], argv[optind][0], nextchar);
-#endif
-               }
-
-#if defined _LIBC && defined USE_IN_LIBIO
-             if (n >= 0)
-               {
-                 if (_IO_fwide (stderr, 0) > 0)
-                   __fwprintf (stderr, L"%s", buf);
-                 else
-                   fputs (buf, stderr);
-
-                 free (buf);
-               }
-#endif
-           }
-         nextchar = (char *) "";
-         optind++;
-         optopt = 0;
-         return '?';
-       }
+      /* It is not a long option.  Skip the initial punctuation.  */
+      d->__nextchar = argv[d->optind] + 1;
     }
 
   /* Look at and handle the next short option-character.  */
 
   {
-    char c = *nextchar++;
-    char *temp = my_index (optstring, c);
+    char c = *d->__nextchar++;
+    const char *temp = strchr (optstring, c);
 
-    /* Increment `optind' when we start to process its last character.  */
-    if (*nextchar == '\0')
-      ++optind;
+    /* Increment 'optind' when we start to process its last character.  */
+    if (*d->__nextchar == '\0')
+      ++d->optind;
 
-    if (temp == NULL || c == ':')
+    if (temp == NULL || c == ':' || c == ';')
       {
        if (print_errors)
-         {
-#if defined _LIBC && defined USE_IN_LIBIO
-             char *buf;
-             int n;
-#endif
-
-           if (posixly_correct)
-             {
-               /* 1003.2 specifies the format of this message.  */
-#if defined _LIBC && defined USE_IN_LIBIO
-               n = __asprintf (&buf, _("%s: illegal option -- %c\n"),
-                               argv[0], c);
-#else
-               fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
-#endif
-             }
-           else
-             {
-#if defined _LIBC && defined USE_IN_LIBIO
-               n = __asprintf (&buf, _("%s: invalid option -- %c\n"),
-                               argv[0], c);
-#else
-               fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
-#endif
-             }
-
-#if defined _LIBC && defined USE_IN_LIBIO
-           if (n >= 0)
-             {
-               if (_IO_fwide (stderr, 0) > 0)
-                 __fwprintf (stderr, L"%s", buf);
-               else
-                 fputs (buf, stderr);
-
-               free (buf);
-             }
-#endif
-         }
-       optopt = c;
+         fprintf (stderr, _("%s: invalid option -- '%c'\n"), argv[0], c);
+       d->optopt = c;
        return '?';
       }
+
     /* Convenience. Treat POSIX -W foo same as long option --foo */
-    if (temp[0] == 'W' && temp[1] == ';')
+    if (temp[0] == 'W' && temp[1] == ';' && longopts != NULL)
       {
-       char *nameend;
-       const struct option *p;
-       const struct option *pfound = NULL;
-       int exact = 0;
-       int ambig = 0;
-       int indfound = 0;
-       int option_index;
-
        /* This is an option that requires an argument.  */
-       if (*nextchar != '\0')
-         {
-           optarg = nextchar;
-           /* If we end this ARGV-element by taking the rest as an arg,
-              we must advance to the next element now.  */
-           optind++;
-         }
-       else if (optind == argc)
+       if (*d->__nextchar != '\0')
+         d->optarg = d->__nextchar;
+       else if (d->optind == argc)
          {
            if (print_errors)
-             {
-               /* 1003.2 specifies the format of this message.  */
-#if defined _LIBC && defined USE_IN_LIBIO
-               char *buf;
-
-               if (__asprintf (&buf,
-                               _("%s: option requires an argument -- %c\n"),
-                               argv[0], c) >= 0)
-                 {
-                   if (_IO_fwide (stderr, 0) > 0)
-                     __fwprintf (stderr, L"%s", buf);
-                   else
-                     fputs (buf, stderr);
+             fprintf (stderr,
+                      _("%s: option requires an argument -- '%c'\n"),
+                      argv[0], c);
 
-                   free (buf);
-                 }
-#else
-               fprintf (stderr, _("%s: option requires an argument -- %c\n"),
-                        argv[0], c);
-#endif
-             }
-           optopt = c;
+           d->optopt = c;
            if (optstring[0] == ':')
              c = ':';
            else
@@ -982,208 +657,55 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
            return c;
          }
        else
-         /* We already incremented `optind' once;
-            increment it again when taking next ARGV-elt as argument.  */
-         optarg = argv[optind++];
-
-       /* optarg is now the argument, see if it's in the
-          table of longopts.  */
-
-       for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
-         /* Do nothing.  */ ;
+         d->optarg = argv[d->optind];
 
-       /* Test all long options for either exact match
-          or abbreviated matches.  */
-       for (p = longopts, option_index = 0; p->name; p++, option_index++)
-         if (!strncmp (p->name, nextchar, nameend - nextchar))
-           {
-             if ((unsigned int) (nameend - nextchar) == strlen (p->name))
-               {
-                 /* Exact match found.  */
-                 pfound = p;
-                 indfound = option_index;
-                 exact = 1;
-                 break;
-               }
-             else if (pfound == NULL)
-               {
-                 /* First nonexact match found.  */
-                 pfound = p;
-                 indfound = option_index;
-               }
-             else
-               /* Second or later nonexact match found.  */
-               ambig = 1;
-           }
-       if (ambig && !exact)
-         {
-           if (print_errors)
-             {
-#if defined _LIBC && defined USE_IN_LIBIO
-               char *buf;
-
-               if (__asprintf (&buf, _("%s: option `-W %s' is ambiguous\n"),
-                               argv[0], argv[optind]) >= 0)
-                 {
-                   if (_IO_fwide (stderr, 0) > 0)
-                     __fwprintf (stderr, L"%s", buf);
-                   else
-                     fputs (buf, stderr);
-
-                   free (buf);
-                 }
-#else
-               fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
-                        argv[0], argv[optind]);
-#endif
-             }
-           nextchar += strlen (nextchar);
-           optind++;
-           return '?';
-         }
-       if (pfound != NULL)
-         {
-           option_index = indfound;
-           if (*nameend)
-             {
-               /* Don't test has_arg with >, because some C compilers don't
-                  allow it to be used on enums.  */
-               if (pfound->has_arg)
-                 optarg = nameend + 1;
-               else
-                 {
-                   if (print_errors)
-                     {
-#if defined _LIBC && defined USE_IN_LIBIO
-                       char *buf;
-
-                       if (__asprintf (&buf, _("\
-%s: option `-W %s' doesn't allow an argument\n"),
-                                       argv[0], pfound->name) >= 0)
-                         {
-                           if (_IO_fwide (stderr, 0) > 0)
-                             __fwprintf (stderr, L"%s", buf);
-                           else
-                             fputs (buf, stderr);
-
-                           free (buf);
-                         }
-#else
-                       fprintf (stderr, _("\
-%s: option `-W %s' doesn't allow an argument\n"),
-                                argv[0], pfound->name);
-#endif
-                     }
-
-                   nextchar += strlen (nextchar);
-                   return '?';
-                 }
-             }
-           else if (pfound->has_arg == 1)
-             {
-               if (optind < argc)
-                 optarg = argv[optind++];
-               else
-                 {
-                   if (print_errors)
-                     {
-#if defined _LIBC && defined USE_IN_LIBIO
-                       char *buf;
-
-                       if (__asprintf (&buf, _("\
-%s: option `%s' requires an argument\n"),
-                                       argv[0], argv[optind - 1]) >= 0)
-                         {
-                           if (_IO_fwide (stderr, 0) > 0)
-                             __fwprintf (stderr, L"%s", buf);
-                           else
-                             fputs (buf, stderr);
-
-                           free (buf);
-                         }
-#else
-                       fprintf (stderr,
-                                _("%s: option `%s' requires an argument\n"),
-                                argv[0], argv[optind - 1]);
-#endif
-                     }
-                   nextchar += strlen (nextchar);
-                   return optstring[0] == ':' ? ':' : '?';
-                 }
-             }
-           nextchar += strlen (nextchar);
-           if (longind != NULL)
-             *longind = option_index;
-           if (pfound->flag)
-             {
-               *(pfound->flag) = pfound->val;
-               return 0;
-             }
-           return pfound->val;
-         }
-         nextchar = NULL;
-         return 'W';   /* Let the application handle it.   */
+       d->__nextchar = d->optarg;
+       d->optarg = NULL;
+       return process_long_option (argc, argv, optstring, longopts, longind,
+                                   0 /* long_only */, d, print_errors, "-W ");
       }
     if (temp[1] == ':')
       {
        if (temp[2] == ':')
          {
            /* This is an option that accepts an argument optionally.  */
-           if (*nextchar != '\0')
+           if (*d->__nextchar != '\0')
              {
-               optarg = nextchar;
-               optind++;
+               d->optarg = d->__nextchar;
+               d->optind++;
              }
            else
-             optarg = NULL;
-           nextchar = NULL;
+             d->optarg = NULL;
+           d->__nextchar = NULL;
          }
        else
          {
            /* This is an option that requires an argument.  */
-           if (*nextchar != '\0')
+           if (*d->__nextchar != '\0')
              {
-               optarg = nextchar;
+               d->optarg = d->__nextchar;
                /* If we end this ARGV-element by taking the rest as an arg,
                   we must advance to the next element now.  */
-               optind++;
+               d->optind++;
              }
-           else if (optind == argc)
+           else if (d->optind == argc)
              {
                if (print_errors)
-                 {
-                   /* 1003.2 specifies the format of this message.  */
-#if defined _LIBC && defined USE_IN_LIBIO
-                   char *buf;
+                 fprintf (stderr,
+                          _("%s: option requires an argument -- '%c'\n"),
+                          argv[0], c);
 
-                   if (__asprintf (&buf, _("\
-%s: option requires an argument -- %c\n"),
-                                   argv[0], c) >= 0)
-                     {
-                       if (_IO_fwide (stderr, 0) > 0)
-                         __fwprintf (stderr, L"%s", buf);
-                       else
-                         fputs (buf, stderr);
-
-                       free (buf);
-                     }
-#else
-                   fprintf (stderr,
-                            _("%s: option requires an argument -- %c\n"),
-                            argv[0], c);
-#endif
-                 }
-               optopt = c;
+               d->optopt = c;
                if (optstring[0] == ':')
                  c = ':';
                else
                  c = '?';
              }
            else
-             /* We already incremented `optind' once;
+             /* We already incremented 'optind' once;
                 increment it again when taking next ARGV-elt as argument.  */
-             optarg = argv[optind++];
-           nextchar = NULL;
+             d->optarg = argv[d->optind++];
+           d->__nextchar = NULL;
          }
       }
     return c;
@@ -1191,28 +713,53 @@ _getopt_internal (argc, argv, optstring, longopts, longind, long_only)
 }
 
 int
-getopt (argc, argv, optstring)
-     int argc;
-     char *const *argv;
-     const char *optstring;
+_getopt_internal (int argc, char **argv, const char *optstring,
+                 const struct option *longopts, int *longind, int long_only,
+                 int posixly_correct)
 {
-  return _getopt_internal (argc, argv, optstring,
-                          (const struct option *) 0,
-                          (int *) 0,
-                          0);
+  int result;
+
+  getopt_data.optind = optind;
+  getopt_data.opterr = opterr;
+
+  result = _getopt_internal_r (argc, argv, optstring, longopts,
+                              longind, long_only, &getopt_data,
+                              posixly_correct);
+
+  optind = getopt_data.optind;
+  optarg = getopt_data.optarg;
+  optopt = getopt_data.optopt;
+
+  return result;
 }
 
-#endif /* Not ELIDE_CODE.  */
+/* glibc gets a LSB-compliant getopt and a POSIX-complaint __posix_getopt.
+   Standalone applications just get a POSIX-compliant getopt.
+   POSIX and LSB both require these functions to take 'char *const *argv'
+   even though this is incorrect (because of the permutation).  */
+#define GETOPT_ENTRY(NAME, POSIXLY_CORRECT)                    \
+  int                                                          \
+  NAME (int argc, char *const *argv, const char *optstring)    \
+  {                                                            \
+    return _getopt_internal (argc, (char **)argv, optstring,   \
+                            0, 0, 0, POSIXLY_CORRECT);         \
+  }
+
+#ifdef _LIBC
+GETOPT_ENTRY(getopt, 0)
+GETOPT_ENTRY(__posix_getopt, 1)
+#else
+GETOPT_ENTRY(getopt, 1)
+#endif
+
 \f
 #ifdef TEST
 
 /* Compile with -DTEST to make an executable for use in testing
-   the above definition of `getopt'.  */
+   the above definition of 'getopt'.  */
 
 int
-main (argc, argv)
-     int argc;
-     char **argv;
+main (int argc, char **argv)
 {
   int c;
   int digit_optind = 0;
@@ -1252,7 +799,7 @@ main (argc, argv)
          break;
 
        case 'c':
-         printf ("option c with value `%s'\n", optarg);
+         printf ("option c with value '%s'\n", optarg);
          break;
 
        case '?':
index c4f1ea48db14db668ebc2aa304077c379b87d859..eb600ac8e4416e87df6ee335a059e3dbef9ea92b 100644 (file)
@@ -1,6 +1,8 @@
 /* Declarations for getopt.
-   Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc.
+   Copyright (C) 1989-2018 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
+   Unlike the bulk of the getopt implementation, this file is NOT part
+   of gnulib; gnulib also has a getopt.h but it is different.
 
    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    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., 51 Franklin Street, Fifth Floor, Boston,
-   MA 02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #ifndef _GETOPT_H
+#define _GETOPT_H 1
 
-#ifndef __need_getopt
-# define _GETOPT_H 1
+#ifndef _GETOPT_STANDALONE
+#include <features.h>
 #endif
 
-/* If __GNU_LIBRARY__ is not already defined, either we are being used
-   standalone, or this is the first header included in the source file.
-   If we are being used with glibc, we need to include <features.h>, but
-   that does not exist if we are standalone.  So: if __GNU_LIBRARY__ is
-   not defined, include <ctype.h>, which will pull in <features.h> for us
-   if it's from glibc.  (Why ctype.h?  It's guaranteed to exist and it
-   doesn't flood the namespace with stuff the way some other headers do.)  */
-#if !defined __GNU_LIBRARY__
-# include <ctype.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* For communication from `getopt' to the caller.
-   When `getopt' finds an option that takes an argument,
-   the argument value is returned here.
-   Also, when `ordering' is RETURN_IN_ORDER,
-   each non-option ARGV-element is returned here.  */
-
-extern char *optarg;
-
-/* Index in ARGV of the next element to be scanned.
-   This is used for communication to and from the caller
-   and for communication between successive calls to `getopt'.
-
-   On entry to `getopt', zero means this is the first call; initialize.
-
-   When `getopt' returns -1, this is the index of the first of the
-   non-option elements that the caller should itself scan.
-
-   Otherwise, `optind' communicates from one call to the next
-   how much of ARGV has been scanned so far.  */
-
-extern int optind;
-
-/* Callers store zero here to inhibit the error message `getopt' prints
-   for unrecognized options.  */
-
-extern int opterr;
-
-/* Set to an option character which was unrecognized.  */
-
-extern int optopt;
-
-#ifndef __need_getopt
-/* Describe the long-named options requested by the application.
-   The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
-   of `struct option' terminated by an element containing a name which is
-   zero.
-
-   The field `has_arg' is:
-   no_argument         (or 0) if the option does not take an argument,
-   required_argument   (or 1) if the option requires an argument,
-   optional_argument   (or 2) if the option takes an optional argument.
-
-   If the field `flag' is not NULL, it points to a variable that is set
-   to the value given in the field `val' when the option is found, but
-   left unchanged if the option is not found.
-
-   To have a long-named option do something other than set an `int' to
-   a compiled-in constant, such as set a value from `optarg', set the
-   option's `flag' field to zero and its `val' field to a nonzero
-   value (the equivalent single-letter option character, if there is
-   one).  For long options that have a zero `flag' field, `getopt'
-   returns the contents of the `val' field.  */
-
-struct option
-{
-# if (defined __STDC__ && __STDC__) || defined __cplusplus
-  const char *name;
+// From <sys/cdefs.h>:
+#ifndef __BEGIN_DECLS
+# ifdef __cplusplus
+#  define __BEGIN_DECLS extern "C" {
+#  define __END_DECLS   }
 # else
-  char *name;
+#  define __BEGIN_DECLS
+#  define __END_DECLS
 # endif
-  /* has_arg can't be an enum because some compilers complain about
-     type mismatches in all the code that assumes it is an int.  */
-  int has_arg;
-  int *flag;
-  int val;
-};
-
-/* Names for the values of the `has_arg' field of `struct option'.  */
-
-# define no_argument           0
-# define required_argument     1
-# define optional_argument     2
-#endif /* need getopt */
-
-
-/* Get definitions and prototypes for functions to process the
-   arguments in ARGV (ARGC of them, minus the program name) for
-   options given in OPTS.
-
-   Return the option character from OPTS just read.  Return -1 when
-   there are no more options.  For unrecognized options, or options
-   missing arguments, `optopt' is set to the option letter, and '?' is
-   returned.
-
-   The OPTS string is a list of characters which are recognized option
-   letters, optionally followed by colons, specifying that that letter
-   takes an argument, to be placed in `optarg'.
-
-   If a letter in OPTS is followed by two colons, its argument is
-   optional.  This behavior is specific to the GNU `getopt'.
-
-   The argument `--' causes premature termination of argument
-   scanning, explicitly telling `getopt' that there are no more
-   options.
-
-   If OPTS begins with `--', then non-option arguments are treated as
-   arguments to the option '\0'.  This behavior is specific to the GNU
-   `getopt'.  */
-
-#if (defined __STDC__ && __STDC__) || defined __cplusplus
-# ifdef __GNU_LIBRARY__
-/* Many other libraries have conflicting prototypes for getopt, with
-   differences in the consts, in stdlib.h.  To avoid compilation
-   errors, only prototype getopt for the GNU C library.  */
-extern int getopt (int ___argc, char *const *___argv, const char *__shortopts);
-# else /* not __GNU_LIBRARY__ */
-extern int getopt ();
-# endif /* __GNU_LIBRARY__ */
-
-# ifndef __need_getopt
-extern int getopt_long (int ___argc, char *const *___argv,
-                       const char *__shortopts,
-                       const struct option *__longopts, int *__longind);
-extern int getopt_long_only (int ___argc, char *const *___argv,
-                            const char *__shortopts,
-                            const struct option *__longopts, int *__longind);
-
-/* Internal only.  Users should not call this directly.  */
-extern int _getopt_internal (int ___argc, char *const *___argv,
-                            const char *__shortopts,
-                            const struct option *__longopts, int *__longind,
-                            int __long_only);
+#endif
+#ifndef __THROW
+# ifdef __cplusplus
+#  define __THROW throw()
+# elif defined(__GNUC__)
+#  define __THROW __attribute__ ((__nothrow__))
+# else
+#  define __THROW
 # endif
-#else /* not __STDC__ */
-extern int getopt ();
-# ifndef __need_getopt
-extern int getopt_long ();
-extern int getopt_long_only ();
-
-extern int _getopt_internal ();
+#endif
+#ifndef __nonnull
+# ifdef __GNUC__
+#  define __nonnull(x) __attribute__ ((__nonnull__ x))
+# else
+#  define __nonnull(x)
 # endif
-#endif /* __STDC__ */
+#endif
 
-#ifdef __cplusplus
-}
+/* The type of the 'argv' argument to getopt_long and getopt_long_only
+   is properly 'char **', since both functions may write to the array
+   (in order to move all the options to the beginning).  However, for
+   compatibility with old versions of LSB, glibc has to use 'char *const *'
+   instead.  */
+#ifndef __getopt_argv_const
+# define __getopt_argv_const const
 #endif
 
-/* Make sure we later can get all the definitions and declarations.  */
-#undef __need_getopt
+#include <bits/getopt_core.h>
+#include <bits/getopt_ext.h>
 
 #endif /* getopt.h */
index 07e55318ca35d57521f20382ec0943f7317fd183..be17a5cf1a016fc143bbc9524d12bb085ed0759b 100644 (file)
@@ -1,7 +1,7 @@
 /* getopt_long and getopt_long_only entry points for GNU getopt.
-   Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98
-     Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
+   Copyright (C) 1987-2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library and is also part of gnulib.
+   Patches to this file should be submitted to both projects.
 
    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    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.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 \f
-#ifdef HAVE_CONFIG_H
-#include "config.h"
+#if !defined(_LIBC) && !defined(_GETOPT_STANDALONE)
+# include <config.h>
 #endif
 
-#ifdef _LIBC
-# include <getopt.h>
-#else
-# include "getopt.h"
-#endif
-
-#if !defined __STDC__ || !__STDC__
-/* This is a separate conditional since some stdc systems
-   reject `defined (const)'.  */
-#ifndef const
-#define const
-#endif
-#endif
-
-#include <stdio.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
-   actually compiling the library itself.  This code is part of the GNU C
-   Library, but also included in many other GNU distributions.  Compiling
-   and linking in this code is a waste when using the GNU C library
-   (especially if it is a shared library).  Rather than having every GNU
-   program understand `configure --with-gnu-libc' and omit the object files,
-   it is simpler to just do this in the source for each such file.  */
-
-#define GETOPT_INTERFACE_VERSION 2
-#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2
-#include <gnu-versions.h>
-#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
-#define ELIDE_CODE
-#endif
-#endif
-
-#ifndef ELIDE_CODE
-
+#include "getopt.h"
+#include "getopt_int.h"
 
-/* This needs to come after some library #include
-   to get __GNU_LIBRARY__ defined.  */
-#ifdef __GNU_LIBRARY__
-#include <stdlib.h>
-#endif
-
-#ifndef        NULL
-#define NULL 0
-#endif
+int
+getopt_long (int argc, char *__getopt_argv_const *argv, const char *options,
+            const struct option *long_options, int *opt_index)
+{
+  return _getopt_internal (argc, (char **) argv, options, long_options,
+                          opt_index, 0, 0);
+}
 
 int
-getopt_long (argc, argv, options, long_options, opt_index)
-     int argc;
-     char *const *argv;
-     const char *options;
-     const struct option *long_options;
-     int *opt_index;
+_getopt_long_r (int argc, char **argv, const char *options,
+               const struct option *long_options, int *opt_index,
+               struct _getopt_data *d)
 {
-  return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+  return _getopt_internal_r (argc, argv, options, long_options, opt_index,
+                            0, d, 0);
 }
 
 /* Like getopt_long, but '-' as well as '--' can indicate a long option.
@@ -84,31 +47,31 @@ getopt_long (argc, argv, options, long_options, opt_index)
    instead.  */
 
 int
-getopt_long_only (argc, argv, options, long_options, opt_index)
-     int argc;
-     char *const *argv;
-     const char *options;
-     const struct option *long_options;
-     int *opt_index;
+getopt_long_only (int argc, char *__getopt_argv_const *argv,
+                 const char *options,
+                 const struct option *long_options, int *opt_index)
 {
-  return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+  return _getopt_internal (argc, (char **) argv, options, long_options,
+                          opt_index, 1, 0);
 }
 
-# ifdef _LIBC
-libc_hidden_def (getopt_long)
-libc_hidden_def (getopt_long_only)
-# endif
+int
+_getopt_long_only_r (int argc, char **argv, const char *options,
+                    const struct option *long_options, int *opt_index,
+                    struct _getopt_data *d)
+{
+  return _getopt_internal_r (argc, argv, options, long_options, opt_index,
+                            1, d, 0);
+}
 
-#endif /* Not ELIDE_CODE.  */
 \f
 #ifdef TEST
 
 #include <stdio.h>
+#include <stdlib.h>
 
 int
-main (argc, argv)
-     int argc;
-     char **argv;
+main (int argc, char **argv)
 {
   int c;
   int digit_optind = 0;
@@ -117,7 +80,7 @@ main (argc, argv)
     {
       int this_option_optind = optind ? optind : 1;
       int option_index = 0;
-      static struct option long_options[] =
+      static const struct option long_options[] =
       {
        {"add", 1, 0, 0},
        {"append", 0, 0, 0},
@@ -167,11 +130,11 @@ main (argc, argv)
          break;
 
        case 'c':
-         printf ("option c with value `%s'\n", optarg);
+         printf ("option c with value '%s'\n", optarg);
          break;
 
        case 'd':
-         printf ("option d with value `%s'\n", optarg);
+         printf ("option d with value '%s'\n", optarg);
          break;
 
        case '?':
diff --git a/getopt/getopt_int.h b/getopt/getopt_int.h
new file mode 100644 (file)
index 0000000..5022503
--- /dev/null
@@ -0,0 +1,118 @@
+/* Internal declarations for getopt.
+   Copyright (C) 1989-2018 Free Software Foundation, Inc.
+   This file is part of the GNU C Library and is also part of gnulib.
+   Patches to this file should be submitted to both projects.
+
+   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, see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef _GETOPT_INT_H
+#define _GETOPT_INT_H  1
+
+#include <getopt.h>
+
+extern int _getopt_internal (int ___argc, char **___argv,
+                            const char *__shortopts,
+                            const struct option *__longopts, int *__longind,
+                            int __long_only, int __posixly_correct);
+
+\f
+/* Reentrant versions which can handle parsing multiple argument
+   vectors at the same time.  */
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+   REQUIRE_ORDER means don't recognize them as options; stop option
+   processing when the first non-option is seen.  This is what POSIX
+   specifies should happen.
+
+   PERMUTE means permute the contents of ARGV as we scan, so that
+   eventually all the non-options are at the end.  This allows options
+   to be given in any order, even with programs that were not written
+   to expect this.
+
+   RETURN_IN_ORDER is an option available to programs that were
+   written to expect options and other ARGV-elements in any order
+   and that care about the ordering of the two.  We describe each
+   non-option ARGV-element as if it were the argument of an option
+   with character code 1.
+
+   The special argument '--' forces an end of option-scanning regardless
+   of the value of 'ordering'.  In the case of RETURN_IN_ORDER, only
+   '--' can cause 'getopt' to return -1 with 'optind' != ARGC.  */
+
+enum __ord
+  {
+    REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+  };
+
+/* Data type for reentrant functions.  */
+struct _getopt_data
+{
+  /* These have exactly the same meaning as the corresponding global
+     variables, except that they are used for the reentrant
+     versions of getopt.  */
+  int optind;
+  int opterr;
+  int optopt;
+  char *optarg;
+
+  /* Internal members.  */
+
+  /* True if the internal members have been initialized.  */
+  int __initialized;
+
+  /* The next char to be scanned in the option-element
+     in which the last option character we returned was found.
+     This allows us to pick up the scan where we left off.
+
+     If this is zero, or a null string, it means resume the scan
+     by advancing to the next ARGV-element.  */
+  char *__nextchar;
+
+  /* See __ord above.  */
+  enum __ord __ordering;
+
+  /* Handle permutation of arguments.  */
+
+  /* Describe the part of ARGV that contains non-options that have
+     been skipped.  'first_nonopt' is the index in ARGV of the first
+     of them; 'last_nonopt' is the index after the last of them.  */
+
+  int __first_nonopt;
+  int __last_nonopt;
+};
+
+/* The initializer is necessary to set OPTIND and OPTERR to their
+   default values and to clear the initialization flag.  */
+#define _GETOPT_DATA_INITIALIZER       { 1, 1 }
+
+extern int _getopt_internal_r (int ___argc, char **___argv,
+                              const char *__shortopts,
+                              const struct option *__longopts, int *__longind,
+                              int __long_only, struct _getopt_data *__data,
+                              int __posixly_correct);
+
+extern int _getopt_long_r (int ___argc, char **___argv,
+                          const char *__shortopts,
+                          const struct option *__longopts, int *__longind,
+                          struct _getopt_data *__data);
+
+extern int _getopt_long_only_r (int ___argc, char **___argv,
+                               const char *__shortopts,
+                               const struct option *__longopts,
+                               int *__longind,
+                               struct _getopt_data *__data);
+
+#endif /* getopt_int.h */
diff --git a/json.cpp b/json.cpp
new file mode 100644 (file)
index 0000000..64eea69
--- /dev/null
+++ b/json.cpp
@@ -0,0 +1,517 @@
+/*
+ * json.cpp
+ *
+ * Home page of code is: https://www.smartmontools.org
+ *
+ * Copyright (C) 2017-18 Christian Franke
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
+
+#include "json.h"
+
+const char * json_cvsid = "$Id: json.cpp 4830 2018-11-02 21:21:48Z chrfranke $"
+  JSON_H_CVSID;
+
+#include "sg_unaligned.h"
+#include "utility.h" // uint128_*()
+
+#include <inttypes.h>
+#include <stdexcept>
+
+static void jassert_failed(int line, const char * expr)
+{
+  char msg[128];
+  // Avoid __FILE__ as it may break reproducible builds
+  snprintf(msg, sizeof(msg), "json.cpp(%d): Assertion failed: %s", line, expr);
+  throw std::logic_error(msg);
+}
+
+#define jassert(expr) (!(expr) ? jassert_failed(__LINE__, #expr) : (void)0)
+
+static void check_key(const char * key)
+{
+  // Limit: object keys should be valid identifiers (lowercase only)
+  char c = key[0];
+  jassert('a' <= c && c <= 'z');
+  for (int i = 1; (c = key[i]); i++)
+    jassert(('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || (c == '_'));
+}
+
+json::ref::ref(json & js, const char * key)
+: m_js(js)
+{
+  check_key(key);
+  m_path.push_back(node_info(key));
+}
+
+json::ref::ref(const ref & base, const char * key)
+: m_js(base.m_js), m_path(base.m_path)
+{
+  check_key(key);
+  m_path.push_back(node_info(key));
+}
+
+json::ref::ref(const ref & base, int index)
+: m_js(base.m_js), m_path(base.m_path)
+{
+  jassert(0 <= index && index < 10000); // Limit: large arrays not supported
+  m_path.push_back(node_info(index));
+}
+
+json::ref::ref(const ref & base, const char * /*dummy*/, const char * key_suffix)
+: m_js(base.m_js), m_path(base.m_path)
+{
+  int n = (int)m_path.size(), i;
+  for (i = n; --i >= 0; ) {
+    std::string & base_key = m_path[i].key;
+    if (base_key.empty())
+      continue; // skip array
+    base_key += key_suffix;
+    break;
+  }
+  jassert(i >= 0); // Limit: top level element must be an object
+}
+
+void json::ref::operator=(bool value)
+{
+  m_js.set_bool(m_path, value);
+}
+
+void json::ref::operator=(long long value)
+{
+  m_js.set_int64(m_path, (int64_t)value);
+}
+
+void json::ref::operator=(unsigned long long value)
+{
+  m_js.set_uint64(m_path, (uint64_t)value);
+}
+
+void json::ref::operator=(int value)
+{
+  operator=((long long)value);
+}
+
+void json::ref::operator=(unsigned value)
+{
+  operator=((unsigned long long)value);
+}
+
+void json::ref::operator=(long value)
+{
+  operator=((long long)value);
+}
+
+void json::ref::operator=(unsigned long value)
+{
+  operator=((unsigned long long)value);
+}
+
+void json::ref::operator=(const std::string & value)
+{
+  m_js.set_string(m_path, value);
+}
+
+void json::ref::operator=(const char * value)
+{
+  jassert(value); // Limit: null not supported
+  operator=(std::string(value));
+}
+
+void json::ref::set_uint128(uint64_t value_hi, uint64_t value_lo)
+{
+  if (!value_hi)
+    operator=((unsigned long long)value_lo);
+  else
+    m_js.set_uint128(m_path, value_hi, value_lo);
+}
+
+bool json::ref::set_if_safe_uint64(uint64_t value)
+{
+  if (!is_safe_uint(value))
+    return false;
+  operator=((unsigned long long)value);
+  return true;
+}
+
+bool json::ref::set_if_safe_uint128(uint64_t value_hi, uint64_t value_lo)
+{
+  if (value_hi)
+    return false;
+  return set_if_safe_uint64(value_lo);
+}
+
+bool json::ref::set_if_safe_le128(const void * pvalue)
+{
+  return set_if_safe_uint128(sg_get_unaligned_le64((const uint8_t *)pvalue + 8),
+                             sg_get_unaligned_le64(                 pvalue    ));
+}
+
+void json::ref::set_unsafe_uint64(uint64_t value)
+{
+  // Output as number "KEY"
+  operator=((unsigned long long)value);
+  if (!m_js.m_verbose && is_safe_uint(value))
+    return;
+  // Output as string "KEY_s"
+  char s[32];
+  snprintf(s, sizeof(s), "%" PRIu64, value);
+  with_suffix("_s") = s;
+}
+
+void json::ref::set_unsafe_uint128(uint64_t value_hi, uint64_t value_lo)
+{
+  if (!m_js.m_verbose && !value_hi)
+    set_unsafe_uint64(value_lo);
+  else {
+    // Output as number "KEY", string "KEY_s" and LE byte array "KEY_le[]"
+    m_js.m_uint128_output = true;
+    set_uint128(value_hi, value_lo);
+    char s[64];
+    with_suffix("_s") = uint128_hilo_to_str(s, value_hi, value_lo);
+    ref le = with_suffix("_le");
+    for (int i = 0; i < 8; i++) {
+      uint64_t v = (value_lo >> (i << 3));
+      if (!v && !value_hi)
+        break;
+      le[i] = v & 0xff;
+    }
+    for (int i = 0; i < 8; i++) {
+      uint64_t v = value_hi >> (i << 3);
+      if (!v)
+        break;
+      le[8 + i] = v & 0xff;
+    }
+  }
+}
+
+void json::ref::set_unsafe_le128(const void * pvalue)
+{
+  set_unsafe_uint128(sg_get_unaligned_le64((const uint8_t *)pvalue + 8),
+                     sg_get_unaligned_le64(                 pvalue    ));
+}
+
+json::node::node()
+: type(nt_unset),
+  intval(0),
+  intval_hi(0)
+{
+}
+
+json::node::node(const std::string & key_)
+: type(nt_unset),
+  intval(0),
+  intval_hi(0),
+  key(key_)
+{
+}
+
+json::node::~node()
+{
+  for (size_t i = 0; i < childs.size(); i++)
+    delete childs[i];
+}
+
+json::node::const_iterator::const_iterator(const json::node * node_p, bool sorted)
+: m_node_p(node_p),
+  m_use_map(sorted && node_p->type == nt_object),
+  m_child_idx(0)
+{
+  if (m_use_map)
+    m_key_iter = node_p->key2index.begin();
+}
+
+bool json::node::const_iterator::at_end() const
+{
+  if (m_use_map)
+    return (m_key_iter == m_node_p->key2index.end());
+  else
+    return (m_child_idx >= m_node_p->childs.size());
+}
+
+unsigned json::node::const_iterator::array_index() const
+{
+  jassert(m_node_p->type == nt_array);
+  return m_child_idx;
+}
+
+void json::node::const_iterator::operator++()
+{
+  if (m_use_map)
+    ++m_key_iter;
+  else
+    ++m_child_idx;
+}
+
+const json::node * json::node::const_iterator::operator*() const
+{
+  if (m_use_map)
+    return m_node_p->childs[m_key_iter->second];
+  else
+    return m_node_p->childs[m_child_idx];
+}
+
+json::node * json::find_or_create_node(const json::node_path & path, node_type type)
+{
+  node * p = &m_root_node;
+  for (unsigned i = 0; i < path.size(); i++) {
+    const node_info & pi = path[i];
+    if (!pi.key.empty()) {
+      // Object
+      if (p->type == nt_unset)
+        p->type = nt_object;
+      else
+        jassert(p->type == nt_object); // Limit: type change not supported
+      // Existing or new object element?
+      node::keymap::iterator ni = p->key2index.find(pi.key);
+      node * p2;
+      if (ni != p->key2index.end()) {
+        // Object element exists
+        p2 = p->childs[ni->second];
+      }
+      else {
+        // Create new object element
+        p->key2index[pi.key] = (unsigned)p->childs.size();
+        p2 = new node(pi.key);
+        p->childs.push_back(p2);
+      }
+      jassert(p2 && p2->key == pi.key);
+      p = p2;
+    }
+
+    else {
+      // Array
+      if (p->type == nt_unset)
+        p->type = nt_array;
+      else
+        jassert(p->type == nt_array); // Limit: type change not supported
+      node * p2;
+      // Existing or new array element?
+      if (pi.index < (int)p->childs.size()) {
+        // Array index exists
+        p2 = p->childs[pi.index];
+        if (!p2) // Already created ?
+          p->childs[pi.index] = p2 = new node;
+      }
+      else {
+        // Grow array, fill gap, create new element
+        p->childs.resize(pi.index + 1);
+        p->childs[pi.index] = p2 = new node;
+      }
+      jassert(p2 && p2->key.empty());
+      p = p2;
+    }
+  }
+
+  if (   p->type == nt_unset
+      || (   nt_int <= p->type && p->type <= nt_uint128
+          && nt_int <=    type &&    type <= nt_uint128))
+    p->type = type;
+  else
+    jassert(p->type == type); // Limit: type change not supported
+  return p;
+}
+
+json::json()
+: m_enabled(false),
+  m_verbose(false),
+  m_uint128_output(false)
+{
+}
+
+void json::set_bool(const node_path & path, bool value)
+{
+  if (!m_enabled)
+    return;
+  find_or_create_node(path, nt_bool)->intval = (value ? 1 : 0);
+}
+
+void json::set_int64(const node_path & path, int64_t value)
+{
+  if (!m_enabled)
+    return;
+  find_or_create_node(path, nt_int)->intval = (uint64_t)value;
+}
+
+void json::set_uint64(const node_path & path, uint64_t value)
+{
+  if (!m_enabled)
+    return;
+  find_or_create_node(path, nt_uint)->intval = value;
+}
+
+void json::set_uint128(const node_path & path, uint64_t value_hi, uint64_t value_lo)
+{
+  if (!m_enabled)
+    return;
+  node * p = find_or_create_node(path, nt_uint128);
+  p->intval_hi = value_hi;
+  p->intval = value_lo;
+}
+
+void json::set_string(const node_path & path, const std::string & value)
+{
+  if (!m_enabled)
+    return;
+  find_or_create_node(path, nt_string)->strval = value;
+}
+
+static void print_string(FILE * f, const char * s)
+{
+  putc('"', f);
+  for (int i = 0; s[i]; i++) {
+    char c = s[i];
+    if (c == '"' || c == '\\')
+      putc('\\', f);
+    else if (c == '\t') {
+      putc('\\', f); c = 't';
+    }
+    else if ((unsigned char)c < ' ')
+      c = '?'; // Not ' '-'~', '\t' or UTF-8
+    putc(c, f);
+  }
+  putc('"', f);
+}
+
+void json::print_json(FILE * f, bool pretty, bool sorted, const node * p, int level)
+{
+  if (!p->key.empty())
+    fprintf(f, "\"%s\":%s", p->key.c_str(), (pretty ? " " : ""));
+
+  switch (p->type) {
+    case nt_object:
+    case nt_array:
+      putc((p->type == nt_object ? '{' : '['), f);
+      if (!p->childs.empty()) {
+        bool first = true;
+        for (node::const_iterator it(p, sorted); !it.at_end(); ++it) {
+          if (!first)
+            putc(',', f);
+          if (pretty)
+            fprintf(f, "\n%*s", (level + 1) * 2, "");
+          const node * p2 = *it;
+          if (!p2) {
+            // Unset element of sparse array
+            jassert(p->type == nt_array);
+            fputs("null", f);
+          }
+          else {
+            // Recurse
+            print_json(f, pretty, sorted, p2, level + 1);
+          }
+          first = false;
+        }
+        if (pretty)
+          fprintf(f, "\n%*s", level * 2, "");
+      }
+      putc((p->type == nt_object ? '}' : ']'), f);
+      break;
+
+    case nt_bool:
+      fputs((p->intval ? "true" : "false"), f);
+      break;
+
+    case nt_int:
+      fprintf(f, "%" PRId64, (int64_t)p->intval);
+      break;
+
+    case nt_uint:
+      fprintf(f, "%" PRIu64, p->intval);
+      break;
+
+    case nt_uint128:
+      {
+        char buf[64];
+        fputs(uint128_hilo_to_str(buf, p->intval_hi, p->intval), f);
+      }
+      break;
+
+    case nt_string:
+      print_string(f, p->strval.c_str());
+      break;
+
+    default: jassert(false);
+  }
+}
+
+void json::print_flat(FILE * f, bool sorted, const node * p, std::string & path)
+{
+  switch (p->type) {
+    case nt_object:
+    case nt_array:
+      fprintf(f, "%s = %s;\n", path.c_str(), (p->type == nt_object ? "{}" : "[]"));
+      if (!p->childs.empty()) {
+        unsigned len = path.size();
+        for (node::const_iterator it(p, sorted); !it.at_end(); ++it) {
+          const node * p2 = *it;
+          if (p->type == nt_array) {
+            char buf[10]; snprintf(buf, sizeof(buf), "[%u]", it.array_index());
+            path += buf;
+          }
+          else {
+            path += '.'; path += p2->key;
+          }
+          if (!p2) {
+            // Unset element of sparse array
+            jassert(p->type == nt_array);
+            fprintf(f, "%s = null;\n", path.c_str());
+          }
+          else {
+            // Recurse
+            print_flat(f, sorted, p2, path);
+          }
+          path.erase(len);
+        }
+      }
+      break;
+
+    case nt_bool:
+      fprintf(f, "%s = %s;\n", path.c_str(), (p->intval ? "true" : "false"));
+      break;
+
+    case nt_int:
+      fprintf(f, "%s = %" PRId64 ";\n", path.c_str(), (int64_t)p->intval);
+      break;
+
+    case nt_uint:
+      fprintf(f, "%s = %" PRIu64 ";\n", path.c_str(), p->intval);
+      break;
+
+    case nt_uint128:
+      {
+        char buf[64];
+        fprintf(f, "%s = %s;\n", path.c_str(),
+                uint128_hilo_to_str(buf, p->intval_hi, p->intval));
+      }
+      break;
+
+    case nt_string:
+      fprintf(f, "%s = ", path.c_str());
+      print_string(f, p->strval.c_str());
+      fputs(";\n", f);
+      break;
+
+    default: jassert(false);
+  }
+}
+
+void json::print(FILE * f, const print_options & options) const
+{
+  if (m_root_node.type == nt_unset)
+    return;
+  jassert(m_root_node.type == nt_object);
+
+  if (!options.flat) {
+    print_json(f, options.pretty, options.sorted, &m_root_node, 0);
+    if (options.pretty)
+      putc('\n', f);
+  }
+  else {
+    std::string path("json");
+    print_flat(f, options.sorted, &m_root_node, path);
+  }
+}
diff --git a/json.h b/json.h
new file mode 100644 (file)
index 0000000..79be323
--- /dev/null
+++ b/json.h
@@ -0,0 +1,195 @@
+/*
+ * json.h
+ *
+ * Home page of code is: https://www.smartmontools.org
+ *
+ * Copyright (C) 2017-18 Christian Franke
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef JSON_H_CVSID
+#define JSON_H_CVSID "$Id: json.h 4825 2018-10-25 19:47:35Z chrfranke $"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string>
+#include <vector>
+#include <map>
+
+/// Create and print JSON output.
+class json
+{
+private:
+  struct node_info
+  {
+    std::string key;
+    int index;
+
+    node_info()
+      : index(0) { }
+    explicit node_info(const char * key_)
+      : key(key_), index(0) { }
+    explicit node_info(int index_)
+      : index(index_) { }
+  };
+
+  typedef std::vector<node_info> node_path;
+
+public:
+  /// Return true if value is a safe JSON integer.
+  /// Same as Number.isSafeInteger(value) in JavaScript.
+  static bool is_safe_uint(unsigned long long value)
+    { return (value < (1ULL << 53)); }
+
+  json();
+
+  /// Reference to a JSON element.
+  class ref
+  {
+  public:
+    /// Return reference to object element.
+    ref operator[](const char * key) const
+      { return ref(*this, key); };
+
+    /// Return reference to array element.
+    ref operator[](int index) const
+      { return ref(*this, index); };
+
+    // Assignment operators create or change element.
+    void operator=(bool value);
+
+    void operator=(int value);
+    void operator=(unsigned value);
+    void operator=(long value);
+    void operator=(unsigned long value);
+    void operator=(long long value);
+    void operator=(unsigned long long value);
+
+    void operator=(const char * value);
+    void operator=(const std::string & value);
+
+    /// Return reference to element with KEY_SUFFIX appended to last key.
+    ref with_suffix(const char * key_suffix) const
+      { return ref(*this, "", key_suffix); }
+
+    void set_uint128(uint64_t value_hi, uint64_t value_lo);
+
+    // Output only if safe integer.
+    bool set_if_safe_uint64(uint64_t value);
+    bool set_if_safe_uint128(uint64_t value_hi, uint64_t value_lo);
+    bool set_if_safe_le128(const void * pvalue);
+
+    // If unsafe integer, output also as string with key "NUMBER_s".
+    void set_unsafe_uint64(uint64_t value);
+    void set_unsafe_uint128(uint64_t value_hi, uint64_t value_lo);
+    void set_unsafe_le128(const void * pvalue);
+
+  private:
+    friend class json;
+    ref(json & js, const char * key);
+    ref(const ref & base, const char * key);
+    ref(const ref & base, int index);
+    ref(const ref & base, const char * /*dummy*/, const char * key_suffix);
+
+    json & m_js;
+    node_path m_path;
+  };
+
+  /// Return reference to element of top level object.
+  ref operator[](const char * key)
+    { return ref(*this, key); };
+
+  /// Enable/disable JSON output.
+  void enable(bool yes = true)
+    { m_enabled = yes; }
+
+  /// Return true if enabled.
+  bool is_enabled() const
+    { return m_enabled; }
+
+  /// Enable/disable extra string output for safe integers also.
+  void set_verbose(bool yes = true)
+    { m_verbose = yes; }
+
+  /// Return true if any 128-bit value has been output.
+  bool has_uint128_output() const
+    { return m_uint128_output; }
+
+  /// Options for print().
+  struct print_options {
+    bool pretty; //< Pretty-print output.
+    bool sorted; //< Sort object keys.
+    bool flat;   //< Print flat format.
+    print_options()
+      : pretty(false), sorted(false), flat(false) { }
+  };
+
+  /// Print JSON tree to a file.
+  void print(FILE * f, const print_options & options) const;
+
+private:
+  enum node_type {
+    nt_unset, nt_object, nt_array,
+    nt_bool, nt_int, nt_uint, nt_uint128, nt_string
+  };
+
+  struct node
+  {
+    node();
+    explicit node(const std::string & key_);
+    ~node();
+
+    node_type type;
+
+    uint64_t intval, intval_hi;
+    std::string strval;
+
+    std::string key;
+    std::vector<node *> childs;
+    typedef std::map<std::string, unsigned> keymap;
+    keymap key2index;
+
+    class const_iterator
+    {
+    public:
+      const_iterator(const node * node_p, bool sorted);
+      bool at_end() const;
+      unsigned array_index() const;
+      void operator++();
+      const node * operator*() const;
+
+    private:
+      const node * m_node_p;
+      bool m_use_map;
+      unsigned m_child_idx;
+      keymap::const_iterator m_key_iter;
+    };
+
+#if __cplusplus >= 201103
+    node(const node &) = delete;
+    void operator=(const node &) = delete;
+#else
+    private: node(const node &); void operator=(const node &);
+#endif
+  };
+
+  bool m_enabled;
+  bool m_verbose;
+  bool m_uint128_output;
+
+  node m_root_node;
+
+  node * find_or_create_node(const node_path & path, node_type type);
+
+  void set_bool(const node_path & path, bool value);
+  void set_int64(const node_path & path, int64_t value);
+  void set_uint64(const node_path & path, uint64_t value);
+  void set_uint128(const node_path & path, uint64_t value_hi, uint64_t value_lo);
+  void set_string(const node_path & path, const std::string & value);
+
+  static void print_json(FILE * f, bool pretty, bool sorted, const node * p, int level);
+  static void print_flat(FILE * f, bool sorted, const node * p, std::string & path);
+};
+
+#endif // JSON_H_CVSID
index 050733e31b8223ea98b0c97ac192ac9ccc165640..072160ab385e96f30cd8afde17d8be0c96f20cbf 100644 (file)
@@ -4,20 +4,13 @@
  * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2003-11 Philip Williams, Bruce Allen
- * Copyright (C) 2008-16 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2008-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
-#include "int64.h"
+
 #include <stdio.h>
 #include "atacmds.h"
 #include "knowndrives.h"
@@ -32,7 +25,7 @@
 
 #include <stdexcept>
 
-const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 4208 2016-01-22 19:45:35Z chrfranke $"
+const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 4842 2018-12-02 16:07:26Z chrfranke $"
                                      KNOWNDRIVES_H_CVSID;
 
 #define MODEL_STRING_LENGTH                         40
@@ -166,7 +159,7 @@ static inline dbentry_type get_dbentry_type(const drive_settings * dbentry)
 // Compile regular expression, print message on failure.
 static bool compile(regular_expression & regex, const char *pattern)
 {
-  if (!regex.compile(pattern, REG_EXTENDED)) {
+  if (!regex.compile(pattern)) {
     pout("Internal error: unable to compile regular expression \"%s\": %s\n"
          "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n",
       pattern, regex.get_errmsg());
@@ -723,7 +716,7 @@ static parse_ptr get_token(parse_ptr src, token_info & token, const char * path,
           }
           token.value += c;
         }
-        // Lookahead to detect string constant concatentation
+        // Lookahead to detect string constant concatenation
         src = skip_white(++src, path, line);
       } while (*src == '"');
       break;
@@ -783,7 +776,7 @@ static bool parse_drive_database(parse_ptr src, drive_database & db, const char
           case 1: case 2:
             if (!token.value.empty()) {
               regular_expression regex;
-              if (!regex.compile(token.value.c_str(), REG_EXTENDED)) {
+              if (!regex.compile(token.value.c_str())) {
                 pout("%s(%d): Error in regular expression: %s\n", path, token.line, regex.get_errmsg());
                 ok = false;
               }
index 3c754e7d96888e0cd6a858fdcbf641ab5424fe1e..92191b6bf404f5a7498ac7317c502acbdfb104e3 100644 (file)
@@ -6,20 +6,13 @@
  * Copyright (C) 2003-11 Philip Williams, Bruce Allen
  * Copyright (C) 2008-15 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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef KNOWNDRIVES_H_
 #define KNOWNDRIVES_H_
 
-#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h 4162 2015-10-31 16:36:16Z chrfranke $\n"
+#define KNOWNDRIVES_H_CVSID "$Id: knowndrives.h 4760 2018-08-19 18:45:53Z chrfranke $\n"
 
 // Structure to store drive database entries, see drivedb.h for a description.
 struct drive_settings {
index 9c2bf6e8e5e47d76ddfaf93398da56342ced17ed..fc07a050faa8afd2db7f130d874a663644494771 100644 (file)
@@ -1,3 +1,13 @@
+/*
+ * megaraid.h
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2008 Jordan Hargrave
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
 int megaraid_io_interface(int device, int target, struct scsi_cmnd_io *, int);
 
 #undef u32
index b3f6a0f180051f5f1b1262845183ddd03a34eba0..ba602ae08f7c7f1fa4452b4f9b0844dda388ac5d 100644 (file)
@@ -5,20 +5,13 @@
  *
  * Copyright (C) 2016 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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
 #include "nvmecmds.h"
 
-const char * nvmecmds_cvsid = "$Id: nvmecmds.cpp 4313 2016-05-01 16:17:53Z chrfranke $"
+const char * nvmecmds_cvsid = "$Id: nvmecmds.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
   NVMECMDS_H_CVSID;
 
 #include "dev_interface.h"
@@ -56,7 +49,7 @@ static void debug_hex_dump(const void * data, unsigned size)
       sz = size;
   }
 
-  dStrHex(p, sz, 0);
+  dStrHex((const uint8_t *)p, sz, 0);
   if (sz < size)
     pout(" ...\n");
 }
@@ -196,7 +189,8 @@ bool nvme_read_id_ns(nvme_device * device, unsigned nsid, nvme_id_ns & id_ns)
 }
 
 // Read NVMe log page with identifier LID.
-bool nvme_read_log_page(nvme_device * device, unsigned char lid, void * data, unsigned size)
+bool nvme_read_log_page(nvme_device * device, unsigned char lid, void * data,
+                       unsigned size, bool broadcast_nsid)
 {
   if (!(4 <= size && size <= 0x4000 && (size % 4) == 0))
     throw std::logic_error("nvme_read_log_page(): invalid size");
@@ -204,7 +198,7 @@ bool nvme_read_log_page(nvme_device * device, unsigned char lid, void * data, un
   memset(data, 0, size);
   nvme_cmd_in in;
   in.set_data_in(nvme_admin_get_log_page, data, size);
-  in.nsid = device->get_nsid();
+  in.nsid = broadcast_nsid ? 0xffffffff : device->get_nsid();
   in.cdw10 = lid | (((size / 4) - 1) << 16);
 
   return nvme_pass_through(device, in);
@@ -213,7 +207,7 @@ bool nvme_read_log_page(nvme_device * device, unsigned char lid, void * data, un
 // Read NVMe Error Information Log.
 bool nvme_read_error_log(nvme_device * device, nvme_error_log_page * error_log, unsigned num_entries)
 {
-  if (!nvme_read_log_page(device, 0x01, error_log, num_entries * sizeof(*error_log)))
+  if (!nvme_read_log_page(device, 0x01, error_log, num_entries * sizeof(*error_log), true))
     return false;
 
   if (isbigendian()) {
@@ -234,7 +228,7 @@ bool nvme_read_error_log(nvme_device * device, nvme_error_log_page * error_log,
 // Read NVMe SMART/Health Information log.
 bool nvme_read_smart_log(nvme_device * device, nvme_smart_log & smart_log)
 {
-  if (!nvme_read_log_page(device, 0x02, &smart_log, sizeof(smart_log)))
+  if (!nvme_read_log_page(device, 0x02, &smart_log, sizeof(smart_log), true))
     return false;
 
   if (isbigendian()) {
index 3e8f62f84789379ef3f2500f9fa1d265ac82b2c2..96d4c7c4c0da49b32314cc855ab2a8efbd49009c 100644 (file)
@@ -3,29 +3,22 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2016 Christian Franke
+ * Copyright (C) 2016-18 Christian Franke
  *
  * Original code from <linux/nvme.h>:
  *   Copyright (C) 2011-2014 Intel Corporation
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef NVMECMDS_H
 #define NVMECMDS_H
 
-#define NVMECMDS_H_CVSID "$Id: nvmecmds.h 4488 2017-09-25 11:54:15Z samm2 $"
+#define NVMECMDS_H_CVSID "$Id: nvmecmds.h 4842 2018-12-02 16:07:26Z chrfranke $"
 
-#include "int64.h"
+#include <stdint.h>
 
-// The code below was orginally imported from <linux/nvme.h> include file from
+// The code below was originally imported from <linux/nvme.h> include file from
 // Linux kernel sources.  Types from <linux/types.h> were replaced.
 // Symbol names are unchanged but placed in a namespace to allow inclusion
 // of the original <linux/nvme.h>.
@@ -33,19 +26,6 @@ namespace smartmontools {
 
 ////////////////////////////////////////////////////////////////////////////
 // BEGIN: From <linux/nvme.h>
-/*
- * Definitions for the NVM Express interface
- * Copyright (c) 2011-2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- */
 
 struct nvme_error_log_page {
   uint64_t        error_count;
@@ -248,7 +228,8 @@ bool nvme_read_id_ctrl(nvme_device * device, smartmontools::nvme_id_ctrl & id_ct
 bool nvme_read_id_ns(nvme_device * device, unsigned nsid, smartmontools::nvme_id_ns & id_ns);
 
 // Read NVMe log page with identifier LID.
-bool nvme_read_log_page(nvme_device * device, unsigned char lid, void * data, unsigned size);
+bool nvme_read_log_page(nvme_device * device, unsigned char lid, void * data,
+                       unsigned size, bool broadcast_nsid);
 
 // Read NVMe Error Information Log.
 bool nvme_read_error_log(nvme_device * device, smartmontools::nvme_error_log_page * error_log,
index fe7053e13c9ae747e66716d7eb04daaf03573662..a2cdb4b78531d3a20ee9ca15b735182ab30f85f4 100644 (file)
@@ -3,31 +3,28 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2016-17 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2016-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
+#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
+
 #include "nvmeprint.h"
 
-const char * nvmeprint_cvsid = "$Id: nvmeprint.cpp 4580 2017-11-03 19:41:14Z chrfranke $"
+const char * nvmeprint_cvsid = "$Id: nvmeprint.cpp 4859 2018-12-16 18:09:44Z chrfranke $"
   NVMEPRINT_H_CVSID;
 
-#include "int64.h"
 #include "utility.h"
 #include "dev_interface.h"
 #include "nvmecmds.h"
 #include "atacmds.h" // dont_print_serial_number
 #include "scsicmds.h" // dStrHex()
 #include "smartctl.h"
+#include "sg_unaligned.h"
+
+#include <inttypes.h>
 
 using namespace smartmontools;
 
@@ -58,9 +55,11 @@ static const char * le128_to_str(char (& str)[64], uint64_t hi, uint64_t lo, uns
     }
   }
   else {
-    // More than 64-bit, print approximate value, prepend ~ flag
-    snprintf(str, sizeof(str), "~%.0f",
-             hi * (0xffffffffffffffffULL + 1.0) + lo);
+    // More than 64-bit, prepend '~' flag on low precision
+    int i = 0;
+    if (uint128_to_str_precision_bits() < 128)
+      str[i++] = '~';
+    uint128_hilo_to_str(str + i, (int)sizeof(str) - i, hi, lo);
   }
 
   return str;
@@ -88,6 +87,13 @@ static const char * lbacap_to_str(char (& str)[64], uint64_t lba_cnt, int lba_bi
   return le128_to_str(str, (lba_cnt >> (64 - lba_bits)), (lba_cnt << lba_bits), 1);
 }
 
+// Output capacity specified as 64bit LBA count to JSON
+static void lbacap_to_js(const json::ref & jref, uint64_t lba_cnt, int lba_bits)
+{
+  jref["blocks"].set_unsafe_uint64(lba_cnt);
+  jref["bytes"].set_unsafe_uint128((lba_cnt >> (64 - lba_bits)), (lba_cnt << lba_bits));
+}
+
 // Format a Kelvin temperature value in Celsius.
 static const char * kelvin_to_str(char (& str)[64], int k)
 {
@@ -98,73 +104,96 @@ static const char * kelvin_to_str(char (& str)[64], int k)
   return str;
 }
 
-static inline unsigned le16_to_uint(const unsigned char (& val)[2])
-{
-  return ((val[1] << 8) | val[0]);
-}
-
 static void print_drive_info(const nvme_id_ctrl & id_ctrl, const nvme_id_ns & id_ns,
   unsigned nsid, bool show_all)
 {
   char buf[64];
-  pout("Model Number:                       %s\n", format_char_array(buf, id_ctrl.mn));
-  if (!dont_print_serial_number)
-    pout("Serial Number:                      %s\n", format_char_array(buf, id_ctrl.sn));
-  pout("Firmware Version:                   %s\n", format_char_array(buf, id_ctrl.fr));
+  jout("Model Number:                       %s\n", format_char_array(buf, id_ctrl.mn));
+  jglb["model_name"] = buf;
+  if (!dont_print_serial_number) {
+    jout("Serial Number:                      %s\n", format_char_array(buf, id_ctrl.sn));
+    jglb["serial_number"] = buf;
+  }
+
+  jout("Firmware Version:                   %s\n", format_char_array(buf, id_ctrl.fr));
+  jglb["firmware_version"] = buf;
 
   // Vendor and Subsystem IDs are usually equal
   if (show_all || id_ctrl.vid != id_ctrl.ssvid) {
-    pout("PCI Vendor ID:                      0x%04x\n", id_ctrl.vid);
-    pout("PCI Vendor Subsystem ID:            0x%04x\n", id_ctrl.ssvid);
+    jout("PCI Vendor ID:                      0x%04x\n", id_ctrl.vid);
+    jout("PCI Vendor Subsystem ID:            0x%04x\n", id_ctrl.ssvid);
   }
   else {
-    pout("PCI Vendor/Subsystem ID:            0x%04x\n", id_ctrl.vid);
+    jout("PCI Vendor/Subsystem ID:            0x%04x\n", id_ctrl.vid);
   }
+  jglb["nvme_pci_vendor"]["id"] = id_ctrl.vid;
+  jglb["nvme_pci_vendor"]["subsystem_id"] = id_ctrl.ssvid;
 
-  pout("IEEE OUI Identifier:                0x%02x%02x%02x\n",
+  jout("IEEE OUI Identifier:                0x%02x%02x%02x\n",
        id_ctrl.ieee[2], id_ctrl.ieee[1], id_ctrl.ieee[0]);
+  jglb["nvme_ieee_oui_identifier"] = sg_get_unaligned_le(3, id_ctrl.ieee);
 
   // Capacity info is optional for devices without namespace management
   if (show_all || le128_is_non_zero(id_ctrl.tnvmcap) || le128_is_non_zero(id_ctrl.unvmcap)) {
-    pout("Total NVM Capacity:                 %s\n", le128_to_str(buf, id_ctrl.tnvmcap, 1));
-    pout("Unallocated NVM Capacity:           %s\n", le128_to_str(buf, id_ctrl.unvmcap, 1));
+    jout("Total NVM Capacity:                 %s\n", le128_to_str(buf, id_ctrl.tnvmcap, 1));
+    jglb["nvme_total_capacity"].set_unsafe_le128(id_ctrl.tnvmcap);
+    jout("Unallocated NVM Capacity:           %s\n", le128_to_str(buf, id_ctrl.unvmcap, 1));
+    jglb["nvme_unallocated_capacity"].set_unsafe_le128(id_ctrl.unvmcap);
   }
 
-  pout("Controller ID:                      %d\n", id_ctrl.cntlid);
+  jout("Controller ID:                      %d\n", id_ctrl.cntlid);
+  jglb["nvme_controller_id"] = id_ctrl.cntlid;
 
   // Print namespace info if available
-  pout("Number of Namespaces:               %u\n", id_ctrl.nn);
+  jout("Number of Namespaces:               %u\n", id_ctrl.nn);
+  jglb["nvme_number_of_namespaces"] = id_ctrl.nn;
 
   if (nsid && id_ns.nsze) {
     const char * align = &("  "[nsid < 10 ? 0 : (nsid < 100 ? 1 : 2)]);
     int fmt_lba_bits = id_ns.lbaf[id_ns.flbas & 0xf].ds;
 
+    json::ref jrns = jglb["nvme_namespaces"][0];
+    jrns["id"] = nsid;
+
     // Size and Capacity are equal if thin provisioning is not supported
     if (show_all || id_ns.ncap != id_ns.nsze || (id_ns.nsfeat & 0x01)) {
-      pout("Namespace %u Size:                 %s%s\n", nsid, align,
+      jout("Namespace %u Size:                 %s%s\n", nsid, align,
            lbacap_to_str(buf, id_ns.nsze, fmt_lba_bits));
-      pout("Namespace %u Capacity:             %s%s\n", nsid, align,
+      jout("Namespace %u Capacity:             %s%s\n", nsid, align,
            lbacap_to_str(buf, id_ns.ncap, fmt_lba_bits));
     }
     else {
-      pout("Namespace %u Size/Capacity:        %s%s\n", nsid, align,
+      jout("Namespace %u Size/Capacity:        %s%s\n", nsid, align,
            lbacap_to_str(buf, id_ns.nsze, fmt_lba_bits));
     }
+    lbacap_to_js(jrns["size"], id_ns.nsze, fmt_lba_bits);
+    lbacap_to_js(jrns["capacity"], id_ns.ncap, fmt_lba_bits);
+    lbacap_to_js(jglb["user_capacity"], id_ns.ncap, fmt_lba_bits); // TODO: use nsze?
+
     // Utilization may be always equal to Capacity if thin provisioning is not supported
     if (show_all || id_ns.nuse != id_ns.ncap || (id_ns.nsfeat & 0x01))
-      pout("Namespace %u Utilization:          %s%s\n", nsid, align,
+      jout("Namespace %u Utilization:          %s%s\n", nsid, align,
            lbacap_to_str(buf, id_ns.nuse, fmt_lba_bits));
+    lbacap_to_js(jrns["utilization"], id_ns.nuse, fmt_lba_bits);
 
-    pout("Namespace %u Formatted LBA Size:   %s%u\n", nsid, align, (1U << fmt_lba_bits));
+    jout("Namespace %u Formatted LBA Size:   %s%u\n", nsid, align, (1U << fmt_lba_bits));
+    jrns["formatted_lba_size"] = (1U << fmt_lba_bits);
+    jglb["logical_block_size"] = (1U << fmt_lba_bits);
 
-    if (show_all || nonempty(id_ns.eui64, sizeof(id_ns.eui64)))
-      pout("Namespace %u IEEE EUI-64:          %s%02x%02x%02x %02x%02x%02x%02x%02x\n",
+    if (show_all || nonempty(id_ns.eui64, sizeof(id_ns.eui64))) {
+      jout("Namespace %u IEEE EUI-64:          %s%02x%02x%02x %02x%02x%02x%02x%02x\n",
            nsid, align, id_ns.eui64[0], id_ns.eui64[1], id_ns.eui64[2], id_ns.eui64[3],
            id_ns.eui64[4], id_ns.eui64[5], id_ns.eui64[6], id_ns.eui64[7]);
+      jrns["eui64"]["oui"]    = sg_get_unaligned_be(3, id_ns.eui64);
+      jrns["eui64"]["ext_id"] = sg_get_unaligned_be(5, id_ns.eui64 + 3);
+    }
   }
 
-  char td[DATEANDEPOCHLEN]; dateandtimezone(td);
-  pout("Local Time is:                      %s\n", td);
+  time_t now = time(0);
+  char td[DATEANDEPOCHLEN]; dateandtimezoneepoch(td, now);
+  jout("Local Time is:                      %s\n", td);
+  jglb["local_time"]["time_t"] = now;
+  jglb["local_time"]["asctime"] = td;
 }
 
 // Format scaled power value.
@@ -270,60 +299,101 @@ static void print_drive_capabilities(const nvme_id_ctrl & id_ctrl, const nvme_id
 
 static void print_critical_warning(unsigned char w)
 {
-  pout("SMART overall-health self-assessment test result: %s\n",
+  jout("SMART overall-health self-assessment test result: %s\n",
        (!w ? "PASSED" : "FAILED!"));
+  jglb["smart_status"]["passed"] = !w;
+
+  json::ref jref = jglb["smart_status"]["nvme"];
+  jref["value"] = w;
 
   if (w) {
    if (w & 0x01)
-     pout("- available spare has fallen below threshold\n");
+     jout("- available spare has fallen below threshold\n");
+   jref["spare_below_threshold"] = !!(w & 0x01);
    if (w & 0x02)
-     pout("- temperature is above or below threshold\n");
+     jout("- temperature is above or below threshold\n");
+   jref["temperature_above_or_below_threshold"] = !!(w & 0x02);
    if (w & 0x04)
-     pout("- NVM subsystem reliability has been degraded\n");
+     jout("- NVM subsystem reliability has been degraded\n");
+   jref["reliability_degraded"] = !!(w & 0x04);
    if (w & 0x08)
-     pout("- media has been placed in read only mode\n");
+     jout("- media has been placed in read only mode\n");
+   jref["media_read_only"] = !!(w & 0x08);
    if (w & 0x10)
-     pout("- volatile memory backup device has failed\n");
+     jout("- volatile memory backup device has failed\n");
+   jref["volatile_memory_backup_failed"] = !!(w & 0x10);
    if (w & ~0x1f)
-     pout("- unknown critical warning(s) (0x%02x)\n", w & ~0x1f);
+     jout("- unknown critical warning(s) (0x%02x)\n", w & ~0x1f);
+   jref["other"] = w & ~0x1f;
   }
 
-  pout("\n");
+  jout("\n");
 }
 
-static void print_smart_log(const nvme_smart_log & smart_log, unsigned nsid,
+static void print_smart_log(const nvme_smart_log & smart_log,
   const nvme_id_ctrl & id_ctrl, bool show_all)
 {
+  json::ref jref = jglb["nvme_smart_health_information_log"];
   char buf[64];
-  pout("SMART/Health Information (NVMe Log 0x02, NSID 0x%x)\n", nsid);
-  pout("Critical Warning:                   0x%02x\n", smart_log.critical_warning);
-  pout("Temperature:                        %s\n",
-       kelvin_to_str(buf, le16_to_uint(smart_log.temperature)));
-  pout("Available Spare:                    %u%%\n", smart_log.avail_spare);
-  pout("Available Spare Threshold:          %u%%\n", smart_log.spare_thresh);
-  pout("Percentage Used:                    %u%%\n", smart_log.percent_used);
-  pout("Data Units Read:                    %s\n", le128_to_str(buf, smart_log.data_units_read, 1000*512));
-  pout("Data Units Written:                 %s\n", le128_to_str(buf, smart_log.data_units_written, 1000*512));
-  pout("Host Read Commands:                 %s\n", le128_to_str(buf, smart_log.host_reads));
-  pout("Host Write Commands:                %s\n", le128_to_str(buf, smart_log.host_writes));
-  pout("Controller Busy Time:               %s\n", le128_to_str(buf, smart_log.ctrl_busy_time));
-  pout("Power Cycles:                       %s\n", le128_to_str(buf, smart_log.power_cycles));
-  pout("Power On Hours:                     %s\n", le128_to_str(buf, smart_log.power_on_hours));
-  pout("Unsafe Shutdowns:                   %s\n", le128_to_str(buf, smart_log.unsafe_shutdowns));
-  pout("Media and Data Integrity Errors:    %s\n", le128_to_str(buf, smart_log.media_errors));
-  pout("Error Information Log Entries:      %s\n", le128_to_str(buf, smart_log.num_err_log_entries));
+  jout("SMART/Health Information (NVMe Log 0x02)\n");
+  jout("Critical Warning:                   0x%02x\n", smart_log.critical_warning);
+  jref["critical_warning"] = smart_log.critical_warning;
+
+  int k = sg_get_unaligned_le16(smart_log.temperature);
+  jout("Temperature:                        %s\n", kelvin_to_str(buf, k));
+  if (k) {
+    jref["temperature"] = k - 273;
+    jglb["temperature"]["current"] = k - 273;
+  }
+
+  jout("Available Spare:                    %u%%\n", smart_log.avail_spare);
+  jref["available_spare"] = smart_log.avail_spare;
+  jout("Available Spare Threshold:          %u%%\n", smart_log.spare_thresh);
+  jref["available_spare_threshold"] = smart_log.spare_thresh;
+  jout("Percentage Used:                    %u%%\n", smart_log.percent_used);
+  jref["percentage_used"] = smart_log.percent_used;
+  jout("Data Units Read:                    %s\n", le128_to_str(buf, smart_log.data_units_read, 1000*512));
+  jref["data_units_read"].set_unsafe_le128(smart_log.data_units_read);
+  jout("Data Units Written:                 %s\n", le128_to_str(buf, smart_log.data_units_written, 1000*512));
+  jref["data_units_written"].set_unsafe_le128(smart_log.data_units_written);
+  jout("Host Read Commands:                 %s\n", le128_to_str(buf, smart_log.host_reads));
+  jref["host_reads"].set_unsafe_le128(smart_log.host_reads);
+  jout("Host Write Commands:                %s\n", le128_to_str(buf, smart_log.host_writes));
+  jref["host_writes"].set_unsafe_le128(smart_log.host_writes);
+  jout("Controller Busy Time:               %s\n", le128_to_str(buf, smart_log.ctrl_busy_time));
+  jref["controller_busy_time"].set_unsafe_le128(smart_log.ctrl_busy_time);
+  jout("Power Cycles:                       %s\n", le128_to_str(buf, smart_log.power_cycles));
+  jref["power_cycles"].set_unsafe_le128(smart_log.power_cycles);
+  jglb["power_cycle_count"].set_if_safe_le128(smart_log.power_cycles);
+  jout("Power On Hours:                     %s\n", le128_to_str(buf, smart_log.power_on_hours));
+  jref["power_on_hours"].set_unsafe_le128(smart_log.power_on_hours);
+  jglb["power_on_time"]["hours"].set_if_safe_le128(smart_log.power_on_hours);
+  jout("Unsafe Shutdowns:                   %s\n", le128_to_str(buf, smart_log.unsafe_shutdowns));
+  jref["unsafe_shutdowns"].set_unsafe_le128(smart_log.unsafe_shutdowns);
+  jout("Media and Data Integrity Errors:    %s\n", le128_to_str(buf, smart_log.media_errors));
+  jref["media_errors"].set_unsafe_le128(smart_log.media_errors);
+  jout("Error Information Log Entries:      %s\n", le128_to_str(buf, smart_log.num_err_log_entries));
+  jref["num_err_log_entries"].set_unsafe_le128(smart_log.num_err_log_entries);
 
   // Temperature thresholds are optional
-  if (show_all || id_ctrl.wctemp || smart_log.warning_temp_time)
-    pout("Warning  Comp. Temperature Time:    %d\n", smart_log.warning_temp_time);
-  if (show_all || id_ctrl.cctemp || smart_log.critical_comp_time)
-    pout("Critical Comp. Temperature Time:    %d\n", smart_log.critical_comp_time);
+  if (show_all || id_ctrl.wctemp || smart_log.warning_temp_time) {
+    jout("Warning  Comp. Temperature Time:    %d\n", smart_log.warning_temp_time);
+    jref["warning_temp_time"] = smart_log.warning_temp_time;
+  }
+  if (show_all || id_ctrl.cctemp || smart_log.critical_comp_time) {
+    jout("Critical Comp. Temperature Time:    %d\n", smart_log.critical_comp_time);
+    jref["critical_comp_time"] = smart_log.critical_comp_time;
+  }
 
   // Temperature sensors are optional
   for (int i = 0; i < 8; i++) {
-    if (show_all || smart_log.temp_sensor[i])
-      pout("Temperature Sensor %d:               %s\n", i + 1,
-           kelvin_to_str(buf, smart_log.temp_sensor[i]));
+    int k = smart_log.temp_sensor[i];
+    if (show_all || k) {
+      jout("Temperature Sensor %d:               %s\n", i + 1,
+           kelvin_to_str(buf, k));
+      if (k)
+        jref["temperature_sensors"][i] = k - 273;
+    }
   }
   if (show_all || smart_log.thm_temp1_trans_count)
     pout("Thermal Temp. 1 Transition Count:   %d\n", smart_log.thm_temp1_trans_count);
@@ -396,7 +466,7 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
   // Read Identify Controller always
   nvme_id_ctrl id_ctrl;
   if (!nvme_read_id_ctrl(device, id_ctrl)) {
-    pout("Read NVMe Identify Controller failed: %s\n", device->get_errmsg());
+    jerr("Read NVMe Identify Controller failed: %s\n", device->get_errmsg());
     return FAILID;
   }
 
@@ -418,7 +488,7 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
     else {
         // Identify current namespace
         if (!nvme_read_id_ns(device, nsid, id_ns)) {
-          pout("Read NVMe Identify Namespace 0x%x failed: %s\n", nsid, device->get_errmsg());
+          jerr("Read NVMe Identify Namespace 0x%x failed: %s\n", nsid, device->get_errmsg());
           return FAILID;
         }
     }
@@ -439,7 +509,7 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
   if (options.smart_check_status || options.smart_vendor_attrib) {
     nvme_smart_log smart_log;
     if (!nvme_read_smart_log(device, smart_log)) {
-      pout("Read NVMe SMART/Health Information failed: %s\n\n", device->get_errmsg());
+      jerr("Read NVMe SMART/Health Information failed: %s\n\n", device->get_errmsg());
       return FAILSMART;
     }
 
@@ -450,7 +520,7 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
     }
 
     if (options.smart_vendor_attrib) {
-      print_smart_log(smart_log, device->get_nsid(), id_ctrl, show_all);
+      print_smart_log(smart_log, id_ctrl, show_all);
     }
   }
 
@@ -462,7 +532,7 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
       reinterpret_cast<nvme_error_log_page *>(error_log_buf.data());
 
     if (!nvme_read_error_log(device, error_log, num_entries)) {
-      pout("Read Error Information Log failed: %s\n\n", device->get_errmsg());
+      jerr("Read Error Information Log failed: %s\n\n", device->get_errmsg());
       return retval | FAILSMART;
     }
 
@@ -473,10 +543,22 @@ int nvmePrintMain(nvme_device * device, const nvme_print_options & options)
   if (options.log_page_size) {
     // Align size to dword boundary
     unsigned size = ((options.log_page_size + 4-1) / 4) * 4;
+    bool broadcast_nsid;
     raw_buffer log_buf(size);
 
-    if (!nvme_read_log_page(device, options.log_page, log_buf.data(), size)) {
-      pout("Read NVMe Log 0x%02x failed: %s\n\n", options.log_page, device->get_errmsg());
+    switch (options.log_page) {
+    case 1:
+    case 2:
+    case 3:
+      broadcast_nsid = true;
+      break;
+    default:
+      broadcast_nsid = false;
+      break;
+    }
+    if (!nvme_read_log_page(device, options.log_page, log_buf.data(),
+                           size, broadcast_nsid)) {
+      jerr("Read NVMe Log 0x%02x failed: %s\n\n", options.log_page, device->get_errmsg());
       return retval | FAILSMART;
     }
 
index 667396abd7afe0ef0277c1ee70b863f446f179fa..1d4a1faeb4aee13d8e0b95118d1e12f1fd84e561 100644 (file)
@@ -5,20 +5,13 @@
  *
  * Copyright (C) 2016 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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef NVMEPRINT_H
 #define NVMEPRINT_H
 
-#define NVMEPRINT_H_CVSID "$Id: nvmeprint.h 4311 2016-04-27 21:03:01Z chrfranke $"
+#define NVMEPRINT_H_CVSID "$Id: nvmeprint.h 4760 2018-08-19 18:45:53Z chrfranke $"
 
 #include "nvmecmds.h"
 
index 134f510efa9467ccad01d8ddbe36a2430a58d998..e2638c791b150b109bed5e9a9492898370c59550 100644 (file)
@@ -6,14 +6,7 @@
  * Copyright (C) 2004-8 Geoffrey Keating <geoffk@geoffk.org>
  * Copyright (C) 2014 Alex Samorukov <samm@os2.kiev.ua>
  *
- * 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
- *  along with smartmontools.  If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include <stdbool.h>
@@ -35,7 +28,7 @@
 #include <CoreFoundation/CoreFoundation.h>
 
 #include "config.h"
-#include "int64.h"
+
 #include "atacmds.h"
 #include "scsicmds.h"
 #include "nvmecmds.h"
 
 #define ARGUSED(x) ((void)(x))
 // Needed by '-V' option (CVS versioning) of smartd/smartctl
-const char *os_darwin_cpp_cvsid="$Id: os_darwin.cpp 4552 2017-10-11 10:11:35Z samm2 $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+const char *os_darwin_cpp_cvsid="$Id: os_darwin.cpp 4831 2018-11-09 07:18:23Z samm2 $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 // examples for smartctl
 static const char  smartctl_examples[] =
          "=================================================== SMARTCTL EXAMPLES =====\n\n"
          "  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"
@@ -78,7 +65,7 @@ static struct {
   IONVMeSMARTInterface **smartIfNVMe;
 } devices[20];
 
-const char * dev_darwin_cpp_cvsid = "$Id: os_darwin.cpp 4552 2017-10-11 10:11:35Z samm2 $"
+const char * dev_darwin_cpp_cvsid = "$Id: os_darwin.cpp 4831 2018-11-09 07:18:23Z samm2 $"
   DEV_INTERFACE_H_CVSID;
 
 /////////////////////////////////////////////////////////////////////////////
@@ -367,9 +354,9 @@ static int make_device_names (char*** devlist, const char* name) {
   {
     for (index = 0; index < result; index++)
       if ((*devlist)[index])
-      free ((*devlist)[index]);
-      free (*devlist);
-    }
+        free ((*devlist)[index]);
+    free (*devlist);
+  }
   if(!result) // no devs found
     return 0;
 
@@ -416,7 +403,7 @@ bool darwin_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou
   int rc = 0;
   
   if (! ifp)
-    return -1;
+    return false;
   smartIf = *ifp;
   clear_err(); errno = 0;
   do {
@@ -470,8 +457,9 @@ bool darwin_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou
             err = -1; // thresholds exceeded condition
             out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
           }
-          else
+          else {
             out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
+          }
           break;
         }
         else err = 0;
@@ -570,7 +558,7 @@ bool darwin_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out
   unsigned int page = in.cdw10 & 0xff;
 
   if (! ifp)
-    return -1;
+    return false;
   smartIfNVMe = *ifp;
   // currently only GetIdentifyData and SMARTReadData are supported
   switch (in.opcode) {
index 2ac40fe29178a5ae89e1508f9d9d123c51db280b..a43609a9392dfb4715643c09074fa5fdac01bd91 100644 (file)
@@ -5,26 +5,13 @@
  *
  * Copyright (C) 2004-8 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef OS_DARWIN_H_
 #define OS_DARWIN_H_
 
-#define OS_DARWIN_H_CVSID "$Id: os_darwin.h 4438 2017-09-20 18:00:42Z samm2 $\n"
+#define OS_DARWIN_H_CVSID "$Id: os_darwin.h 4760 2018-08-19 18:45:53Z chrfranke $\n"
 
 #define kIOATABlockStorageDeviceClass   "IOATABlockStorageDevice"
 
index dd9ea3be0bb3985d5fe091da169ad5d1afe7fb86..3ab7bbfde0596e85f8e13d7d580adb57632e3abd 100644 (file)
@@ -5,16 +5,10 @@
  *
  * Copyright (C) 2003-10 Eduard Martinescu
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
+#include <sys/param.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <dirent.h>
@@ -37,7 +31,7 @@
 #include <sys/utsname.h>
 
 #include "config.h"
-#include "int64.h"
+
 // set by /usr/include/sys/ata.h, suppress warning
 #undef ATA_READ_LOG_EXT
 #include "atacmds.h"
@@ -80,8 +74,8 @@
 #define PATHINQ_SETTINGS_SIZE   128
 #endif
 
-const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 4425 2017-04-24 16:34:16Z samm2 $" \
-ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+const char *os_XXXX_c_cvsid="$Id: os_freebsd.cpp 4848 2018-12-05 18:30:46Z chrfranke $" \
+ATACMDS_H_CVSID CCISS_H_CVSID CONFIG_H_CVSID OS_FREEBSD_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 #define NO_RETURN 0
 #define BAD_SMART 1
@@ -484,7 +478,7 @@ bool freebsd_nvme_device::open()
        }
        nsid = 0xFFFFFFFF; // broadcast id
   }
-  else if (sscanf(dev, NVME_CTRLR_PREFIX"%d"NVME_NS_PREFIX"%d%c", 
+  else if (sscanf(dev, NVME_CTRLR_PREFIX"%d" NVME_NS_PREFIX "%d%c", 
        &ctrlid, &nsid, &tmp) == 2) 
   {
        if(ctrlid < 0 || nsid < 0) {
@@ -519,8 +513,14 @@ bool freebsd_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out
 {
   // nvme_passthru_cmd pt;
   struct nvme_pt_command pt;
+  struct nvme_completion *cp_p;
   memset(&pt, 0, sizeof(pt));
 
+#if __FreeBSD_version >= 1200058 && __FreeBSD_version < 1200081
+  pt.cmd.opc_fuse = NVME_CMD_SET_OPC(in.opcode);
+#else
+  pt.cmd.opc = in.opcode;
+#endif
   pt.cmd.opc = in.opcode;
   pt.cmd.nsid = in.nsid;
   pt.buf = in.buffer;
@@ -538,10 +538,14 @@ bool freebsd_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out
   if (status < 0)
     return set_err(errno, "NVME_PASSTHROUGH_CMD: %s", strerror(errno));
 
-  out.result=pt.cpl.cdw0; // Command specific result (DW0)
+  cp_p = &pt.cpl;
+  out.result=cp_p->cdw0; // Command specific result (DW0)
+
+  if (nvme_completion_is_error(cp_p)) {  /* ignore DNR and More bits */
+    uint16_t nvme_status = ((cp_p->status.sct << 8) | cp_p->status.sc) & 0x3ff;
 
-  if (nvme_completion_is_error(&pt.cpl))
-    return set_nvme_err(out, nvme_completion_is_error(&pt.cpl));
+    return set_nvme_err(out, nvme_status);
+  }
 
   return true;
 }
@@ -609,7 +613,7 @@ bool freebsd_escalade_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_ou
 
   if (m_disknum < 0) {
     printwarning(NO_DISK_3WARE,NULL);
-    return -1;
+    return false;
   }
 
   memset(ioctl_buffer, 0, TW_IOCTL_BUFFER_SIZE);
@@ -1071,7 +1075,7 @@ bool freebsd_scsi_device::scsi_pass_through(scsi_cmnd_io * iop)
     /* datalen */ iop->dxfer_len,
     /* senselen */ iop->max_sense_len,
     /* cdblen */ iop->cmnd_len,
-    /* timout (converted to seconds) */ iop->timeout*1000);
+    /* timeout (converted to seconds) */ iop->timeout*1000);
   memcpy(ccb->csio.cdb_io.cdb_bytes,iop->cmnd,iop->cmnd_len);
 
   if (cam_send_ccb(m_camdev,ccb) < 0) {
@@ -1447,6 +1451,8 @@ protected:
   virtual smart_device * get_custom_smart_device(const char * name, const char * type);
 
   virtual std::string get_valid_custom_dev_types_str();
+private:
+  bool get_nvme_devlist(smart_device_list & devlist, const char * type);
 };
 
 
@@ -1716,6 +1722,12 @@ bool freebsd_smart_interface::scan_smart_devices(smart_device_list & devlist,
     return false;
   }
 
+#ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
+  bool scan_nvme = !type || !strcmp(type, "nvme");
+#else
+  bool scan_nvme = type &&  !strcmp(type, "nvme");
+#endif
+
   // Make namelists
   char * * atanames = 0; int numata = 0;
   if (!type || !strcmp(type, "ata")) {
@@ -1758,9 +1770,31 @@ bool freebsd_smart_interface::scan_smart_devices(smart_device_list & devlist,
         devlist.push_back(scsidev);
     }
   }
+
+  if (scan_nvme)
+    get_nvme_devlist(devlist, type);
   return true;
 }
 
+bool freebsd_smart_interface::get_nvme_devlist(smart_device_list & devlist,
+    const char * type)
+{
+  char ctrlpath[64];
+
+  for (int ctrlr = 0;; ctrlr++) {
+    sprintf(ctrlpath, "%s%d", NVME_CTRLR_PREFIX, ctrlr);
+    int fd = ::open(ctrlpath, O_RDWR);
+    if (fd < 0)
+       break;
+    ::close(fd);
+    nvme_device * nvmedev = get_nvme_device(ctrlpath, type, 0);
+    if (nvmedev)
+        devlist.push_back(nvmedev);
+    else
+        break;
+ }
+  return true;
+}
 
 #if (FREEBSDVER < 800000) // without this build fail on FreeBSD 8
 static char done[USB_MAX_DEVICES];
@@ -1979,7 +2013,7 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam
           if(usbdevlist(bus,vendor_id, product_id, version)){
             const char * usbtype = get_usb_dev_type_by_id(vendor_id, product_id, version);
             if (usbtype)
-              return get_sat_device(usbtype, new freebsd_scsi_device(this, test_name, ""));
+              return get_scsi_passthrough_device(usbtype, new freebsd_scsi_device(this, test_name, ""));
           }
           return 0;
         }
@@ -2004,6 +2038,8 @@ smart_device * freebsd_smart_interface::autodetect_smart_device(const char * nam
   // form /dev/nvme* or nvme*
   if(!strncmp("/dev/nvme", test_name, strlen("/dev/nvme")))
     return new freebsd_nvme_device(this, name, "", 0 /* use default nsid */);
+  if(!strncmp("/dev/nvd", test_name, strlen("/dev/nvd")))
+    set_err(EINVAL, "To monitor NVMe disks use /dev/nvme* device names");
 
   // device type unknown
   return 0;
index 49b0299709de650e767f445538f9ec8ca41dfc15..73ce245f94d9bdccd332970ea52449cc7af07365 100644 (file)
@@ -5,20 +5,7 @@
  *
  * Copyright (C) 2003-8 Eduard Martinescu
  *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 /*-
@@ -82,7 +69,7 @@
 #ifndef OS_FREEBSD_H_
 #define OS_FREEBSD_H_
 
-#define OS_FREEBSD_H_CVSID "$Id: os_freebsd.h 4431 2017-08-08 19:38:15Z chrfranke $"
+#define OS_FREEBSD_H_CVSID "$Id: os_freebsd.h 4760 2018-08-19 18:45:53Z chrfranke $"
 
 #define MAX_NUM_DEV 26
 
index c094a20e8baa9d5e87c1e9921da9b52c038b564d..c7ae70aa4a332b6e02a8f5c24480f7805b9ef141 100644 (file)
@@ -5,16 +5,9 @@
  *
  * Copyright (C) YEAR YOUR_NAME
  * Copyright (C) 2003-8 Bruce Allen
- * Copyright (C) 2008 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2008-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 
@@ -53,8 +46,7 @@
      replace the 'stub' function calls provided in this file.
 
      Provide the functions defined in this file by fleshing out the
-     skeletons below.  You can entirely eliminate the function
-     'unsupported()'.
+     skeletons below.
 
  [5] Contact smartmontools-support@listi.jpberlin.de to see
      about checking your code into the smartmontools CVS archive.
@@ -71,7 +63,6 @@
 
 // These are needed to define prototypes and structures for the
 // functions defined below
-#include "int64.h"
 #include "atacmds.h"
 #include "utility.h"
 
 // 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_cpp_cvsid="$Id: os_generic.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
-  ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_GENERIC_H_CVSID UTILITY_H_CVSID;
+const char * os_XXXX_cpp_cvsid="$Id: os_generic.cpp 4842 2018-12-02 16:07:26Z chrfranke $"
+  ATACMDS_H_CVSID CONFIG_H_CVSID OS_GENERIC_H_CVSID UTILITY_H_CVSID;
 
 // This is here to prevent compiler warnings for unused arguments of
 // functions.
 #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;
-    
-#ifdef HAVE_UNAME
-    struct utsname ostype;
-    uname(&ostype);
-    osname=ostype.sysname;
-#else
-    osname="host's";
-#endif
-
-    pout("\n"
-         "############################################################################\n"
-         "WARNING: smartmontools has not been ported to the %s Operating System.\n"
-         "Please see the files os_generic.cpp and os_generic.h for porting instructions.\n"
-         "############################################################################\n\n",
-         osname);
-    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).
 static void print_smartctl_examples(){
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
          "  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"
@@ -146,17 +95,6 @@ static void print_smartctl_examples(){
          "  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;
 }
 
@@ -213,8 +151,6 @@ ata_device * generic_smart_interface::get_ata_device(const char * name, const ch
 {
   ARGUSED(name);
   ARGUSED(type);
-
-  unsupported();
   return NULL;
 }
 
@@ -224,8 +160,6 @@ scsi_device * generic_smart_interface::get_scsi_device(const char * name, const
 {
   ARGUSED(name);
   ARGUSED(type);
-
-  unsupported();
   return NULL;
 }
 
@@ -234,9 +168,7 @@ scsi_device * generic_smart_interface::get_scsi_device(const char * name, const
 smart_device * generic_smart_interface::autodetect_smart_device(const char * name)
 {
   ARGUSED(name);
-
-  // for the given name return the apropriate device type 
-  unsupported();
+  // for the given name return the appropriate device type 
   return NULL;
 }
 
@@ -248,8 +180,6 @@ bool generic_smart_interface::scan_smart_devices(smart_device_list & devlist,
   ARGUSED(devlist);
   ARGUSED(type);
   ARGUSED(pattern);
-
-  unsupported();
   return false;
 }
 
@@ -259,8 +189,6 @@ smart_device * generic_smart_interface::get_custom_smart_device(const char * nam
 {
   ARGUSED(name);
   ARGUSED(type);
-
-  unsupported();
   return NULL;
 }
 
index 0220cb3e12953cc67de981f8439b4e2b00fe0d2f..c7f1ac6b659dff6478e934b7d7ff81a5b04dea99 100644 (file)
@@ -6,26 +6,13 @@
  * Copyright (C) YEAR YOUR_NAME
  * Copyright (C) 2003-8 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 // 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 4431 2017-08-08 19:38:15Z chrfranke $\n"
+#define OS_GENERIC_H_CVSID "$Id: os_generic.h 4760 2018-08-19 18:45:53Z chrfranke $\n"
 
 // Additional material should start here.  Note: to keep the '-V' CVS
 // reporting option working as intended, you should only #include
index 134d5bc9e715a4d103275059c96229da4365d969..cfa1a518d064b9356f465bd2f2ce48eba1e4ec78 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (C) 2003-11 Bruce Allen
  * Copyright (C) 2003-11 Doug Gilbert <dgilbert@interlog.com>
- * Copyright (C) 2008-16 Christian Franke
+ * Copyright (C) 2008-18 Christian Franke
  *
  * Original AACRaid code:
  *  Copyright (C) 2014    Raghava Aditya <raghava.aditya@pmcs.com>
@@ -26,7 +26,7 @@
  *
  *  Copyright (C) 1999-2003 3ware Inc.
  *
- *  Kernel compatablity By:     Andre Hedrick <andre@suse.com>
+ *  Kernel compatibility 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, see <http://www.gnu.org/licenses/>.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 // This file contains the linux-specific IOCTL parts of
 #include <sys/uio.h>
 #include <sys/types.h>
 #include <dirent.h>
-#ifndef makedev // old versions of types.h do not include sysmacros.h
+#ifdef HAVE_SYS_SYSMACROS_H
+// glibc 2.25: The inclusion of <sys/sysmacros.h> by <sys/types.h> is
+// deprecated.  A warning is printed if major(), minor() or makedev()
+// is used but <sys/sysmacros.h> is not included.
 #include <sys/sysmacros.h>
 #endif
-#ifdef WITH_SELINUX
+#ifdef HAVE_LIBSELINUX
 #include <selinux/selinux.h>
 #endif
 
-#include "int64.h"
 #include "atacmds.h"
 #include "os_linux.h"
 #include "scsicmds.h"
@@ -89,6 +79,7 @@
 #include "cciss.h"
 #include "megaraid.h"
 #include "aacraid.h"
+#include "nvmecmds.h"
 
 #include "dev_interface.h"
 #include "dev_ata_cmd_set.h"
 
 #define ARGUSED(x) ((void)(x))
 
-const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp 4582 2017-11-03 20:54:56Z chrfranke $"
+const char * os_linux_cpp_cvsid = "$Id: os_linux.cpp 4854 2018-12-11 20:32:29Z chrfranke $"
   OS_LINUX_H_CVSID;
 extern unsigned char failuretest_permissive;
 
@@ -538,6 +529,22 @@ static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
     struct sg_io_hdr io_hdr_v3;
     struct sg_io_v4  io_hdr_v4;
 
+#ifdef SCSI_CDB_CHECK
+    bool ok = is_scsi_cdb(iop->cmnd, iop->cmnd_len);
+    if (! ok) {
+       int n = iop->cmnd_len;
+       const unsigned char * ucp = iop->cmnd;
+
+       pout(">>>>>>>> %s: cdb seems invalid, opcode=0x%x, len=%d, cdb:\n",
+            __func__, ((n > 0) ? ucp[0] : 0), n);
+        if (n > 0) {
+           if (n > 16)
+               pout("  <<truncating to first 16 bytes>>\n");
+           dStrHex((const uint8_t *)ucp, ((n > 16) ? 16 : n), 1);
+       }
+     }
+#endif
+
     if (report > 0) {
         int k, j;
         const unsigned char * ucp = iop->cmnd;
@@ -557,8 +564,7 @@ static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
             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);
+            dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
         }
         else
             snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
@@ -678,8 +684,7 @@ static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
                 if (len > 0) {
                     pout("  Incoming data, len=%d%s:\n", len,
                          (trunc ? " [only first 256 bytes shown]" : ""));
-                    dStrHex((const char*)iop->dxferp, (trunc ? 256 : len),
-                            1);
+                    dStrHex(iop->dxferp, (trunc ? 256 : len), 1);
                 } else
                     pout("  Incoming data trimmed to nothing by resid\n");
             }
@@ -713,7 +718,7 @@ static int sg_io_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report,
             if (report > 1) {
                 pout("  >>> Sense buffer, len=%d:\n",
                      (int)iop->resp_sense_len);
-                dStrHex((const char *)iop->sensep, iop->resp_sense_len , 1);
+                dStrHex(iop->sensep, iop->resp_sense_len , 1);
             }
         }
         if (report) {
@@ -739,7 +744,7 @@ struct linux_ioctl_send_command
 {
     int inbufsize;
     int outbufsize;
-    UINT8 buff[MAX_DXFER_LEN + 16];
+    uint8_t buff[MAX_DXFER_LEN + 16];
 };
 
 /* The Linux SCSI_IOCTL_SEND_COMMAND ioctl is primitive and it doesn't
@@ -771,8 +776,7 @@ static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
             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);
+            dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
         }
         else
             snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
@@ -820,8 +824,7 @@ static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
 
                 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);
+                dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
             }
         }
         return 0;
@@ -837,7 +840,7 @@ static int sisc_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report)
         iop->resp_sense_len = len;
         if (report > 1) {
             pout("  >>> Sense buffer, len=%d:\n", (int)len);
-            dStrHex((const char *)wrk.buff, len , 1);
+            dStrHex(wrk.buff, len , 1);
         }
     }
     if (report) {
@@ -894,7 +897,7 @@ static int do_normal_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop,
         sg_io_state = SG_IO_UNSUPP;
         /* FALLTHRU */
     case SG_IO_UNSUPP:
-        /* depricated SCSI_IOCTL_SEND_COMMAND ioctl */
+        /* deprecated SCSI_IOCTL_SEND_COMMAND ioctl */
         return sisc_cmnd_io(dev_fd, iop, report);
     case SG_IO_USE_V3:
     case SG_IO_USE_V4:
@@ -1059,8 +1062,7 @@ bool linux_aacraid_device::scsi_pass_through(scsi_cmnd_io *iop)
         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);
+        dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
     }
     else
       snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
@@ -1292,29 +1294,31 @@ bool linux_megaraid_device::open()
       int err = errno;
       linux_smart_device::close();
       return set_err(err, "can't get bus number");
-    } // we dont need this device anymore
+    } // we don't need this device anymore
     linux_smart_device::close();
   }
   /* Perform mknod of device ioctl node */
   FILE * fp = fopen("/proc/devices", "r");
-  while (fgets(line, sizeof(line), fp) != NULL) {
-    int n1 = 0;
-    if (sscanf(line, "%d megaraid_sas_ioctl%n", &mjr, &n1) == 1 && n1 == 22) {
-      n1=mknod("/dev/megaraid_sas_ioctl_node", S_IFCHR, makedev(mjr, 0));
-      if(report > 0)
-        pout("Creating /dev/megaraid_sas_ioctl_node = %d\n", n1 >= 0 ? 0 : errno);
-      if (n1 >= 0 || errno == EEXIST)
-        break;
-    }
-    else if (sscanf(line, "%d megadev%n", &mjr, &n1) == 1 && n1 == 11) {
-      n1=mknod("/dev/megadev0", S_IFCHR, makedev(mjr, 0));
-      if(report > 0)
-        pout("Creating /dev/megadev0 = %d\n", n1 >= 0 ? 0 : errno);
-      if (n1 >= 0 || errno == EEXIST)
-        break;
+  if (fp) {
+    while (fgets(line, sizeof(line), fp) != NULL) {
+      int n1 = 0;
+      if (sscanf(line, "%d megaraid_sas_ioctl%n", &mjr, &n1) == 1 && n1 == 22) {
+        n1=mknod("/dev/megaraid_sas_ioctl_node", S_IFCHR, makedev(mjr, 0));
+        if(report > 0)
+          pout("Creating /dev/megaraid_sas_ioctl_node = %d\n", n1 >= 0 ? 0 : errno);
+        if (n1 >= 0 || errno == EEXIST)
+          break;
+      }
+      else if (sscanf(line, "%d megadev%n", &mjr, &n1) == 1 && n1 == 11) {
+        n1=mknod("/dev/megadev0", S_IFCHR, makedev(mjr, 0));
+        if(report > 0)
+          pout("Creating /dev/megadev0 = %d\n", n1 >= 0 ? 0 : errno);
+        if (n1 >= 0 || errno == EEXIST)
+          break;
+      }
     }
+    fclose(fp);
   }
-  fclose(fp);
 
   /* Open Device IOCTL node */
   if ((m_fd = ::open("/dev/megaraid_sas_ioctl_node", O_RDWR)) >= 0) {
@@ -1363,8 +1367,7 @@ bool linux_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop)
             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);
+            dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
         }
         else
             snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
@@ -1588,7 +1591,7 @@ static int setup_3ware_nodes(const char *nodename, const char *driver_name)
   struct stat      stat_buf;
   FILE             *file;
   int              retval = 0;
-#ifdef WITH_SELINUX
+#ifdef HAVE_LIBSELINUX
   security_context_t orig_context = NULL;
   security_context_t node_context = NULL;
   int                selinux_enabled  = is_selinux_enabled();
@@ -1618,7 +1621,7 @@ static int setup_3ware_nodes(const char *nodename, const char *driver_name)
     pout("No major number for /dev/%s listed in /proc/devices. Is the %s driver loaded?\n", nodename, driver_name);
     return 2;
   }
-#ifdef WITH_SELINUX
+#ifdef HAVE_LIBSELINUX
   /* Prepare a database of contexts for files in /dev
    * and save the current context */
   if (selinux_enabled) {
@@ -1626,16 +1629,17 @@ static int setup_3ware_nodes(const char *nodename, const char *driver_name)
       pout("Error initializing contexts database for /dev");
     if (getfscreatecon(&orig_context) < 0) {
       pout("Error retrieving original SELinux fscreate context");
-      if (selinux_enforced)
+      if (selinux_enforced) {
         matchpathcon_fini();
         return 6;
       }
+    }
   }
 #endif
   /* Now check if nodes are correct */
   for (index=0; index<16; index++) {
     snprintf(nodestring, sizeof(nodestring), "/dev/%s%d", nodename, index);
-#ifdef WITH_SELINUX
+#ifdef HAVE_LIBSELINUX
     /* Get context of the node and set it as the default */
     if (selinux_enabled) {
       if (matchpathcon(nodestring, S_IRUSR | S_IWUSR, &node_context) < 0) {
@@ -1664,7 +1668,7 @@ static int setup_3ware_nodes(const char *nodename, const char *driver_name)
         retval = 3;
         break;
       } else {
-#ifdef WITH_SELINUX
+#ifdef HAVE_LIBSELINUX
        if (selinux_enabled && node_context) {
          freecon(node_context);
          node_context = NULL;
@@ -1696,7 +1700,7 @@ static int setup_3ware_nodes(const char *nodename, const char *driver_name)
         break;
       }
     }
-#ifdef WITH_SELINUX
+#ifdef HAVE_LIBSELINUX
     if (selinux_enabled && node_context) {
       freecon(node_context);
       node_context = NULL;
@@ -1704,7 +1708,7 @@ static int setup_3ware_nodes(const char *nodename, const char *driver_name)
 #endif
   }
 
-#ifdef WITH_SELINUX
+#ifdef HAVE_LIBSELINUX
   if (selinux_enabled) {
     if(setfscreatecon(orig_context) < 0) {
       pout("Error re-setting original fscreate context");
@@ -2264,8 +2268,8 @@ int linux_marvell_device::ata_command_interface(smart_command_set command, int s
     break;
   default:
     pout("Unrecognized command %d in mvsata_os_specific_handler()\n", command);
-    EXIT(1);
-    break;
+    errno = EINVAL;
+    return -1;
   }
   // There are two different types of ioctls().  The HDIO_DRIVE_TASK
   // one is this:
@@ -2744,17 +2748,21 @@ static bool read_id(const std::string & path, unsigned short & id)
   return ok;
 }
 
-// Get USB bridge ID for "sdX"
+// Get USB bridge ID for "sdX" or "sgN"
 static bool get_usb_id(const char * name, unsigned short & vendor_id,
                        unsigned short & product_id, unsigned short & version)
 {
-  // Only "sdX" supported
-  if (!(!strncmp(name, "sd", 2) && !strchr(name, '/')))
+  // Only "sdX" or "sgN" supported
+  if (!(name[0] == 's' && (name[1] == 'd' || name[1] == 'g') && !strchr(name, '/')))
     return false;
 
-  // Start search at dir referenced by symlink "/sys/block/sdX/device"
+  // Start search at dir referenced by symlink
+  // "/sys/block/sdX/device" or
+  // "/sys/class/scsi_generic/sgN"
   // -> "/sys/devices/.../usb*/.../host*/target*/..."
-  std::string dir = strprintf("/sys/block/%s/device", name);
+  std::string dir = strprintf("/sys/%s/%s%s",
+    (name[1] == 'd' ? "block" : "class/scsi_generic"), name,
+    (name[1] == 'd' ? "/device" : ""));
 
   // Stop search at "/sys/devices"
   struct stat st;
@@ -2793,8 +2801,8 @@ public:
 
   virtual std::string get_app_examples(const char * appname);
 
-  virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
-    const char * pattern = 0);
+  virtual bool scan_smart_devices(smart_device_list & devlist,
+    const smart_devtype_list & types, const char * pattern = 0);
 
 protected:
   virtual ata_device * get_ata_device(const char * name, const char * type);
@@ -2811,9 +2819,11 @@ protected:
   virtual std::string get_valid_custom_dev_types_str();
 
 private:
-  bool get_dev_list(smart_device_list & devlist, const char * pattern,
-    bool scan_ata, bool scan_scsi, bool scan_nvme,
-    const char * req_type, bool autodetect);
+  static const int devxy_to_n_max = 103; // Max value of devxy_to_n() below
+
+  void get_dev_list(smart_device_list & devlist, const char * pattern,
+    bool scan_scsi, bool (* p_dev_sdxy_seen)[devxy_to_n_max+1],
+    bool scan_nvme, const char * req_type, bool autodetect);
 
   bool get_dev_megasas(smart_device_list & devlist);
   smart_device * missing_option(const char * opt);
@@ -2838,35 +2848,64 @@ std::string linux_smart_interface::get_app_examples(const char * appname)
   return "";
 }
 
-// we are going to take advantage of the fact that Linux's devfs will only
-// have device entries for devices that exist.
-bool linux_smart_interface::get_dev_list(smart_device_list & devlist,
-  const char * pattern, bool scan_ata, bool scan_scsi, bool scan_nvme,
-  const char * req_type, bool autodetect)
+// "/dev/sdXY" -> 0-103
+// "/dev/disk/by-id/NAME" -> "../../sdXY" -> 0-103
+// Other -> -1
+static int devxy_to_n(const char * name, bool debug)
+{
+  const char * xy;
+  char dest[256];
+  if (str_starts_with(name, "/dev/sd")) {
+    // Assume "/dev/sdXY"
+    xy = name + sizeof("/dev/sd") - 1;
+  }
+  else {
+    // Assume "/dev/disk/by-id/NAME", check link target
+    int sz = readlink(name, dest, sizeof(dest)-1);
+    if (!(0 < sz && sz < (int)sizeof(dest)))
+      return -1;
+    dest[sz] = 0;
+    if (!str_starts_with(dest, "../../sd"))
+      return -1;
+    if (debug)
+      pout("%s -> %s\n", name, dest);
+    xy = dest + sizeof("../../sd") - 1;
+  }
+
+  char x = xy[0];
+  if (!('a' <= x && x <= 'z'))
+    return -1;
+  char y = xy[1];
+  if (!y)
+    // "[a-z]" -> 0-25
+    return x - 'a';
+
+  if (!(x <= 'c' && 'a' <= y && y <= 'z' && !xy[2]))
+    return -1;
+  // "[a-c][a-z]" -> 26-103
+  return (x - 'a' + 1) * ('z' - 'a' + 1) + (y - 'a');
+}
+
+void linux_smart_interface::get_dev_list(smart_device_list & devlist,
+  const char * pattern, bool scan_scsi, bool (* p_dev_sdxy_seen)[devxy_to_n_max+1],
+  bool scan_nvme, const char * req_type, bool autodetect)
 {
+  bool debug = (ata_debugmode || scsi_debugmode || nvme_debugmode);
+
   // Use glob to look for any directory entries matching the pattern
   glob_t globbuf;
   memset(&globbuf, 0, sizeof(globbuf));
   int retglob = glob(pattern, GLOB_ERR, NULL, &globbuf);
   if (retglob) {
-    //  glob failed: free memory and return
+    // glob failed: free memory and return
     globfree(&globbuf);
 
-    if (retglob==GLOB_NOMATCH){
-      pout("glob(3) found no matches for pattern %s\n", pattern);
-      return true;
-    }
-
-    if (retglob==GLOB_NOSPACE)
-      set_err(ENOMEM, "glob(3) ran out of memory matching pattern %s", pattern);
-#ifdef GLOB_ABORTED // missing in old versions of glob.h
-    else if (retglob==GLOB_ABORTED)
-      set_err(EINVAL, "glob(3) aborted matching pattern %s", pattern);
-#endif
-    else
-      set_err(EINVAL, "Unexplained error in glob(3) of pattern %s", pattern);
+    if (debug)
+      pout("glob(3) error %d for pattern %s\n", retglob, pattern);
 
-    return false;
+    if (retglob == GLOB_NOSPACE)
+      throw std::bad_alloc();
+    return;
   }
 
   // did we find too many paths?
@@ -2878,64 +2917,40 @@ bool linux_smart_interface::get_dev_list(smart_device_list & devlist,
     n = max_pathc;
   }
 
-  // now step through the list returned by glob.  If not a link, copy
-  // to list.  If it is a link, evaluate it and see if the path ends
-  // in "disc".
-  for (int i = 0; i < n; i++){
-    // see if path is a link
-    char linkbuf[1024];
-    int retlink = readlink(globbuf.gl_pathv[i], linkbuf, sizeof(linkbuf)-1);
-
-    char tmpname[1024]={0};
-    const char * name = 0;
-    bool is_scsi = scan_scsi;
-    // if not a link (or a strange link), keep it
-    if (retlink<=0 || retlink>1023)
-      name = globbuf.gl_pathv[i];
-    else {
-      // or if it's a link that points to a disc, follow it
-      linkbuf[retlink] = 0;
-      const char *p;
-      if ((p=strrchr(linkbuf, '/')) && !strcmp(p+1, "disc"))
-        // This is the branch of the code that gets followed if we are
-        // using devfs WITH traditional compatibility links. In this
-        // case, we add the traditional device name to the list that
-        // is returned.
-        name = globbuf.gl_pathv[i];
-      else {
-        // This is the branch of the code that gets followed if we are
-        // using devfs WITHOUT traditional compatibility links.  In
-        // this case, we check that the link to the directory is of
-        // the correct type, and then append "disc" to it.
-        bool match_ata  = strstr(linkbuf, "ide");
-        bool match_scsi = strstr(linkbuf, "scsi");
-        if (((match_ata && scan_ata) || (match_scsi && scan_scsi)) && !(match_ata && match_scsi)) {
-          is_scsi = match_scsi;
-          snprintf(tmpname, sizeof(tmpname), "%s/disc", globbuf.gl_pathv[i]);
-          name = tmpname;
-        }
+  // now step through the list returned by glob.
+  for (int i = 0; i < n; i++) {
+    const char * name = globbuf.gl_pathv[i];
+
+    if (p_dev_sdxy_seen) {
+      // Follow "/dev/disk/by-id/*" symlink and check for duplicate "/dev/sdXY"
+      int dev_n = devxy_to_n(name, debug);
+      if (!(0 <= dev_n && dev_n <= devxy_to_n_max))
+        continue;
+      if ((*p_dev_sdxy_seen)[dev_n]) {
+        if (debug)
+         pout("%s: duplicate, ignored\n", name);
+        continue;
       }
+      (*p_dev_sdxy_seen)[dev_n] = true;
     }
 
-    if (name) {
-      // Found a name, add device to list.
-      smart_device * dev;
-      if (autodetect)
-        dev = autodetect_smart_device(name);
-      else if (is_scsi)
-        dev = new linux_scsi_device(this, name, req_type, true /*scanning*/);
-      else if (scan_nvme)
-        dev = new linux_nvme_device(this, name, req_type, 0 /* use default nsid */);
-      else
-        dev = new linux_ata_device(this, name, req_type);
-      if (dev) // autodetect_smart_device() may return nullptr.
-        devlist.push_back(dev);
+    smart_device * dev;
+    if (autodetect) {
+      dev = autodetect_smart_device(name);
+      if (!dev)
+        continue;
     }
+    else if (scan_scsi)
+      dev = new linux_scsi_device(this, name, req_type, true /*scanning*/);
+    else if (scan_nvme)
+      dev = new linux_nvme_device(this, name, req_type, 0 /* use default nsid */);
+    else
+      dev = new linux_ata_device(this, name, req_type);
+    devlist.push_back(dev);
   }
 
   // free memory
   globfree(&globbuf);
-  return true;
 }
 
 // getting devices from LSI SAS MegaRaid, if available
@@ -2947,6 +2962,8 @@ bool linux_smart_interface::get_dev_megasas(smart_device_list & devlist)
   char line[128];
   bool scan_megasas = false;
   FILE * fp = fopen("/proc/devices", "r");
+  if (!fp)
+    return false;
   while (fgets(line, sizeof(line), fp) != NULL) {
     n1=0;
     if (sscanf(line, "%d megaraid_sas_ioctl%n", &mjr, &n1) == 1 && n1 == 22) {
@@ -2993,53 +3010,69 @@ bool linux_smart_interface::get_dev_megasas(smart_device_list & devlist)
 }
 
 bool linux_smart_interface::scan_smart_devices(smart_device_list & devlist,
-  const char * type, const char * pattern /*= 0*/)
-{
-  if (pattern) {
-    set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
-    return false;
+  const smart_devtype_list & types, const char * pattern /*= 0*/)
+{
+  if (pattern)
+    return set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
+
+  // Scan type list
+  bool by_id = false;
+  const char * type_ata = 0, * type_scsi = 0, * type_sat = 0, * type_nvme = 0;
+  for (unsigned i = 0; i < types.size(); i++) {
+    const char * type = types[i].c_str();
+    if (!strcmp(type, "by-id"))
+      by_id = true;
+    else if (!strcmp(type, "ata"))
+      type_ata = "ata";
+    else if (!strcmp(type, "scsi"))
+      type_scsi = "scsi";
+    else if (!strcmp(type, "sat"))
+      type_sat = "sat";
+    else if (!strcmp(type, "nvme"))
+      type_nvme = "nvme";
+    else
+      return set_err(EINVAL, "Invalid type '%s', valid arguments are: by-id, ata, scsi, sat, nvme",
+                     type);
+  }
+  // Use default if no type specified
+  if (!(type_ata || type_scsi || type_sat || type_nvme)) {
+     type_ata = type_scsi = type_sat = "";
+#ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
+     type_nvme = "";
+#endif
   }
 
-  if (!type)
-    type = "";
+  if (type_ata)
+    get_dev_list(devlist, "/dev/hd[a-t]", false, 0, false, type_ata, false);
 
-  bool scan_ata  = (!*type || !strcmp(type, "ata" ));
-  // "sat" detection will be later handled in linux_scsi_device::autodetect_open()
-  bool scan_scsi = (!*type || !strcmp(type, "scsi") || !strcmp(type, "sat"));
+  if (type_scsi || type_sat) {
+    // "sat" detection will be later handled in linux_scsi_device::autodetect_open()
+    const char * type_scsi_sat = ((type_scsi && type_sat) ? "" // detect both
+                                  : (type_scsi ? type_scsi : type_sat));
+    bool autodetect = !*type_scsi_sat; // If no type specified, detect USB also
 
-#ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
-  bool scan_nvme = (!*type || !strcmp(type, "nvme"));
-#else
-  bool scan_nvme = (          !strcmp(type, "nvme"));
-#endif
+    bool dev_sdxy_seen[devxy_to_n_max+1] = {false, };
+    bool (*p_dev_sdxy_seen)[devxy_to_n_max+1] = 0;
+    if (by_id) {
+      // Scan unique symlinks first
+      get_dev_list(devlist, "/dev/disk/by-id/*", true, &dev_sdxy_seen, false,
+                   type_scsi_sat, autodetect);
+      p_dev_sdxy_seen = &dev_sdxy_seen; // Check for duplicates below
+    }
 
-  if (!(scan_ata || scan_scsi || scan_nvme)) {
-    set_err(EINVAL, "Invalid type '%s', valid arguments are: ata, scsi, sat, nvme", type);
-    return false;
-  }
+    get_dev_list(devlist, "/dev/sd[a-z]", true, p_dev_sdxy_seen, false, type_scsi_sat, autodetect);
+    get_dev_list(devlist, "/dev/sd[a-c][a-z]", true, p_dev_sdxy_seen, false, type_scsi_sat, autodetect);
 
-  if (scan_ata)
-    get_dev_list(devlist, "/dev/hd[a-t]", true, false, false, type, false);
-  if (scan_scsi) {
-    bool autodetect = !*type; // Try USB autodetection if no type specifed
-    get_dev_list(devlist, "/dev/sd[a-z]", false, true, false, type, autodetect);
-    // Support up to 104 devices
-    get_dev_list(devlist, "/dev/sd[a-c][a-z]", false, true, false, type, autodetect);
     // get device list from the megaraid device
     get_dev_megasas(devlist);
   }
-  if (scan_nvme) {
-    get_dev_list(devlist, "/dev/nvme[0-9]", false, false, true, type, false);
-    get_dev_list(devlist, "/dev/nvme[1-9][0-9]", false, false, true, type, false);
-  }
 
-  // if we found traditional links, we are done
-  if (devlist.size() > 0)
-    return true;
+  if (type_nvme) {
+    get_dev_list(devlist, "/dev/nvme[0-9]", false, 0, true, type_nvme, false);
+    get_dev_list(devlist, "/dev/nvme[1-9][0-9]", false, 0, true, type_nvme, false);
+  }
 
-  // else look for devfs entries without traditional links
-  // TODO: Add udev support
-  return get_dev_list(devlist, "/dev/discs/disc*", scan_ata, scan_scsi, false, type, false);
+  return true;
 }
 
 ata_device * linux_smart_interface::get_ata_device(const char * name, const char * type)
@@ -3176,7 +3209,7 @@ static bool is_hpsa(const char * name)
 {
   char path[128];
   snprintf(path, sizeof(path), "/sys/block/%s/device", name);
-  char * syshostpath = canonicalize_file_name(path);
+  char * syshostpath = realpath(path, (char *)0);
   if (!syshostpath)
     return false;
 
@@ -3264,7 +3297,7 @@ smart_device * linux_smart_interface::autodetect_smart_device(const char * name)
 
       // Return SAT/USB device for this type
       // (Note: linux_scsi_device::autodetect_open() will not be called in this case)
-      return get_sat_device(usbtype, new linux_scsi_device(this, name, ""));
+      return get_scsi_passthrough_device(usbtype, new linux_scsi_device(this, name, ""));
     }
 
     // Fail if hpsa driver
index e9f6e8d5a54e77c0e9b58b2305d249e9a9745e6a..0b9636eab12f2a2f092f2042962ec352508eff61 100644 (file)
  *
  *  Copyright (C) 1999-2003 3ware Inc.
  *
- *  Kernel compatablity By:     Andre Hedrick <andre@suse.com>
+ *  Kernel compatibility 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/
- * 
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 
 #ifndef OS_LINUX_H_
 #define OS_LINUX_H_
 
-#define OS_LINUX_H_CVSID "$Id: os_linux.h 4431 2017-08-08 19:38:15Z chrfranke $\n"
+#define OS_LINUX_H_CVSID "$Id: os_linux.h 4842 2018-12-02 16:07:26Z chrfranke $\n"
 
 /* 
    The following definitions/macros/prototypes are used for three
index 9b818970335c70c635f732123bb8f416b881c6c9..13481b4fdda78d7c796092c30a28d0018a1654ea 100644 (file)
@@ -6,19 +6,11 @@
  * Copyright (C) 2003-8 Sergey Svishchev
  * Copyright (C) 2016 Kimihiro Nonaka
  *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
-#include "int64.h"
+
 #include "atacmds.h"
 #include "scsicmds.h"
 #include "utility.h"
 
 #include <sys/drvctlio.h>
 #include <sys/utsname.h>
+#include <sys/stat.h>
 #include <errno.h>
 #include <unistd.h>
 
 // based on "sys/dev/ic/nvmeio.h" from NetBSD kernel sources
 #include "netbsd_nvme_ioctl.h" // NVME_PASSTHROUGH_CMD, nvme_completion_is_error
 
-const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
+const char * os_netbsd_cpp_cvsid = "$Id: os_netbsd.cpp 4780 2018-09-16 15:03:22Z chrfranke $"
   OS_NETBSD_H_CVSID;
 
-enum warnings {
-  BAD_SMART, 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 currently support twe(4) and twa(4) devices (3ware Escalade, Apache)\n",
-  };
-
-  if (msgNo >= 0 && msgNo <= MAX_MSG) {
-    if (!printed[msgNo]) {
-      printed[msgNo] = 1;
-      pout("%s", message[msgNo]);
-      if (extra)
-        pout("%s", extra);
-    }
-  }
-  return;
-}
-
 #define ARGUSED(x) ((void)(x))
 
 /////////////////////////////////////////////////////////////////////////////
@@ -206,7 +174,7 @@ bool netbsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou
   req.sec_count = in.in_regs.sector_count;
   req.sec_num = in.in_regs.lba_low;
   req.head = in.in_regs.device;
-  req.cylinder = le16toh(in.in_regs.lba_mid | (in.in_regs.lba_high << 8));
+  req.cylinder = in.in_regs.lba_mid | (in.in_regs.lba_high << 8);
 
   switch (in.direction) {
     case ata_cmd_in::no_data:
@@ -237,41 +205,14 @@ bool netbsd_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou
   out.out_regs.sector_count = req.sec_count;
   out.out_regs.lba_low = req.sec_num;
   out.out_regs.device = req.head;
-  out.out_regs.lba_mid = le16toh(req.cylinder);
-  out.out_regs.lba_high = le16toh(req.cylinder) >> 8;
+  out.out_regs.lba_mid = req.cylinder;
+  out.out_regs.lba_high = req.cylinder >> 8;
   out.out_regs.status = req.command;
-
-  // Command specific processing
-  if (in.in_regs.command == ATA_SMART_CMD
-       && in.in_regs.features == ATA_SMART_STATUS
-       && in.out_needed.lba_high)
-  {
-    unsigned const char normal_lo=0x4f, normal_hi=0xc2;
-    unsigned const char failed_lo=0xf4, failed_hi=0x2c;
-
-    // Cyl low and Cyl high unchanged means "Good SMART status"
-    if (!(out.out_regs.lba_mid==normal_lo && out.out_regs.lba_high==normal_hi)
-    // These values mean "Bad SMART status"
-        && !(out.out_regs.lba_mid==failed_lo && out.out_regs.lba_high==failed_hi))
-
-    {
-      // We haven't gotten output that makes sense; print out some debugging info
-      char buf[512];
-      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);
-      out.out_regs.lba_high = failed_hi;
-      out.out_regs.lba_mid = failed_lo;
-    }
+  /* Undo byte-swapping for IDENTIFY */
+  if (in.in_regs.command == ATA_IDENTIFY_DEVICE && isbigendian()) {
+     for (int i = 0; i < 256; i+=2)
+      swap2 ((char *)req.databuf + i);
   }
-
   return true;
 }
 
@@ -319,7 +260,7 @@ bool netbsd_nvme_device::open()
     }
     nsid = 0xFFFFFFFF; // broadcast id
   }
-  else if (sscanf(dev, NVME_PREFIX"%d"NVME_NS_PREFIX"%d%c",
+  else if (sscanf(dev, NVME_PREFIX "%d" NVME_NS_PREFIX "%d%c",
     &ctrlid, &nsid, &tmp) == 2)
   {
     if(ctrlid < 0 || nsid <= 0) {
@@ -595,21 +536,13 @@ std::string netbsd_smart_interface::get_app_examples(const char * appname)
 
     p = 'a' + getrawpartition();
     return strprintf(
-        "=================================================== SMARTCTL EXAMPLES =====\n\n"
-#ifdef HAVE_GETOPT_LONG
+      "=================================================== SMARTCTL EXAMPLES =====\n\n"
       "  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"
-#else
-      "  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"
-#endif
       , p, p, p, p);
   }
   return "";
@@ -717,7 +650,7 @@ bool netbsd_smart_interface::get_nvme_devlist(smart_device_list & devlist,
 
     uint32_t n = 0;
     for (int nsid = 1; n < laa.l_children; nsid++) {
-      snprintf(nspath, sizeof(nspath), NVME_PREFIX"%d"NVME_NS_PREFIX"%d",
+      snprintf(nspath, sizeof(nspath), NVME_PREFIX "%d" NVME_NS_PREFIX "%d",
         ctrl, nsid);
       if (stat(nspath, &sb) == -1 || !S_ISCHR(sb.st_mode))
         break;
@@ -754,7 +687,7 @@ bool netbsd_smart_interface::scan_smart_devices(smart_device_list & devlist,
 #ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL
   bool scan_nvme = !*type || !strcmp(type, "nvme");
 #else
-  bool scan_nvme =           !strcmp(type, "nvme");
+  bool scan_nvme =          !strcmp(type, "nvme");
 #endif
 
   // Make namelists
@@ -834,25 +767,30 @@ smart_device * netbsd_smart_interface::autodetect_smart_device(const char * name
     }
   }
 
-  if (str_starts_with(test_name, net_dev_raw_prefix)) {
+  if (str_starts_with(test_name, net_dev_raw_prefix))
     test_name += strlen(net_dev_raw_prefix);
-    if (!strncmp(net_dev_ata_disk, test_name, strlen(net_dev_ata_disk)))
-      return get_ata_device(test_name, "ata");
-    if (!strncmp(net_dev_scsi_disk, test_name, strlen(net_dev_scsi_disk))) {
+  else if (str_starts_with(test_name, net_dev_prefix))
+    test_name += strlen(net_dev_prefix);
+  else
+    return 0; // device is not starting with /dev/ or /dev/r*
+
+  if (!strncmp(net_dev_ata_disk, test_name, strlen(net_dev_ata_disk)))
+    return get_ata_device(name, "ata");
+
+  if (!strncmp(net_dev_scsi_disk, test_name, strlen(net_dev_scsi_disk))) {
       // XXX Try to detect possible USB->(S)ATA bridge
       // XXX get USB vendor ID, product ID and version from sd(4)/umass(4).
       // XXX check sat device via get_usb_dev_type_by_id().
-
       // No USB bridge found, assume regular SCSI device
-      return get_scsi_device(test_name, "scsi");
-    }
-    if (!strncmp(net_dev_scsi_tape, test_name, strlen(net_dev_scsi_tape)))
-      return get_scsi_device(test_name, "scsi");
-  } else if (str_starts_with(test_name, net_dev_prefix)) {
-    if (!strncmp(NVME_PREFIX, test_name, strlen(NVME_PREFIX)))
-      return get_nvme_device(test_name, "nvme", 0 /* use default nsid */);
+      return get_scsi_device(name, "scsi");
   }
 
+  if (!strncmp(net_dev_scsi_tape, test_name, strlen(net_dev_scsi_tape)))
+    return get_scsi_device(name, "scsi");
+
+  if (!strncmp(net_dev_nvme_ctrl, test_name, strlen(net_dev_nvme_ctrl)))
+    return get_nvme_device(name, "nvme", 0 /* use default nsid */);
+
   // device type unknown
   return 0;
 }
index 51ee5a30204ecb7cc9c18285c7bd693572af50c8..a2b7132258038c51da8764b19ed3118da7446825 100644 (file)
@@ -5,26 +5,13 @@
  *
  * Copyright (C) 2003-8 Sergey Svishchev
  *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef OS_NETBSD_H_
 #define OS_NETBSD_H_
 
-#define OS_NETBSD_H_CVSID "$Id: os_netbsd.h 4431 2017-08-08 19:38:15Z chrfranke $\n"
+#define OS_NETBSD_H_CVSID "$Id: os_netbsd.h 4760 2018-08-19 18:45:53Z chrfranke $\n"
 
 #include <sys/device.h>
 #include <sys/param.h>
index 1c947fee8933486d68bcb7ca07912f47ddee0501..eef9f358dd907006340515d0094e0371d1814954 100644 (file)
@@ -7,19 +7,11 @@
  *
  * Derived from os_netbsd.cpp by Sergey Svishchev, Copyright (C) 2003-8
  *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
-#include "int64.h"
+
 #include "atacmds.h"
 #include "scsicmds.h"
 #include "utility.h"
@@ -27,7 +19,7 @@
 
 #include <errno.h>
 
-const char * os_openbsd_cpp_cvsid = "$Id: os_openbsd.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
+const char * os_openbsd_cpp_cvsid = "$Id: os_openbsd.cpp 4842 2018-12-02 16:07:26Z chrfranke $"
   OS_OPENBSD_H_CVSID;
 
 enum warnings {
@@ -41,7 +33,7 @@ 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",
+    PACKAGE_STRING " does not currently support twe(4) devices (3ware Escalade)\n",
   };
 
   if (msgNo >= 0 && msgNo <= MAX_MSG) {
@@ -420,9 +412,8 @@ print_smartctl_examples()
   char p;
 
   p = 'a' + getrawpartition();
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
-#ifdef HAVE_GETOPT_LONG
   printf(
+    "=================================================== SMARTCTL EXAMPLES =====\n\n"
     "  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"
@@ -431,15 +422,5 @@ print_smartctl_examples()
     "                                      (Prints Self-Test & Attribute errors)\n",
     p, p, p, p
     );
-#else
-  printf(
-    "  smartctl -a /dev/wd0%c                     (Prints all SMART information)\n"
-    "  smartctl -s on -o on -S on /dev/wd0%c        (Enables SMART on first disk)\n"
-    "  smartctl -t long /dev/wd0%c            (Executes extended disk self-test)\n"
-    "  smartctl -A -l selftest -q errorsonly /dev/wd0%c"
-    "                                      (Prints Self-Test & Attribute errors)\n",
-    p, p, p, p
-    );
-#endif
   return;
 }
index d7aa7d0a9fb74c80d830df89534e00a12edfcc72..29b2f2ba8e6bfcf720cc86eca0d7ae82ff64892f 100644 (file)
@@ -7,26 +7,13 @@
  *
  * Derived from os_netbsd.c by Sergey Svishchev, Copyright (C) 2003-8
  *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef OS_OPENBSD_H_
 #define OS_OPENBSD_H_
 
-#define OS_OPENBSD_H_CVSID "$Id: os_openbsd.h 4431 2017-08-08 19:38:15Z chrfranke $\n"
+#define OS_OPENBSD_H_CVSID "$Id: os_openbsd.h 4760 2018-08-19 18:45:53Z chrfranke $\n"
 
 /* from NetBSD: atareg.h,v 1.17, by Manuel Bouyer */
 /* Actually fits _perfectly_ into OBSDs wdcreg.h, but... */
index 16f9b273890e9c81700c4de74b6460abff6fc6e0..33f0b0eeeaf98be37db4e8fd3e3d4cc2e2ab17a1 100644 (file)
@@ -5,14 +5,7 @@
  *
  * Copyright (C) 2004-8 Yuri Dario
  *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 /*
@@ -23,7 +16,6 @@
 
 // These are needed to define prototypes for the functions defined below
 #include "config.h"
-#include "int64.h"
 
 #include <ctype.h>
 #include <errno.h>
@@ -35,7 +27,7 @@
 #include "os_os2.h"
 
 // Needed by '-V' option (CVS versioning) of smartd/smartctl
-const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 4431 2017-08-08 19:38:15Z chrfranke $" \
+const char *os_XXXX_c_cvsid="$Id: os_os2.cpp 4842 2018-12-02 16:07:26Z chrfranke $" \
 ATACMDS_H_CVSID OS_XXXX_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 // global handle to device driver
@@ -45,9 +37,7 @@ static HFILE hDevice;
 // 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(
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
          "  smartctl -a hd0                       (Prints all SMART information)\n\n"
          "  smartctl --smart=on --offlineauto=on --saveauto=on hd0\n"
          "                                              (Enables SMART on first disk)\n\n"
@@ -55,16 +45,6 @@ void print_smartctl_examples(){
          "  smartctl --attributes --log=selftest --quietmode=errorsonly hd0\n"
          "                                      (Prints Self-Test & Attribute errors)\n"
          );
-#else
-  printf(
-         "  smartctl -a hd0                       (Prints all SMART on first disk with DANIS506)\n"
-         "  smartctl -a ahci0                     (Prints all SMART on first disk with OS2AHCI)\n"
-         "  smartctl -s on -o on -S on hd0         (Enables SMART on first disk)\n"
-         "  smartctl -t long hd0              (Executes extended disk self-test)\n"
-         "  smartctl -A -l selftest -q errorsonly hd0\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-         );
-#endif
   return;
 }
 
@@ -394,7 +374,7 @@ static int dani_ioctl( int device, void* arg)
    return 0;
 }
 
-// Interface to ATA devices.  See os_linux.cpp for the cannonical example.
+// Interface to ATA devices.  See os_linux.cpp for the canonical example.
 // DETAILED DESCRIPTION OF ARGUMENTS
 //   device: is the integer handle provided by deviceopen()
 //   command: defines the different operations, see atacmds.h
index 6ed52c69284720b79e040c029de5a4a9687b3554..a5febb052f01f882f9d2a8871a5c0ac50e76ab76 100644 (file)
--- a/os_os2.h
+++ b/os_os2.h
@@ -5,20 +5,13 @@
  *
  * Copyright (C) 2004-8 Yuri Dario
  *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef OS_OS2_H_
 #define OS_OS2_H_
 
-#define OS_XXXX_H_CVSID "$Id: os_os2.h 4431 2017-08-08 19:38:15Z chrfranke $\n"
+#define OS_XXXX_H_CVSID "$Id: os_os2.h 4760 2018-08-19 18:45:53Z chrfranke $\n"
 
 // Additional material should start here.  Note: to keep the '-V' CVS
 // reporting option working as intended, you should only #include
index 0f039f4a758d8b3d9a8fac9f8d33f4f0e24a6624..677e559ca41c51bdebb3bee739745ec2ab78480b 100644 (file)
@@ -1,11 +1,18 @@
-
+/*
+ * os_qnxnto.cpp
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2007 Joerg Hering
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
 
 // 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"
 // should have one *_H_CVSID macro appearing below for each file
 // appearing with #include "*.h" above.  Please list these (below) in
 // alphabetic/dictionary order.
-const char *os_XXXX_c_cvsid="$Id: os_qnxnto.cpp 3806 2013-03-29 20:17:03Z chrfranke $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_QNXNTO_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+const char *os_XXXX_c_cvsid="$Id: os_qnxnto.cpp 4842 2018-12-02 16:07:26Z chrfranke $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID OS_QNXNTO_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 
 // This is here to prevent compiler warnings for unused arguments of
 // functions.
 #define ARGUSED(x) ((void)(x))
 
-// Please eliminate the following block: both the #include and
-// the 'unsupported()' function.  They are only here to warn
-// unsuspecting users that their Operating System is not supported! If
-// you wish, you can use a similar warning mechanism for any of the
-// functions in this file that you can not (or choose not to)
-// implement.
-
-
-#ifdef HAVE_UNAME
-#include <sys/utsname.h>
-#endif
 //----------------------------------------------------------------------------------------------
 // private Functions
 static int ata_sense_data(void *sdata,int *error,int *key,int *asc,int *ascq);
 static int ata_interpret_sense(struct cam_pass_thru *cpt,void *sense,int *status,int rcount);
 static int ata_pass_thru(int fd,struct cam_pass_thru *pcpt);
-//----------------------------------------------------------------------------------------------
-static void unsupported(){
-  static int warninggiven;
-
-  if (!warninggiven) {
-    char *osname;
-
-#ifdef HAVE_UNAME
-    struct utsname ostype;
-    uname(&ostype);
-    osname=ostype.sysname;
-#else
-    osname="host's";
-#endif
-
-    pout("\n"
-         "############################################################################\n"
-         "WARNING: smartmontools has not been ported to the %s Operating System.\n"
-         "Please see the files os_generic.cpp and os_generic.h for porting instructions.\n"
-         "############################################################################\n\n",
-         osname);
-    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(
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
          "  smartctl -a /dev/hd0                       (Prints all SMART information)\n\n"
          "  smartctl --smart=on --offlineauto=on --saveauto=on /dev/hd0\n"
          "                                              (Enables SMART on first disk)\n\n"
@@ -88,17 +54,6 @@ void print_smartctl_examples(){
          "  smartctl -a --device=3ware,2 /dev/sda\n"
          "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
          );
-#else
-  printf(
-         "  smartctl -a /dev/hd0                       (Prints all SMART information)\n"
-         "  smartctl -s on -o on -S on /dev/hd0         (Enables SMART on first disk)\n"
-         "  smartctl -t long /dev/hd0              (Executes extended disk self-test)\n"
-         "  smartctl -A -l selftest -q errorsonly /dev/hd0\n"
-         "                                      (Prints Self-Test & Attribute errors)\n"
-         "  smartctl -a -d 3ware,2 /dev/sda\n"
-         "          (Prints all SMART info for 3rd ATA disk on 3ware RAID controller)\n"
-         );
-#endif
   return;
 }
 
@@ -134,7 +89,6 @@ int len,dev_prefix_len;
 int make_device_names (char*** devlist, const char* name) {
   ARGUSED(devlist);
   ARGUSED(name);
-  unsupported();
   return 0;
 }
 
@@ -159,7 +113,7 @@ int deviceclose(int fd)
   return(close(fd));
 }
 //----------------------------------------------------------------------------------------------
-// Interface to ATA devices.  See os_linux.cpp for the cannonical example.
+// Interface to ATA devices.  See os_linux.cpp for the canonical example.
 // DETAILED DESCRIPTION OF ARGUMENTS
 //   device: is the integer handle provided by deviceopen()
 //   command: defines the different operations, see atacmds.h
@@ -377,7 +331,6 @@ int do_scsi_cmnd_io(int fd,struct scsi_cmnd_io * iop,int report)
   ARGUSED(fd);
   ARGUSED(iop);
   ARGUSED(report);
-  unsupported();
   return -ENOSYS;
 }
 //----------------------------------------------------------------------------------------------
index 189b8563c773287eb50e59b7567a735579e013a8..82e7cb7afb6fd1097f38233c3db55e4f1bd60a88 100644 (file)
@@ -1,29 +1,16 @@
 /*
- * os_generic.h
+ * os_qnxnto.h
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) Joerg Hering
+ * Copyright (C) 2007 Joerg Hering
  * Copyright (C) 2003-8 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 #ifndef OS_QNXNTO_H_
 #define OS_QNXNTO_H_
-#define OS_QNXNTO_H_CVSID "$Id: os_qnxnto.h 4431 2017-08-08 19:38:15Z chrfranke $\n"
+#define OS_QNXNTO_H_CVSID "$Id: os_qnxnto.h 4761 2018-08-20 19:33:04Z chrfranke $\n"
 
 // Additional material should start here.  Note: to keep the '-V' CVS
 // reporting option working as intended, you should only #include
index e70afe784ba337baaf62c363e93d7643ac57a3a0..9ac8f2fc2718eed6a016d80cee8b6db65a42e633 100644 (file)
@@ -6,15 +6,7 @@
  * Copyright (C) 2003-08 SAWADA Keiji
  * Copyright (C) 2003-15 Casper Dik
  *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include <stdlib.h>
@@ -27,7 +19,7 @@
 
 // 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"
@@ -37,8 +29,8 @@
 
 #define ARGUSED(x) ((void)(x))
 
-const char *os_XXXX_c_cvsid="$Id: os_solaris.cpp 4253 2016-03-26 19:47:47Z chrfranke $" \
-ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_SOLARIS_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
+const char *os_XXXX_c_cvsid="$Id: os_solaris.cpp 4805 2018-10-09 19:34:46Z chrfranke $" \
+ATACMDS_H_CVSID CONFIG_H_CVSID OS_SOLARIS_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
 
 // The printwarning() function warns about unimplemented functions
 int printedout[2];
@@ -70,9 +62,7 @@ int printwarning(int which){
 
 // print examples for smartctl
 void print_smartctl_examples(){
-  printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
-#ifdef HAVE_GETOPT_LONG
-  printf(
+  printf("=================================================== SMARTCTL EXAMPLES =====\n\n"
          "  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"
@@ -80,15 +70,6 @@ void print_smartctl_examples(){
          "  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;
 }
 
@@ -320,8 +301,8 @@ int ata_command_interface(int fd, smart_command_set command, int select, char *d
        return smart_status_check(fd);
     default:
        pout("Unrecognized command %d in ata_command_interface() of os_solaris.c\n", command);
-       EXIT(1);
-       break;
+        errno = EINVAL;
+        return -1;
     }
 #else /* WITH_SOLARIS_SPARC_ATA */
     ARGUSED(fd); ARGUSED(command); ARGUSED(select); ARGUSED(data);
@@ -363,7 +344,7 @@ int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
 
       pout("  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);
+      dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
     }
   }
   memset(&uscsi, 0, sizeof (uscsi));
@@ -420,7 +401,7 @@ int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
              iop->sensep[12], iop->sensep[13]);
       if (report > 1) {
           pout("  >>> Sense buffer, len=%d:\n", len);
-          dStrHex((const char *)iop->sensep, ((len > 252) ? 252 : len) , 1);
+          dStrHex(iop->sensep, ((len > 252) ? 252 : len) , 1);
       }
     } else if (iop->scsi_status)
       pout("  status=%x\n", iop->scsi_status);
@@ -432,7 +413,7 @@ int do_scsi_cmnd_io(int fd, struct scsi_cmnd_io * iop, int report)
         trunc = (len > 256) ? 1 : 0;
         pout("  Incoming data, len=%d%s:\n", len,
              (trunc ? " [only first 256 bytes shown]" : ""));
-        dStrHex((char *)iop->dxferp, (trunc ? 256 : len) , 1);
+        dStrHex(iop->dxferp, (trunc ? 256 : len) , 1);
       }
     }
   }
index 79c2c3e33c05b13103079a2e67749d2814c47b0c..5b0dab7fce18d86169f855dbb621d9e9026a5315 100644 (file)
@@ -6,26 +6,13 @@
  * Copyright (C) 2003-8 SAWADA Keiji
  * Copyright (C) 2003-8 Casper Dik
  *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef OS_SOLARIS_H_
 #define OS_SOLARIS_H_
 
-#define OS_SOLARIS_H_CVSID "$Id: os_solaris.h 4431 2017-08-08 19:38:15Z chrfranke $\n"
+#define OS_SOLARIS_H_CVSID "$Id: os_solaris.h 4760 2018-08-19 18:45:53Z chrfranke $\n"
 
 // Additional material should start here.  Note: to keep the '-V' CVS
 // reporting option working as intended, you should only #include
index bce23f11cecd46df182605230fc062065a4a5093..acb20e773dd0d26709998cc04fa466c9f67a8216 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2004-17 Christian Franke
+ * Copyright (C) 2004-18 Christian Franke
  *
  * Original AACRaid code:
  *  Copyright (C) 2015    Nidhi Malhotra <nidhi.malhotra@pmcs.com>
  * Original Areca code:
  *  Copyright (C) 2012    Hank Wu <hank@areca.com.tw>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
 #define WINVER 0x0502
 #define _WIN32_WINNT WINVER
 
-#include "int64.h"
 #include "atacmds.h"
 #include "scsicmds.h"
 #include "nvmecmds.h"
 #include "utility.h"
-#include "smartctl.h" // TODO: Do not use smartctl only variables here
 
 #include "dev_interface.h"
 #include "dev_ata_cmd_set.h"
 #include "dev_areca.h"
 
 #include "os_win32/wmiquery.h"
+#include "os_win32/popen.h"
+
+// TODO: Move from smartctl.h to other include file
+extern unsigned char failuretest_permissive;
 
 #include <errno.h>
 
 #define strnicmp strncasecmp
 #endif
 
-const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 4559 2017-10-22 16:17:50Z chrfranke $";
+const char * os_win32_cpp_cvsid = "$Id: os_win32.cpp 4848 2018-12-05 18:30:46Z chrfranke $";
 
 /////////////////////////////////////////////////////////////////////////////
 // Windows I/O-controls, some declarations are missing in the include files
@@ -553,7 +548,7 @@ static int smart_get_version(HANDLE hdevice, GETVERSIONINPARAMS_EX * ata_version
   assert(num_out == sizeof(GETVERSIONINPARAMS));
 
   if (ata_debugmode > 1) {
-    pout("  SMART_GET_VERSION suceeded, bytes returned: %u\n"
+    pout("  SMART_GET_VERSION succeeded, bytes returned: %u\n"
          "    Vers = %d.%d, Caps = 0x%x, DeviceMap = 0x%02x\n",
       (unsigned)num_out, vers.bVersion, vers.bRevision,
       (unsigned)vers.fCapabilities, vers.bIDEDeviceMap);
@@ -647,7 +642,7 @@ static int smart_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, unsigned dat
   }
 
   if (ata_debugmode > 1) {
-    pout("  %s suceeded, bytes returned: %u (buffer %u)\n", name,
+    pout("  %s succeeded, bytes returned: %u (buffer %u)\n", name,
       (unsigned)num_out, (unsigned)outpar->cBufferSize);
     print_ide_regs_io(regs, (regs->bFeaturesReg == ATA_SMART_STATUS ?
       (const IDEREGS *)(outpar->bBuffer) : NULL));
@@ -736,7 +731,7 @@ static int ide_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, char * data, u
   }
 
   if (ata_debugmode > 1) {
-    pout("  IOCTL_IDE_PASS_THROUGH suceeded, bytes returned: %u (buffer %u)\n",
+    pout("  IOCTL_IDE_PASS_THROUGH succeeded, bytes returned: %u (buffer %u)\n",
       (unsigned)num_out, (unsigned)buf->DataBufferSize);
     print_ide_regs_io(regs, &buf->IdeReg);
   }
@@ -777,7 +772,7 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev
   //ab.apt.PathId = 0;
   //ab.apt.TargetId = 0;
   //ab.apt.Lun = 0;
-  ab.apt.TimeOutValue = 10;
+  ab.apt.TimeOutValue = 60; // seconds
   unsigned size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf);
   ab.apt.DataBufferOffset = size;
 
@@ -853,7 +848,7 @@ static int ata_pass_through_ioctl(HANDLE hdevice, IDEREGS * regs, IDEREGS * prev
   }
 
   if (ata_debugmode > 1) {
-    pout("  IOCTL_ATA_PASS_THROUGH suceeded, bytes returned: %u\n", (unsigned)num_out);
+    pout("  IOCTL_ATA_PASS_THROUGH succeeded, bytes returned: %u\n", (unsigned)num_out);
     print_ide_regs_io(regs, ctfregs);
   }
   *regs = *ctfregs;
@@ -984,7 +979,7 @@ static int ata_via_scsi_miniport_smart_ioctl(HANDLE hdevice, IDEREGS * regs, cha
   }
 
   if (ata_debugmode > 1) {
-    pout("  IOCTL_SCSI_MINIPORT_%s suceeded, bytes returned: %u (buffer %u)\n", name,
+    pout("  IOCTL_SCSI_MINIPORT_%s succeeded, bytes returned: %u (buffer %u)\n", name,
       (unsigned)num_out, (unsigned)sb.params.out.cBufferSize);
     print_ide_regs_io(regs, (code == IOCTL_SCSI_MINIPORT_RETURN_STATUS ?
                              (const IDEREGS *)(sb.params.out.bBuffer) : 0));
@@ -1051,7 +1046,7 @@ static int ata_via_3ware_miniport_ioctl(HANDLE hdevice, IDEREGS * regs, char * d
     memcpy(data, sb.buffer, datasize);
 
   if (ata_debugmode > 1) {
-    pout("  ATA via IOCTL_SCSI_MINIPORT suceeded, bytes returned: %u\n", (unsigned)num_out);
+    pout("  ATA via IOCTL_SCSI_MINIPORT succeeded, bytes returned: %u\n", (unsigned)num_out);
     print_ide_regs_io(regs, &sb.regs);
   }
   *regs = sb.regs;
@@ -1092,7 +1087,7 @@ static int update_3ware_devicemap_ioctl(HANDLE hdevice)
     return -1;
   }
   if (ata_debugmode > 1)
-    pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT suceeded\n");
+    pout("  UPDATE DEVICEMAP via IOCTL_SCSI_MINIPORT succeeded\n");
   return 0;
 }
 
@@ -1143,7 +1138,7 @@ static int storage_query_property_ioctl(HANDLE hdevice, STORAGE_DEVICE_DESCRIPTO
 // IOCTL_STORAGE_PREDICT_FAILURE
 
 // Call IOCTL_STORAGE_PREDICT_FAILURE, return PredictFailure value
-// or -1 on error, opionally return VendorSpecific data.
+// or -1 on error, optionally return VendorSpecific data.
 // (This works without admin rights)
 
 static int storage_predict_failure_ioctl(HANDLE hdevice, char * data = 0)
@@ -1307,7 +1302,7 @@ static bool get_usb_id(int phydrive, int logdrive,
   std::string prev_usb_ant;
   std::string prev_ant, ant, dep;
 
-  const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"", REG_EXTENDED);
+  const regular_expression regex("^.*PnPEntity\\.DeviceID=\"([^\"]*)\"");
 
   while (we.next(wo)) {
     prev_ant = ant;
@@ -1319,7 +1314,7 @@ static bool get_usb_id(int phydrive, int logdrive,
       pout(" %s:\n", ant.c_str());
 
     // Extract DeviceID
-    regmatch_t match[2];
+    regular_expression::match_range match[2];
     if (!(regex.execute(dep.c_str(), 2, match) && match[1].rm_so >= 0)) {
       if (debug)
         pout("  | (\"%s\")\n", dep.c_str());
@@ -1600,7 +1595,7 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int
   // 3ware RAID if vendor id present
   m_is_3ware = (vers_ex.wIdentifier == SMART_VENDOR_3WARE);
 
-  unsigned long portmap = 0;
+  unsigned portmap = 0;
   if (port >= 0 && devmap >= 0) {
     // 3ware RAID: check vendor id
     if (!m_is_3ware) {
@@ -1623,14 +1618,13 @@ bool win_ata_device::open(int phydrive, int logdrive, const char * options, int
 
   {
     // 3ware RAID: update devicemap first
-
     if (!update_3ware_devicemap_ioctl(h)) {
       if (   smart_get_version(h, &vers_ex) >= 0
           && vers_ex.wIdentifier == SMART_VENDOR_3WARE    )
         portmap = vers_ex.dwDeviceMapEx;
     }
     // Check port existence
-    if (!(portmap & (1L << port))) {
+    if (!(portmap & (1U << port))) {
       if (!is_permissive()) {
         close();
         return set_err(ENOENT, "%s: Port %d is empty or does not exist", devpath, port);
@@ -2065,13 +2059,14 @@ int csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info, port_2_index_map & p
   }
 
   // Create port -> index map
-  //                                 IRST Release
-  // Phy[i].Value               9.x   10.4   14.8   15.2
-  // ---------------------------------------------------
-  // bPortIdentifier           0xff   0xff   port   0x00
-  // Identify.bPhyIdentifier   port   port   port   port
-  // Attached.bPhyIdentifier   0x00   0x00   0x00   port
-  // Empty ports in Phy[]?     no     ?      yes    yes
+  //                                     IRST Release
+  // Phy[i].Value               9.x   10.x   14.8   15.2   16.0
+  // ----------------------------------------------------------
+  // bPortIdentifier           0xff   0xff   port   0x00   port
+  // Identify.bPhyIdentifier   index? index? index  index  port
+  // Attached.bPhyIdentifier   0x00   0x00   0x00   index  0x00
+  //
+  // Empty ports with hotplug support may appear in Phy[].
 
   int number_of_ports;
   for (int mode = 0; ; mode++) {
@@ -2079,14 +2074,15 @@ int csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info, port_2_index_map & p
        p2i[i] = -1;
 
      number_of_ports = 0;
-     bool unique = true;
+     bool found = false;
      for (int i = 0; i < max_number_of_ports; i++) {
         const CSMI_SAS_PHY_ENTITY & pe = phy_info.Phy[i];
         if (pe.Identify.bDeviceType == CSMI_SAS_NO_DEVICE_ATTACHED)
           continue;
 
-        // Use a bPhyIdentifier or the bPortIdentifier if unique,
-        // otherwise use table index
+        // Try to detect which field contains the actual port number.
+        // Use a bPhyIdentifier or the bPortIdentifier if unique
+        // and not always identical to table index, otherwise use index.
         int port;
         switch (mode) {
           case 0:  port = pe.Attached.bPhyIdentifier; break;
@@ -2095,16 +2091,18 @@ int csmi_device::get_phy_info(CSMI_SAS_PHY_INFO & phy_info, port_2_index_map & p
           default: port = i; break;
         }
         if (!(port < max_number_of_ports && p2i[port] == -1)) {
-          unique = false;
+          found = false;
           break;
         }
 
         p2i[port] = i;
         if (number_of_ports <= port)
           number_of_ports = port + 1;
+        if (port != i)
+          found = true;
      }
 
-     if (unique || mode > 2)
+     if (found || mode > 2)
        break;
   }
 
@@ -2164,7 +2162,7 @@ unsigned csmi_device::get_ports_used()
         continue;
     }
 
-    ports_used |= (1 << p);
+    ports_used |= (1U << p);
   }
 
   return ports_used;
@@ -2558,63 +2556,6 @@ static int get_clipboard(char * data, int datasize)
 }
 
 
-// Run a command, write stdout to dataout
-// TODO: Combine with daemon_win32.cpp:daemon_spawn()
-
-static int run_cmd(const char * cmd, char * dataout, int outsize)
-{
-  // Create stdout pipe
-  SECURITY_ATTRIBUTES sa = {sizeof(sa), 0, TRUE};
-  HANDLE pipe_out_w, h;
-  if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize))
-    return -1;
-  HANDLE self = GetCurrentProcess();
-  HANDLE pipe_out_r;
-  if (!DuplicateHandle(self, h, self, &pipe_out_r,
-    GENERIC_READ, FALSE/*!inherit*/, DUPLICATE_CLOSE_SOURCE)) {
-    CloseHandle(pipe_out_w);
-    return -1;
-  }
-  HANDLE pipe_err_w;
-  if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
-    0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
-    CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
-    return -1;
-  }
-
-  // Create process
-  STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
-  si.hStdInput  = INVALID_HANDLE_VALUE;
-  si.hStdOutput = pipe_out_w; si.hStdError  = pipe_err_w;
-  si.dwFlags = STARTF_USESTDHANDLES;
-  PROCESS_INFORMATION pi;
-  if (!CreateProcess(
-    NULL, const_cast<char *>(cmd),
-    NULL, NULL, TRUE/*inherit*/,
-    CREATE_NO_WINDOW/*do not create a new console window*/,
-    NULL, NULL, &si, &pi)) {
-    CloseHandle(pipe_err_w); CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
-    return -1;
-  }
-  CloseHandle(pi.hThread);
-  CloseHandle(pipe_err_w); CloseHandle(pipe_out_w);
-
-  // Copy stdout to output buffer
-  int i = 0;
-  while (i < outsize) {
-    DWORD num_read;
-    if (!ReadFile(pipe_out_r, dataout+i, outsize-i, &num_read, NULL) || num_read == 0)
-      break;
-    i += num_read;
-  }
-  CloseHandle(pipe_out_r);
-  // Wait for process
-  WaitForSingleObject(pi.hProcess, INFINITE);
-  CloseHandle(pi.hProcess);
-  return i;
-}
-
-
 static const char * findstr(const char * str, const char * sub)
 {
   const char * s = strstr(str, sub);
@@ -2641,7 +2582,11 @@ bool win_tw_cli_device::open()
     snprintf(cmd, sizeof(cmd), "tw_cli /%s show all", name+n1);
     if (ata_debugmode > 1)
       pout("%s: Run: \"%s\"\n", name, cmd);
-    size = run_cmd(cmd, buffer, sizeof(buffer));
+    FILE * f = popen(cmd, "rb");
+    if (f) {
+      size = fread(buffer, 1, sizeof(buffer), f);
+      pclose(f);
+    }
   }
   else {
     return set_err(EINVAL);
@@ -3495,8 +3440,7 @@ bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io *iop)
       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 : (int)iop->dxfer_len) , 1);
+        dStrHex(iop->dxferp, (trunc ? 256 : (int)iop->dxfer_len) , 1);
       }
     else
       j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
@@ -3601,7 +3545,7 @@ bool win_aacraid_device::scsi_pass_through(struct scsi_cmnd_io *iop)
     int trunc = (iop->dxfer_len > 256) ? 1 : 0;
     pout("  Incoming data, len=%d, resid=%d%s:\n", (int)iop->dxfer_len, iop->resid,
       (trunc ? " [only first 256 bytes shown]" : ""));
-    dStrHex((CHAR*)pDataIO, (trunc ? 256 : (int)(iop->dxfer_len)) , 1);
+    dStrHex((const uint8_t *)pDataIO, (trunc ? 256 : (int)(iop->dxfer_len)) , 1);
   }
   return true;
 }
@@ -3671,7 +3615,12 @@ bool win_nvme_device::open_scsi(int n)
   return true;
 }
 
-// Check if NVMe pass-through works
+// Check if NVMe DeviceIoControl(IOCTL_SCSI_MINIPORT) pass-through works.
+// On Win10 and later that returns false with an errorNumber of 1
+// ("Incorrect function"). Win10 has new pass-through:
+// DeviceIoControl(IOCTL_STORAGE_PROTOCOL_COMMAND). However for commonly
+// requested NVMe commands like Identify and Get Features Microsoft want
+// "Protocol specific queries" sent.
 bool win_nvme_device::probe()
 {
   smartmontools::nvme_id_ctrl id_ctrl;
@@ -3897,7 +3846,7 @@ bool win10_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out &
   STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER * spsq =
     reinterpret_cast<STORAGE_PROTOCOL_SPECIFIC_QUERY_WITH_BUFFER *>(spsq_raw_buf.data());
 
-  // Set NVMe specifc STORAGE_PROPERTY_QUERY
+  // Set NVMe specific STORAGE_PROPERTY_QUERY
   spsq->PropertyQuery.QueryType = PropertyStandardQuery;
   spsq->ProtocolSpecific.ProtocolType = win10::ProtocolTypeNvme;
 
@@ -3997,7 +3946,7 @@ protected:
   virtual std::string get_valid_custom_dev_types_str();
 
 private:
-  ata_device * get_usb_device(const char * name, int phydrive, int logdrive = -1);
+  smart_device * get_usb_device(const char * name, int phydrive, int logdrive = -1);
 };
 
 
@@ -4031,10 +3980,7 @@ std::string win_smart_interface::get_os_version_str()
   assert(vptr == vstr+strlen(vstr) && vptr+vlen+1 == vstr+sizeof(vstr));
 
   // Starting with Windows 8.1, GetVersionEx() does no longer report the
-  // actual OS version, see:
-  // http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
-
-  // RtlGetVersion() is not affected
+  // actual OS version.  RtlGetVersion() is not affected.
   LONG /*NTSTATUS*/ (WINAPI /*NTAPI*/ * RtlGetVersion_p)(LPOSVERSIONINFOEXW) =
     (LONG (WINAPI *)(LPOSVERSIONINFOEXW))
     GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlGetVersion");
@@ -4074,12 +4020,21 @@ std::string win_smart_interface::get_os_version_str()
           case 14393:   w = "w10-1607"; break;
           case 15063:   w = "w10-1703"; break;
           case 16299:   w = "w10-1709"; break;
-          default:      w = "w10";  build = vi.dwBuildNumber; break;
+          case 17134:   w = "w10-1803"; break;
+          case 17763:   w = "w10-1809"; break;
+          default:      w = "w10";
+                        build = vi.dwBuildNumber; break;
         } break;
       case 0xa0<<1 | 1:
         switch (vi.dwBuildNumber) {
-          case 14393:   w = "2016-1607"; break;
-          default:      w = "2016"; build = vi.dwBuildNumber; break;
+          case 14393:   w = "2016";      break;
+          case 16299:   w = "2016-1709"; break;
+          case 17134:   w = "2016-1803"; break;
+          case 17763:   w = "2019";      break;
+          default:      w = (vi.dwBuildNumber < 17763
+                          ? "2016"
+                          : "2019");
+                        build = vi.dwBuildNumber; break;
         } break;
     }
   }
@@ -4095,11 +4050,11 @@ std::string win_smart_interface::get_os_version_str()
       (vi.dwPlatformId==VER_PLATFORM_WIN32_NT ? "nt" : "??"),
       (unsigned)vi.dwMajorVersion, (unsigned)vi.dwMinorVersion, w64);
   else if (build)
-    snprintf(vptr, vlen, "-%s%s-b%u", w, w64, build);
+    snprintf(vptr, vlen, "-%s-b%u%s", w, build, w64);
   else if (vi.wServicePackMinor)
-    snprintf(vptr, vlen, "-%s%s-sp%u.%u", w, w64, vi.wServicePackMajor, vi.wServicePackMinor);
+    snprintf(vptr, vlen, "-%s-sp%u.%u%s", w, vi.wServicePackMajor, vi.wServicePackMinor, w64);
   else if (vi.wServicePackMajor)
-    snprintf(vptr, vlen, "-%s%s-sp%u", w, w64, vi.wServicePackMajor);
+    snprintf(vptr, vlen, "-%s-sp%u%s", w, vi.wServicePackMajor, w64);
   else
     snprintf(vptr, vlen, "-%s%s", w, w64);
   return vstr;
@@ -4326,9 +4281,12 @@ static win_dev_type get_controller_type(HANDLE hdevice, bool admin, GETVERSIONIN
     case BusTypeUsb:
       return DEV_USB;
 
-    case 0x11: // BusTypeNVMe?
+    case 0x11: // BusTypeNvme
       return DEV_NVME;
 
+    case 0x12: //BusTypeSCM 
+    case 0x13: //BusTypeUfs
+    case 0x14: //BusTypeMax,
     default:
       return DEV_UNKNOWN;
   }
@@ -4407,7 +4365,7 @@ static win_dev_type get_dev_type(const char * name, int & phydrive, int & logdri
 }
 
 
-ata_device * win_smart_interface::get_usb_device(const char * name,
+smart_device * win_smart_interface::get_usb_device(const char * name,
   int phydrive, int logdrive /* = -1 */)
 {
   // Get USB bridge ID
@@ -4423,7 +4381,7 @@ ata_device * win_smart_interface::get_usb_device(const char * name,
     return 0;
 
   // Return SAT/USB device for this type
-  return get_sat_device(usbtype, new win_scsi_device(this, name, ""));
+  return get_scsi_passthrough_device(usbtype, new win_scsi_device(this, name, ""));
 }
 
 smart_device * win_smart_interface::autodetect_smart_device(const char * name)
@@ -4520,7 +4478,7 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
     }
   }
 
-  char name[20];
+  char name[32];
 
   if (ata || scsi || sat || usb || nvme) {
     // Scan up to 128 drives and 2 3ware controllers
@@ -4555,7 +4513,7 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
             // Add physical drives
             int len = strlen(name);
             for (unsigned int pi = 0; pi < 32; pi++) {
-              if (vers_ex.dwDeviceMapEx & (1L << pi)) {
+              if (vers_ex.dwDeviceMapEx & (1U << pi)) {
                 snprintf(name+len, sizeof(name)-1-len, ",%u", pi);
                 devlist.push_back( new win_ata_device(this, name, "ata") );
               }
@@ -4619,7 +4577,7 @@ bool win_smart_interface::scan_smart_devices(smart_device_list & devlist,
         continue;
 
       for (int pi = 0; pi < 32; pi++) {
-        if (!(ports_used & (1 << pi)))
+        if (!(ports_used & (1U << pi)))
           continue;
         snprintf(name, sizeof(name)-1, "/dev/csmi%d,%d", i, pi);
         devlist.push_back( new win_csmi_device(this, name, "ata") );
index 799ee45c41d295454fa5740427ddfd9f4a8bbb87..66e022acb797d2f6d19039d9d94419e127002769 100644 (file)
@@ -3,16 +3,9 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2004-14 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2004-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #define WINVER 0x0600
@@ -20,7 +13,7 @@
 
 #include "daemon_win32.h"
 
-const char * daemon_win32_cpp_cvsid = "$Id: daemon_win32.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
+const char * daemon_win32_cpp_cvsid = "$Id: daemon_win32.cpp 4842 2018-12-02 16:07:26Z chrfranke $"
   DAEMON_WIN32_H_CVSID;
 
 #include <stdio.h>
@@ -464,120 +457,6 @@ int daemon_detach(const char * ident)
 }
 
 
-/////////////////////////////////////////////////////////////////////////////
-
-// 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 self = GetCurrentProcess();
-
-  // Create stdin pipe with inheritable read side
-  SECURITY_ATTRIBUTES sa;
-  memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa);
-  sa.bInheritHandle = TRUE;
-  HANDLE pipe_inp_r, pipe_inp_w, h;
-  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;
-  }
-
-  // Create stdout pipe with inheritable write side
-  memset(&sa, 0, sizeof(sa)); sa.nLength = sizeof(sa);
-  sa.bInheritHandle = TRUE;
-  HANDLE pipe_out_w;
-  if (!CreatePipe(&h, &pipe_out_w, &sa/*inherit*/, outsize)) {
-    CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
-    return -1;
-  }
-
-  HANDLE pipe_out_r;
-  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
-  HANDLE pipe_err_w;
-  if (!DuplicateHandle(self, pipe_out_w, self, &pipe_err_w,
-    0, TRUE/*inherit*/, DUPLICATE_SAME_ACCESS)) {
-    CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
-    CloseHandle(pipe_inp_r); CloseHandle(pipe_inp_w);
-    return -1;
-  }
-
-  // Create process with pipes as stdio
-  STARTUPINFO si;
-  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;
-  PROCESS_INFORMATION pi;
-  if (!CreateProcessA(
-    NULL, (char*)cmd,
-    NULL, NULL, TRUE/*inherit*/,
-    CREATE_NO_WINDOW, // DETACHED_PROCESS does not work
-    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
-  DWORD num_io;
-  int i;
-  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);
-
-  // Copy stdout to output buffer until full, rest to /dev/null
-  // convert \r\n => \n
-  for (i = 0; ; ) {
-    char buf[256];
-    if (!ReadFile(pipe_out_r, buf, sizeof(buf), &num_io, NULL) || num_io == 0)
-      break;
-    for (int 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);
-
-  // Wait for process exitcode
-  DWORD exitcode = 42;
-  WaitForSingleObject(pi.hProcess, INFINITE);
-  GetExitCodeProcess(pi.hProcess, &exitcode);
-  CloseHandle(pi.hProcess);
-  return exitcode;
-}
-
-
 /////////////////////////////////////////////////////////////////////////////
 // Initd Functions
 
@@ -985,7 +864,7 @@ static int svcadm_main(const char * ident, const daemon_winsvc_options * svc_opt
       SERVICE_WIN32_OWN_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*/))) {
+      ""/*no dependencies*/, 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)
index 813225189af38eba6fa6a6e08ad3b879785ddb80..f0f4b217f90162a6485ee7ff2a827f17f6093dbd 100644 (file)
@@ -3,22 +3,15 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2004-12 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2004-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef DAEMON_WIN32_H
 #define DAEMON_WIN32_H
 
-#define DAEMON_WIN32_H_CVSID "$Id: daemon_win32.h 4431 2017-08-08 19:38:15Z chrfranke $"
+#define DAEMON_WIN32_H_CVSID "$Id: daemon_win32.h 4818 2018-10-17 05:32:17Z chrfranke $"
 
 #include <signal.h>
 
@@ -59,9 +52,4 @@ int daemon_enable_console(const char * title);
 // Detach from console
 int daemon_detach(const char * ident);
 
-// 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
index 19a0a1c6fa7b0de5b58d6317084d42e139e50908..f9e281a55e33c7b7148d07e5e152121f3787c1d5 100644 (file)
@@ -5,15 +5,9 @@
 ;
 ; Copyright (C) 2006-17 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.
+; SPDX-License-Identifier: GPL-2.0-or-later
 ;
-; You should have received a copy of the GNU General Public License
-; (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
-;
-; $Id: installer.nsi 4575 2017-10-29 16:06:18Z chrfranke $
+; $Id: installer.nsi 4760 2018-08-19 18:45:53Z chrfranke $
 ;
 
 
@@ -47,6 +41,19 @@ InstallColors /windows
 ;InstallDir "$PROGRAMFILES\smartmontools"
 ;InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "InstallLocation"
 
+!ifdef VERSION
+  VIProductVersion "${VERSION}"
+  VIAddVersionKey /LANG=1033-English "CompanyName" "www.smartmontools.org"
+  VIAddVersionKey /LANG=1033-English "FileDescription" "SMART Monitoring Tools"
+  VIAddVersionKey /LANG=1033-English "FileVersion" "${VERSION}"
+ !ifdef YY
+  VIAddVersionKey /LANG=1033-English "LegalCopyright" "(C) 2002-20${YY}, Bruce Allen, Christian Franke, www.smartmontools.org"
+ !endif
+  VIAddVersionKey /LANG=1033-English "OriginalFilename" "${OUTFILE}"
+  VIAddVersionKey /LANG=1033-English "ProductName" "smartmontools"
+  VIAddVersionKey /LANG=1033-English "ProductVersion" "${VERSION}"
+!endif
+
 Var EDITOR
 
 !ifdef INPDIR64
@@ -229,18 +236,16 @@ Section "Uninstaller" UNINST_SECTION
 
   CreateDirectory "$INSTDIR"
 
-  ; Keep old Install_Dir registry entry for GSmartControl
-  ReadRegStr $0 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\GSmartControl" "InstallLocation"
-  ReadRegStr $1 HKLM "Software\smartmontools" "Install_Dir"
-  ${If} "$0$1" != ""
-    WriteRegStr HKLM "Software\smartmontools" "Install_Dir" "$INSTDIR"
-  ${EndIf}
+  ; Remove old "Install_Dir" registry entry (smartmontools < r3911/6.3)
+  ; No longer needed for GSmartControl
+  DeleteRegKey HKLM "Software\smartmontools" ; TODO: Remove after smartmontools 6.7
 
   ; Write uninstall keys and program
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "DisplayName" "smartmontools"
 !ifdef VERSTR
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "DisplayVersion" "${VERSTR}"
 !endif
+  ; Important: GSmartControl (>= 1.0.0) reads "InstallLocation" to detect location of bin\smartctl-nc.exe
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "InstallLocation" "$INSTDIR"
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "UninstallString" '"$INSTDIR\uninst-smartmontools.exe"'
   WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "Publisher"     "smartmontools.org"
@@ -272,7 +277,6 @@ Section "Start Menu Shortcuts" MENU_SECTION
   ${OrIf} ${FileExists} "$INSTDIR\bin\smartd.exe"
     SetOutPath "$INSTDIR\bin"
     !insertmacro FileExe "bin\runcmdu.exe" ""
-    Delete "$INSTDIR\bin\runcmdu.exe.manifest" ; TODO: Remove after smartmontools 6.5
   ${EndIf}
 
   ; smartctl
@@ -419,9 +423,8 @@ Section "Uninstall"
     ${EndIf}
   ${EndIf}
 
-  ; Remove installer registry keys
+  ; Remove installer registry key
   DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools"
-  DeleteRegKey HKLM "Software\smartmontools"
 
   ; Remove conf file ?
   ${If} ${FileExists} "$INSTDIR\bin\smartd.conf"
@@ -464,9 +467,7 @@ Section "Uninstall"
   Delete "$INSTDIR\bin\drivedb.h.old"
   Delete "$INSTDIR\bin\update-smart-drivedb.exe"
   Delete "$INSTDIR\bin\runcmda.exe"
-  Delete "$INSTDIR\bin\runcmda.exe.manifest" ; TODO: Remove after smartmontools 6.5
   Delete "$INSTDIR\bin\runcmdu.exe"
-  Delete "$INSTDIR\bin\runcmdu.exe.manifest" ; TODO: Remove after smartmontools 6.5
   Delete "$INSTDIR\bin\wtssendmsg.exe"
   Delete "$INSTDIR\doc\AUTHORS.txt"
   Delete "$INSTDIR\doc\ChangeLog.txt"
@@ -540,14 +541,11 @@ Function .onInit
   ${If} $INSTDIR == "" ; /D=PATH option not specified ?
     ReadRegStr $INSTDIR HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\smartmontools" "InstallLocation"
     ${If} $INSTDIR == "" ; Not already installed ?
-      ReadRegStr $INSTDIR HKLM "Software\smartmontools" "Install_Dir"
-      ${If} $INSTDIR == "" ; Not already installed (smartmontools < r3911/6.3) ?
-        StrCpy $INSTDIR "$PROGRAMFILES\smartmontools"
+      StrCpy $INSTDIR "$PROGRAMFILES\smartmontools"
 !ifdef INPDIR64
-        StrCpy $INSTDIR32 $INSTDIR
-        StrCpy $INSTDIR64 "$PROGRAMFILES64\smartmontools"
+      StrCpy $INSTDIR32 $INSTDIR
+      StrCpy $INSTDIR64 "$PROGRAMFILES64\smartmontools"
 !endif
-      ${EndIf}
     ${EndIf}
   ${EndIf}
 
@@ -569,10 +567,13 @@ Function .onInit
   !insertmacro AdjustSectionSize ${SMARTCTL_NC_SECTION}
 !endif
 
-  ; Use Notepad++ if installed
+  ; Use 32-bit or 64-bit Notepad++ if installed
   StrCpy $EDITOR "$PROGRAMFILES\Notepad++\notepad++.exe"
   ${IfNot} ${FileExists} "$EDITOR"
-    StrCpy $EDITOR "notepad.exe"
+    StrCpy $EDITOR "$PROGRAMFILES64\Notepad++\notepad++.exe"
+    ${IfNot} ${FileExists} "$EDITOR"
+      StrCpy $EDITOR "notepad.exe"
+    ${EndIf}
   ${EndIf}
 
   Call ParseCmdLine
@@ -685,7 +686,6 @@ Function CheckRunCmdA
     StrCpy $runcmda "t"
     SetOutPath "$INSTDIR\bin"
     !insertmacro FileExe "bin\runcmda.exe" ""
-    Delete "$INSTDIR\bin\runcmda.exe.manifest" ; TODO: Remove after smartmontools 6.5
   ${EndIf}
 FunctionEnd
 
diff --git a/os_win32/popen.h b/os_win32/popen.h
new file mode 100644 (file)
index 0000000..b8dda2c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * os_win32/popen.h
+ *
+ * Home page of code is: https://www.smartmontools.org
+ *
+ * Copyright (C) 2018 Christian Franke
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef POPEN_H
+#define POPEN_H
+
+#define POPEN_H_CVSID "$Id: popen.h 4818 2018-10-17 05:32:17Z chrfranke $"
+
+#include <stdio.h>
+
+// MinGW <stdio.h> defines these to _popen/_pclose
+#undef popen
+#undef pclose
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// popen(3) reimplementation for Windows
+//
+// The _popen() from MSVCRT is not useful as it always opens a new
+// console window if parent process has none.
+//
+// Differences to popen(3):
+// - Only modes "r[bt]" are supported
+// - stdin and stderr from parent are not inherited to child process
+//   but redirected to null device
+// - Only one child process can be run at a time
+
+FILE * popen(const char * command, const char * mode);
+
+int pclose(FILE * f);
+
+#ifdef __cplusplus
+}
+#endif
+
+// wait(3) macros from <sys/wait.h>
+#ifndef WIFEXITED
+#define WIFEXITED(status)   (((status) & 0xff) == 0x00)
+#define WIFSIGNALED(status) (((status) & 0xff) != 0x00)
+#define WIFSTOPPED(status)  (0)
+#define WEXITSTATUS(status) ((status) >> 8)
+#define WTERMSIG(status)    ((status) & 0xff)
+#define WSTOPSIG(status)    (0)
+#endif // WIFEXITED
+
+#endif // POPEN_H
diff --git a/os_win32/popen_win32.cpp b/os_win32/popen_win32.cpp
new file mode 100644 (file)
index 0000000..d0dc680
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * os_win32/popen_win32.cpp
+ *
+ * Home page of code is: https://www.smartmontools.org
+ *
+ * Copyright (C) 2018 Christian Franke
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "popen.h"
+
+const char * popen_win32_cpp_cvsid = "$Id: popen_win32.cpp 4818 2018-10-17 05:32:17Z chrfranke $"
+  POPEN_H_CVSID;
+
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h> // _open_osfhandle()
+#include <signal.h> // SIGSEGV
+#include <stdlib.h>
+#include <string.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+static FILE * s_popen_file;
+static HANDLE s_popen_process;
+
+extern "C"
+FILE * popen(const char * command, const char * mode)
+{
+  // Fail if previous run is still in progress
+  if (s_popen_file) {
+    errno = EEXIST;
+    return (FILE *)0;
+  }
+
+  // mode "w" is not implemented
+  if (!(mode[0] == 'r' && (!mode[1] || !mode[2]))) {
+    errno = EINVAL;
+    return (FILE *)0;
+  }
+
+  // Set flags for text or binary mode
+  // Note: _open_osfhandle() ignores _fmode and defaults to O_BINARY
+  int oflags; const char * fomode;
+  switch (mode[1]) {
+    case 0:
+    case 't':
+      oflags = O_RDONLY|O_TEXT;
+      fomode = "rt";
+      break;
+    case 'b':
+      oflags = O_RDONLY|O_BINARY;
+      fomode = "rb";
+      break;
+    default:
+      errno = EINVAL;
+      return (FILE *)0;
+  }
+
+  // Create stdout pipe with inheritable write end
+  HANDLE pipe_out_r, pipe_out_w;
+  if (!CreatePipe(&pipe_out_r, &pipe_out_w, (SECURITY_ATTRIBUTES *)0, 1024)) {
+    errno = EMFILE;
+    return (FILE *)0;
+  }
+  if (!SetHandleInformation(pipe_out_w, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT)) {
+    CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
+    errno = EMFILE;
+    return (FILE *)0;
+  }
+
+  // Connect pipe read end to new FD
+  int fd = _open_osfhandle((intptr_t)pipe_out_r, oflags);
+  if (fd < 0) {
+    CloseHandle(pipe_out_r); CloseHandle(pipe_out_w);
+    return (FILE *)0;
+  }
+
+  // Connect FD to new FILE
+  FILE * f = fdopen(fd, fomode);
+  if (!f) {
+    int err = errno;
+    close(fd); // CloseHandle(pipe_out_r)
+    CloseHandle(pipe_out_w);
+    errno = err;
+    return (FILE *)0;
+  }
+
+  // Build command line "cmd /c COMMAND"
+  int cmdlen = strlen(command);
+  char * shellcmd = (char *)malloc(7 + cmdlen + 1);
+  if (!shellcmd) {
+    fclose(f); // CloseHandle(pipe_out_r)
+    CloseHandle(pipe_out_w);
+    errno = ENOMEM;
+    return (FILE *)0;
+  }
+  memcpy(shellcmd, "cmd /c ", 7);
+  memcpy(shellcmd + 7, command, cmdlen + 1);
+
+  // Redirect stdin stderr to null device
+  // Don't inherit parent's stdin, script may hang if parent has no console.
+  SECURITY_ATTRIBUTES sa_inherit = { sizeof(sa_inherit), (SECURITY_DESCRIPTOR *)0, TRUE };
+  HANDLE null_in  = CreateFile("nul", GENERIC_READ , 0, &sa_inherit, OPEN_EXISTING, 0, (HANDLE)0);
+  HANDLE null_err = CreateFile("nul", GENERIC_WRITE, 0, &sa_inherit, OPEN_EXISTING, 0, (HANDLE)0);
+
+  // Set stdio handles
+  STARTUPINFO si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
+  si.hStdInput  = null_in;
+  si.hStdOutput = pipe_out_w;
+  si.hStdError  = null_err;
+  si.dwFlags = STARTF_USESTDHANDLES;
+
+  // Create process
+  PROCESS_INFORMATION pi;
+  BOOL ok = CreateProcessA(
+    getenv("COMSPEC"), // "C:\WINDOWS\system32\cmd.exe" or nullptr
+    shellcmd, // "cmd /c COMMAND" ("cmd" searched in PATH if COMSPEC not set)
+    (SECURITY_ATTRIBUTES *)0, (SECURITY_ATTRIBUTES *)0,
+    TRUE, // inherit
+    CREATE_NO_WINDOW, // DETACHED_PROCESS would open new console(s)
+    (void *)0, (char *)0, &si, &pi
+  );
+  free(shellcmd);
+
+  // Close inherited handles
+  CloseHandle(null_err);
+  CloseHandle(null_in);
+  CloseHandle(pipe_out_w);
+
+  if (!ok) {
+    fclose(f); // CloseHandle(pipe_out_r)
+    errno = ENOENT;
+    return (FILE *)0;
+  }
+
+  // Store process and FILE for pclose()
+  CloseHandle(pi.hThread);
+  s_popen_process = pi.hProcess;
+  s_popen_file = f;
+
+  return f;
+}
+
+extern "C"
+int pclose(FILE * f)
+{
+  if (f != s_popen_file) {
+    errno = EBADF;
+    return -1;
+  }
+
+  fclose(f);
+  s_popen_file = 0;
+
+  // Wait for process exitcode
+  DWORD exitcode = 42;
+  bool ok = (   WaitForSingleObject(s_popen_process, INFINITE) == WAIT_OBJECT_0
+             && GetExitCodeProcess(s_popen_process, &exitcode));
+
+  CloseHandle(s_popen_process);
+  s_popen_process = 0;
+
+  if (!ok) {
+    errno = ECHILD;
+    return -1;
+  }
+
+  // Modify exitcode for wait(3) macros
+  if (exitcode >> 23)
+    return ((exitcode << 9) >> 1) | SIGSEGV;
+  else
+    return exitcode << 8;
+}
index fe190f5d65f6d949f692c8647fabf10fb3a9acb5..026171726139bbae7bb89645bfcae5bc8e5d3177 100644 (file)
@@ -5,17 +5,10 @@
  *
  * Copyright (C) 2011 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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
-char svnid[] = "$Id: runcmd.c 4431 2017-08-08 19:38:15Z chrfranke $";
+char svnid[] = "$Id: runcmd.c 4760 2018-08-19 18:45:53Z chrfranke $";
 
 #include <stdio.h>
 #include <windows.h>
index a3cba243d3761361554acfc44d74a7e24280588d..c585c89152f666f5aa48f19294b9fe91cd02d5d7 100644 (file)
@@ -5,15 +5,9 @@
 #
 # Copyright (C) 2016 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.
+# SPDX-License-Identifier: GPL-2.0-or-later
 #
-# You should have received a copy of the GNU General Public License
-# (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
-#
-# $Id: smartd_mailer.ps1 4338 2016-09-07 19:31:28Z chrfranke $
+# $Id: smartd_mailer.ps1 4760 2018-08-19 18:45:53Z chrfranke $
 #
 
 $ErrorActionPreference = "Stop"
index 9b2d178fce1f2511ab5f9855da3fea9168b5b5c9..ceff74330d2f35fdfd81b05bd7c8541ef73604e4 100644 (file)
@@ -6,15 +6,9 @@
 ::
 :: Copyright (C) 2012-17 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.
+:: SPDX-License-Identifier: GPL-2.0-or-later
 ::
-:: You should have received a copy of the GNU General Public License
-:: (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
-::
-:: $Id: smartd_warning.cmd 4569 2017-10-25 20:13:11Z chrfranke $
+:: $Id: smartd_warning.cmd 4760 2018-08-19 18:45:53Z chrfranke $
 ::
 
 verify other 2>nul
index 449f802ecbb9b6496640aea2a8e88c294651ea6d..00947b39d58b1f3a27b3615edb29338ff9531d8a 100644 (file)
@@ -5,21 +5,13 @@
  *
  * Copyright (C) 2004-8 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef SYSLOG_H
 #define SYSLOG_H
 
-#define SYSLOG_H_CVSID "$Id: syslog.h 4431 2017-08-08 19:38:15Z chrfranke $\n"
+#define SYSLOG_H_CVSID "$Id: syslog.h 4760 2018-08-19 18:45:53Z chrfranke $\n"
 
 #include <stdarg.h>
 
index 57e14e6c3de487c48f0181a1449078cdd9dce0f7..dfa08343fc83a0619dc15fe9dcdd6511374a02ad 100644 (file)
@@ -5,14 +5,7 @@
  *
  * Copyright (C) 2004-15 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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 // Win32 Emulation of syslog() for smartd
@@ -33,7 +26,7 @@
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h> // RegisterEventSourceA(), ReportEventA(), ...
 
-const char *syslog_win32_cpp_cvsid = "$Id: syslog_win32.cpp 4149 2015-10-17 15:38:01Z chrfranke $"
+const char *syslog_win32_cpp_cvsid = "$Id: syslog_win32.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
   SYSLOG_H_CVSID;
 
 #ifdef _MSC_VER
index 44347e21ce76cde9a0817efe14a3ea8814036e4e..8266a611e6546afe6b198564243ca7c998d906be 100644 (file)
@@ -5,18 +5,10 @@
 ; *
 ; * Copyright (C) 2004-10 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-; *
+; * SPDX-License-Identifier: GPL-2.0-or-later
 ; */
 ;
-;// $Id: syslogevt.mc 4431 2017-08-08 19:38:15Z chrfranke $
+;// $Id: syslogevt.mc 4760 2018-08-19 18:45:53Z chrfranke $
 ;
 ;// Use message compiler "mc" or "windmc" to generate
 ;//   syslogevt.rc, syslogevt.h, msg00001.bin
index 145b055f8d140357e42a765771f83305b3d10e14..c1bf5c9bf3f3f200d18331e085db3f8bd78a2580 100644 (file)
@@ -5,15 +5,9 @@
 ;
 ; Copyright (C) 2011-13 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.
+; SPDX-License-Identifier: GPL-2.0-or-later
 ;
-; You should have received a copy of the GNU General Public License
-; (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
-;
-; $Id: update-smart-drivedb.nsi 4431 2017-08-08 19:38:15Z chrfranke $
+; $Id: update-smart-drivedb.nsi 4760 2018-08-19 18:45:53Z chrfranke $
 ;
 
 
index 5ced75b4f4878e7ef6bc3fc7fa12d4ce550edda5..929b22a40a292b20031bcee2bd23fa5f7d680d02 100644 (file)
@@ -5,14 +5,7 @@
  *
  * Copyright (C) 2011-13 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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
@@ -23,7 +16,7 @@
 
 #include <stdio.h>
 
-const char * wmiquery_cpp_cvsid = "$Id: wmiquery.cpp 4431 2017-08-08 19:38:15Z chrfranke $"
+const char * wmiquery_cpp_cvsid = "$Id: wmiquery.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
   WMIQUERY_H_CVSID;
 
 
index 3000a37545ff62f795117b45bf43dc427d938a7a..47b6f37f08104e469130d8bde1e81c908f6a3e1b 100644 (file)
@@ -3,28 +3,17 @@
  *
  * Home page of code is: http://www.smartmontools.org
  *
- * Copyright (C) 2011-15 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, see <http://www.gnu.org/licenses/>.
+ * Copyright (C) 2011-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef WMIQUERY_H
 #define WMIQUERY_H
 
-#define WMIQUERY_H_CVSID "$Id: wmiquery.h 4152 2015-10-17 16:08:21Z chrfranke $"
+#define WMIQUERY_H_CVSID "$Id: wmiquery.h 4760 2018-08-19 18:45:53Z chrfranke $"
 
-#ifdef HAVE_WBEMCLI_H
 #include <wbemcli.h>
-#else
-#include "wbemcli_small.h"
-#endif
 
 #include <string>
 
@@ -168,11 +157,13 @@ public:
 
   /// Execute query, get result list.
   /// Return false on error.
-  bool vquery(wbem_enumerator & result, const char * qstr, va_list args) /*const*/;
+  bool vquery(wbem_enumerator & result, const char * qstr, va_list args) /*const*/
+       __attribute_format_printf(3, 0);
 
   /// Execute query, get single result object.
   /// Return false on error or result size != 1.
-  bool vquery1(wbem_object & obj, const char * qstr, va_list args) /*const*/;
+  bool vquery1(wbem_object & obj, const char * qstr, va_list args) /*const*/
+       __attribute_format_printf(3, 0);
 
   /// Version of vquery() with printf() formatting.
   bool query(wbem_enumerator & result, const char * qstr, ...) /*const*/
index 2977f1a66c09d0126db377f7d01ae6adfd1d2dff..7a0814b39f030c73eb01046c425c065ed03bd086 100644 (file)
@@ -5,20 +5,13 @@
  *
  * Copyright (C) 2012 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, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #define WINVER 0x0500
 #define _WIN32_WINNT WINVER
 
-char svnid[] = "$Id: wtssendmsg.c 4431 2017-08-08 19:38:15Z chrfranke $";
+char svnid[] = "$Id: wtssendmsg.c 4760 2018-08-19 18:45:53Z chrfranke $";
 
 #include <stdio.h>
 #include <string.h>
@@ -30,7 +23,7 @@ char svnid[] = "$Id: wtssendmsg.c 4431 2017-08-08 19:38:15Z chrfranke $";
 
 static int usage()
 {
-  printf("wtssendmsg $Revision: 4431 $ - Display a message box on client desktops\n"
+  printf("wtssendmsg $Revision: 4760 $ - Display a message box on client desktops\n"
          "Copyright (C) 2012 Christian Franke, smartmontools.org\n\n"
          "Usage: wtssendmsg [-cas] [-v] [\"Caption\"] \"Message\"|-\n"
          "       wtssendmsg -v\n\n"
index dcb714326d59567a92f0253556e8b92e3c3f4788..16419494b4bbd71ac2bbf36582a717c267599ee5 100644 (file)
@@ -1,5 +1,5 @@
 /* Extended regular expression matching and search library.
-   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2002-2018 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
 
    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., 51 Franklin Street, Fifth Floor, Boston,
-   MA 02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifdef _LIBC
+# include <locale/weight.h>
+#endif
 
 static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
-                                         int length, reg_syntax_t syntax);
+                                         size_t 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);
+static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len);
 #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,
+#ifdef RE_ENABLE_I18N
+static void optimize_utf8 (re_dfa_t *dfa);
+#endif
+static reg_errcode_t analyze (regex_t *preg);
+static reg_errcode_t preorder (bin_tree_t *root,
+                              reg_errcode_t (fn (void *, bin_tree_t *)),
+                              void *extra);
+static reg_errcode_t postorder (bin_tree_t *root,
+                               reg_errcode_t (fn (void *, bin_tree_t *)),
+                               void *extra);
+static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node);
+static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node);
+static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg,
+                                bin_tree_t *node);
+static reg_errcode_t calc_first (void *extra, bin_tree_t *node);
+static reg_errcode_t calc_next (void *extra, bin_tree_t *node);
+static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node);
+static Idx duplicate_node (re_dfa_t *dfa, Idx org_idx, unsigned int constraint);
+static Idx search_duplicated_node (const re_dfa_t *dfa, Idx 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,
+                                        Idx node, bool root);
+static reg_errcode_t calc_inveclosure (re_dfa_t *dfa);
+static Idx 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);
+                                 Idx 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);
+                                Idx 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);
+                                    Idx 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);
+                                 Idx 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);
@@ -77,57 +84,52 @@ 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);
+                                           reg_syntax_t syntax,
+                                           bool accept_hyphen);
 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,
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
                                        re_charset_t *mbcset,
-                                       int *equiv_class_alloc,
+                                       Idx *equiv_class_alloc,
                                        const unsigned char *name);
-static reg_errcode_t build_charclass (re_bitset_ptr_t sbcset,
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+                                     bitset_t sbcset,
                                      re_charset_t *mbcset,
-                                     int *char_class_alloc,
-                                     const unsigned char *class_name,
+                                     Idx *char_class_alloc,
+                                     const char *class_name,
                                      reg_syntax_t syntax);
 #else  /* not RE_ENABLE_I18N */
-static reg_errcode_t build_equiv_class (re_bitset_ptr_t sbcset,
+static reg_errcode_t build_equiv_class (bitset_t sbcset,
                                        const unsigned char *name);
-static reg_errcode_t build_charclass (re_bitset_ptr_t sbcset,
-                                     const unsigned char *class_name,
+static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
+                                     bitset_t sbcset,
+                                     const 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 *build_charclass_op (re_dfa_t *dfa,
+                                      RE_TRANSLATE_TYPE trans,
+                                      const char *class_name,
+                                      const char *extra,
+                                      bool non_match, reg_errcode_t *err);
+static bin_tree_t *create_tree (re_dfa_t *dfa,
+                               bin_tree_t *left, bin_tree_t *right,
+                               re_token_type_t type);
+static bin_tree_t *create_token_tree (re_dfa_t *dfa,
+                                     bin_tree_t *left, bin_tree_t *right,
+                                     const re_token_t *token);
 static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
+static void free_token (re_token_t *node);
+static reg_errcode_t free_tree (void *extra, bin_tree_t *node);
+static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node);
 \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 =
+static const char __re_error_msgid[] =
   {
 #define REG_NOERROR_IDX        0
     gettext_noop ("Success")   /* REG_NOERROR */
@@ -151,9 +153,9 @@ const char __re_error_msgid[] attribute_hidden =
     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 */
+    gettext_noop ("Unmatched [, [^, [:, [., or [=")    /* REG_EBRACK */
     "\0"
-#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
+#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 \\(")
@@ -181,7 +183,7 @@ const char __re_error_msgid[] attribute_hidden =
     gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
   };
 
-const size_t __re_error_msgid_idx[] attribute_hidden =
+static const size_t __re_error_msgid_idx[] =
   {
     REG_NOERROR_IDX,
     REG_NOMATCH_IDX,
@@ -208,21 +210,19 @@ const size_t __re_error_msgid_idx[] attribute_hidden =
    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
+   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;
+re_compile_pattern (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;
+     setting no_sub, unless RE_NO_SUB is set.  */
+  bufp->no_sub = !!(re_syntax_options & RE_NO_SUB);
 
   /* Match anchors at newline.  */
   bufp->newline_anchor = 1;
@@ -237,7 +237,7 @@ re_compile_pattern (pattern, length, bufp)
 weak_alias (__re_compile_pattern, re_compile_pattern)
 #endif
 
-/* Set by `re_set_syntax' to the current regexp syntax to recognize.  Can
+/* 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
@@ -253,8 +253,7 @@ reg_syntax_t re_syntax_options;
    defined in regex.h.  We return the old syntax.  */
 
 reg_syntax_t
-re_set_syntax (syntax)
-    reg_syntax_t syntax;
+re_set_syntax (reg_syntax_t syntax)
 {
   reg_syntax_t ret = re_syntax_options;
 
@@ -266,10 +265,9 @@ weak_alias (__re_set_syntax, re_set_syntax)
 #endif
 
 int
-re_compile_fastmap (bufp)
-    struct re_pattern_buffer *bufp;
+re_compile_fastmap (struct re_pattern_buffer *bufp)
 {
-  re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
+  re_dfa_t *dfa = bufp->buffer;
   char *fastmap = bufp->fastmap;
 
   memset (fastmap, '\0', sizeof (char) * SBC_MAX);
@@ -288,7 +286,8 @@ weak_alias (__re_compile_fastmap, re_compile_fastmap)
 #endif
 
 static inline void
-re_set_fastmap (char *fastmap, int icase, int ch)
+__attribute__ ((always_inline))
+re_set_fastmap (char *fastmap, bool icase, int ch)
 {
   fastmap[ch] = 1;
   if (icase)
@@ -299,72 +298,126 @@ re_set_fastmap (char *fastmap, int icase, int ch)
    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_compile_fastmap_iter (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));
+  re_dfa_t *dfa = bufp->buffer;
+  Idx node_cnt;
+  bool icase = (dfa->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];
+      Idx 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);
+       {
+         re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c);
+#ifdef RE_ENABLE_I18N
+         if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+           {
+             unsigned char buf[MB_LEN_MAX];
+             unsigned char *p;
+             wchar_t wc;
+             mbstate_t state;
+
+             p = buf;
+             *p++ = dfa->nodes[node].opr.c;
+             while (++node < dfa->nodes_len
+                    && dfa->nodes[node].type == CHARACTER
+                    && dfa->nodes[node].mb_partial)
+               *p++ = dfa->nodes[node].opr.c;
+             memset (&state, '\0', sizeof (state));
+             if (__mbrtowc (&wc, (const char *) buf, p - buf,
+                            &state) == p - buf
+                 && (__wcrtomb ((char *) buf, __towlower (wc), &state)
+                     != (size_t) -1))
+               re_set_fastmap (fastmap, false, buf[0]);
+           }
+#endif
+       }
       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);
+         int i, ch;
+         for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+           {
+             int j;
+             bitset_word_t w = dfa->nodes[node].opr.sbcset[i];
+             for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+               if (w & ((bitset_word_t) 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)
-           {
+         Idx i;
+
 # ifdef _LIBC
-             if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0)
+         /* See if we have to try all bytes which start multiple 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' (and
+                 it is caught by SIMPLE_BRACKET).  */
+             if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0
+                 && (cset->ncoll_syms || cset->nranges))
                {
-                 /* 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);
+                 for (i = 0; i < SBC_MAX; ++i)
+                   if (table[i] < 0)
+                     re_set_fastmap (fastmap, icase, i);
                }
-# 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 */
+# endif /* _LIBC */
+
+         /* See if we have to start the match at all multibyte characters,
+            i.e. where we would not find an invalid sequence.  This only
+            applies to multibyte character sets; for single byte character
+            sets, the SIMPLE_BRACKET again suffices.  */
+         if (dfa->mb_cur_max > 1
+             && (cset->nchar_classes || cset->non_match || cset->nranges
+# ifdef _LIBC
+                 || cset->nequiv_classes
+# endif /* _LIBC */
+                ))
+           {
+             unsigned char c = 0;
+             do
+               {
+                 mbstate_t mbs;
+                 memset (&mbs, 0, sizeof (mbs));
+                 if (__mbrtowc (NULL, (char *) &c, 1, &mbs) == (size_t) -2)
+                   re_set_fastmap (fastmap, false, (int) c);
+               }
+             while (++c != 0);
            }
-         for (i = 0; i < cset->nmbchars; ++i)
+
+         else
            {
-             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);
+             /* ... Else catch all bytes which can start the mbchars.  */
+             for (i = 0; i < cset->nmbchars; ++i)
+               {
+                 char buf[256];
+                 mbstate_t state;
+                 memset (&state, '\0', sizeof (state));
+                 if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1)
+                   re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
+                 if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
+                   {
+                     if (__wcrtomb (buf, __towlower (cset->mbchars[i]), &state)
+                         != (size_t) -1)
+                       re_set_fastmap (fastmap, false, *(unsigned char *) buf);
+                   }
+               }
            }
        }
 #endif /* RE_ENABLE_I18N */
-      else if (type == END_OF_RE || type == OP_PERIOD)
+      else if (type == OP_PERIOD
+#ifdef RE_ENABLE_I18N
+              || type == OP_UTF8_PERIOD
+#endif /* RE_ENABLE_I18N */
+              || type == END_OF_RE)
        {
          memset (fastmap, '\1', sizeof (char) * SBC_MAX);
          if (type == END_OF_RE)
@@ -380,15 +433,15 @@ re_compile_fastmap_iter (bufp, init_state, fastmap)
    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
+     '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.
+     '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.
 
@@ -411,10 +464,7 @@ re_compile_fastmap_iter (bufp, init_state, fastmap)
    the return codes and their meanings.)  */
 
 int
-regcomp (preg, pattern, cflags)
-    regex_t *__restrict preg;
-    const char *__restrict pattern;
-    int cflags;
+regcomp (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
@@ -454,7 +504,7 @@ regcomp (preg, pattern, cflags)
   /* 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.  */
+       buffer.  This function never fails in this implementation.  */
     (void) re_compile_fastmap (preg);
   else
     {
@@ -466,6 +516,7 @@ regcomp (preg, pattern, cflags)
   return (int) ret;
 }
 #ifdef _LIBC
+libc_hidden_def (__regcomp)
 weak_alias (__regcomp, regcomp)
 #endif
 
@@ -473,11 +524,8 @@ weak_alias (__regcomp, regcomp)
    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;
+regerror (int errcode, const regex_t *_Restrict_ preg, char *_Restrict_ errbuf,
+         size_t errbuf_size)
 {
   const char *msg;
   size_t msg_size;
@@ -497,17 +545,13 @@ regerror (errcode, preg, errbuf, errbuf_size)
 
   if (BE (errbuf_size != 0, 1))
     {
+      size_t cpy_size = msg_size;
       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
+         cpy_size = errbuf_size - 1;
+         errbuf[cpy_size] = '\0';
        }
-      else
-       memcpy (errbuf, msg, msg_size);
+      memcpy (errbuf, msg, cpy_size);
     }
 
   return msg_size;
@@ -517,24 +561,43 @@ weak_alias (__regerror, regerror)
 #endif
 
 
+#ifdef RE_ENABLE_I18N
+/* This static array is used for the map to single-byte characters when
+   UTF-8 is used.  Otherwise we would allocate memory just to initialize
+   it the same all the time.  UTF-8 is the preferred encoding so this is
+   a worthwhile optimization.  */
+static const bitset_t utf8_sb_map =
+{
+  /* Set the first 128 bits.  */
+# if defined __GNUC__ && !defined __STRICT_ANSI__
+  [0 ... 0x80 / BITSET_WORD_BITS - 1] = BITSET_WORD_MAX
+# else
+#  if 4 * BITSET_WORD_BITS < ASCII_CHARS
+#   error "bitset_word_t is narrower than 32 bits"
+#  elif 3 * BITSET_WORD_BITS < ASCII_CHARS
+  BITSET_WORD_MAX, BITSET_WORD_MAX, BITSET_WORD_MAX,
+#  elif 2 * BITSET_WORD_BITS < ASCII_CHARS
+  BITSET_WORD_MAX, BITSET_WORD_MAX,
+#  elif 1 * BITSET_WORD_BITS < ASCII_CHARS
+  BITSET_WORD_MAX,
+#  endif
+  (BITSET_WORD_MAX
+   >> (SBC_MAX % BITSET_WORD_BITS == 0
+       ? 0
+       : BITSET_WORD_BITS - SBC_MAX % BITSET_WORD_BITS))
+# endif
+};
+#endif
+
+
 static void
 free_dfa_content (re_dfa_t *dfa)
 {
-  int i, j;
-
-  re_free (dfa->subexps);
+  Idx i, j;
 
-  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);
-    }
+  if (dfa->nodes)
+    for (i = 0; i < dfa->nodes_len; ++i)
+      free_token (dfa->nodes + i);
   re_free (dfa->nexts);
   for (i = 0; i < dfa->nodes_len; ++i)
     {
@@ -550,20 +613,23 @@ free_dfa_content (re_dfa_t *dfa)
   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);
-    }
+  if (dfa->state_table)
+    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 RE_ENABLE_I18N
+  if (dfa->sb_char != utf8_sb_map)
+    re_free (dfa->sb_char);
+#endif
+  re_free (dfa->subexp_map);
 #ifdef DEBUG
   re_free (dfa->re_str);
 #endif
@@ -575,16 +641,25 @@ free_dfa_content (re_dfa_t *dfa)
 /* Free dynamically allocated space used by PREG.  */
 
 void
-regfree (preg)
-    regex_t *preg;
+regfree (regex_t *preg)
 {
-  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  re_dfa_t *dfa = preg->buffer;
   if (BE (dfa != NULL, 1))
-    free_dfa_content (dfa);
+    {
+      lock_fini (dfa->lock);
+      free_dfa_content (dfa);
+    }
+  preg->buffer = NULL;
+  preg->allocated = 0;
 
   re_free (preg->fastmap);
+  preg->fastmap = NULL;
+
+  re_free (preg->translate);
+  preg->translate = NULL;
 }
 #ifdef _LIBC
+libc_hidden_def (__regfree)
 weak_alias (__regfree, regfree)
 #endif
 \f
@@ -603,8 +678,7 @@ char *
    regcomp/regexec above without link errors.  */
 weak_function
 # endif
-re_comp (s)
-     const char *s;
+re_comp (const char *s)
 {
   reg_errcode_t ret;
   char *fastmap;
@@ -627,13 +701,13 @@ re_comp (s)
 
   if (re_comp_buf.fastmap == NULL)
     {
-      re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
+      re_comp_buf.fastmap = re_malloc (char, 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
+  /* 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.  */
@@ -644,7 +718,7 @@ re_comp (s)
   if (!ret)
     return NULL;
 
-  /* Yes, we're discarding `const' here if !HAVE_LIBINTL.  */
+  /* Yes, we're discarding 'const' here if !HAVE_LIBINTL.  */
   return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
 }
 
@@ -662,11 +736,8 @@ libc_freeres_fn (free_mem)
    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;
+re_compile_internal (regex_t *preg, const char * pattern, size_t length,
+                    reg_syntax_t syntax)
 {
   reg_errcode_t err = REG_NOERROR;
   re_dfa_t *dfa;
@@ -682,8 +753,8 @@ re_compile_internal (preg, pattern, length, syntax)
   preg->regs_allocated = REGS_UNALLOCATED;
 
   /* Initialize the dfa.  */
-  dfa = (re_dfa_t *) preg->buffer;
-  if (preg->allocated < sizeof (re_dfa_t))
+  dfa = preg->buffer;
+  if (BE (preg->allocated < sizeof (re_dfa_t), 0))
     {
       /* If zero allocated, but buffer is non-null, try to realloc
         enough space.  This loses if buffer's address is bogus, but
@@ -693,28 +764,35 @@ re_compile_internal (preg, pattern, length, syntax)
       if (dfa == NULL)
        return REG_ESPACE;
       preg->allocated = sizeof (re_dfa_t);
+      preg->buffer = dfa;
     }
-  preg->buffer = (unsigned char *) dfa;
   preg->used = sizeof (re_dfa_t);
 
   err = init_dfa (dfa, length);
+  if (BE (err == REG_NOERROR && lock_init (dfa->lock) != 0, 0))
+    err = REG_ESPACE;
   if (BE (err != REG_NOERROR, 0))
     {
-      re_free (dfa);
+      free_dfa_content (dfa);
       preg->buffer = NULL;
       preg->allocated = 0;
       return err;
     }
 #ifdef DEBUG
+  /* Note: length+1 will not overflow since it is checked in init_dfa.  */
   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);
+                            (syntax & RE_ICASE) != 0, dfa);
   if (BE (err != REG_NOERROR, 0))
     {
-      re_free (dfa);
+    re_compile_internal_free_return:
+      free_workarea_compile (preg);
+      re_string_destruct (&regexp);
+      lock_fini (dfa->lock);
+      free_dfa_content (dfa);
       preg->buffer = NULL;
       preg->allocated = 0;
       return err;
@@ -726,12 +804,17 @@ re_compile_internal (preg, pattern, length, syntax)
   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);
+  /* Analyze the tree and create the nfa.  */
+  err = analyze (preg);
   if (BE (err != REG_NOERROR, 0))
     goto re_compile_internal_free_return;
 
+#ifdef RE_ENABLE_I18N
+  /* If possible, do searching in single byte encoding to speed things up.  */
+  if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL)
+    optimize_utf8 (dfa);
+#endif
+
   /* Then create the initial state of the dfa.  */
   err = create_initial_state (dfa);
 
@@ -741,7 +824,7 @@ re_compile_internal (preg, pattern, length, syntax)
 
   if (BE (err != REG_NOERROR, 0))
     {
-    re_compile_internal_free_return:
+      lock_fini (dfa->lock);
       free_dfa_content (dfa);
       preg->buffer = NULL;
       preg->allocated = 0;
@@ -754,41 +837,100 @@ re_compile_internal (preg, pattern, length, syntax)
    as the initial length of some arrays.  */
 
 static reg_errcode_t
-init_dfa (dfa, pat_len)
-     re_dfa_t *dfa;
-     int pat_len;
+init_dfa (re_dfa_t *dfa, size_t pat_len)
 {
-  int table_size;
+  __re_size_t table_size;
+#if !defined(_LIBC) && !defined(_REGEX_STANDALONE)
+  const char *codeset_name;
+#endif
+#ifdef RE_ENABLE_I18N
+  size_t max_i18n_object_size = MAX (sizeof (wchar_t), sizeof (wctype_t));
+#else
+  size_t max_i18n_object_size = 0;
+#endif
+  size_t max_object_size =
+    MAX (sizeof (struct re_state_table_entry),
+        MAX (sizeof (re_token_t),
+             MAX (sizeof (re_node_set),
+                  MAX (sizeof (regmatch_t),
+                       max_i18n_object_size))));
 
   memset (dfa, '\0', sizeof (re_dfa_t));
 
+  /* Force allocation of str_tree_storage the first time.  */
+  dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
+
+  /* Avoid overflows.  The extra "/ 2" is for the table_size doubling
+     calculation below, and for similar doubling calculations
+     elsewhere.  And it's <= rather than <, because some of the
+     doubling calculations add 1 afterwards.  */
+  if (BE (MIN (IDX_MAX, SIZE_MAX / max_object_size) / 2 <= pat_len, 0))
+    return REG_ESPACE;
+
   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)
+  for (table_size = 1; ; 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;
+  dfa->mb_cur_max = MB_CUR_MAX;
+#ifdef _LIBC
+  if (dfa->mb_cur_max == 6
+      && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0)
+    dfa->is_utf8 = 1;
+  dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII)
+                      != 0);
+#else
+# if !defined(_REGEX_STANDALONE)
+  codeset_name = nl_langinfo (CODESET);
+  if ((codeset_name[0] == 'U' || codeset_name[0] == 'u')
+      && (codeset_name[1] == 'T' || codeset_name[1] == 't')
+      && (codeset_name[2] == 'F' || codeset_name[2] == 'f')
+      && strcmp (codeset_name + 3 + (codeset_name[3] == '-'), "8") == 0)
+    dfa->is_utf8 = 1;
+# endif
+
+  /* We check exhaustively in the loop below if this charset is a
+     superset of ASCII.  */
+  dfa->map_notascii = 0;
+#endif
 
-  if (BE (dfa->nodes == NULL || dfa->state_table == NULL
-         || dfa->subexps == NULL, 0))
+#ifdef RE_ENABLE_I18N
+  if (dfa->mb_cur_max > 1)
     {
-      /* 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;
+      if (dfa->is_utf8)
+       dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map;
+      else
+       {
+         int i, j, ch;
+
+         dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
+         if (BE (dfa->sb_char == NULL, 0))
+           return REG_ESPACE;
+
+         /* Set the bits corresponding to single byte chars.  */
+         for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
+           for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
+             {
+               wint_t wch = __btowc (ch);
+               if (wch != WEOF)
+                 dfa->sb_char[i] |= (bitset_word_t) 1 << j;
+# ifndef _LIBC
+               if (isascii (ch) && wch != ch)
+                 dfa->map_notascii = 1;
+# endif
+             }
+       }
     }
+#endif
+
+  if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0))
+    return REG_ESPACE;
   return REG_NOERROR;
 }
 
@@ -796,29 +938,68 @@ init_dfa (dfa, pat_len)
    "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;
+static void
+init_word_char (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)
+  int i = 0;
+  int j;
+  int ch = 0;
+  dfa->word_ops_used = 1;
+  if (BE (dfa->map_notascii == 0, 1))
+    {
+      /* Avoid uint32_t and uint64_t as some non-GCC platforms lack
+        them, an issue when this code is used in Gnulib.  */
+      bitset_word_t bits0 = 0x00000000;
+      bitset_word_t bits1 = 0x03ff0000;
+      bitset_word_t bits2 = 0x87fffffe;
+      bitset_word_t bits3 = 0x07fffffe;
+      if (BITSET_WORD_BITS == 64)
+       {
+         /* Pacify gcc -Woverflow on 32-bit platformns.  */
+         dfa->word_char[0] = bits1 << 31 << 1 | bits0;
+         dfa->word_char[1] = bits3 << 31 << 1 | bits2;
+         i = 2;
+       }
+      else if (BITSET_WORD_BITS == 32)
+       {
+         dfa->word_char[0] = bits0;
+         dfa->word_char[1] = bits1;
+         dfa->word_char[2] = bits2;
+         dfa->word_char[3] = bits3;
+         i = 4;
+       }
+      else
+        goto general_case;
+      ch = 128;
+
+      if (BE (dfa->is_utf8, 1))
+       {
+         memset (&dfa->word_char[i], '\0', (SBC_MAX - ch) / 8);
+         return;
+       }
+    }
+
+ general_case:
+  for (; i < BITSET_WORDS; ++i)
+    for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
       if (isalnum (ch) || ch == '_')
-       dfa->word_char[i] |= 1 << j;
-  return REG_NOERROR;
+       dfa->word_char[i] |= (bitset_word_t) 1 << j;
 }
 
 /* Free the work area which are only used while compiling.  */
 
 static void
-free_workarea_compile (preg)
-     regex_t *preg;
+free_workarea_compile (regex_t *preg)
 {
-  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
-  free_bin_tree (dfa->str_tree);
+  re_dfa_t *dfa = preg->buffer;
+  bin_tree_storage_t *storage, *next;
+  for (storage = dfa->str_tree_storage; storage; storage = next)
+    {
+      next = storage->next;
+      re_free (storage);
+    }
+  dfa->str_tree_storage = NULL;
+  dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
   dfa->str_tree = NULL;
   re_free (dfa->org_indices);
   dfa->org_indices = NULL;
@@ -827,16 +1008,15 @@ free_workarea_compile (preg)
 /* Create initial states for all contexts.  */
 
 static reg_errcode_t
-create_initial_state (dfa)
-     re_dfa_t *dfa;
+create_initial_state (re_dfa_t *dfa)
 {
-  int first, i;
+  Idx 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;
+  first = dfa->str_tree->first->node_idx;
   dfa->init_node = first;
   err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
   if (BE (err != REG_NOERROR, 0))
@@ -849,10 +1029,10 @@ create_initial_state (dfa)
   if (dfa->nbackref > 0)
     for (i = 0; i < init_nodes.nelem; ++i)
       {
-       int node_idx = init_nodes.elems[i];
+       Idx node_idx = init_nodes.elems[i];
        re_token_type_t type = dfa->nodes[node_idx].type;
 
-       int clexp_idx;
+       Idx clexp_idx;
        if (type != OP_BACK_REF)
          continue;
        for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx)
@@ -860,7 +1040,7 @@ create_initial_state (dfa)
            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)
+               && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx)
              break;
          }
        if (clexp_idx == init_nodes.nelem)
@@ -868,10 +1048,13 @@ create_initial_state (dfa)
 
        if (type == OP_BACK_REF)
          {
-           int dest_idx = dfa->edests[node_idx].elems[0];
+           Idx 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);
+               reg_errcode_t merge_err
+                  = re_node_set_merge (&init_nodes, dfa->eclosures + dest_idx);
+               if (merge_err != REG_NOERROR)
+                 return merge_err;
                i = 0;
              }
          }
@@ -904,246 +1087,408 @@ create_initial_state (dfa)
   return REG_NOERROR;
 }
 \f
+#ifdef RE_ENABLE_I18N
+/* If it is possible to do searching in single byte encoding instead of UTF-8
+   to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change
+   DFA nodes where needed.  */
+
+static void
+optimize_utf8 (re_dfa_t *dfa)
+{
+  Idx node;
+  int i;
+  bool mb_chars = false;
+  bool has_period = false;
+
+  for (node = 0; node < dfa->nodes_len; ++node)
+    switch (dfa->nodes[node].type)
+      {
+      case CHARACTER:
+       if (dfa->nodes[node].opr.c >= ASCII_CHARS)
+         mb_chars = true;
+       break;
+      case ANCHOR:
+       switch (dfa->nodes[node].opr.ctx_type)
+         {
+         case LINE_FIRST:
+         case LINE_LAST:
+         case BUF_FIRST:
+         case BUF_LAST:
+           break;
+         default:
+           /* Word anchors etc. cannot be handled.  It's okay to test
+              opr.ctx_type since constraints (for all DFA nodes) are
+              created by ORing one or more opr.ctx_type values.  */
+           return;
+         }
+       break;
+      case OP_PERIOD:
+       has_period = true;
+       break;
+      case OP_BACK_REF:
+      case OP_ALT:
+      case END_OF_RE:
+      case OP_DUP_ASTERISK:
+      case OP_OPEN_SUBEXP:
+      case OP_CLOSE_SUBEXP:
+       break;
+      case COMPLEX_BRACKET:
+       return;
+      case SIMPLE_BRACKET:
+       /* Just double check.  */
+       {
+         int rshift = (ASCII_CHARS % BITSET_WORD_BITS == 0
+                       ? 0
+                       : BITSET_WORD_BITS - ASCII_CHARS % BITSET_WORD_BITS);
+         for (i = ASCII_CHARS / BITSET_WORD_BITS; i < BITSET_WORDS; ++i)
+           {
+             if (dfa->nodes[node].opr.sbcset[i] >> rshift != 0)
+               return;
+             rshift = 0;
+           }
+       }
+       break;
+      default:
+       abort ();
+      }
+
+  if (mb_chars || has_period)
+    for (node = 0; node < dfa->nodes_len; ++node)
+      {
+       if (dfa->nodes[node].type == CHARACTER
+           && dfa->nodes[node].opr.c >= ASCII_CHARS)
+         dfa->nodes[node].mb_partial = 0;
+       else if (dfa->nodes[node].type == OP_PERIOD)
+         dfa->nodes[node].type = OP_UTF8_PERIOD;
+      }
+
+  /* The search can be in single byte locale.  */
+  dfa->mb_cur_max = 1;
+  dfa->is_utf8 = 0;
+  dfa->has_mb_node = dfa->nbackref > 0 || has_period;
+}
+#endif
+\f
 /* Analyze the structure tree, and calculate "first", "next", "edest",
    "eclosure", and "inveclosure".  */
 
 static reg_errcode_t
-analyze (dfa)
-     re_dfa_t *dfa;
+analyze (regex_t *preg)
 {
-  int i;
+  re_dfa_t *dfa = preg->buffer;
   reg_errcode_t ret;
 
   /* Allocate arrays.  */
-  dfa->nexts = re_malloc (int, dfa->nodes_alloc);
-  dfa->org_indices = re_malloc (int, dfa->nodes_alloc);
+  dfa->nexts = re_malloc (Idx, dfa->nodes_alloc);
+  dfa->org_indices = re_malloc (Idx, 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))
+         || dfa->eclosures == NULL, 0))
     return REG_ESPACE;
-  /* Initialize them.  */
-  for (i = 0; i < dfa->nodes_len; ++i)
+
+  dfa->subexp_map = re_malloc (Idx, preg->re_nsub);
+  if (dfa->subexp_map != NULL)
     {
-      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);
+      Idx i;
+      for (i = 0; i < preg->re_nsub; i++)
+       dfa->subexp_map[i] = i;
+      preorder (dfa->str_tree, optimize_subexps, dfa);
+      for (i = 0; i < preg->re_nsub; i++)
+       if (dfa->subexp_map[i] != i)
+         break;
+      if (i == preg->re_nsub)
+       {
+         re_free (dfa->subexp_map);
+         dfa->subexp_map = NULL;
+       }
     }
 
-  ret = analyze_tree (dfa, dfa->str_tree);
-  if (BE (ret == REG_NOERROR, 1))
+  ret = postorder (dfa->str_tree, lower_subexps, preg);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  ret = postorder (dfa->str_tree, calc_first, dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  preorder (dfa->str_tree, calc_next, dfa);
+  ret = preorder (dfa->str_tree, link_nfa_nodes, dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+  ret = calc_eclosure (dfa);
+  if (BE (ret != REG_NOERROR, 0))
+    return ret;
+
+  /* We only need this during the prune_impossible_nodes pass in regexec.c;
+     skip it if p_i_n will not run, as calc_inveclosure can be quadratic.  */
+  if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match)
+      || dfa->nbackref)
     {
-      ret = calc_eclosure (dfa);
-      if (ret == REG_NOERROR)
-       calc_inveclosure (dfa);
+      dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len);
+      if (BE (dfa->inveclosures == NULL, 0))
+       return REG_ESPACE;
+      ret = calc_inveclosure (dfa);
     }
+
   return ret;
 }
 
-/* Helper functions for analyze.
-   This function calculate "first", "next", and "edest" for the subtree
-   whose root is NODE.  */
+/* Our parse trees are very unbalanced, so we cannot use a stack to
+   implement parse tree visits.  Instead, we use parent pointers and
+   some hairy code in these two functions.  */
+static reg_errcode_t
+postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+          void *extra)
+{
+  bin_tree_t *node, *prev;
+
+  for (node = root; ; )
+    {
+      /* Descend down the tree, preferably to the left (or to the right
+        if that's the only child).  */
+      while (node->left || node->right)
+       if (node->left)
+         node = node->left;
+       else
+         node = node->right;
+
+      do
+       {
+         reg_errcode_t err = fn (extra, node);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+         if (node->parent == NULL)
+           return REG_NOERROR;
+         prev = node;
+         node = node->parent;
+       }
+      /* Go up while we have a node that is reached from the right.  */
+      while (node->right == prev || node->right == NULL);
+      node = node->right;
+    }
+}
 
 static reg_errcode_t
-analyze_tree (dfa, node)
-     re_dfa_t *dfa;
-     bin_tree_t *node;
+preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
+         void *extra)
 {
-  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;
+  bin_tree_t *node;
+
+  for (node = root; ; )
+    {
+      reg_errcode_t err = fn (extra, node);
+      if (BE (err != REG_NOERROR, 0))
+       return err;
+
+      /* Go to the left node, or up and to the right.  */
+      if (node->left)
+       node = node->left;
+      else
+       {
+         bin_tree_t *prev = NULL;
+         while (node->right == prev || node->right == NULL)
+           {
+             prev = node;
+             node = node->parent;
+             if (!node)
+               return REG_NOERROR;
+           }
+         node = node->right;
+       }
+    }
+}
+
+/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell
+   re_search_internal to map the inner one's opr.idx to this one's.  Adjust
+   backreferences as well.  Requires a preorder visit.  */
+static reg_errcode_t
+optimize_subexps (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+
+  if (node->token.type == OP_BACK_REF && dfa->subexp_map)
+    {
+      int idx = node->token.opr.idx;
+      node->token.opr.idx = dfa->subexp_map[idx];
+      dfa->used_bkref_map |= 1 << node->token.opr.idx;
     }
-  /* Calculate "first" etc. for the right child.  */
-  if (node->right != NULL)
+
+  else if (node->token.type == SUBEXP
+          && node->left && node->left->token.type == SUBEXP)
     {
-      ret = analyze_tree (dfa, node->right);
-      if (BE (ret != REG_NOERROR, 0))
-       return ret;
+      Idx other_idx = node->left->token.opr.idx;
+
+      node->left = node->left->left;
+      if (node->left)
+       node->left->parent = node;
+
+      dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx];
+      if (other_idx < BITSET_WORD_BITS)
+       dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx);
     }
+
   return REG_NOERROR;
 }
 
-/* Calculate "first" for the node NODE.  */
-static void
-calc_first (dfa, node)
-     re_dfa_t *dfa;
-     bin_tree_t *node;
+/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation
+   of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP.  */
+static reg_errcode_t
+lower_subexps (void *extra, bin_tree_t *node)
 {
-  int idx, type;
-  idx = node->node_idx;
-  type = (node->type == 0) ? dfa->nodes[idx].type : node->type;
+  regex_t *preg = (regex_t *) extra;
+  reg_errcode_t err = REG_NOERROR;
 
-  switch (type)
+  if (node->left && node->left->token.type == SUBEXP)
     {
-#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;
+      node->left = lower_subexp (&err, preg, node->left);
+      if (node->left)
+       node->left->parent = node;
+    }
+  if (node->right && node->right->token.type == SUBEXP)
+    {
+      node->right = lower_subexp (&err, preg, node->right);
+      if (node->right)
+       node->right->parent = node;
     }
-}
 
-/* Calculate "next" for the node NODE.  */
+  return err;
+}
 
-static void
-calc_next (dfa, node)
-     re_dfa_t *dfa;
-     bin_tree_t *node;
+static bin_tree_t *
+lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node)
 {
-  int idx, type;
-  bin_tree_t *parent = node->parent;
-  if (parent == NULL)
+  re_dfa_t *dfa = preg->buffer;
+  bin_tree_t *body = node->left;
+  bin_tree_t *op, *cls, *tree1, *tree;
+
+  if (preg->no_sub
+      /* We do not optimize empty subexpressions, because otherwise we may
+        have bad CONCAT nodes with NULL children.  This is obviously not
+        very common, so we do not lose much.  An example that triggers
+        this case is the sed "script" /\(\)/x.  */
+      && node->left != NULL
+      && (node->token.opr.idx >= BITSET_WORD_BITS
+         || !(dfa->used_bkref_map
+              & ((bitset_word_t) 1 << node->token.opr.idx))))
+    return node->left;
+
+  /* Convert the SUBEXP node to the concatenation of an
+     OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP.  */
+  op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP);
+  cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP);
+  tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls;
+  tree = create_tree (dfa, op, tree1, CONCAT);
+  if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0))
     {
-      node->next = -1;
-      idx = node->node_idx;
-      if (node->type == 0)
-       dfa->nexts[idx] = node->next;
-      return;
+      *err = REG_ESPACE;
+      return NULL;
     }
 
-  idx = parent->node_idx;
-  type = (parent->type == 0) ? dfa->nodes[idx].type : parent->type;
+  op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx;
+  op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp;
+  return tree;
+}
 
-  switch (type)
+/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton
+   nodes.  Requires a postorder visit.  */
+static reg_errcode_t
+calc_first (void *extra, bin_tree_t *node)
+{
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+  if (node->token.type == CONCAT)
+    {
+      node->first = node->left->first;
+      node->node_idx = node->left->node_idx;
+    }
+  else
+    {
+      node->first = node;
+      node->node_idx = re_dfa_add_node (dfa, node->token);
+      if (BE (node->node_idx == -1, 0))
+       return REG_ESPACE;
+      if (node->token.type == ANCHOR)
+       dfa->nodes[node->node_idx].constraint = node->token.opr.ctx_type;
+    }
+  return REG_NOERROR;
+}
+
+/* Pass 2: compute NEXT on the tree.  Preorder visit.  */
+static reg_errcode_t
+calc_next (void *extra, bin_tree_t *node)
+{
+  switch (node->token.type)
     {
     case OP_DUP_ASTERISK:
-    case OP_DUP_PLUS:
-      node->next = idx;
+      node->left->next = node;
       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 */
+      node->left->next = node->right->first;
+      node->right->next = node->next;
+      break;
     default:
-      if (parent->next == -1)
-       calc_next (dfa, parent);
-      node->next = parent->next;
+      if (node->left)
+       node->left->next = node->next;
+      if (node->right)
+       node->right->next = node->next;
       break;
     }
-  idx = node->node_idx;
-  if (node->type == 0)
-    dfa->nexts[idx] = node->next;
+  return REG_NOERROR;
 }
 
-/* Calculate "edest" for the node NODE.  */
-
-static void
-calc_epsdest (dfa, node)
-     re_dfa_t *dfa;
-     bin_tree_t *node;
+/* Pass 3: link all DFA nodes to their NEXT node (any order will do).  */
+static reg_errcode_t
+link_nfa_nodes (void *extra, bin_tree_t *node)
 {
-  int idx;
-  idx = node->node_idx;
-  if (node->type == 0)
+  re_dfa_t *dfa = (re_dfa_t *) extra;
+  Idx idx = node->node_idx;
+  reg_errcode_t err = REG_NOERROR;
+
+  switch (node->token.type)
     {
-      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);
+    case CONCAT:
+      break;
+
+    case END_OF_RE:
+      assert (node->next == NULL);
+      break;
+
+    case OP_DUP_ASTERISK:
+    case OP_ALT:
+      {
+       Idx left, right;
+       dfa->has_plural_match = 1;
+       if (node->left != NULL)
+         left = node->left->first->node_idx;
+       else
+         left = node->next->node_idx;
+       if (node->right != NULL)
+         right = node->right->first->node_idx;
+       else
+         right = node->next->node_idx;
+       assert (left > -1);
+       assert (right > -1);
+       err = re_node_set_init_2 (dfa->edests + idx, left, right);
+      }
+      break;
+
+    case ANCHOR:
+    case OP_OPEN_SUBEXP:
+    case OP_CLOSE_SUBEXP:
+      err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx);
+      break;
+
+    case OP_BACK_REF:
+      dfa->nexts[idx] = node->next->node_idx;
+      if (node->token.type == OP_BACK_REF)
+       err = re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]);
+      break;
+
+    default:
+      assert (!IS_EPSILON_NODE (node->token.type));
+      dfa->nexts[idx] = node->next->node_idx;
+      break;
     }
+
+  return err;
 }
 
 /* Duplicate the epsilon closure of the node ROOT_NODE.
@@ -1151,18 +1496,15 @@ calc_epsdest (dfa, node)
    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;
+duplicate_node_closure (re_dfa_t *dfa, Idx top_org_node, Idx top_clone_node,
+                       Idx root_node, unsigned int init_constraint)
 {
-  reg_errcode_t err;
-  int org_node, clone_node, ret;
+  Idx org_node, clone_node;
+  bool ok;
   unsigned int constraint = init_constraint;
   for (org_node = top_org_node, clone_node = top_clone_node;;)
     {
-      int org_dest, clone_dest;
+      Idx org_dest, clone_dest;
       if (dfa->nodes[org_node].type == OP_BACK_REF)
        {
          /* If the back reference epsilon-transit, its destination must
@@ -1171,12 +1513,12 @@ duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
             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;
+         clone_dest = duplicate_node (dfa, org_dest, constraint);
+         if (BE (clone_dest == -1, 0))
+           return REG_ESPACE;
          dfa->nexts[clone_node] = dfa->nexts[org_node];
-         ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
-         if (BE (ret < 0, 0))
+         ok = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+         if (BE (! ok, 0))
            return REG_ESPACE;
        }
       else if (dfa->edests[org_node].nelem == 0)
@@ -1193,45 +1535,41 @@ duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
             destination.  */
          org_dest = dfa->edests[org_node].elems[0];
          re_node_set_empty (dfa->edests + clone_node);
-         if (dfa->nodes[org_node].type == ANCHOR)
+         /* If the node is root_node itself, it means the epsilon closure
+            has a loop.  Then tie it to the destination of the root_node.  */
+         if (org_node == root_node && clone_node != org_node)
            {
-             /* 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;
+             ok = re_node_set_insert (dfa->edests + clone_node, org_dest);
+             if (BE (! ok, 0))
+               return REG_ESPACE;
+             break;
            }
-         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))
+         /* In case the node has another constraint, append it.  */
+         constraint |= dfa->nodes[org_node].constraint;
+         clone_dest = duplicate_node (dfa, org_dest, constraint);
+         if (BE (clone_dest == -1, 0))
+           return REG_ESPACE;
+         ok = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+         if (BE (! ok, 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. '|', '*', '+', '?'.   */
+            destinations. In the bin_tree_t and DFA, that's '|' and '*'.   */
          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))
+             /* There is no such duplicated node, create a new one.  */
+             reg_errcode_t err;
+             clone_dest = duplicate_node (dfa, org_dest, constraint);
+             if (BE (clone_dest == -1, 0))
+               return REG_ESPACE;
+             ok = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+             if (BE (! ok, 0))
                return REG_ESPACE;
              err = duplicate_node_closure (dfa, org_dest, clone_dest,
                                            root_node, constraint);
@@ -1240,19 +1578,19 @@ duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
            }
          else
            {
-             /* There are a duplicated node which satisfy the constraint,
+             /* There is a duplicated node which satisfies the constraint,
                 use it to avoid infinite loop.  */
-             ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
-             if (BE (ret < 0, 0))
+             ok = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+             if (BE (! ok, 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))
+         clone_dest = duplicate_node (dfa, org_dest, constraint);
+         if (BE (clone_dest == -1, 0))
+           return REG_ESPACE;
+         ok = re_node_set_insert (dfa->edests + clone_node, clone_dest);
+         if (BE (! ok, 0))
            return REG_ESPACE;
        }
       org_node = org_dest;
@@ -1264,13 +1602,11 @@ duplicate_node_closure (dfa, top_org_node, top_clone_node, root_node,
 /* 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;
+static Idx
+search_duplicated_node (const re_dfa_t *dfa, Idx org_node,
+                       unsigned int constraint)
 {
-  int idx;
+  Idx idx;
   for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx)
     {
       if (org_node == dfa->org_indices[idx]
@@ -1281,62 +1617,58 @@ search_duplicated_node (dfa, org_node, constraint)
 }
 
 /* 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.  */
+   Return the index of the new node, or -1 if insufficient storage is
+   available.  */
 
-static reg_errcode_t
-duplicate_node (new_idx, dfa, org_idx, constraint)
-     re_dfa_t *dfa;
-     int *new_idx, org_idx;
-     unsigned int constraint;
+static Idx
+duplicate_node (re_dfa_t *dfa, Idx org_idx, unsigned int constraint)
 {
-  re_token_t dup;
-  int dup_idx;
+  Idx dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]);
+  if (BE (dup_idx != -1, 1))
+    {
+      dfa->nodes[dup_idx].constraint = constraint;
+      dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].constraint;
+      dfa->nodes[dup_idx].duplicated = 1;
 
-  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;
+      /* Store the index of the original node.  */
+      dfa->org_indices[dup_idx] = org_idx;
+    }
+  return dup_idx;
 }
 
-static void
-calc_inveclosure (dfa)
-     re_dfa_t *dfa;
+static reg_errcode_t
+calc_inveclosure (re_dfa_t *dfa)
 {
-  int src, idx, dest;
+  Idx src, idx;
+  bool ok;
+  for (idx = 0; idx < dfa->nodes_len; ++idx)
+    re_node_set_init_empty (dfa->inveclosures + idx);
+
   for (src = 0; src < dfa->nodes_len; ++src)
     {
+      Idx *elems = dfa->eclosures[src].elems;
       for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
        {
-         dest = dfa->eclosures[src].elems[idx];
-         re_node_set_insert (dfa->inveclosures + dest, src);
+         ok = re_node_set_insert_last (dfa->inveclosures + elems[idx], src);
+         if (BE (! ok, 0))
+           return REG_ESPACE;
        }
     }
+
+  return REG_NOERROR;
 }
 
 /* Calculate "eclosure" for all the node in DFA.  */
 
 static reg_errcode_t
-calc_eclosure (dfa)
-     re_dfa_t *dfa;
+calc_eclosure (re_dfa_t *dfa)
 {
-  int node_idx, incomplete;
+  Idx node_idx;
+  bool incomplete;
 #ifdef DEBUG
   assert (dfa->nodes_len > 0);
 #endif
-  incomplete = 0;
+  incomplete = false;
   /* For each nodes, calculate epsilon closure.  */
   for (node_idx = 0; ; ++node_idx)
     {
@@ -1346,24 +1678,25 @@ calc_eclosure (dfa)
        {
          if (!incomplete)
            break;
-         incomplete = 0;
+         incomplete = false;
          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);
+      /* Calculate epsilon closure of 'node_idx'.  */
+      err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, true);
       if (BE (err != REG_NOERROR, 0))
        return err;
 
       if (dfa->eclosures[node_idx].nelem == 0)
        {
-         incomplete = 1;
+         incomplete = true;
          re_node_set_free (&eclosure_elem);
        }
     }
@@ -1373,16 +1706,13 @@ calc_eclosure (dfa)
 /* 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;
+calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, Idx node, bool root)
 {
   reg_errcode_t err;
-  unsigned int constraint;
-  int i, incomplete;
+  Idx i;
   re_node_set eclosure;
-  incomplete = 0;
+  bool ok;
+  bool incomplete = false;
   err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
   if (BE (err != REG_NOERROR, 0))
     return err;
@@ -1391,15 +1721,14 @@ calc_eclosure_iter (new_set, dfa, node, root)
      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)
+  /* If the current node has constraints, duplicate all nodes
+     since they must inherit the constraints.  */
+  if (dfa->nodes[node].constraint
+      && dfa->edests[node].nelem
+      && !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);
+      err = duplicate_node_closure (dfa, node, node, node,
+                                   dfa->nodes[node].constraint);
       if (BE (err != REG_NOERROR, 0))
        return err;
     }
@@ -1409,37 +1738,41 @@ calc_eclosure_iter (new_set, dfa, node, root)
     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,
+       Idx 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;
+           incomplete = true;
            continue;
          }
-       /* If we haven't calculated the epsilon closure of `edest' yet,
+       /* 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);
+           err = calc_eclosure_iter (&eclosure_elem, dfa, edest, false);
            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,
+       /* Merge the epsilon closure of 'edest'.  */
+       err = re_node_set_merge (&eclosure, &eclosure_elem);
+       if (BE (err != REG_NOERROR, 0))
+         return err;
+       /* 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;
+           incomplete = true;
            re_node_set_free (&eclosure_elem);
          }
       }
 
-  /* Epsilon closures include itself.  */
-  re_node_set_insert (&eclosure, node);
+  /* An epsilon closure includes itself.  */
+  ok = re_node_set_insert (&eclosure, node);
+  if (BE (! ok, 0))
+    return REG_ESPACE;
   if (incomplete && !root)
     dfa->eclosures[node].nelem = 0;
   else
@@ -1453,26 +1786,17 @@ calc_eclosure_iter (new_set, dfa, node, root)
 /* 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;
+static void
+fetch_token (re_token_t *result, 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;
+  re_string_skip_bytes (input, peek_token (result, input, syntax));
 }
 
 /* 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;
+peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
 {
   unsigned char c;
 
@@ -1485,9 +1809,10 @@ peek_token (token, input, syntax)
   c = re_string_peek_byte (input, 0);
   token->opr.c = c;
 
+  token->word_char = 0;
 #ifdef RE_ENABLE_I18N
   token->mb_partial = 0;
-  if (MB_CUR_MAX > 1 &&
+  if (input->mb_cur_max > 1 &&
       !re_string_first_byte (input, re_string_cur_idx (input)))
     {
       token->type = CHARACTER;
@@ -1507,6 +1832,17 @@ peek_token (token, input, syntax)
       c2 = re_string_peek_byte_case (input, 1);
       token->opr.c = c2;
       token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+      if (input->mb_cur_max > 1)
+       {
+         wint_t wc = re_string_wchar_at (input,
+                                         re_string_cur_idx (input) + 1);
+         token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+       }
+      else
+#endif
+       token->word_char = IS_WORD_CHAR (c2) != 0;
+
       switch (c2)
        {
        case '|':
@@ -1518,35 +1854,35 @@ peek_token (token, input, syntax)
          if (!(syntax & RE_NO_BK_REFS))
            {
              token->type = OP_BACK_REF;
-             token->opr.idx = c2 - '0';
+             token->opr.idx = c2 - '1';
            }
          break;
        case '<':
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = WORD_FIRST;
+             token->opr.ctx_type = WORD_FIRST;
            }
          break;
        case '>':
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = WORD_LAST;
+             token->opr.ctx_type = WORD_LAST;
            }
          break;
        case 'b':
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = WORD_DELIM;
+             token->opr.ctx_type = WORD_DELIM;
            }
          break;
        case 'B':
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = INSIDE_WORD;
+             token->opr.ctx_type = NOT_WORD_DELIM;
            }
          break;
        case 'w':
@@ -1557,18 +1893,26 @@ peek_token (token, input, syntax)
          if (!(syntax & RE_NO_GNU_OPS))
            token->type = OP_NOTWORD;
          break;
+       case 's':
+         if (!(syntax & RE_NO_GNU_OPS))
+           token->type = OP_SPACE;
+         break;
+       case 'S':
+         if (!(syntax & RE_NO_GNU_OPS))
+           token->type = OP_NOTSPACE;
+         break;
        case '`':
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = BUF_FIRST;
+             token->opr.ctx_type = BUF_FIRST;
            }
          break;
        case '\'':
          if (!(syntax & RE_NO_GNU_OPS))
            {
              token->type = ANCHOR;
-             token->opr.idx = BUF_LAST;
+             token->opr.ctx_type = BUF_LAST;
            }
          break;
        case '(':
@@ -1602,6 +1946,16 @@ peek_token (token, input, syntax)
     }
 
   token->type = CHARACTER;
+#ifdef RE_ENABLE_I18N
+  if (input->mb_cur_max > 1)
+    {
+      wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input));
+      token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
+    }
+  else
+#endif
+    token->word_char = IS_WORD_CHAR (token->opr.c);
+
   switch (c)
     {
     case '\n':
@@ -1646,16 +2000,15 @@ peek_token (token, input, syntax)
       token->type = OP_PERIOD;
       break;
     case '^':
-      if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
+      if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) &&
          re_string_cur_idx (input) != 0)
        {
          char prev = re_string_peek_byte (input, -1);
-         if (prev != '|' && prev != '(' &&
-             (!(syntax & RE_NEWLINE_ALT) || prev != '\n'))
+         if (!(syntax & RE_NEWLINE_ALT) || prev != '\n')
            break;
        }
       token->type = ANCHOR;
-      token->opr.idx = LINE_FIRST;
+      token->opr.ctx_type = LINE_FIRST;
       break;
     case '$':
       if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
@@ -1669,7 +2022,7 @@ peek_token (token, input, syntax)
            break;
        }
       token->type = ANCHOR;
-      token->opr.idx = LINE_LAST;
+      token->opr.ctx_type = LINE_LAST;
       break;
     default:
       break;
@@ -1681,10 +2034,7 @@ peek_token (token, input, syntax)
    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;
+peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
 {
   unsigned char c;
   if (re_string_eoi (input))
@@ -1696,7 +2046,7 @@ peek_token_bracket (token, input, syntax)
   token->opr.c = c;
 
 #ifdef RE_ENABLE_I18N
-  if (MB_CUR_MAX > 1 &&
+  if (input->mb_cur_max > 1 &&
       !re_string_first_byte (input, re_string_cur_idx (input)))
     {
       token->type = CHARACTER;
@@ -1704,7 +2054,8 @@ peek_token_bracket (token, input, syntax)
     }
 #endif /* RE_ENABLE_I18N */
 
-  if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS))
+  if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS)
+      && re_string_cur_idx (input) + 1 < re_string_length (input))
     {
       /* In this case, '\' escape a character.  */
       unsigned char c2;
@@ -1718,7 +2069,10 @@ peek_token_bracket (token, input, syntax)
     {
       unsigned char c2;
       int token_len;
-      c2 = re_string_peek_byte (input, 1);
+      if (re_string_cur_idx (input) + 1 < re_string_length (input))
+       c2 = re_string_peek_byte (input, 1);
+      else
+       c2 = 0;
       token->opr.c = c2;
       token_len = 2;
       switch (c2)
@@ -1726,16 +2080,18 @@ peek_token_bracket (token, input, syntax)
        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.  */
+         FALLTHROUGH;
        default:
          token->type = CHARACTER;
          token->opr.c = c;
@@ -1765,7 +2121,7 @@ peek_token_bracket (token, input, syntax)
 
 /* 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.
+   If an error occurs, ERR is set by error code, and return NULL.
    This function build the following tree, from regular expression <reg_exp>:
           CAT
           / \
@@ -1776,27 +2132,23 @@ peek_token_bracket (token, input, syntax)
    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;
+parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax,
+       reg_errcode_t *err)
 {
-  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  re_dfa_t *dfa = preg->buffer;
   bin_tree_t *tree, *eor, *root;
   re_token_t current_token;
-  int new_idx;
-  current_token = fetch_token (regexp, syntax);
+  dfa->syntax = syntax;
+  fetch_token (&current_token, regexp, syntax | RE_CARET_ANCHORS_HERE);
   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);
+  eor = create_tree (dfa, NULL, NULL, END_OF_RE);
   if (tree != NULL)
-    root = create_tree (tree, eor, CONCAT, 0);
+    root = create_tree (dfa, tree, eor, CONCAT);
   else
     root = eor;
-  if (BE (new_idx == -1 || eor == NULL || root == NULL, 0))
+  if (BE (eor == NULL || root == NULL, 0))
     {
       *err = REG_ESPACE;
       return NULL;
@@ -1811,48 +2163,44 @@ parse (regexp, preg, syntax, err)
          /   \
    <branch1> <branch2>
 
-   ALT means alternative, which represents the operator `|'.  */
+   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;
+parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+              reg_syntax_t syntax, Idx nest, reg_errcode_t *err)
 {
-  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  re_dfa_t *dfa = preg->buffer;
   bin_tree_t *tree, *branch = NULL;
-  int new_idx;
+  bitset_word_t initial_bkref_map = dfa->completed_bkref_map;
   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);
+      fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
       if (token->type != OP_ALT && token->type != END_OF_RE
          && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
        {
+         bitset_word_t accumulated_bkref_map = dfa->completed_bkref_map;
+         dfa->completed_bkref_map = initial_bkref_map;
          branch = parse_branch (regexp, preg, token, syntax, nest, err);
          if (BE (*err != REG_NOERROR && branch == NULL, 0))
            {
-             free_bin_tree (tree);
+             if (tree != NULL)
+               postorder (tree, free_tree, NULL);
              return NULL;
            }
+         dfa->completed_bkref_map |= accumulated_bkref_map;
        }
       else
        branch = NULL;
-      tree = create_tree (tree, branch, 0, new_idx);
-      if (BE (new_idx == -1 || tree == NULL, 0))
+      tree = create_tree (dfa, tree, branch, OP_ALT);
+      if (BE (tree == NULL, 0))
        {
          *err = REG_ESPACE;
          return NULL;
        }
-      dfa->has_plural_match = 1;
     }
   return tree;
 }
@@ -1867,15 +2215,11 @@ parse_reg_exp (regexp, preg, token, syntax, nest, err)
    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;
+parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token,
+             reg_syntax_t syntax, Idx nest, reg_errcode_t *err)
 {
-  bin_tree_t *tree, *exp;
+  bin_tree_t *tree, *expr;
+  re_dfa_t *dfa = preg->buffer;
   tree = parse_expression (regexp, preg, token, syntax, nest, err);
   if (BE (*err != REG_NOERROR && tree == NULL, 0))
     return NULL;
@@ -1883,24 +2227,28 @@ parse_branch (regexp, preg, token, syntax, nest, err)
   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))
+      expr = parse_expression (regexp, preg, token, syntax, nest, err);
+      if (BE (*err != REG_NOERROR && expr == NULL, 0))
        {
-         free_bin_tree (tree);
+         if (tree != NULL)
+           postorder (tree, free_tree, NULL);
          return NULL;
        }
-      if (tree != NULL && exp != NULL)
+      if (tree != NULL && expr != NULL)
        {
-         tree = create_tree (tree, exp, CONCAT, 0);
-         if (tree == NULL)
+         bin_tree_t *newtree = create_tree (dfa, tree, expr, CONCAT);
+         if (newtree == NULL)
            {
+             postorder (expr, free_tree, NULL);
+             postorder (tree, free_tree, NULL);
              *err = REG_ESPACE;
              return NULL;
            }
+         tree = newtree;
        }
       else if (tree == NULL)
-       tree = exp;
-      /* Otherwise exp == NULL, we don't need to create new tree.  */
+       tree = expr;
+      /* Otherwise expr == NULL, we don't need to create new tree.  */
     }
   return tree;
 }
@@ -1912,39 +2260,31 @@ parse_branch (regexp, preg, token, syntax, nest, err)
 */
 
 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;
+parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token,
+                 reg_syntax_t syntax, Idx nest, reg_errcode_t *err)
 {
-  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  re_dfa_t *dfa = 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))
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
        {
          *err = REG_ESPACE;
          return NULL;
        }
 #ifdef RE_ENABLE_I18N
-      if (MB_CUR_MAX > 1)
+      if (dfa->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))
+             fetch_token (token, regexp, syntax);
+             mbc_remain = create_token_tree (dfa, NULL, NULL, token);
+             tree = create_tree (dfa, tree, mbc_remain, CONCAT);
+             if (BE (mbc_remain == NULL || tree == NULL, 0))
                {
                  *err = REG_ESPACE;
                  return NULL;
@@ -1953,27 +2293,28 @@ parse_expression (regexp, preg, token, syntax, nest, err)
        }
 #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))
+      if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1))
        {
          *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))
+      dfa->used_bkref_map |= 1 << token->opr.idx;
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
        {
          *err = REG_ESPACE;
          return NULL;
@@ -1981,10 +2322,17 @@ parse_expression (regexp, preg, token, syntax, nest, err)
       ++dfa->nbackref;
       dfa->has_mb_node = 1;
       break;
+
+    case OP_OPEN_DUP_NUM:
+      if (syntax & RE_CONTEXT_INVALID_DUP)
+       {
+         *err = REG_BADRPT;
+         return NULL;
+       }
+      FALLTHROUGH;
     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;
@@ -1992,10 +2340,10 @@ parse_expression (regexp, preg, token, syntax, nest, err)
        }
       else if (syntax & RE_CONTEXT_INDEP_OPS)
        {
-         *token = fetch_token (regexp, syntax);
+         fetch_token (token, regexp, syntax);
          return parse_expression (regexp, preg, token, syntax, nest, err);
        }
-      /* else fall through  */
+      FALLTHROUGH;
     case OP_CLOSE_SUBEXP:
       if ((token->type == OP_CLOSE_SUBEXP) &&
          !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
@@ -2003,43 +2351,46 @@ parse_expression (regexp, preg, token, syntax, nest, err)
          *err = REG_ERPAREN;
          return NULL;
        }
-      /* else fall through  */
+      FALLTHROUGH;
     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))
+      /* mb_partial and word_char bits should be initialized already
+        by peek_token.  */
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (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)
+      if ((token->opr.ctx_type
+          & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST))
+         && dfa->word_ops_used == 0)
+       init_word_char (dfa);
+      if (token->opr.ctx_type == WORD_DELIM
+         || token->opr.ctx_type == NOT_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))
+         if (token->opr.ctx_type == WORD_DELIM)
+           {
+             token->opr.ctx_type = WORD_FIRST;
+             tree_first = create_token_tree (dfa, NULL, NULL, token);
+             token->opr.ctx_type = WORD_LAST;
+           }
+         else
+           {
+             token->opr.ctx_type = INSIDE_WORD;
+             tree_first = create_token_tree (dfa, NULL, NULL, token);
+             token->opr.ctx_type = INSIDE_NOTWORD;
+           }
+         tree_last = create_token_tree (dfa, NULL, NULL, token);
+         tree = create_tree (dfa, tree_first, tree_last, OP_ALT);
+         if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0))
            {
              *err = REG_ESPACE;
              return NULL;
@@ -2047,9 +2398,8 @@ parse_expression (regexp, preg, token, syntax, nest, err)
        }
       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))
+         tree = create_token_tree (dfa, NULL, NULL, token);
+         if (BE (tree == NULL, 0))
            {
              *err = REG_ESPACE;
              return NULL;
@@ -2059,35 +2409,48 @@ parse_expression (regexp, preg, token, syntax, nest, err)
         by repetition operators.
         eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
             it must not be "<ANCHOR(^)><REPEAT(*)>".  */
-      *token = fetch_token (regexp, syntax);
+      fetch_token (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))
+      tree = create_token_tree (dfa, NULL, NULL, token);
+      if (BE (tree == NULL, 0))
        {
          *err = REG_ESPACE;
          return NULL;
        }
-      if (MB_CUR_MAX > 1)
+      if (dfa->mb_cur_max > 1)
        dfa->has_mb_node = 1;
       break;
+
     case OP_WORD:
-      tree = build_word_op (dfa, 0, err);
+    case OP_NOTWORD:
+      tree = build_charclass_op (dfa, regexp->trans,
+                                "alnum",
+                                "_",
+                                token->type == OP_NOTWORD, err);
       if (BE (*err != REG_NOERROR && tree == NULL, 0))
        return NULL;
       break;
-    case OP_NOTWORD:
-      tree = build_word_op (dfa, 1, err);
+
+    case OP_SPACE:
+    case OP_NOTSPACE:
+      tree = build_charclass_op (dfa, regexp->trans,
+                                "space",
+                                "",
+                                token->type == OP_NOTSPACE, 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
@@ -2095,15 +2458,30 @@ parse_expression (regexp, preg, token, syntax, nest, err)
 #endif
       return NULL;
     }
-  *token = fetch_token (regexp, syntax);
+  fetch_token (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;
+      bin_tree_t *dup_tree = parse_dup_op (tree, regexp, dfa, token,
+                                          syntax, err);
+      if (BE (*err != REG_NOERROR && dup_tree == NULL, 0))
+       {
+         if (tree != NULL)
+           postorder (tree, free_tree, NULL);
+         return NULL;
+       }
+      tree = dup_tree;
+      /* In BRE consecutive duplications are not allowed.  */
+      if ((syntax & RE_CONTEXT_INVALID_DUP)
+         && (token->type == OP_DUP_ASTERISK
+             || token->type == OP_OPEN_DUP_NUM))
+       {
+         if (tree != NULL)
+           postorder (tree, free_tree, NULL);
+         *err = REG_BADRPT;
+         return NULL;
+       }
     }
 
   return tree;
@@ -2117,44 +2495,15 @@ parse_expression (regexp, preg, token, syntax, nest, err)
 */
 
 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;
+parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
+              reg_syntax_t syntax, Idx nest, reg_errcode_t *err)
 {
-  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
-  bin_tree_t *tree, *left_par, *right_par;
+  re_dfa_t *dfa = preg->buffer;
+  bin_tree_t *tree;
   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);
+  fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
 
   /* The subexpression may be a null string.  */
   if (token->type == OP_CLOSE_SUBEXP)
@@ -2162,52 +2511,43 @@ parse_sub_exp (regexp, preg, token, syntax, nest, err)
   else
     {
       tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
-      if (BE (*err != REG_NOERROR && tree == NULL, 0))
+      if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0))
+       {
+         if (tree != NULL)
+           postorder (tree, free_tree, NULL);
+         *err = REG_EPAREN;
+       }
+      if (BE (*err != REG_NOERROR, 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))
+
+  if (cur_nsub <= '9' - '1')
+    dfa->completed_bkref_map |= 1 << cur_nsub;
+
+  tree = create_tree (dfa, tree, NULL, SUBEXP);
+  if (BE (tree == NULL, 0))
     {
       *err = REG_ESPACE;
       return NULL;
     }
-  dfa->nodes[new_idx].opr.idx = cur_nsub;
-
+  tree->token.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;
+parse_dup_op (bin_tree_t *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);
+  bin_tree_t *tree = NULL, *old_tree = NULL;
+  Idx i, start, end, 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;
+      end = 0;
+      start = fetch_number (regexp, token, syntax);
       if (start == -1)
        {
          if (token->type == CHARACTER && token->opr.c == ',')
@@ -2225,123 +2565,117 @@ parse_dup_op (dup_elem, regexp, dfa, token, syntax, err)
                 : ((token->type == CHARACTER && token->opr.c == ',')
                    ? fetch_number (regexp, token, syntax) : -2));
        }
-      if (BE (start == -2 || end == -2, 0))
+      if (BE (start == -2 || end == -2, 0))
+       {
+         /* Invalid sequence.  */
+         if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
+           {
+             if (token->type == END_OF_RE)
+               *err = REG_EBRACE;
+             else
+               *err = REG_BADBR;
+
+             return NULL;
+           }
+
+         /* If the syntax bit is set, rollback.  */
+         re_string_set_index (regexp, start_idx);
+         *token = start_token;
+         token->type = CHARACTER;
+         /* mb_partial and word_char bits should be already initialized by
+            peek_token.  */
+         return elem;
+       }
+
+      if (BE ((end != -1 && start > end)
+             || token->type != OP_CLOSE_DUP_NUM, 0))
        {
-         /* Invalid sequence.  */
-         if (token->type == OP_CLOSE_DUP_NUM)
-           goto parse_dup_op_invalid_interval;
-         else
-           goto parse_dup_op_ebrace;
+         /* First number greater than second.  */
+         *err = REG_BADBR;
+         return NULL;
        }
-      if (BE (start == 0 && end == 0, 0))
+
+      if (BE (RE_DUP_MAX < (end == -1 ? start : end), 0))
        {
-         /* We treat "<re>{0}" and "<re>{0,0}" as null string.  */
-         *token = fetch_token (regexp, syntax);
-         free_bin_tree (dup_elem);
+         *err = REG_ESIZE;
          return NULL;
        }
+    }
+  else
+    {
+      start = (token->type == OP_DUP_PLUS) ? 1 : 0;
+      end = (token->type == OP_DUP_QUESTION) ? 1 : -1;
+    }
 
-      /* 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;
-         }
+  fetch_token (token, regexp, syntax);
 
-      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)
+  if (BE (elem == NULL, 0))
+    return NULL;
+  if (BE (start == 0 && end == 0, 0))
+    {
+      postorder (elem, free_tree, NULL);
+      return NULL;
+    }
+
+  /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}".  */
+  if (BE (start > 0, 0))
+    {
+      tree = elem;
+      for (i = 2; i <= start; ++i)
        {
-         /* 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;
-               }
-           }
+         elem = duplicate_tree (elem, dfa);
+         tree = create_tree (dfa, tree, elem, CONCAT);
+         if (BE (elem == NULL || tree == NULL, 0))
+           goto parse_dup_op_espace;
        }
+
+      if (start == end)
+       return tree;
+
+      /* Duplicate ELEM before it is marked optional.  */
+      elem = duplicate_tree (elem, dfa);
+      if (BE (elem == NULL, 0))
+        goto parse_dup_op_espace;
+      old_tree = tree;
     }
   else
+    old_tree = NULL;
+
+  if (elem->token.type == SUBEXP)
     {
-      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;
-       }
+      uintptr_t subidx = elem->token.opr.idx;
+      postorder (elem, mark_opt_subexp, (void *) subidx);
     }
-  *token = fetch_token (regexp, syntax);
+
+  tree = create_tree (dfa, elem, NULL,
+                     (end == -1 ? OP_DUP_ASTERISK : OP_ALT));
+  if (BE (tree == NULL, 0))
+    goto parse_dup_op_espace;
+
+  /* This loop is actually executed only when end != -1,
+     to rewrite <re>{0,n} as (<re>(<re>...<re>?)?)?...  We have
+     already created the start+1-th copy.  */
+  if (TYPE_SIGNED (Idx) || end != -1)
+    for (i = start + 2; i <= end; ++i)
+      {
+       elem = duplicate_tree (elem, dfa);
+       tree = create_tree (dfa, tree, elem, CONCAT);
+       if (BE (elem == NULL || tree == NULL, 0))
+         goto parse_dup_op_espace;
+
+       tree = create_tree (dfa, tree, NULL, OP_ALT);
+       if (BE (tree == NULL, 0))
+         goto parse_dup_op_espace;
+      }
+
+  if (old_tree)
+    tree = create_tree (dfa, old_tree, tree, CONCAT);
+
   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.
@@ -2349,23 +2683,40 @@ parse_dup_op (dup_elem, regexp, dfa, token, syntax, err)
 #define BRACKET_NAME_BUF_SIZE 32
 
 #ifndef _LIBC
+
+# ifdef RE_ENABLE_I18N
+/* Convert the byte B to the corresponding wide character.  In a
+   unibyte locale, treat B as itself if it is an encoding error.
+   In a multibyte locale, return WEOF if B is an encoding error.  */
+static wint_t
+parse_byte (unsigned char b, re_charset_t *mbcset)
+{
+  wint_t wc = __btowc (b);
+  return wc == WEOF && !mbcset ? b : wc;
+}
+#endif
+
   /* 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
+     mbcset->range_ends, is a pointer argument since 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;
+build_range_exp (const reg_syntax_t syntax,
+                 bitset_t sbcset,
+                 re_charset_t *mbcset,
+                 Idx *range_alloc,
+                 const bracket_elem_t *start_elem,
+                 const bracket_elem_t *end_elem)
 # else /* not RE_ENABLE_I18N */
-build_range_exp (sbcset, start_elem, end_elem)
+build_range_exp (const reg_syntax_t syntax,
+                 bitset_t sbcset,
+                 const bracket_elem_t *start_elem,
+                 const bracket_elem_t *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.  */
@@ -2384,8 +2735,9 @@ build_range_exp (sbcset, start_elem, end_elem)
 
 # 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'};
+    wchar_t wc;
+    wint_t start_wc;
+    wint_t end_wc;
 
     start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
                : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
@@ -2394,47 +2746,57 @@ build_range_exp (sbcset, start_elem, end_elem)
              : ((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);
+               ? parse_byte (start_ch, mbcset) : 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)
+             ? parse_byte (end_ch, mbcset) : end_elem->opr.wch);
+    if (start_wc == WEOF || end_wc == WEOF)
+      return REG_ECOLLATE;
+    else if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_wc > end_wc, 0))
       return REG_ERANGE;
 
-    /* Check the space of the arrays.  */
-    if (*range_alloc == mbcset->nranges)
+    /* Got valid collation sequence values, add them as a new entry.
+       However, for !_LIBC we have no collation elements: if the
+       character set is single byte, the single byte character set
+       that we build below suffices.  parse_bracket_exp passes
+       no MBCSET if dfa->mb_cur_max == 1.  */
+    if (mbcset)
       {
-       /* 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;
-      }
+       /* Check the space of the arrays.  */
+       if (BE (*range_alloc == mbcset->nranges, 0))
+         {
+           /* There is not enough space, need realloc.  */
+           wchar_t *new_array_start, *new_array_end;
+           Idx 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))
+             {
+               re_free (new_array_start);
+               re_free (new_array_end);
+               return REG_ESPACE;
+             }
 
-    mbcset->range_starts[mbcset->nranges] = start_wc;
-    mbcset->range_ends[mbcset->nranges++] = end_wc;
+           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)
+    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)
+       if (start_wc <= wc && wc <= end_wc)
          bitset_set (sbcset, wc);
       }
   }
@@ -2450,7 +2812,7 @@ build_range_exp (sbcset, start_elem, end_elem)
     if (start_ch > end_ch)
       return REG_ERANGE;
     /* Build the table for single byte characters.  */
-    for (ch = 0; ch <= SBC_MAX; ++ch)
+    for (ch = 0; ch < SBC_MAX; ++ch)
       if (start_ch <= ch  && ch <= end_ch)
        bitset_set (sbcset, ch);
   }
@@ -2468,14 +2830,11 @@ build_range_exp (sbcset, start_elem, end_elem)
 
 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;
+build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset,
+                       Idx *coll_sym_alloc, const unsigned char *name)
 # else /* not RE_ENABLE_I18N */
-build_collating_symbol (sbcset, name)
+build_collating_symbol (bitset_t sbcset, const unsigned char *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))
@@ -2492,12 +2851,8 @@ build_collating_symbol (sbcset, name)
    "[[.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;
+parse_bracket_exp (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;
@@ -2507,45 +2862,40 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
   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;
-           }
+  /* Local function for parse_bracket_exp used in _LIBC environment.
+     Seek the collating symbol entry corresponding to NAME.
+     Return the index of the symbol in the SYMB_TABLE,
+     or -1 if not found.  */
 
-         /* Next entry.  */
-         elem += second;
-       }
-      return elem;
+  auto inline int32_t
+  __attribute__ ((always_inline))
+  seek_collating_symbol_entry (const unsigned char *name, size_t name_len)
+    {
+      int32_t elem;
+
+      for (elem = 0; elem < table_size; elem++)
+       if (symb_table[2 * elem] != 0)
+         {
+           int32_t idx = symb_table[2 * elem + 1];
+           /* Skip the name of collating element name.  */
+           idx += 1 + extra[idx];
+           if (/* Compare the length of the name.  */
+               name_len == extra[idx]
+               /* Compare the name.  */
+               && memcmp (name, &extra[idx + 1], name_len) == 0)
+             /* Yep, this is the entry.  */
+             return elem;
+         }
+      return -1;
     }
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      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;
+  auto inline unsigned int
+  __attribute__ ((always_inline))
+  lookup_collation_sequence_value (bracket_elem_t *br_elem)
     {
       if (br_elem->type == SB_CHAR)
        {
@@ -2557,12 +2907,13 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
          else
            {
              wint_t wc = __btowc (br_elem->opr.ch);
-             return collseq_table_lookup (collseqwc, wc);
+             return __collseq_table_lookup (collseqwc, wc);
            }
        }
       else if (br_elem->type == MB_CHAR)
        {
-         return collseq_table_lookup (collseqwc, br_elem->opr.wch);
+         if (nrules != 0)
+           return __collseq_table_lookup (collseqwc, br_elem->opr.wch);
        }
       else if (br_elem->type == COLL_SYM)
        {
@@ -2572,7 +2923,7 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
              int32_t elem, idx;
              elem = seek_collating_symbol_entry (br_elem->opr.name,
                                                  sym_name_len);
-             if (symb_table[2 * elem] != 0)
+             if (elem != -1)
                {
                  /* We found the entry.  */
                  idx = symb_table[2 * elem + 1];
@@ -2590,7 +2941,7 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
                  /* Return the collation sequence value.  */
                  return *(unsigned int *) (extra + idx);
                }
-             else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
+             else if (sym_name_len == 1)
                {
                  /* No valid character.  Match it as a single byte
                     character.  */
@@ -2603,55 +2954,22 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
       return UINT_MAX;
     }
 
-  /* Local function for parse_bracket_exp used in _LIBC environement.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      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
+     mbcset->range_ends, is a pointer argument since 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;
+  auto inline reg_errcode_t
+  __attribute__ ((always_inline))
+  build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc,
+                  bracket_elem_t *start_elem, bracket_elem_t *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
@@ -2659,6 +2977,7 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
              0))
        return REG_ERANGE;
 
+      /* FIXME: Implement rational ranges here, too.  */
       start_collseq = lookup_collation_sequence_value (start_elem);
       end_collseq = lookup_collation_sequence_value (end_elem);
       /* Check start/end collation sequence values.  */
@@ -2667,14 +2986,41 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
       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 */
+      /* Got valid collation sequence values, add them as a new entry.
+        However, if we have no collation elements, and the character set
+        is single byte, the single byte character set that we
+        build below suffices. */
+      if (nrules > 0 || dfa->mb_cur_max > 1)
+       {
+         /* Check the space of the arrays.  */
+         if (BE (*range_alloc == mbcset->nranges, 0))
+           {
+             /* There is not enough space, need realloc.  */
+             uint32_t *new_array_start;
+             uint32_t *new_array_end;
+             Idx new_nranges;
+
+             /* +1 in case of mbcset->nranges is 0.  */
+             new_nranges = 2 * mbcset->nranges + 1;
+             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;
+           }
+
+         mbcset->range_starts[mbcset->nranges] = start_collseq;
+         mbcset->range_ends[mbcset->nranges++] = end_collseq;
+       }
 
       /* Build the table for single byte characters.  */
-      for (ch = 0; ch <= SBC_MAX; ch++)
+      for (ch = 0; ch < SBC_MAX; ch++)
        {
          uint32_t ch_collseq;
          /*
@@ -2683,43 +3029,37 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
          if (nrules == 0)
            ch_collseq = collseqmb[ch];
          else
-           ch_collseq = collseq_table_lookup (collseqwc, __btowc (ch));
+           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.
+  /* Local function for parse_bracket_exp used in _LIBC environment.
      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.  */
+     pointer argument since 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;
+  auto inline reg_errcode_t
+  __attribute__ ((always_inline))
+  build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset,
+                         Idx *coll_sym_alloc, 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)
+         if (elem != -1)
            {
              /* 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)
+         else if (name_len == 1)
            {
              /* No valid character, treat it as a normal
                 character.  */
@@ -2729,23 +3069,23 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
          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)
+         if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0))
            {
              /* Not enough, realloc it.  */
              /* +1 in case of mbcset->ncoll_syms is 0.  */
-             *coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
+             Idx new_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))
+             int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t,
+                                                  new_coll_sym_alloc);
+             if (BE (new_coll_syms == NULL, 0))
                return REG_ESPACE;
+             mbcset->coll_syms = new_coll_syms;
+             *coll_sym_alloc = new_coll_sym_alloc;
            }
          mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
-# endif /* RE_ENABLE_I18N */
          return REG_NOERROR;
        }
       else
@@ -2765,13 +3105,13 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
   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;
+  Idx coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
+  Idx equiv_class_alloc = 0, char_class_alloc = 0;
 #endif /* not RE_ENABLE_I18N */
+  bool non_match = false;
   bin_tree_t *work_tree;
-  int token_len, new_idx;
+  int token_len;
+  bool first_round = true;
 #ifdef _LIBC
   collseqmb = (const unsigned char *)
     _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
@@ -2781,7 +3121,7 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
       /*
       if (MB_CUR_MAX > 1)
       */
-       collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
+      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);
@@ -2789,7 +3129,7 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
                                                   _NL_COLLATE_SYMB_EXTRAMB);
     }
 #endif
-  sbcset = (re_bitset_ptr_t) calloc (sizeof (unsigned int), BITSET_UINTS);
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
 #ifdef RE_ENABLE_I18N
   mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
 #endif /* RE_ENABLE_I18N */
@@ -2799,6 +3139,10 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
   if (BE (sbcset == NULL, 0))
 #endif /* RE_ENABLE_I18N */
     {
+      re_free (sbcset);
+#ifdef RE_ENABLE_I18N
+      re_free (mbcset);
+#endif
       *err = REG_ESPACE;
       return NULL;
     }
@@ -2812,13 +3156,11 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
   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 */
+      non_match = true;
       if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
-       bitset_set (sbcset, '\0');
+       bitset_set (sbcset, '\n');
       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))
@@ -2826,12 +3168,6 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
          *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.  */
@@ -2844,48 +3180,58 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
       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;
+      int token_len2 = 0;
+      bool is_range_exp = false;
       re_token_t token2;
 
       start_elem.opr.name = start_name_buf;
+      start_elem.type = COLL_SYM;
       ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
-                                  syntax);
+                                  syntax, first_round);
       if (BE (ret != REG_NOERROR, 0))
        {
          *err = ret;
          goto parse_bracket_exp_free_return;
        }
+      first_round = false;
 
+      /* Get information about the next token.  We need it in any case.  */
       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)
+
+      /* Do not check for ranges if we know they are not allowed.  */
+      if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS)
        {
-         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;
+             *err = REG_EBRACK;
              goto parse_bracket_exp_free_return;
            }
-         if (token2.type == OP_CLOSE_BRACKET)
+         if (token->type == OP_CHARSET_RANGE)
            {
-             /* We treat the last '-' as a normal character.  */
-             re_string_skip_bytes (regexp, -token_len);
-             token->type = CHARACTER;
+             re_string_skip_bytes (regexp, token_len); /* Skip '-'.  */
+             token_len2 = peek_token_bracket (&token2, regexp, syntax);
+             if (BE (token2.type == END_OF_RE, 0))
+               {
+                 *err = REG_EBRACK;
+                 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 = true;
            }
-         else
-           is_range_exp = 1;
        }
 
-      if (is_range_exp == 1)
+      if (is_range_exp == true)
        {
          end_elem.opr.name = end_name_buf;
+         end_elem.type = COLL_SYM;
          ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
-                                      dfa, syntax);
+                                      dfa, syntax, true);
          if (BE (ret != REG_NOERROR, 0))
            {
              *err = ret;
@@ -2893,16 +3239,19 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
            }
 
          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 */
+
+#ifdef _LIBC
+         *err = build_range_exp (sbcset, mbcset, &range_alloc,
                                  &start_elem, &end_elem);
+#else
+# ifdef RE_ENABLE_I18N
+         *err = build_range_exp (syntax, sbcset,
+                                 dfa->mb_cur_max > 1 ? mbcset : NULL,
+                                 &range_alloc, &start_elem, &end_elem);
+# else
+         *err = build_range_exp (syntax, sbcset, &start_elem, &end_elem);
+# endif
+#endif /* RE_ENABLE_I18N */
          if (BE (*err != REG_NOERROR, 0))
            goto parse_bracket_exp_free_return;
        }
@@ -2916,16 +3265,18 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
 #ifdef RE_ENABLE_I18N
            case MB_CHAR:
              /* Check whether the array has enough space.  */
-             if (mbchar_alloc == mbcset->nmbchars)
+             if (BE (mbchar_alloc == mbcset->nmbchars, 0))
                {
+                 wchar_t *new_mbchars;
                  /* 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))
+                 new_mbchars = re_realloc (mbcset->mbchars, wchar_t,
+                                           mbchar_alloc);
+                 if (BE (new_mbchars == NULL, 0))
                    goto parse_bracket_exp_espace;
+                 mbcset->mbchars = new_mbchars;
                }
              mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
              break;
@@ -2949,11 +3300,12 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
                goto parse_bracket_exp_free_return;
              break;
            case CHAR_CLASS:
-             *err = build_charclass (sbcset,
+             *err = build_charclass (regexp->trans, sbcset,
 #ifdef RE_ENABLE_I18N
                                      mbcset, &char_class_alloc,
 #endif /* RE_ENABLE_I18N */
-                                     start_elem.opr.name, syntax);
+                                     (const char *) start_elem.opr.name,
+                                     syntax);
              if (BE (*err != REG_NOERROR, 0))
               goto parse_bracket_exp_free_return;
              break;
@@ -2962,6 +3314,11 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
              break;
            }
        }
+      if (BE (token->type == END_OF_RE, 0))
+       {
+         *err = REG_EBRACK;
+         goto parse_bracket_exp_free_return;
+       }
       if (token->type == OP_CLOSE_BRACKET)
        break;
     }
@@ -2969,52 +3326,66 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
   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
+  /* Ensure only single byte characters are set.  */
+  if (dfa->mb_cur_max > 1)
+    bitset_mask (sbcset, dfa->sb_char);
+
   if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
-      || mbcset->nranges || (MB_CUR_MAX > 1 && (mbcset->nchar_classes
-                                               || mbcset->non_match)))
+      || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes
+                                                    || mbcset->non_match)))
     {
-      re_token_t alt_token;
       bin_tree_t *mbc_tree;
+      int sbc_idx;
       /* Build a tree for complex bracket.  */
+      dfa->has_mb_node = 1;
       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))
+      mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (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;
+      for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx)
+       if (sbcset[sbc_idx])
+         break;
+      /* If there are no bits set in sbcset, there is no point
+        of having both SIMPLE_BRACKET and COMPLEX_BRACKET.  */
+      if (sbc_idx < BITSET_WORDS)
+       {
+         /* Build a tree for simple bracket.  */
+         br_token.type = SIMPLE_BRACKET;
+         br_token.opr.sbcset = sbcset;
+         work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+         if (BE (work_tree == NULL, 0))
+           goto parse_bracket_exp_espace;
+
+         /* Then join them by ALT node.  */
+         work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT);
+         if (BE (work_tree == NULL, 0))
+           goto parse_bracket_exp_espace;
+       }
+      else
+       {
+         re_free (sbcset);
+         work_tree = mbc_tree;
+       }
     }
   else
+#endif /* not RE_ENABLE_I18N */
     {
+#ifdef RE_ENABLE_I18N
       free_charset (mbcset);
-      return work_tree;
+#endif
+      /* Build a tree for simple bracket.  */
+      br_token.type = SIMPLE_BRACKET;
+      br_token.opr.sbcset = sbcset;
+      work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (work_tree == NULL, 0))
+       goto parse_bracket_exp_espace;
     }
-#else /* not RE_ENABLE_I18N */
   return work_tree;
-#endif /* not RE_ENABLE_I18N */
 
  parse_bracket_exp_espace:
   *err = REG_ESPACE;
@@ -3029,13 +3400,9 @@ parse_bracket_exp (regexp, dfa, token, syntax, err)
 /* 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;
+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, bool accept_hyphen)
 {
 #ifdef RE_ENABLE_I18N
   int cur_char_size;
@@ -3052,6 +3419,17 @@ parse_bracket_element (elem, regexp, token, token_len, dfa, syntax)
   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);
+  if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen)
+    {
+      /* A '-' must only appear as anything but a range indicator before
+        the closing bracket.  Everything else is an error.  */
+      re_token_t token2;
+      (void) peek_token_bracket (&token2, regexp, syntax);
+      if (token2.type != OP_CLOSE_BRACKET)
+       /* The actual error value is not standardized since this whole
+          case is undefined.  But ERANGE makes good sense.  */
+       return REG_ERANGE;
+    }
   elem->type = SB_CHAR;
   elem->opr.ch = token->opr.c;
   return REG_NOERROR;
@@ -3062,21 +3440,23 @@ parse_bracket_element (elem, regexp, token, token_len, dfa, syntax)
    [=<equivalent_class>=].  */
 
 static reg_errcode_t
-parse_bracket_symbol (elem, regexp, token)
-     bracket_elem_t *elem;
-     re_string_t *regexp;
-     re_token_t *token;
+parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp,
+                     re_token_t *token)
 {
   unsigned char ch, delim = token->opr.c;
   int i = 0;
+  if (re_string_eoi(regexp))
+    return REG_EBRACK;
   for (;; ++i)
     {
-      if (re_string_eoi(regexp) || i >= BRACKET_NAME_BUF_SIZE)
+      if (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 (re_string_eoi(regexp))
+       return REG_EBRACK;
       if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
        break;
       elem->opr.name[i] = ch;
@@ -3104,20 +3484,17 @@ parse_bracket_symbol (elem, regexp, token)
      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.  */
+     is a pointer argument since 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;
+build_equiv_class (bitset_t sbcset, re_charset_t *mbcset,
+                  Idx *equiv_class_alloc, const unsigned char *name)
 #else /* not RE_ENABLE_I18N */
-build_equiv_class (sbcset, name)
+build_equiv_class (bitset_t sbcset, const unsigned char *name)
 #endif /* not RE_ENABLE_I18N */
-     re_bitset_ptr_t sbcset;
-     const unsigned char *name;
 {
-#if defined _LIBC && defined RE_ENABLE_I18N
+#ifdef _LIBC
   uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
   if (nrules != 0)
     {
@@ -3127,8 +3504,6 @@ build_equiv_class (sbcset, name)
       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);
@@ -3138,52 +3513,50 @@ build_equiv_class (sbcset, name)
                                                   _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))
+      idx1 = findidx (table, indirect, extra, &cp, -1);
+      if (BE (idx1 == 0 || *cp != '\0', 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];
+      /* Build single byte matching table for this equivalence class.  */
+      len = weights[idx1 & 0xffffff];
       for (ch = 0; ch < SBC_MAX; ++ch)
        {
          char_buf[0] = ch;
          cp = char_buf;
-         idx2 = findidx (&cp);
+         idx2 = findidx (table, indirect, extra, &cp, 1);
 /*
          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);
-           }
+         /* Compare only if the length matches and the collation rule
+            index is the same.  */
+         if (len == weights[idx2 & 0xffffff] && (idx1 >> 24) == (idx2 >> 24)
+             && memcmp (weights + (idx1 & 0xffffff) + 1,
+                        weights + (idx2 & 0xffffff) + 1, len) == 0)
+           bitset_set (sbcset, ch);
        }
       /* Check whether the array has enough space.  */
-      if (*equiv_class_alloc == mbcset->nequiv_classes)
+      if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0))
        {
          /* Not enough, realloc it.  */
          /* +1 in case of mbcset->nequiv_classes is 0.  */
-         *equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
+         Idx new_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))
+         int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes,
+                                                  int32_t,
+                                                  new_equiv_class_alloc);
+         if (BE (new_equiv_classes == NULL, 0))
            return REG_ESPACE;
+         mbcset->equiv_classes = new_equiv_classes;
+         *equiv_class_alloc = new_equiv_class_alloc;
        }
       mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
     }
   else
-#endif /* _LIBC && RE_ENABLE_I18N */
+#endif /* _LIBC */
     {
       if (BE (strlen ((const char *) name) != 1, 0))
        return REG_ECOLLATE;
@@ -3196,22 +3569,20 @@ build_equiv_class (sbcset, name)
      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.  */
+     is a pointer argument since 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;
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+                re_charset_t *mbcset, Idx *char_class_alloc,
+                const char *class_name, reg_syntax_t syntax)
 #else /* not RE_ENABLE_I18N */
-build_charclass (sbcset, class_name, syntax)
+build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
+                const char *class_name, reg_syntax_t 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;
+  const char *name = class_name;
 
   /* In case of REG_ICASE "upper" and "lower" match the both of
      upper and lower cases.  */
@@ -3221,51 +3592,62 @@ build_charclass (sbcset, class_name, syntax)
 
 #ifdef RE_ENABLE_I18N
   /* Check the space of the arrays.  */
-  if (*char_class_alloc == mbcset->nchar_classes)
+  if (BE (*char_class_alloc == mbcset->nchar_classes, 0))
     {
       /* Not enough, realloc it.  */
       /* +1 in case of mbcset->nchar_classes is 0.  */
-      *char_class_alloc = 2 * mbcset->nchar_classes + 1;
+      Idx new_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))
+      wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t,
+                                              new_char_class_alloc);
+      if (BE (new_char_classes == NULL, 0))
        return REG_ESPACE;
+      mbcset->char_classes = new_char_classes;
+      *char_class_alloc = new_char_class_alloc;
     }
   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);       \
-      }
+#define BUILD_CHARCLASS_LOOP(ctype_func)       \
+  do {                                         \
+    if (BE (trans != NULL, 0))                 \
+      {                                                \
+       for (i = 0; i < SBC_MAX; ++i)           \
+         if (ctype_func (i))                   \
+           bitset_set (sbcset, trans[i]);      \
+      }                                                \
+    else                                       \
+      {                                                \
+       for (i = 0; i < SBC_MAX; ++i)           \
+         if (ctype_func (i))                   \
+           bitset_set (sbcset, i);             \
+      }                                                \
+  } while (0)
 
   if (strcmp (name, "alnum") == 0)
-    BUILD_CHARCLASS_LOOP (isalnum)
+    BUILD_CHARCLASS_LOOP (isalnum);
   else if (strcmp (name, "cntrl") == 0)
-    BUILD_CHARCLASS_LOOP (iscntrl)
+    BUILD_CHARCLASS_LOOP (iscntrl);
   else if (strcmp (name, "lower") == 0)
-    BUILD_CHARCLASS_LOOP (islower)
+    BUILD_CHARCLASS_LOOP (islower);
   else if (strcmp (name, "space") == 0)
-    BUILD_CHARCLASS_LOOP (isspace)
+    BUILD_CHARCLASS_LOOP (isspace);
   else if (strcmp (name, "alpha") == 0)
-    BUILD_CHARCLASS_LOOP (isalpha)
+    BUILD_CHARCLASS_LOOP (isalpha);
   else if (strcmp (name, "digit") == 0)
-    BUILD_CHARCLASS_LOOP (isdigit)
+    BUILD_CHARCLASS_LOOP (isdigit);
   else if (strcmp (name, "print") == 0)
-    BUILD_CHARCLASS_LOOP (isprint)
+    BUILD_CHARCLASS_LOOP (isprint);
   else if (strcmp (name, "upper") == 0)
-    BUILD_CHARCLASS_LOOP (isupper)
+    BUILD_CHARCLASS_LOOP (isupper);
   else if (strcmp (name, "blank") == 0)
-    BUILD_CHARCLASS_LOOP (isblank)
+    BUILD_CHARCLASS_LOOP (isblank);
   else if (strcmp (name, "graph") == 0)
-    BUILD_CHARCLASS_LOOP (isgraph)
+    BUILD_CHARCLASS_LOOP (isgraph);
   else if (strcmp (name, "punct") == 0)
-    BUILD_CHARCLASS_LOOP (ispunct)
+    BUILD_CHARCLASS_LOOP (ispunct);
   else if (strcmp (name, "xdigit") == 0)
-    BUILD_CHARCLASS_LOOP (isxdigit)
+    BUILD_CHARCLASS_LOOP (isxdigit);
   else
     return REG_ECTYPE;
 
@@ -3273,62 +3655,43 @@ build_charclass (sbcset, class_name, syntax)
 }
 
 static bin_tree_t *
-build_word_op (dfa, not, err)
-     re_dfa_t *dfa;
-     int not;
-     reg_errcode_t *err;
+build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans,
+                   const char *class_name,
+                   const char *extra, bool non_match,
+                   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;
+  Idx alloc = 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 */
+  sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
   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 */
+  mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
+  if (BE (mbcset == NULL, 0))
+    {
+      re_free (sbcset);
+      *err = REG_ESPACE;
+      return NULL;
     }
+  mbcset->non_match = non_match;
+#endif /* RE_ENABLE_I18N */
 
   /* We don't care the syntax in this case.  */
-  ret = build_charclass (sbcset,
+  ret = build_charclass (trans, sbcset,
 #ifdef RE_ENABLE_I18N
                         mbcset, &alloc,
 #endif /* RE_ENABLE_I18N */
-                        (const unsigned char *) "alpha", 0);
+                        class_name, 0);
 
   if (BE (ret != REG_NOERROR, 0))
     {
@@ -3340,42 +3703,43 @@ build_word_op (dfa, not, err)
       return NULL;
     }
   /* \w match '_' also.  */
-  bitset_set (sbcset, '_');
+  for (; *extra; extra++)
+    bitset_set (sbcset, *extra);
 
   /* 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);
 
+#ifdef RE_ENABLE_I18N
+  /* Ensure only single byte characters are set.  */
+  if (dfa->mb_cur_max > 1)
+    bitset_mask (sbcset, dfa->sb_char);
+#endif
+
   /* Build a tree for simple bracket.  */
+#if defined GCC_LINT || defined lint
+  memset (&br_token, 0, sizeof br_token);
+#endif
   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))
+  tree = create_token_tree (dfa, NULL, NULL, &br_token);
+  if (BE (tree == NULL, 0))
     goto build_word_op_espace;
 
 #ifdef RE_ENABLE_I18N
-  if (MB_CUR_MAX > 1)
+  if (dfa->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))
+      mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
+      if (BE (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))
+      tree = create_tree (dfa, tree, mbc_tree, OP_ALT);
+      if (BE (mbc_tree != NULL, 1))
        return tree;
     }
   else
@@ -3397,29 +3761,29 @@ build_word_op (dfa, not, err)
 }
 
 /* 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.  */
+   Fetch a number from 'input', and return the number.
+   Return -1 if the number field is empty like "{,1}".
+   Return RE_DUP_MAX + 1 if the number field is too large.
+   Return -2 if an error occurred.  */
 
-static int
-fetch_number (input, token, syntax)
-     re_string_t *input;
-     re_token_t *token;
-     reg_syntax_t syntax;
+static Idx
+fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax)
 {
-  int num = -1;
+  Idx num = -1;
   unsigned char c;
   while (1)
     {
-      *token = fetch_token (input, syntax);
+      fetch_token (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;
+            ? -2
+            : num == -1
+            ? c - '0'
+            : MIN (RE_DUP_MAX + 1, num * 10 + c - '0'));
     }
   return num;
 }
@@ -3442,32 +3806,46 @@ free_charset (re_charset_t *cset)
 \f
 /* Functions for binary tree operation.  */
 
-/* Create a node of tree.
-   Note: This function automatically free left and right if malloc fails.  */
+/* Create a tree node.  */
+
+static bin_tree_t *
+create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+            re_token_type_t type)
+{
+  re_token_t t;
+#if defined GCC_LINT || defined lint
+  memset (&t, 0, sizeof t);
+#endif
+  t.type = type;
+  return create_token_tree (dfa, left, right, &t);
+}
 
 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;
+create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
+                  const re_token_t *token)
 {
   bin_tree_t *tree;
-  tree = re_malloc (bin_tree_t, 1);
-  if (BE (tree == NULL, 0))
+  if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0))
     {
-      free_bin_tree (left);
-      free_bin_tree (right);
-      return NULL;
+      bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1);
+
+      if (storage == NULL)
+       return NULL;
+      storage->next = dfa->str_tree_storage;
+      dfa->str_tree_storage = storage;
+      dfa->str_tree_storage_idx = 0;
     }
+  tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++];
+
   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);
+  tree->token = *token;
+  tree->token.duplicated = 0;
+  tree->token.opt_subexp = 0;
+  tree->first = NULL;
+  tree->next = NULL;
+  tree->node_idx = -1;
 
   if (left != NULL)
     left->parent = tree;
@@ -3476,69 +3854,85 @@ create_tree (left, right, type, index)
   return tree;
 }
 
-/* Free the sub tree pointed by TREE.  */
+/* Mark the tree SRC as an optional subexpression.
+   To be called from preorder or postorder.  */
+
+static reg_errcode_t
+mark_opt_subexp (void *extra, bin_tree_t *node)
+{
+  Idx idx = (uintptr_t) extra;
+  if (node->token.type == SUBEXP && node->token.opr.idx == idx)
+    node->token.opt_subexp = 1;
+
+  return REG_NOERROR;
+}
+
+/* Free the allocated memory inside NODE. */
 
 static void
-free_bin_tree (tree)
-     bin_tree_t *tree;
+free_token (re_token_t *node)
+{
+#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);
+}
+
+/* Worker function for tree walking.  Free the allocated memory inside NODE
+   and its children. */
+
+static reg_errcode_t
+free_tree (void *extra, bin_tree_t *node)
 {
-  if (tree == NULL)
-    return;
-  /*re_node_set_free (&tree->eclosure);*/
-  free_bin_tree (tree->left);
-  free_bin_tree (tree->right);
-  re_free (tree);
+  free_token (&node->token);
+  return REG_NOERROR;
 }
 
-/* Duplicate the node SRC, and return new node.  */
+
+/* Duplicate the node SRC, and return new node.  This is a preorder
+   visit similar to the one implemented by the generic visitor, but
+   we need more infrastructure to maintain two parallel trees --- so,
+   it's easier to duplicate.  */
 
 static bin_tree_t *
-duplicate_tree (src, dfa)
-     const bin_tree_t *src;
-     re_dfa_t *dfa;
+duplicate_tree (const bin_tree_t *root, 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;
-    }
+  const bin_tree_t *node;
+  bin_tree_t *dup_root;
+  bin_tree_t **p_new = &dup_root, *dup_node = root->parent;
 
-  /* Secondaly, duplicate the right.  */
-  if (src->right != NULL)
+  for (node = root; ; )
     {
-      right = duplicate_tree (src->right, dfa);
-      if (right == NULL)
+      /* Create a new tree and link it back to the current parent.  */
+      *p_new = create_token_tree (dfa, NULL, NULL, &node->token);
+      if (*p_new == NULL)
+       return NULL;
+      (*p_new)->parent = dup_node;
+      (*p_new)->token.duplicated = 1;
+      dup_node = *p_new;
+
+      /* Go to the left node, or up and to the right.  */
+      if (node->left)
        {
-         free_bin_tree (left);
-         return NULL;
+         node = node->left;
+         p_new = &dup_node->left;
        }
-    }
-
-  /* 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))
+      else
        {
-         free_bin_tree (left);
-         free_bin_tree (right);
-         return NULL;
+         const bin_tree_t *prev = NULL;
+         while (node->right == prev || node->right == NULL)
+           {
+             prev = node;
+             node = node->parent;
+             dup_node = dup_node->parent;
+             if (!node)
+               return dup_root;
+           }
+         node = node->right;
+         p_new = &dup_node->right;
        }
     }
-  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;
 }
index 521a761282b708116373abd1916314a06264e1b7..2d55bea032c4623243b48bb396c66089ec6c7718 100644 (file)
@@ -1,5 +1,5 @@
 /* Extended regular expression matching and search library.
-   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2002-2018 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
 
    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., 51 Franklin Street, Fifth Floor, Boston,
-   MA 02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#if !defined(_LIBC) && !defined(_REGEX_STANDALONE)
+# include <config.h>
+
+# if (__GNUC__ == 4 && 6 <= __GNUC_MINOR__) || 4 < __GNUC__
+#  pragma GCC diagnostic ignored "-Wsuggest-attribute=pure"
+# endif
+# if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__
+#  pragma GCC diagnostic ignored "-Wold-style-definition"
+#  pragma GCC diagnostic ignored "-Wtype-limits"
+# endif
+#endif
+
+/* Make sure no one compiles this code with a C++ compiler.  */
+#if defined __cplusplus && defined _LIBC
+# error "This is C code, use a C compiler"
+#endif
 
 #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) \
+# 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) \
+# 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) \
+# 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) \
+# define re_match(bufp, string, size, pos, regs) \
        __re_match (bufp, string, size, pos, regs)
-#  define re_search(bufp, string, size, startpos, range, 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) \
+# 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) \
+# 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)
+# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
+
+# include "../locale/localeinfo.h"
 #endif
 
-/* POSIX says that <sys/types.h> must be included (by the caller) before
-   <regex.h>.  */
-#include <sys/types.h>
+/* On some systems, limits.h sets RE_DUP_MAX to a lower value than
+   GNU regex allows.  Include it before <regex.h>, which correctly
+   #undefs RE_DUP_MAX and sets it to the right value.  */
+#include <limits.h>
+
 #include <regex.h>
 #include "regex_internal.h"
 
index dc96a34b32a68f664a113b37bb59fa0a094b7e32..c713f543bd105edefd9cddc203dd6fa983e371d0 100644 (file)
@@ -1,7 +1,6 @@
 /* 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.
+   Copyright (C) 1985, 1989-2018 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
    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., 51 Franklin Street, Fifth Floor, Boston,
-   MA 02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
 
 #ifndef _REGEX_H
 #define _REGEX_H 1
 
+#include <sys/types.h>
+
 /* 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>.  */
+/* Define __USE_GNU to declare GNU extensions that violate the
+   POSIX name space rules.  */
+#if defined(_GNU_SOURCE) || defined(_REGEX_STANDALONE)
+# define __USE_GNU 1
+#endif
+
+#ifdef _REGEX_LARGE_OFFSETS
+
+/* Use types and values that are wide enough to represent signed and
+   unsigned byte offsets in memory.  This currently works only when
+   the regex code is used outside of the GNU C library; it is not yet
+   supported within glibc itself, and glibc users should not define
+   _REGEX_LARGE_OFFSETS.  */
+
+/* The type of object sizes.  */
+typedef size_t __re_size_t;
+
+/* The type of object sizes, in places where the traditional code
+   uses unsigned long int.  */
+typedef size_t __re_long_size_t;
+
+#else
+
+/* The traditional GNU regex implementation mishandles strings longer
+   than INT_MAX.  */
+typedef unsigned int __re_size_t;
+typedef unsigned long int __re_long_size_t;
 
-#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
@@ -50,34 +71,35 @@ typedef unsigned long int active_reg_t;
    add or remove a bit, only one other definition need change.  */
 typedef unsigned long int reg_syntax_t;
 
+#ifdef __USE_GNU
 /* 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)
+# 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)
+# 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)
+# 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.
+       ^  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)
+# 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.
@@ -85,71 +107,71 @@ typedef unsigned long int reg_syntax_t;
      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)
+# 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)
+# 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)
+# 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)
+# 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)
+# 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)
+# 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)
+# 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)
+# define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
 
-/* If this bit is set, then `{...}' defines an interval, and \{ and \}
+/* 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 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)
+# 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)
+# 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)
+# 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)
+# 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)
+# 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)
+# 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)
+# 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.
@@ -157,16 +179,30 @@ typedef unsigned long int reg_syntax_t;
    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)
+# 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)
+# 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)
+# define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
+
+/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only
+   for ^, because it is difficult to scan the regex backwards to find
+   whether ^ should be special.  */
+# define RE_CARET_ANCHORS_HERE (RE_ICASE << 1)
+
+/* If this bit is set, then \{ cannot be first in a regex or
+   immediately after an alternation, open-group or \} operator.  */
+# define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1)
+
+/* If this bit is set, then no_sub will be set to 1 during
+   re_compile_pattern.  */
+# define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1)
+#endif
 
 /* This global variable defines the particular regexp syntax to use (for
    some interfaces).  When a regexp is compiled, the syntax used is
@@ -174,63 +210,64 @@ typedef unsigned long int reg_syntax_t;
    already-compiled regexps.  */
 extern reg_syntax_t re_syntax_options;
 \f
+#ifdef __USE_GNU
 /* 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_EMACS 0
 
-#define RE_SYNTAX_AWK                                                  \
+# 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_CHAR_CLASSES                                                   \
    | 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_GNU_AWK                                             \
+  ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS            \
+    | RE_INVALID_INTERVAL_ORD)                                         \
+   & ~(RE_DOT_NOT_NULL | RE_CONTEXT_INDEP_OPS                          \
+      | RE_CONTEXT_INVALID_OPS ))
 
-#define RE_SYNTAX_POSIX_AWK                                            \
+# define RE_SYNTAX_POSIX_AWK                                           \
   (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS             \
-   | RE_INTERVALS          | RE_NO_GNU_OPS)
+   | RE_INTERVALS          | RE_NO_GNU_OPS                             \
+   | RE_INVALID_INTERVAL_ORD)
 
-#define RE_SYNTAX_GREP                                                 \
-  (RE_BK_PLUS_QM              | RE_CHAR_CLASSES                                \
-   | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS                           \
-   | RE_NEWLINE_ALT)
+# define RE_SYNTAX_GREP                                                        \
+  ((RE_SYNTAX_POSIX_BASIC | RE_NEWLINE_ALT)                            \
+   & ~(RE_CONTEXT_INVALID_DUP | RE_DOT_NOT_NULL))
 
-#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_EGREP                                               \
+  ((RE_SYNTAX_POSIX_EXTENDED | RE_INVALID_INTERVAL_ORD | RE_NEWLINE_ALT) \
+   & ~(RE_CONTEXT_INVALID_OPS | RE_DOT_NOT_NULL))
 
-#define RE_SYNTAX_POSIX_EGREP                                          \
-  (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES                    \
-   | RE_INVALID_INTERVAL_ORD)
+/* POSIX grep -E behavior is no longer incompatible with GNU.  */
+# define RE_SYNTAX_POSIX_EGREP                                         \
+  RE_SYNTAX_EGREP
 
 /* P1003.2/D11.2, section 4.20.7.1, lines 5078ff.  */
-#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
+# define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
 
-#define RE_SYNTAX_SED 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                                                \
+# 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)
+# define RE_SYNTAX_POSIX_BASIC                                         \
+  (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP)
 
 /* 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                                  \
+# define RE_SYNTAX_POSIX_MINIMAL_BASIC                                 \
   (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
 
-#define RE_SYNTAX_POSIX_EXTENDED                                       \
+# 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                            \
@@ -238,24 +275,35 @@ extern reg_syntax_t re_syntax_options;
 
 /* 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                               \
+# 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
+
+/* Maximum number of duplicates an interval can allow.  POSIX-conforming
+   systems might define this in <limits.h>, but we want our
    value, so remove any previous define.  */
-#ifdef RE_DUP_MAX
-# undef RE_DUP_MAX
+# ifdef _REGEX_INCLUDE_LIMITS_H
+#  include <limits.h>
+# endif
+# ifdef RE_DUP_MAX
+#  undef RE_DUP_MAX
+# endif
+
+/* RE_DUP_MAX is 2**15 - 1 because an earlier implementation stored
+   the counter as a 2-byte signed integer.  This is no longer true, so
+   RE_DUP_MAX could be increased to (INT_MAX / 10 - 1), or to
+   ((SIZE_MAX - 9) / 10) if _REGEX_LARGE_OFFSETS is defined.
+   However, there would be a huge performance problem if someone
+   actually used a pattern like a\{214748363\}, so RE_DUP_MAX retains
+   its historical value.  */
+# define RE_DUP_MAX (0x7fff)
 #endif
-/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows.  */
-#define RE_DUP_MAX (0x7fff)
 
 
-/* POSIX `cflags' bits (i.e., information for `regcomp').  */
+/* 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.  */
@@ -263,19 +311,19 @@ extern reg_syntax_t re_syntax_options;
 
 /* If this bit is set, then ignore case when matching.
    If not set, then case is significant.  */
-#define REG_ICASE (REG_EXTENDED << 1)
+#define REG_ICASE (1 << 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)
+#define REG_NEWLINE (1 << 2)
 
 /* 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)
+#define REG_NOSUB (1 << 3)
 
 
-/* POSIX `eflags' bits (i.e., information for regexec).  */
+/* 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
@@ -287,143 +335,184 @@ extern reg_syntax_t re_syntax_options;
 /* Like REG_NOTBOL, except for the end-of-line.  */
 #define REG_NOTEOL (1 << 1)
 
+/* Use PMATCH[0] to delimit the start and end of the search in the
+   buffer.  */
+#define REG_STARTEND (1 << 2)
+
 
 /* If any error codes are removed, changed, or added, update the
-   `re_error_msg' table in regex.c.  */
+   '__re_error_msgid' table in regcomp.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).  */
+  _REG_ENOSYS = -1,    /* This will never happen for this implementation.  */
+  _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.  */
+  _REG_BADPAT,         /* Invalid pattern.  */
+  _REG_ECOLLATE,       /* Invalid collating element.  */
+  _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_EEND,           /* Premature end.  */
+  _REG_ESIZE,          /* Too large (e.g., repeat count too large).  */
+  _REG_ERPAREN         /* Unmatched ) or \); not returned from regcomp.  */
 } reg_errcode_t;
+
+#if defined _XOPEN_SOURCE || defined __USE_XOPEN2K
+# define REG_ENOSYS    _REG_ENOSYS
+#endif
+#define REG_NOERROR    _REG_NOERROR
+#define REG_NOMATCH    _REG_NOMATCH
+#define REG_BADPAT     _REG_BADPAT
+#define REG_ECOLLATE   _REG_ECOLLATE
+#define REG_ECTYPE     _REG_ECTYPE
+#define REG_EESCAPE    _REG_EESCAPE
+#define REG_ESUBREG    _REG_ESUBREG
+#define REG_EBRACK     _REG_EBRACK
+#define REG_EPAREN     _REG_EPAREN
+#define REG_EBRACE     _REG_EBRACE
+#define REG_BADBR      _REG_BADBR
+#define REG_ERANGE     _REG_ERANGE
+#define REG_ESPACE     _REG_ESPACE
+#define REG_BADRPT     _REG_BADRPT
+#define REG_EEND       _REG_EEND
+#define REG_ESIZE      _REG_ESIZE
+#define REG_ERPAREN    _REG_ERPAREN
 \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.  */
+   the pattern compiler, the fields 'buffer', 'allocated', 'fastmap',
+   and 'translate' can be set.  After the pattern has been compiled,
+   the fields 're_nsub', 'not_bol' and 'not_eol' are available.  All
+   other fields are private to the regex routines.  */
 
 #ifndef RE_TRANSLATE_TYPE
-# define RE_TRANSLATE_TYPE char *
+# define __RE_TRANSLATE_TYPE unsigned char *
+# ifdef __USE_GNU
+#  define RE_TRANSLATE_TYPE __RE_TRANSLATE_TYPE
+# endif
+#endif
+
+#ifdef __USE_GNU
+# define __REPB_PREFIX(name) name
+#else
+# define __REPB_PREFIX(name) __##name
 #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;
+  /* Space that holds the compiled pattern.  The type
+     'struct re_dfa_t' is private and is not declared here.  */
+  struct re_dfa_t *__REPB_PREFIX(buffer);
 
-       /* Number of bytes to which `buffer' points.  */
-  unsigned long int allocated;
+  /* Number of bytes to which 'buffer' points.  */
+  __re_long_size_t __REPB_PREFIX(allocated);
 
-       /* Number of bytes actually used in `buffer'.  */
-  unsigned long int used;
+  /* Number of bytes actually used in 'buffer'.  */
+  __re_long_size_t __REPB_PREFIX(used);
 
-        /* Syntax setting with which the pattern was compiled.  */
-  reg_syntax_t syntax;
+  /* Syntax setting with which the pattern was compiled.  */
+  reg_syntax_t __REPB_PREFIX(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;
+  /* 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 *__REPB_PREFIX(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;
+  /* 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 __REPB_PREFIX(translate);
 
-       /* Number of subexpressions found by the compiler.  */
+  /* 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]]] */
+  /* 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 __REPB_PREFIX(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.  */
+#ifdef __USE_GNU
+# define REGS_UNALLOCATED 0
+# define REGS_REALLOCATE 1
+# define REGS_FIXED 2
+#endif
+  unsigned __REPB_PREFIX(regs_allocated) : 2;
+
+  /* Set to zero when 're_compile_pattern' compiles a pattern; set to
+     one by 're_compile_fastmap' if it updates the fastmap.  */
+  unsigned __REPB_PREFIX(fastmap_accurate) : 1;
+
+  /* If set, 're_match_2' does not return information about
+     subexpressions.  */
+  unsigned __REPB_PREFIX(no_sub) : 1;
+
+  /* If set, a beginning-of-line anchor doesn't match at the beginning
+     of the string.  */
+  unsigned __REPB_PREFIX(not_bol) : 1;
+
+  /* Similarly for an end-of-line anchor.  */
+  unsigned __REPB_PREFIX(not_eol) : 1;
+
+  /* If true, an anchor at a newline matches.  */
+  unsigned __REPB_PREFIX(newline_anchor) : 1;
 };
 
 typedef struct re_pattern_buffer regex_t;
 \f
 /* Type for byte offsets within the string.  POSIX mandates this.  */
+#ifdef _REGEX_LARGE_OFFSETS
+/* POSIX 1003.1-2008 requires that regoff_t be at least as wide as
+   ptrdiff_t and ssize_t.  We don't know of any hosts where ptrdiff_t
+   is wider than ssize_t, so ssize_t is safe.  ptrdiff_t is not
+   visible here, so use ssize_t.  */
+typedef ssize_t regoff_t;
+#else
+/* The traditional GNU regex implementation mishandles strings longer
+   than INT_MAX.  */
 typedef int regoff_t;
+#endif
 
 
+#ifdef __USE_GNU
 /* 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;
+  __re_size_t 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
+/* 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
 #endif
 
 
 /* POSIX specification for registers.  Aside from the different names than
-   `re_registers', POSIX uses an array of structures, instead of a
+   're_registers', POSIX uses an array of structures, instead of a
    structure of arrays.  */
 typedef struct
 {
@@ -433,38 +522,27 @@ typedef struct
 \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 defined(__STDC__) || defined(__cplusplus)
-
-# define _RE_ARGS(args) args
-
-#else /* not __STDC__ */
-
-# define _RE_ARGS(args) ()
-
-#endif /* not __STDC__ */
-
+#ifdef __USE_GNU
 /* 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));
+   You can also simply assign to the 're_syntax_options' variable.  */
+extern reg_syntax_t re_set_syntax (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));
+   and syntax given by the global 're_syntax_options', into the buffer
+   BUFFER.  Return NULL if successful, and an error string if not.
+
+   To free the allocated storage, you must call 'regfree' on BUFFER.
+   Note that the translate table must either have been initialized by
+   'regcomp', with a malloc'ed value, or set to NULL before calling
+   'regfree'.  */
+extern const char *re_compile_pattern (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));
+extern int re_compile_fastmap (struct re_pattern_buffer *__buffer);
 
 
 /* Search in the string STRING (with length LENGTH) for the pattern
@@ -472,91 +550,105 @@ extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
    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));
+extern regoff_t re_search (struct re_pattern_buffer *__buffer,
+                          const char *__String, regoff_t __length,
+                          regoff_t __start, regoff_t __range,
+                          struct re_registers *__regs);
 
 
-/* Like `re_search', but search in the concatenation of STRING1 and
+/* 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));
+extern regoff_t re_search_2 (struct re_pattern_buffer *__buffer,
+                            const char *__string1, regoff_t __length1,
+                            const char *__string2, regoff_t __length2,
+                            regoff_t __start, regoff_t __range,
+                            struct re_registers *__regs,
+                            regoff_t __stop);
 
 
-/* Like `re_search', but return how many characters in STRING the regexp
+/* 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));
+extern regoff_t re_match (struct re_pattern_buffer *__buffer,
+                         const char *__String, regoff_t __length,
+                         regoff_t __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));
+/* Relates to 're_match' as 're_search_2' relates to 're_search'.  */
+extern regoff_t re_match_2 (struct re_pattern_buffer *__buffer,
+                           const char *__string1, regoff_t __length1,
+                           const char *__string2, regoff_t __length2,
+                           regoff_t __start, struct re_registers *__regs,
+                           regoff_t __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
+   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
+   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));
+extern void re_set_registers (struct re_pattern_buffer *__buffer,
+                             struct re_registers *__regs,
+                             __re_size_t __num_regs,
+                             regoff_t *__starts, regoff_t *__ends);
+#endif /* Use GNU */
 
-#if defined _REGEX_RE_COMP || defined _LIBC
+#if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_MISC)
 # ifndef _CRAY
 /* 4.2 bsd compatibility.  */
-extern char *re_comp _RE_ARGS ((const char *));
-extern int re_exec _RE_ARGS ((const char *));
+extern char *re_comp (const char *);
+extern int re_exec (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
+/* For plain 'restrict', use glibc's __restrict if defined.
+   Otherwise, GCC 2.95 and later have "__restrict"; C99 compilers have
+   "restrict", and "configure" may have defined "restrict".
+   Other compilers use __restrict, __restrict__, and _Restrict, and
+   'configure' might #define 'restrict' to those words, so pick a
+   different name.  */
+#ifndef _Restrict_
+# if defined __restrict || 2 < __GNUC__ + (95 <= __GNUC_MINOR__)
+#  define _Restrict_ __restrict
+# elif 199901L <= __STDC_VERSION__ || defined restrict
+#  define _Restrict_ restrict
+# else
+#  define _Restrict_
 # endif
 #endif
-/* gcc 3.1 and up support the [restrict] syntax.  */
-#ifndef __restrict_arr
-# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) && !defined(__WIN32__)
-#  define __restrict_arr __restrict
+/* For [restrict], use glibc's __restrict_arr if available.
+   Otherwise, GCC 3.1 (not in C++ mode) and C99 support [restrict].  */
+#ifndef _Restrict_arr_
+# ifdef __restrict_arr
+#  define _Restrict_arr_ __restrict_arr
+# elif ((199901L <= __STDC_VERSION__ || 3 < __GNUC__ + (1 <= __GNUC_MINOR__)) \
+        && !defined __GNUG__)
+#  define _Restrict_arr_ _Restrict_
 # else
-#  define __restrict_arr
+#  define _Restrict_arr_
 # endif
 #endif
 
 /* POSIX compatibility.  */
-extern int regcomp _RE_ARGS ((regex_t *__restrict __preg,
-                             const char *__restrict __pattern,
-                             int __cflags));
+extern int regcomp (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 int regexec (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 size_t regerror (int __errcode, const regex_t *_Restrict_ __preg,
+                       char *_Restrict_ __errbuf, size_t __errbuf_size);
 
-extern void regfree _RE_ARGS ((regex_t *__preg));
+extern void regfree (regex_t *__preg);
 
 
 #ifdef __cplusplus
@@ -564,11 +656,3 @@ extern void regfree _RE_ARGS ((regex_t *__preg));
 #endif /* C++ */
 
 #endif /* regex.h */
-\f
-/*
-Local variables:
-make-backup-files: t
-version-control: t
-trim-versions-without-asking: nil
-End:
-*/
index 03484d295a1f6e4af4685a96e15355b3e17af422..7f0083b918de6530f73937f152ae45a4a5427e42 100644 (file)
@@ -1,5 +1,5 @@
 /* Extended regular expression matching and search library.
-   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2002-2018 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
 
    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., 51 Franklin Street, Fifth Floor, Boston,
-   MA 02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
 
-static void re_string_construct_common (const char *str, int len,
+static void re_string_construct_common (const char *str, Idx 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,
+                                       RE_TRANSLATE_TYPE trans, bool icase,
+                                       const re_dfa_t *dfa);
+static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa,
                                          const re_node_set *nodes,
-                                         unsigned int hash);
-static re_dfastate_t *create_cd_newstate (re_dfa_t *dfa,
+                                         re_hashval_t hash);
+static re_dfastate_t *create_cd_newstate (const 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);
+                                         re_hashval_t hash);
+static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
+                                               Idx new_buf_len);
+#ifdef RE_ENABLE_I18N
+static void build_wcs_buffer (re_string_t *pstr);
+static reg_errcode_t 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 unsigned int re_string_context_at (const re_string_t *input, Idx idx,
+                                         int eflags) __attribute__ ((pure));
 \f
 /* Functions for string operation.  */
 
@@ -46,43 +45,41 @@ static unsigned int inline calc_state_hash (const re_node_set *nodes,
    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;
+__attribute_warn_unused_result__
+re_string_allocate (re_string_t *pstr, const char *str, Idx len, Idx init_len,
+                   RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa)
 {
   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;
+  Idx init_buf_len;
+
+  /* Ensure at least one character fits into the buffers.  */
+  if (init_len < dfa->mb_cur_max)
+    init_len = dfa->mb_cur_max;
+  init_buf_len = (len + 1 < init_len) ? len + 1: init_len;
+  re_string_construct_common (str, len, pstr, trans, icase, dfa);
 
   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;
+  pstr->word_char = dfa->word_char;
+  pstr->word_ops_used = dfa->word_ops_used;
+  pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
+  pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len;
+  pstr->valid_raw_len = pstr->valid_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;
+__attribute_warn_unused_result__
+re_string_construct (re_string_t *pstr, const char *str, Idx len,
+                    RE_TRANSLATE_TYPE trans, bool icase, const re_dfa_t *dfa)
 {
   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;
+  memset (pstr, '\0', sizeof (re_string_t));
+  re_string_construct_common (str, len, pstr, trans, icase, dfa);
 
   if (len > 0)
     {
@@ -90,15 +87,27 @@ re_string_construct (pstr, str, len, trans, icase)
       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->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str;
 
   if (icase)
     {
 #ifdef RE_ENABLE_I18N
-      if (MB_CUR_MAX > 1)
-       build_wcs_upper_buffer (pstr);
+      if (dfa->mb_cur_max > 1)
+       {
+         while (1)
+           {
+             ret = build_wcs_upper_buffer (pstr);
+             if (BE (ret != REG_NOERROR, 0))
+               return ret;
+             if (pstr->valid_raw_len >= len)
+               break;
+             if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max)
+               break;
+             ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+             if (BE (ret != REG_NOERROR, 0))
+               return ret;
+           }
+       }
       else
 #endif /* RE_ENABLE_I18N  */
        build_upper_buffer (pstr);
@@ -106,7 +115,7 @@ re_string_construct (pstr, str, len, trans, icase)
   else
     {
 #ifdef RE_ENABLE_I18N
-      if (MB_CUR_MAX > 1)
+      if (dfa->mb_cur_max > 1)
        build_wcs_buffer (pstr);
       else
 #endif /* RE_ENABLE_I18N  */
@@ -114,48 +123,52 @@ re_string_construct (pstr, str, len, trans, icase)
          if (trans != NULL)
            re_string_translate_buffer (pstr);
          else
-           pstr->valid_len = len;
+           {
+             pstr->valid_len = pstr->bufs_len;
+             pstr->valid_raw_len = pstr->bufs_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;
+__attribute_warn_unused_result__
+re_string_realloc_buffers (re_string_t *pstr, Idx new_buf_len)
 {
 #ifdef RE_ENABLE_I18N
-  if (MB_CUR_MAX > 1)
+  if (pstr->mb_cur_max > 1)
     {
-      wint_t *new_array = re_realloc (pstr->wcs, wint_t, new_buf_len);
-      if (BE (new_array == NULL, 0))
+      wint_t *new_wcs;
+
+      /* Avoid overflow in realloc.  */
+      const size_t max_object_size = MAX (sizeof (wint_t), sizeof (Idx));
+      if (BE (MIN (IDX_MAX, SIZE_MAX / max_object_size) < new_buf_len, 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))
+
+      new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len);
+      if (BE (new_wcs == NULL, 0))
        return REG_ESPACE;
-      pstr->mbs = new_array;
+      pstr->wcs = new_wcs;
+      if (pstr->offsets != NULL)
+       {
+         Idx *new_offsets = re_realloc (pstr->offsets, Idx, new_buf_len);
+         if (BE (new_offsets == NULL, 0))
+           return REG_ESPACE;
+         pstr->offsets = new_offsets;
+       }
     }
-  if (MBS_CASE_ALLOCATED (pstr))
+#endif /* RE_ENABLE_I18N  */
+  if (pstr->mbs_allocated)
     {
-      unsigned char *new_array = re_realloc (pstr->mbs_case, unsigned char,
-                                            new_buf_len);
-      if (BE (new_array == NULL, 0))
+      unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char,
+                                          new_buf_len);
+      if (BE (new_mbs == NULL, 0))
        return REG_ESPACE;
-      pstr->mbs_case = new_array;
-      if (!MBS_ALLOCATED (pstr))
-       pstr->mbs = pstr->mbs_case;
+      pstr->mbs = new_mbs;
     }
   pstr->bufs_len = new_buf_len;
   return REG_NOERROR;
@@ -163,18 +176,21 @@ re_string_realloc_buffers (pstr, new_buf_len)
 
 
 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;
+re_string_construct_common (const char *str, Idx len, re_string_t *pstr,
+                           RE_TRANSLATE_TYPE trans, bool icase,
+                           const re_dfa_t *dfa)
 {
-  memset (pstr, '\0', sizeof (re_string_t));
   pstr->raw_mbs = (const unsigned char *) str;
   pstr->len = len;
+  pstr->raw_len = len;
   pstr->trans = trans;
-  pstr->icase = icase ? 1 : 0;
+  pstr->icase = icase;
+  pstr->mbs_allocated = (trans != NULL || icase);
+  pstr->mb_cur_max = dfa->mb_cur_max;
+  pstr->is_utf8 = dfa->is_utf8;
+  pstr->map_notascii = dfa->map_notascii;
+  pstr->stop = pstr->len;
+  pstr->raw_stop = pstr->stop;
 }
 
 #ifdef RE_ENABLE_I18N
@@ -191,41 +207,60 @@ re_string_construct_common (str, len, pstr, trans, icase)
    built and starts from PSTR->VALID_LEN.  */
 
 static void
-build_wcs_buffer (pstr)
-     re_string_t *pstr;
+build_wcs_buffer (re_string_t *pstr)
 {
+#ifdef _LIBC
+  unsigned char buf[MB_LEN_MAX];
+  assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+  unsigned char buf[64];
+#endif
   mbstate_t prev_st;
-  int byte_idx, end_idx, mbclen, remain_len;
+  Idx byte_idx, end_idx, remain_len;
+  size_t mbclen;
+
   /* 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;
+  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;
+      const char *p;
+
       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))
+      /* Apply the translation if we need.  */
+      if (BE (pstr->trans != NULL, 0))
        {
-         /* The buffer doesn't have enough space, finish to build.  */
-         pstr->cur_state = prev_st;
-         break;
+         int i, ch;
+
+         for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+           {
+             ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i];
+             buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch];
+           }
+         p = (const char *) buf;
        }
-      else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0))
+      else
+       p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx;
+      mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+      if (BE (mbclen == (size_t) -1 || mbclen == 0
+             || (mbclen == (size_t) -2 && pstr->bufs_len >= pstr->len), 0))
        {
          /* We treat these cases as a singlebyte character.  */
          mbclen = 1;
          wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+         if (BE (pstr->trans != NULL, 0))
+           wc = pstr->trans[wc];
          pstr->cur_state = prev_st;
        }
-
-      /* Apply the translateion if we need.  */
-      if (pstr->trans != NULL && mbclen == 1)
+      else if (BE (mbclen == (size_t) -2, 0))
        {
-         int ch = pstr->trans[pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]];
-         pstr->mbs_case[byte_idx] = ch;
+         /* The buffer doesn't have enough space, finish to build.  */
+         pstr->cur_state = prev_st;
+         break;
        }
+
       /* Write wide character and padding.  */
       pstr->wcs[byte_idx++] = wc;
       /* Write paddings.  */
@@ -233,96 +268,262 @@ build_wcs_buffer (pstr)
        pstr->wcs[byte_idx++] = WEOF;
     }
   pstr->valid_len = byte_idx;
+  pstr->valid_raw_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;
+static reg_errcode_t
+__attribute_warn_unused_result__
+build_wcs_upper_buffer (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;)
+  Idx src_idx, byte_idx, end_idx, remain_len;
+  size_t mbclen;
+#ifdef _LIBC
+  char buf[MB_LEN_MAX];
+  assert (MB_LEN_MAX >= pstr->mb_cur_max);
+#else
+  char buf[64];
+#endif
+
+  byte_idx = pstr->valid_len;
+  end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len;
+
+  /* The following optimization assumes that ASCII characters can be
+     mapped to wide characters with a simple cast.  */
+  if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed)
     {
-      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)
+      while (byte_idx < end_idx)
        {
-         /* 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)
+         wchar_t wc;
+
+         if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx])
+             && mbsinit (&pstr->cur_state))
            {
-             ch = pstr->trans[ch];
-             pstr->mbs_case[byte_idx] = ch;
+             /* In case of a singlebyte character.  */
+             pstr->mbs[byte_idx]
+               = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]);
+             /* The next step uses the assumption that wchar_t is encoded
+                ASCII-safe: all ASCII values can be converted like this.  */
+             pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx];
+             ++byte_idx;
+             continue;
+           }
+
+         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, 1))
+           {
+             wchar_t wcu = __towupper (wc);
+             if (wcu != wc)
+               {
+                 size_t mbcdlen;
+
+                 mbcdlen = __wcrtomb (buf, wcu, &prev_st);
+                 if (BE (mbclen == mbcdlen, 1))
+                   memcpy (pstr->mbs + byte_idx, buf, mbclen);
+                 else
+                   {
+                     src_idx = byte_idx;
+                     goto offsets_needed;
+                   }
+               }
+             else
+               memcpy (pstr->mbs + byte_idx,
+                       pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen);
+             pstr->wcs[byte_idx++] = wcu;
+             /* Write paddings.  */
+             for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+               pstr->wcs[byte_idx++] = WEOF;
+           }
+         else if (mbclen == (size_t) -1 || mbclen == 0
+                  || (mbclen == (size_t) -2 && pstr->bufs_len >= pstr->len))
+           {
+             /* It is an invalid character, an incomplete character
+                at the end of the string, or '\0'.  Just use the byte.  */
+             int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx];
+             pstr->mbs[byte_idx] = ch;
+             /* And also cast it to wide char.  */
+             pstr->wcs[byte_idx++] = (wchar_t) ch;
+             if (BE (mbclen == (size_t) -1, 0))
+               pstr->cur_state = prev_st;
            }
-         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;
+           {
+             /* The buffer doesn't have enough space, finish to build.  */
+             pstr->cur_state = prev_st;
+             break;
+           }
        }
+      pstr->valid_len = byte_idx;
+      pstr->valid_raw_len = byte_idx;
+      return REG_NOERROR;
     }
+  else
+    for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;)
+      {
+       wchar_t wc;
+       const char *p;
+      offsets_needed:
+       remain_len = end_idx - byte_idx;
+       prev_st = pstr->cur_state;
+       if (BE (pstr->trans != NULL, 0))
+         {
+           int i, ch;
+
+           for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i)
+             {
+               ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i];
+               buf[i] = pstr->trans[ch];
+             }
+           p = (const char *) buf;
+         }
+       else
+         p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx;
+       mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state);
+       if (BE (mbclen < (size_t) -2, 1))
+         {
+           wchar_t wcu = __towupper (wc);
+           if (wcu != wc)
+             {
+               size_t mbcdlen;
+
+               mbcdlen = __wcrtomb ((char *) buf, wcu, &prev_st);
+               if (BE (mbclen == mbcdlen, 1))
+                 memcpy (pstr->mbs + byte_idx, buf, mbclen);
+               else if (mbcdlen != (size_t) -1)
+                 {
+                   size_t i;
+
+                   if (byte_idx + mbcdlen > pstr->bufs_len)
+                     {
+                       pstr->cur_state = prev_st;
+                       break;
+                     }
+
+                   if (pstr->offsets == NULL)
+                     {
+                       pstr->offsets = re_malloc (Idx, pstr->bufs_len);
+
+                       if (pstr->offsets == NULL)
+                         return REG_ESPACE;
+                     }
+                   if (!pstr->offsets_needed)
+                     {
+                       for (i = 0; i < (size_t) byte_idx; ++i)
+                         pstr->offsets[i] = i;
+                       pstr->offsets_needed = 1;
+                     }
+
+                   memcpy (pstr->mbs + byte_idx, buf, mbcdlen);
+                   pstr->wcs[byte_idx] = wcu;
+                   pstr->offsets[byte_idx] = src_idx;
+                   for (i = 1; i < mbcdlen; ++i)
+                     {
+                       pstr->offsets[byte_idx + i]
+                         = src_idx + (i < mbclen ? i : mbclen - 1);
+                       pstr->wcs[byte_idx + i] = WEOF;
+                     }
+                   pstr->len += mbcdlen - mbclen;
+                   if (pstr->raw_stop > src_idx)
+                     pstr->stop += mbcdlen - mbclen;
+                   end_idx = (pstr->bufs_len > pstr->len)
+                             ? pstr->len : pstr->bufs_len;
+                   byte_idx += mbcdlen;
+                   src_idx += mbclen;
+                   continue;
+                 }
+               else
+                 memcpy (pstr->mbs + byte_idx, p, mbclen);
+             }
+           else
+             memcpy (pstr->mbs + byte_idx, p, mbclen);
+
+           if (BE (pstr->offsets_needed != 0, 0))
+             {
+               size_t i;
+               for (i = 0; i < mbclen; ++i)
+                 pstr->offsets[byte_idx + i] = src_idx + i;
+             }
+           src_idx += mbclen;
+
+           pstr->wcs[byte_idx++] = wcu;
+           /* Write paddings.  */
+           for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;)
+             pstr->wcs[byte_idx++] = WEOF;
+         }
+       else if (mbclen == (size_t) -1 || mbclen == 0
+                || (mbclen == (size_t) -2 && pstr->bufs_len >= pstr->len))
+         {
+           /* It is an invalid character or '\0'.  Just use the byte.  */
+           int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx];
+
+           if (BE (pstr->trans != NULL, 0))
+             ch = pstr->trans [ch];
+           pstr->mbs[byte_idx] = ch;
+
+           if (BE (pstr->offsets_needed != 0, 0))
+             pstr->offsets[byte_idx] = src_idx;
+           ++src_idx;
+
+           /* And also cast it to wide char.  */
+           pstr->wcs[byte_idx++] = (wchar_t) ch;
+           if (BE (mbclen == (size_t) -1, 0))
+             pstr->cur_state = prev_st;
+         }
+       else
+         {
+           /* The buffer doesn't have enough space, finish to build.  */
+           pstr->cur_state = prev_st;
+           break;
+         }
+      }
   pstr->valid_len = byte_idx;
+  pstr->valid_raw_len = src_idx;
+  return REG_NOERROR;
 }
 
 /* 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;
+static Idx
+re_string_skip_chars (re_string_t *pstr, Idx new_raw_idx, wint_t *last_wc)
 {
   mbstate_t prev_st;
-  int rawbuf_idx, mbclen;
-  wchar_t wc = 0;
+  Idx rawbuf_idx;
+  size_t mbclen;
+  wint_t wc = WEOF;
 
   /* Skip the characters which are not necessary to check.  */
-  for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_len;
+  for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len;
        rawbuf_idx < new_raw_idx;)
     {
-      int remain_len;
-      remain_len = pstr->len - rawbuf_idx;
+      wchar_t wc2;
+      Idx remain_len = pstr->raw_len - rawbuf_idx;
       prev_st = pstr->cur_state;
-      mbclen = mbrtowc (&wc, (const char *) pstr->raw_mbs + rawbuf_idx,
-                       remain_len, &pstr->cur_state);
+      mbclen = __mbrtowc (&wc2, (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.  */
+         /* We treat these cases as a single byte character.  */
+         if (mbclen == 0 || remain_len == 0)
+           wc = L'\0';
+         else
+           wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx);
          mbclen = 1;
          pstr->cur_state = prev_st;
        }
+      else
+       wc = wc2;
       /* Then proceed the next character.  */
       rawbuf_idx += mbclen;
     }
-  *last_wc = (wint_t) wc;
+  *last_wc = wc;
   return rawbuf_idx;
 }
 #endif /* RE_ENABLE_I18N  */
@@ -331,135 +532,273 @@ re_string_skip_chars (pstr, new_raw_idx, last_wc)
    This function is used in case of REG_ICASE.  */
 
 static void
-build_upper_buffer (pstr)
-     re_string_t *pstr;
+build_upper_buffer (re_string_t *pstr)
 {
-  int char_idx, end_idx;
+  Idx 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;
+      if (BE (pstr->trans != NULL, 0))
+       ch = pstr->trans[ch];
+      pstr->mbs[char_idx] = toupper (ch);
     }
   pstr->valid_len = char_idx;
+  pstr->valid_raw_len = char_idx;
 }
 
 /* Apply TRANS to the buffer in PSTR.  */
 
 static void
-re_string_translate_buffer (pstr)
-     re_string_t *pstr;
+re_string_translate_buffer (re_string_t *pstr)
 {
-  int buf_idx, end_idx;
+  Idx 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->mbs[buf_idx] = pstr->trans[ch];
     }
 
   pstr->valid_len = buf_idx;
+  pstr->valid_raw_len = buf_idx;
 }
 
 /* This function re-construct the buffers.
-   Concretely, convert to wide character in case of MB_CUR_MAX > 1,
+   Concretely, convert to wide character in case of pstr->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;
+__attribute_warn_unused_result__
+re_string_reconstruct (re_string_t *pstr, Idx idx, int eflags)
 {
-  int offset = idx - pstr->raw_mbs_idx;
-  if (offset < 0)
+  Idx offset;
+
+  if (BE (pstr->raw_mbs_idx <= idx, 0))
+    offset = idx - pstr->raw_mbs_idx;
+  else
     {
       /* Reset buffer.  */
 #ifdef RE_ENABLE_I18N
-      if (MB_CUR_MAX > 1)
+      if (pstr->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->len = pstr->raw_len;
+      pstr->stop = pstr->raw_stop;
+      pstr->valid_len = 0;
+      pstr->raw_mbs_idx = 0;
+      pstr->valid_raw_len = 0;
+      pstr->offsets_needed = 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))
+      if (!pstr->mbs_allocated)
        pstr->mbs = (unsigned char *) pstr->raw_mbs;
       offset = idx;
     }
 
-  if (offset != 0)
+  if (BE (offset != 0, 1))
     {
-      /* Are the characters which are already checked remain?  */
-      if (offset < pstr->valid_len)
+      /* Should the already checked characters be kept?  */
+      if (BE (offset < pstr->valid_raw_len, 1))
        {
          /* 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));
+         if (BE (pstr->offsets_needed, 0))
+           {
+             Idx low = 0, high = pstr->valid_len, mid;
+             do
+               {
+                 mid = (high + low) / 2;
+                 if (pstr->offsets[mid] > offset)
+                   high = mid;
+                 else if (pstr->offsets[mid] < offset)
+                   low = mid + 1;
+                 else
+                   break;
+               }
+             while (low < high);
+             if (pstr->offsets[mid] < offset)
+               ++mid;
+             pstr->tip_context = re_string_context_at (pstr, mid - 1,
+                                                       eflags);
+             /* This can be quite complicated, so handle specially
+                only the common and easy case where the character with
+                different length representation of lower and upper
+                case is present at or after offset.  */
+             if (pstr->valid_len > offset
+                 && mid == offset && pstr->offsets[mid] == offset)
+               {
+                 memmove (pstr->wcs, pstr->wcs + offset,
+                          (pstr->valid_len - offset) * sizeof (wint_t));
+                 memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset);
+                 pstr->valid_len -= offset;
+                 pstr->valid_raw_len -= offset;
+                 for (low = 0; low < pstr->valid_len; low++)
+                   pstr->offsets[low] = pstr->offsets[low + offset] - offset;
+               }
+             else
+               {
+                 /* Otherwise, just find out how long the partial multibyte
+                    character at offset is and fill it with WEOF/255.  */
+                 pstr->len = pstr->raw_len - idx + offset;
+                 pstr->stop = pstr->raw_stop - idx + offset;
+                 pstr->offsets_needed = 0;
+                 while (mid > 0 && pstr->offsets[mid - 1] == offset)
+                   --mid;
+                 while (mid < pstr->valid_len)
+                   if (pstr->wcs[mid] != WEOF)
+                     break;
+                   else
+                     ++mid;
+                 if (mid == pstr->valid_len)
+                   pstr->valid_len = 0;
+                 else
+                   {
+                     pstr->valid_len = pstr->offsets[mid] - offset;
+                     if (pstr->valid_len)
+                       {
+                         for (low = 0; low < pstr->valid_len; ++low)
+                           pstr->wcs[low] = WEOF;
+                         memset (pstr->mbs, 255, pstr->valid_len);
+                       }
+                   }
+                 pstr->valid_raw_len = pstr->valid_len;
+               }
+           }
+         else
+#endif
+           {
+             pstr->tip_context = re_string_context_at (pstr, offset - 1,
+                                                       eflags);
+#ifdef RE_ENABLE_I18N
+             if (pstr->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);
+             if (BE (pstr->mbs_allocated, 0))
+               memmove (pstr->mbs, pstr->mbs + offset,
+                        pstr->valid_len - offset);
+             pstr->valid_len -= offset;
+             pstr->valid_raw_len -= offset;
+#if defined DEBUG && DEBUG
+             assert (pstr->valid_len > 0);
 #endif
+           }
        }
       else
        {
+#ifdef RE_ENABLE_I18N
          /* No, skip all characters until IDX.  */
+         Idx prev_valid_len = pstr->valid_len;
+
+         if (BE (pstr->offsets_needed, 0))
+           {
+             pstr->len = pstr->raw_len - idx + offset;
+             pstr->stop = pstr->raw_stop - idx + offset;
+             pstr->offsets_needed = 0;
+           }
+#endif
          pstr->valid_len = 0;
 #ifdef RE_ENABLE_I18N
-         if (MB_CUR_MAX > 1)
+         if (pstr->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));
+             Idx wcs_idx;
+             wint_t wc = WEOF;
+
+             if (pstr->is_utf8)
+               {
+                 const unsigned char *raw, *p, *end;
+
+                 /* Special case UTF-8.  Multi-byte chars start with any
+                    byte other than 0x80 - 0xbf.  */
+                 raw = pstr->raw_mbs + pstr->raw_mbs_idx;
+                 end = raw + (offset - pstr->mb_cur_max);
+                 if (end < pstr->raw_mbs)
+                   end = pstr->raw_mbs;
+                 p = raw + offset - 1;
+#ifdef _LIBC
+                 /* We know the wchar_t encoding is UCS4, so for the simple
+                    case, ASCII characters, skip the conversion step.  */
+                 if (isascii (*p) && BE (pstr->trans == NULL, 1))
+                   {
+                     memset (&pstr->cur_state, '\0', sizeof (mbstate_t));
+                     /* pstr->valid_len = 0; */
+                     wc = (wchar_t) *p;
+                   }
+                 else
+#endif
+                   for (; p >= end; --p)
+                     if ((*p & 0xc0) != 0x80)
+                       {
+                         mbstate_t cur_state;
+                         wchar_t wc2;
+                         Idx mlen = raw + pstr->len - p;
+                         unsigned char buf[6];
+                         size_t mbclen;
+
+                         const unsigned char *pp = p;
+                         if (BE (pstr->trans != NULL, 0))
+                           {
+                             int i = mlen < 6 ? mlen : 6;
+                             while (--i >= 0)
+                               buf[i] = pstr->trans[p[i]];
+                             pp = buf;
+                           }
+                         /* XXX Don't use mbrtowc, we know which conversion
+                            to use (UTF-8 -> UCS4).  */
+                         memset (&cur_state, 0, sizeof (cur_state));
+                         mbclen = __mbrtowc (&wc2, (const char *) pp, mlen,
+                                             &cur_state);
+                         if (raw + offset - p <= mbclen
+                             && mbclen < (size_t) -2)
+                           {
+                             memset (&pstr->cur_state, '\0',
+                                     sizeof (mbstate_t));
+                             pstr->valid_len = mbclen - (raw + offset - p);
+                             wc = wc2;
+                           }
+                         break;
+                       }
+               }
+
+             if (wc == WEOF)
+               pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx;
+             if (wc == WEOF)
+               pstr->tip_context
+                 = re_string_context_at (pstr, prev_valid_len - 1, eflags);
+             else
+               pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0)
+                                     && IS_WIDE_WORD_CHAR (wc))
+                                    ? CONTEXT_WORD
+                                    : ((IS_WIDE_NEWLINE (wc)
+                                        && pstr->newline_anchor)
+                                       ? CONTEXT_NEWLINE : 0));
+             if (BE (pstr->valid_len, 0))
+               {
+                 for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx)
+                   pstr->wcs[wcs_idx] = WEOF;
+                 if (pstr->mbs_allocated)
+                   memset (pstr->mbs, 255, pstr->valid_len);
+               }
+             pstr->valid_raw_len = pstr->valid_len;
            }
          else
 #endif /* RE_ENABLE_I18N */
            {
              int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1];
+             pstr->valid_raw_len = 0;
              if (pstr->trans)
                c = pstr->trans[c];
-             pstr->tip_context = (IS_WORD_CHAR (c) ? CONTEXT_WORD
-                                  : ((newline && IS_NEWLINE (c))
+             pstr->tip_context = (bitset_contain (pstr->word_char, c)
+                                  ? CONTEXT_WORD
+                                  : ((IS_NEWLINE (c) && pstr->newline_anchor)
                                      ? 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;
-       }
+      if (!BE (pstr->mbs_allocated, 0))
+       pstr->mbs += offset;
     }
   pstr->raw_mbs_idx = idx;
   pstr->len -= offset;
@@ -467,65 +806,139 @@ re_string_reconstruct (pstr, idx, eflags, newline)
 
   /* Then build the buffers.  */
 #ifdef RE_ENABLE_I18N
-  if (MB_CUR_MAX > 1)
+  if (pstr->mb_cur_max > 1)
     {
       if (pstr->icase)
-       build_wcs_upper_buffer (pstr);
+       {
+         reg_errcode_t ret = build_wcs_upper_buffer (pstr);
+         if (BE (ret != REG_NOERROR, 0))
+           return ret;
+       }
       else
        build_wcs_buffer (pstr);
     }
   else
 #endif /* RE_ENABLE_I18N */
+    if (BE (pstr->mbs_allocated, 0))
+      {
+       if (pstr->icase)
+         build_upper_buffer (pstr);
+       else if (pstr->trans != NULL)
+         re_string_translate_buffer (pstr);
+      }
+    else
+      pstr->valid_len = pstr->len;
+
+  pstr->cur_idx = 0;
+  return REG_NOERROR;
+}
+
+static unsigned char
+__attribute__ ((pure))
+re_string_peek_byte_case (const re_string_t *pstr, Idx idx)
+{
+  int ch;
+  Idx off;
+
+  /* Handle the common (easiest) cases first.  */
+  if (BE (!pstr->mbs_allocated, 1))
+    return re_string_peek_byte (pstr, idx);
+
+#ifdef RE_ENABLE_I18N
+  if (pstr->mb_cur_max > 1
+      && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx))
+    return re_string_peek_byte (pstr, idx);
+#endif
+
+  off = pstr->cur_idx + idx;
+#ifdef RE_ENABLE_I18N
+  if (pstr->offsets_needed)
+    off = pstr->offsets[off];
+#endif
+
+  ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+#ifdef RE_ENABLE_I18N
+  /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I
+     this function returns CAPITAL LETTER I instead of first byte of
+     DOTLESS SMALL LETTER I.  The latter would confuse the parser,
+     since peek_byte_case doesn't advance cur_idx in any way.  */
+  if (pstr->offsets_needed && !isascii (ch))
+    return re_string_peek_byte (pstr, idx);
+#endif
+
+  return ch;
+}
+
+static unsigned char
+re_string_fetch_byte_case (re_string_t *pstr)
+{
+  if (BE (!pstr->mbs_allocated, 1))
+    return re_string_fetch_byte (pstr);
+
+#ifdef RE_ENABLE_I18N
+  if (pstr->offsets_needed)
     {
-      if (pstr->icase)
-       build_upper_buffer (pstr);
-      else if (pstr->trans != NULL)
-       re_string_translate_buffer (pstr);
+      Idx off;
+      int ch;
+
+      /* For tr_TR.UTF-8 [[:islower:]] there is
+        [[: CAPITAL LETTER I WITH DOT lower:]] in mbs.  Skip
+        in that case the whole multi-byte character and return
+        the original letter.  On the other side, with
+        [[: DOTLESS SMALL LETTER I return [[:I, as doing
+        anything else would complicate things too much.  */
+
+      if (!re_string_first_byte (pstr, pstr->cur_idx))
+       return re_string_fetch_byte (pstr);
+
+      off = pstr->offsets[pstr->cur_idx];
+      ch = pstr->raw_mbs[pstr->raw_mbs_idx + off];
+
+      if (! isascii (ch))
+       return re_string_fetch_byte (pstr);
+
+      re_string_skip_bytes (pstr,
+                           re_string_char_size_at (pstr, pstr->cur_idx));
+      return ch;
     }
-  pstr->cur_idx = 0;
+#endif
 
-  return REG_NOERROR;
+  return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++];
 }
 
 static void
-re_string_destruct (pstr)
-     re_string_t *pstr;
+re_string_destruct (re_string_t *pstr)
 {
 #ifdef RE_ENABLE_I18N
   re_free (pstr->wcs);
+  re_free (pstr->offsets);
 #endif /* RE_ENABLE_I18N  */
-  if (MBS_ALLOCATED (pstr))
+  if (pstr->mbs_allocated)
     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;
+re_string_context_at (const re_string_t *input, Idx idx, int eflags)
 {
   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);
-    }
+  if (BE (idx < 0, 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;
+  if (BE (idx == input->len, 0))
+    return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF
+           : CONTEXT_NEWLINE | CONTEXT_ENDBUF);
 #ifdef RE_ENABLE_I18N
-  if (MB_CUR_MAX > 1)
+  if (input->mb_cur_max > 1)
     {
       wint_t wc;
-      int wc_idx = idx;
+      Idx wc_idx = idx;
       while(input->wcs[wc_idx] == WEOF)
        {
-#ifdef DEBUG
+#if defined DEBUG && DEBUG
          /* It must not happen.  */
          assert (wc_idx >= 0);
 #endif
@@ -534,43 +947,42 @@ re_string_context_at (input, idx, eflags, newline_anchor)
            return input->tip_context;
        }
       wc = input->wcs[wc_idx];
-      if (IS_WIDE_WORD_CHAR (wc))
+      if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc))
        return CONTEXT_WORD;
-      return (newline_anchor && IS_WIDE_NEWLINE (wc)) ? CONTEXT_NEWLINE : 0;
+      return (IS_WIDE_NEWLINE (wc) && input->newline_anchor
+             ? CONTEXT_NEWLINE : 0);
     }
   else
 #endif
     {
       c = re_string_byte_at (input, idx);
-      if (IS_WORD_CHAR (c))
+      if (bitset_contain (input->word_char, c))
        return CONTEXT_WORD;
-      return (newline_anchor && IS_NEWLINE (c)) ? CONTEXT_NEWLINE : 0;
+      return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0;
     }
 }
 \f
 /* Functions for set operation.  */
 
 static reg_errcode_t
-re_node_set_alloc (set, size)
-     re_node_set *set;
-     int size;
+__attribute_warn_unused_result__
+re_node_set_alloc (re_node_set *set, Idx size)
 {
   set->alloc = size;
   set->nelem = 0;
-  set->elems = re_malloc (int, size);
-  if (BE (set->elems == NULL, 0))
+  set->elems = re_malloc (Idx, size);
+  if (BE (set->elems == NULL, 0) && (MALLOC_0_IS_NONNULL || size != 0))
     return REG_ESPACE;
   return REG_NOERROR;
 }
 
 static reg_errcode_t
-re_node_set_init_1 (set, elem)
-     re_node_set *set;
-     int elem;
+__attribute_warn_unused_result__
+re_node_set_init_1 (re_node_set *set, Idx elem)
 {
   set->alloc = 1;
   set->nelem = 1;
-  set->elems = re_malloc (int, 1);
+  set->elems = re_malloc (Idx, 1);
   if (BE (set->elems == NULL, 0))
     {
       set->alloc = set->nelem = 0;
@@ -581,12 +993,11 @@ re_node_set_init_1 (set, elem)
 }
 
 static reg_errcode_t
-re_node_set_init_2 (set, elem1, elem2)
-     re_node_set *set;
-     int elem1, elem2;
+__attribute_warn_unused_result__
+re_node_set_init_2 (re_node_set *set, Idx elem1, Idx elem2)
 {
   set->alloc = 2;
-  set->elems = re_malloc (int, 2);
+  set->elems = re_malloc (Idx, 2);
   if (BE (set->elems == NULL, 0))
     return REG_ESPACE;
   if (elem1 == elem2)
@@ -612,21 +1023,20 @@ re_node_set_init_2 (set, elem1, elem2)
 }
 
 static reg_errcode_t
-re_node_set_init_copy (dest, src)
-     re_node_set *dest;
-     const re_node_set *src;
+__attribute_warn_unused_result__
+re_node_set_init_copy (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);
+      dest->elems = re_malloc (Idx, 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));
+      memcpy (dest->elems, src->elems, src->nelem * sizeof (Idx));
     }
   else
     re_node_set_init_empty (dest);
@@ -638,47 +1048,90 @@ re_node_set_init_copy (dest, src)
    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;
+__attribute_warn_unused_result__
+re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1,
+                          const re_node_set *src2)
 {
-  int i1, i2, id;
-  if (src1->nelem > 0 && src2->nelem > 0)
+  Idx i1, i2, is, id, delta, sbase;
+  if (src1->nelem == 0 || src2->nelem == 0)
+    return REG_NOERROR;
+
+  /* We need dest->nelem + 2 * elems_in_intersection; this is a
+     conservative estimate.  */
+  if (src1->nelem + src2->nelem + dest->nelem > dest->alloc)
     {
-      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;
-       }
+      Idx new_alloc = src1->nelem + src2->nelem + dest->alloc;
+      Idx *new_elems = re_realloc (dest->elems, Idx, new_alloc);
+      if (BE (new_elems == NULL, 0))
+       return REG_ESPACE;
+      dest->elems = new_elems;
+      dest->alloc = new_alloc;
     }
-  else
-    return REG_NOERROR;
 
-  for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;)
+  /* Find the items in the intersection of SRC1 and SRC2, and copy
+     into the top of DEST those that are not already in DEST itself.  */
+  sbase = dest->nelem + src1->nelem + src2->nelem;
+  i1 = src1->nelem - 1;
+  i2 = src2->nelem - 1;
+  id = dest->nelem - 1;
+  for (;;)
     {
-      if (src1->elems[i1] > src2->elems[i2])
+      if (src1->elems[i1] == src2->elems[i2])
        {
-         ++i2;
-         continue;
+         /* Try to find the item in DEST.  Maybe we could binary search?  */
+         while (id >= 0 && dest->elems[id] > src1->elems[i1])
+           --id;
+
+         if (id < 0 || dest->elems[id] != src1->elems[i1])
+            dest->elems[--sbase] = src1->elems[i1];
+
+         if (--i1 < 0 || --i2 < 0)
+           break;
        }
-      if (src1->elems[i1] == src2->elems[i2])
+
+      /* Lower the highest of the two items.  */
+      else 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;
-           }
+         if (--i2 < 0)
+           break;
+       }
+      else
+       {
+         if (--i1 < 0)
+           break;
        }
-      ++i1;
     }
+
+  id = dest->nelem - 1;
+  is = dest->nelem + src1->nelem + src2->nelem - 1;
+  delta = is - sbase + 1;
+
+  /* Now copy.  When DELTA becomes zero, the remaining
+     DEST elements are already in place; this is more or
+     less the same loop that is in re_node_set_merge.  */
+  dest->nelem += delta;
+  if (delta > 0 && id >= 0)
+    for (;;)
+      {
+       if (dest->elems[is] > dest->elems[id])
+         {
+           /* Copy from the top.  */
+           dest->elems[id + delta--] = dest->elems[is--];
+           if (delta == 0)
+             break;
+         }
+       else
+         {
+           /* Slide from the bottom.  */
+           dest->elems[id + delta] = dest->elems[id];
+           if (--id < 0)
+             break;
+         }
+      }
+
+  /* Copy remaining SRC elements.  */
+  memcpy (dest->elems, dest->elems + sbase, delta * sizeof (Idx));
+
   return REG_NOERROR;
 }
 
@@ -686,15 +1139,15 @@ re_node_set_add_intersect (dest, src1, src2)
    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;
+__attribute_warn_unused_result__
+re_node_set_init_union (re_node_set *dest, const re_node_set *src1,
+                       const re_node_set *src2)
 {
-  int i1, i2, id;
+  Idx 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);
+      dest->elems = re_malloc (Idx, dest->alloc);
       if (BE (dest->elems == NULL, 0))
        return REG_ESPACE;
     }
@@ -722,13 +1175,13 @@ re_node_set_init_union (dest, src1, src2)
   if (i1 < src1->nelem)
     {
       memcpy (dest->elems + id, src1->elems + i1,
-            (src1->nelem - i1) * sizeof (int));
+            (src1->nelem - i1) * sizeof (Idx));
       id += src1->nelem - i1;
     }
   else if (i2 < src2->nelem)
     {
       memcpy (dest->elems + id, src2->elems + i2,
-            (src2->nelem - i2) * sizeof (int));
+            (src2->nelem - i2) * sizeof (Idx));
       id += src2->nelem - i2;
     }
   dest->nelem = id;
@@ -739,158 +1192,183 @@ re_node_set_init_union (dest, src1, src2)
    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;
+__attribute_warn_unused_result__
+re_node_set_merge (re_node_set *dest, const re_node_set *src)
 {
-  int si, di;
+  Idx is, id, sbase, delta;
   if (src == NULL || src->nelem == 0)
     return REG_NOERROR;
-  if (dest->alloc < src->nelem + dest->nelem)
+  if (dest->alloc < 2 * src->nelem + dest->nelem)
     {
-      int *new_buffer;
-      dest->alloc = 2 * (src->nelem + dest->alloc);
-      new_buffer = re_realloc (dest->elems, int, dest->alloc);
+      Idx new_alloc = 2 * (src->nelem + dest->alloc);
+      Idx *new_buffer = re_realloc (dest->elems, Idx, new_alloc);
       if (BE (new_buffer == NULL, 0))
        return REG_ESPACE;
       dest->elems = new_buffer;
+      dest->alloc = new_alloc;
     }
 
-  for (si = 0, di = 0 ; si < src->nelem && di < dest->nelem ;)
+  if (BE (dest->nelem == 0, 0))
     {
-      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;
+      dest->nelem = src->nelem;
+      memcpy (dest->elems, src->elems, src->nelem * sizeof (Idx));
+      return REG_NOERROR;
+    }
 
-      if (dest->elems[di] == src_elem)
-       {
-         /* Skip since, DEST already has the element.  */
-         ++di;
-         ++si;
-         continue;
-       }
+  /* Copy into the top of DEST the items of SRC that are not
+     found in DEST.  Maybe we could binary search in DEST?  */
+  for (sbase = dest->nelem + 2 * src->nelem,
+       is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; )
+    {
+      if (dest->elems[id] == src->elems[is])
+       is--, id--;
+      else if (dest->elems[id] < src->elems[is])
+       dest->elems[--sbase] = src->elems[is--];
+      else /* if (dest->elems[id] > src->elems[is]) */
+       --id;
+    }
 
-      /* 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;
+  if (is >= 0)
+    {
+      /* If DEST is exhausted, the remaining items of SRC must be unique.  */
+      sbase -= is + 1;
+      memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (Idx));
     }
 
-  /* Copy remaining src elements.  */
-  if (si < src->nelem)
+  id = dest->nelem - 1;
+  is = dest->nelem + 2 * src->nelem - 1;
+  delta = is - sbase + 1;
+  if (delta == 0)
+    return REG_NOERROR;
+
+  /* Now copy.  When DELTA becomes zero, the remaining
+     DEST elements are already in place.  */
+  dest->nelem += delta;
+  for (;;)
     {
-      memcpy (dest->elems + di, src->elems + si,
-             sizeof (int) * (src->nelem - si));
-      dest->nelem += src->nelem - si;
+      if (dest->elems[is] > dest->elems[id])
+       {
+         /* Copy from the top.  */
+         dest->elems[id + delta--] = dest->elems[is--];
+         if (delta == 0)
+           break;
+       }
+      else
+       {
+         /* Slide from the bottom.  */
+         dest->elems[id + delta] = dest->elems[id];
+         if (--id < 0)
+           {
+             /* Copy remaining SRC elements.  */
+             memcpy (dest->elems, dest->elems + sbase,
+                     delta * sizeof (Idx));
+             break;
+           }
+       }
     }
+
   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.  */
+   SET should not already have ELEM.
+   Return true if successful.  */
 
-static int
-re_node_set_insert (set, elem)
-     re_node_set *set;
-     int elem;
+static bool
+__attribute_warn_unused_result__
+re_node_set_insert (re_node_set *set, Idx 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;
-    }
+  Idx idx;
+  /* In case the set is empty.  */
+  if (set->alloc == 0)
+    return BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1);
 
-  /* Binary search the spot we will add the new element.  */
-  idx = 0;
-  right = set->nelem;
-  while (idx < right)
+  if (BE (set->nelem, 0) == 0)
     {
-      mid = (idx + right) / 2;
-      if (set->elems[mid] < elem)
-       idx = mid + 1;
-      else
-       right = mid;
+      /* We already guaranteed above that set->alloc != 0.  */
+      set->elems[0] = elem;
+      ++set->nelem;
+      return true;
     }
 
   /* Realloc if we need.  */
-  if (set->alloc < set->nelem + 1)
+  if (set->alloc == set->nelem)
     {
-      int *new_array;
+      Idx *new_elems;
       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;
+      new_elems = re_realloc (set->elems, Idx, set->alloc);
+      if (BE (new_elems == NULL, 0))
+       return false;
+      set->elems = new_elems;
+    }
+
+  /* Move the elements which follows the new element.  Test the
+     first element separately to skip a check in the inner loop.  */
+  if (elem < set->elems[0])
+    {
+      idx = 0;
+      for (idx = set->nelem; idx > 0; idx--)
+       set->elems[idx] = set->elems[idx - 1];
     }
   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));
+      for (idx = set->nelem; set->elems[idx - 1] > elem; idx--)
+       set->elems[idx] = set->elems[idx - 1];
     }
+
   /* Insert the new element.  */
   set->elems[idx] = elem;
   ++set->nelem;
-  return 1;
+  return true;
+}
+
+/* Insert the new element ELEM to the re_node_set* SET.
+   SET should not already have any element greater than or equal to ELEM.
+   Return true if successful.  */
+
+static bool
+__attribute_warn_unused_result__
+re_node_set_insert_last (re_node_set *set, Idx elem)
+{
+  /* Realloc if we need.  */
+  if (set->alloc == set->nelem)
+    {
+      Idx *new_elems;
+      set->alloc = (set->alloc + 1) * 2;
+      new_elems = re_realloc (set->elems, Idx, set->alloc);
+      if (BE (new_elems == NULL, 0))
+       return false;
+      set->elems = new_elems;
+    }
+
+  /* Insert the new element.  */
+  set->elems[set->nelem++] = elem;
+  return true;
 }
 
 /* Compare two node sets SET1 and SET2.
-   return 1 if SET1 and SET2 are equivalent, retrun 0 otherwise.  */
+   Return true if SET1 and SET2 are equivalent.  */
 
-static int
-re_node_set_compare (set1, set2)
-     const re_node_set *set1, *set2;
+static bool
+__attribute__ ((pure))
+re_node_set_compare (const re_node_set *set1, const re_node_set *set2)
 {
-  int i;
+  Idx i;
   if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem)
-    return 0;
-  for (i = 0 ; i < set1->nelem ; i++)
+    return false;
+  for (i = set1->nelem ; --i >= 0 ; )
     if (set1->elems[i] != set2->elems[i])
-      return 0;
-  return 1;
+      return false;
+  return true;
 }
 
 /* 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;
+static Idx
+__attribute__ ((pure))
+re_node_set_contains (const re_node_set *set, Idx elem)
 {
-  int idx, right, mid;
+  __re_size_t idx, right, mid;
   if (set->nelem <= 0)
     return 0;
 
@@ -909,73 +1387,77 @@ re_node_set_contains (set, elem)
 }
 
 static void
-re_node_set_remove_at (set, idx)
-     re_node_set *set;
-     int idx;
+re_node_set_remove_at (re_node_set *set, Idx 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;
+  for (; idx < set->nelem; idx++)
+    set->elems[idx] = set->elems[idx + 1];
 }
 \f
 
 /* Add the token TOKEN to dfa->nodes, and return the index of the token.
-   Or return -1, if an error will be occured.  */
+   Or return -1 if an error occurred.  */
 
-static int
-re_dfa_add_node (dfa, token, mode)
-     re_dfa_t *dfa;
-     re_token_t token;
-     int mode;
+static Idx
+re_dfa_add_node (re_dfa_t *dfa, re_token_t token)
 {
-  if (dfa->nodes_len >= dfa->nodes_alloc)
+  if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0))
     {
-      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))
+      size_t new_nodes_alloc = dfa->nodes_alloc * 2;
+      Idx *new_nexts, *new_indices;
+      re_node_set *new_edests, *new_eclosures;
+      re_token_t *new_nodes;
+
+      /* Avoid overflows in realloc.  */
+      const size_t max_object_size = MAX (sizeof (re_token_t),
+                                         MAX (sizeof (re_node_set),
+                                              sizeof (Idx)));
+      if (BE (MIN (IDX_MAX, SIZE_MAX / max_object_size) < new_nodes_alloc, 0))
        return -1;
-      else
-       dfa->nodes = new_array;
-      if (mode)
+
+      new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc);
+      if (BE (new_nodes == NULL, 0))
+       return -1;
+      dfa->nodes = new_nodes;
+      new_nexts = re_realloc (dfa->nexts, Idx, new_nodes_alloc);
+      new_indices = re_realloc (dfa->org_indices, Idx, new_nodes_alloc);
+      new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc);
+      new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc);
+      if (BE (new_nexts == NULL || new_indices == NULL
+             || new_edests == NULL || new_eclosures == NULL, 0))
        {
-         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;
+          re_free (new_nexts);
+          re_free (new_indices);
+          re_free (new_edests);
+          re_free (new_eclosures);
+          return -1;
        }
+      dfa->nexts = new_nexts;
+      dfa->org_indices = new_indices;
+      dfa->edests = new_edests;
+      dfa->eclosures = new_eclosures;
+      dfa->nodes_alloc = new_nodes_alloc;
     }
   dfa->nodes[dfa->nodes_len] = token;
-  dfa->nodes[dfa->nodes_len].duplicated = 0;
   dfa->nodes[dfa->nodes_len].constraint = 0;
+#ifdef RE_ENABLE_I18N
+  dfa->nodes[dfa->nodes_len].accept_mb =
+    ((token.type == OP_PERIOD && dfa->mb_cur_max > 1)
+     || token.type == COMPLEX_BRACKET);
+#endif
+  dfa->nexts[dfa->nodes_len] = -1;
+  re_node_set_init_empty (dfa->edests + dfa->nodes_len);
+  re_node_set_init_empty (dfa->eclosures + dfa->nodes_len);
   return dfa->nodes_len++;
 }
 
-static unsigned int inline
-calc_state_hash (nodes, context)
-     const re_node_set *nodes;
-     unsigned int context;
+static re_hashval_t
+calc_state_hash (const re_node_set *nodes, unsigned int context)
 {
-  unsigned int hash = nodes->nelem + context;
-  int i;
+  re_hashval_t hash = nodes->nelem + context;
+  Idx i;
   for (i = 0 ; i < nodes->nelem ; i++)
     hash += nodes->elems[i];
   return hash;
@@ -990,16 +1472,19 @@ calc_state_hash (nodes, context)
         - 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;
+static re_dfastate_t *
+__attribute_warn_unused_result__
+re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa,
+                 const re_node_set *nodes)
 {
-  unsigned int hash;
+  re_hashval_t hash;
   re_dfastate_t *new_state;
   struct re_state_table_entry *spot;
-  int i;
+  Idx i;
+#if defined GCC_LINT || defined lint
+  /* Suppress bogus uninitialized-variable warnings.  */
+  *err = REG_NOERROR;
+#endif
   if (BE (nodes->nelem == 0, 0))
     {
       *err = REG_NOERROR;
@@ -1019,13 +1504,10 @@ re_acquire_state (err, dfa, nodes)
 
   /* 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;
-    }
+  if (BE (new_state == NULL, 0))
+    *err = REG_ESPACE;
+
+  return new_state;
 }
 
 /* Search for the state whose node_set is equivalent to NODES and
@@ -1038,17 +1520,19 @@ re_acquire_state (err, dfa, nodes)
         - 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;
+static re_dfastate_t *
+__attribute_warn_unused_result__
+re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa,
+                         const re_node_set *nodes, unsigned int context)
 {
-  unsigned int hash;
+  re_hashval_t hash;
   re_dfastate_t *new_state;
   struct re_state_table_entry *spot;
-  int i;
+  Idx i;
+#if defined GCC_LINT || defined lint
+  /* Suppress bogus uninitialized-variable warnings.  */
+  *err = REG_NOERROR;
+#endif
   if (nodes->nelem == 0)
     {
       *err = REG_NOERROR;
@@ -1060,106 +1544,111 @@ re_acquire_state_context (err, dfa, nodes, context)
   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)
+      if (state->hash == hash
+         && state->context == context
+         && re_node_set_compare (state->entrance_nodes, nodes))
        return state;
     }
-  /* There are no appropriate state in `dfa', create the new one.  */
+  /* 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;
-    }
+  if (BE (new_state == NULL, 0))
+    *err = REG_ESPACE;
+
+  return new_state;
 }
 
-/* Allocate memory for DFA state and initialize common properties.
-   Return the new state if succeeded, otherwise return NULL.  */
+/* Finish initialization of the new state NEWSTATE, and using its hash value
+   HASH put in the appropriate bucket of DFA's state table.  Return value
+   indicates the error code if failed.  */
 
-static re_dfastate_t *
-create_newstate_common (dfa, nodes, hash)
-     re_dfa_t *dfa;
-     const re_node_set *nodes;
-     unsigned int hash;
+static reg_errcode_t
+__attribute_warn_unused_result__
+register_state (const re_dfa_t *dfa, re_dfastate_t *newstate,
+               re_hashval_t hash)
 {
-  re_dfastate_t *newstate;
+  struct re_state_table_entry *spot;
   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);
+  Idx i;
+
+  newstate->hash = hash;
+  err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem);
   if (BE (err != REG_NOERROR, 0))
+    return REG_ESPACE;
+  for (i = 0; i < newstate->nodes.nelem; i++)
     {
-      re_free (newstate);
-      return NULL;
+      Idx elem = newstate->nodes.elems[i];
+      if (!IS_EPSILON_NODE (dfa->nodes[elem].type))
+       if (! re_node_set_insert_last (&newstate->non_eps_nodes, elem))
+         return REG_ESPACE;
     }
-  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)
+  if (BE (spot->alloc <= spot->num, 0))
     {
-      re_dfastate_t **new_array;
-      spot->alloc = 2 * spot->num + 2;
-      new_array = re_realloc (spot->array, re_dfastate_t *, spot->alloc);
+      Idx new_alloc = 2 * spot->num + 2;
+      re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *,
+                                             new_alloc);
       if (BE (new_array == NULL, 0))
        return REG_ESPACE;
       spot->array = new_array;
+      spot->alloc = new_alloc;
     }
   spot->array[spot->num++] = newstate;
   return REG_NOERROR;
 }
 
-/* Create the new state which is independ of contexts.
+static void
+free_state (re_dfastate_t *state)
+{
+  re_node_set_free (&state->non_eps_nodes);
+  re_node_set_free (&state->inveclosure);
+  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->word_trtable);
+  re_free (state->trtable);
+  re_free (state);
+}
+
+/* Create the new state which is independent 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;
+__attribute_warn_unused_result__
+create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+                   re_hashval_t hash)
 {
-  int i;
+  Idx i;
   reg_errcode_t err;
   re_dfastate_t *newstate;
-  newstate = create_newstate_common (dfa, nodes, hash);
+
+  newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1);
   if (BE (newstate == NULL, 0))
     return NULL;
-  newstate->entrance_nodes = &newstate->nodes;
+  err = re_node_set_init_copy (&newstate->nodes, nodes);
+  if (BE (err != REG_NOERROR, 0))
+    {
+      re_free (newstate);
+      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;
+#ifdef RE_ENABLE_I18N
+      newstate->accept_mb |= node->accept_mb;
+#endif /* RE_ENABLE_I18N */
 
       /* If the state has the halt node, the state is a halt state.  */
-      else if (type == END_OF_RE)
+      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)
@@ -1178,43 +1667,44 @@ create_ci_newstate (dfa, nodes, hash)
    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;
+__attribute_warn_unused_result__
+create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes,
+                   unsigned int context, re_hashval_t hash)
 {
-  int i, nctx_nodes = 0;
+  Idx i, nctx_nodes = 0;
   reg_errcode_t err;
   re_dfastate_t *newstate;
 
-  newstate = create_newstate_common (dfa, nodes, hash);
+  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->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;
+      unsigned int 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;
+      newstate->accept_mb |= node->accept_mb;
 #endif /* RE_ENABLE_I18N */
+
+      /* If the state has the halt node, the state is a halt state.  */
+      if (type == END_OF_RE)
+       newstate->halt = 1;
       else if (type == OP_BACK_REF)
        newstate->has_backref = 1;
-      else if (type == ANCHOR)
-       constraint = node->opr.ctx_type;
 
       if (constraint)
        {
@@ -1226,7 +1716,9 @@ create_cd_newstate (dfa, nodes, context, hash)
                  free_state (newstate);
                  return NULL;
                }
-             re_node_set_init_copy (newstate->entrance_nodes, nodes);
+             if (re_node_set_init_copy (newstate->entrance_nodes, nodes)
+                 != REG_NOERROR)
+               return NULL;
              nctx_nodes = 0;
              newstate->has_constraint = 1;
            }
@@ -1246,18 +1738,3 @@ create_cd_newstate (dfa, nodes, context, hash)
     }
   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);
-}
index 28d300bd4c207c39357db701bbeb25a925a4278a..809d7938d7f71eedfb6f1e7596be0206267a0fd1 100644 (file)
@@ -1,5 +1,5 @@
 /* Extended regular expression matching and search library.
-   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2002-2018 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
 
    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., 51 Franklin Street, Fifth Floor, Boston,
-   MA 02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
 
 #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>
+#ifndef _REGEX_STANDALONE
+#include <langinfo.h>
+#endif
+#include <locale.h>
+#include <wchar.h>
+#include <wctype.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+/* Properties of integers.  Although Gnulib has intprops.h, glibc does
+   without for now.  */
+#if !defined(_LIBC) && !defined(_REGEX_STANDALONE)
+# include "intprops.h"
+#else
+/* True if the real type T is signed.  */
+# define TYPE_SIGNED(t) (! ((t) 0 < (t) -1))
+
+/* True if adding the nonnegative Idx values A and B would overflow.
+   If false, set *R to A + B.  A, B, and R may be evaluated more than
+   once, or zero times.  Although this is not a full implementation of
+   Gnulib INT_ADD_WRAPV, it is good enough for glibc regex code.
+   FIXME: This implementation is a fragile stopgap, and this file would
+   be simpler and more robust if intprops.h were migrated into glibc.  */
+# define INT_ADD_WRAPV(a, b, r) \
+   (IDX_MAX - (a) < (b) ? true : (*(r) = (a) + (b), false))
+#endif
+
+#ifdef _LIBC
+# include <libc-lock.h>
+# define lock_define(name) __libc_lock_define (, name)
+# define lock_init(lock) (__libc_lock_init (lock), 0)
+# define lock_fini(lock) ((void) 0)
+# define lock_lock(lock) __libc_lock_lock (lock)
+# define lock_unlock(lock) __libc_lock_unlock (lock)
+#elif defined GNULIB_LOCK && !defined USE_UNLOCKED_IO
+# include "glthread/lock.h"
+  /* Use gl_lock_define if empty macro arguments are known to work.
+     Otherwise, fall back on less-portable substitutes.  */
+# if ((defined __GNUC__ && !defined __STRICT_ANSI__) \
+      || (defined __STDC_VERSION__ && 199901L <= __STDC_VERSION__))
+#  define lock_define(name) gl_lock_define (, name)
+# elif USE_POSIX_THREADS
+#  define lock_define(name) pthread_mutex_t name;
+# elif USE_PTH_THREADS
+#  define lock_define(name) pth_mutex_t name;
+# elif USE_SOLARIS_THREADS
+#  define lock_define(name) mutex_t name;
+# elif USE_WINDOWS_THREADS
+#  define lock_define(name) gl_lock_t name;
+# else
+#  define lock_define(name)
+# endif
+# define lock_init(lock) glthread_lock_init (&(lock))
+# define lock_fini(lock) glthread_lock_destroy (&(lock))
+# define lock_lock(lock) glthread_lock_lock (&(lock))
+# define lock_unlock(lock) glthread_lock_unlock (&(lock))
+#elif defined GNULIB_PTHREAD && !defined USE_UNLOCKED_IO
+# include <pthread.h>
+# define lock_define(name) pthread_mutex_t name;
+# define lock_init(lock) pthread_mutex_init (&(lock), 0)
+# define lock_fini(lock) pthread_mutex_destroy (&(lock))
+# define lock_lock(lock) pthread_mutex_lock (&(lock))
+# define lock_unlock(lock) pthread_mutex_unlock (&(lock))
+#else
+# define lock_define(name)
+# define lock_init(lock) 0
+# define lock_fini(lock) ((void) 0)
+  /* The 'dfa' avoids an "unused variable 'dfa'" warning from GCC.  */
+# define lock_lock(lock) ((void) dfa)
+# define lock_unlock(lock) ((void) 0)
 #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
+#if !defined _LIBC && ! (defined isblank || (HAVE_ISBLANK && HAVE_DECL_ISBLANK))
 # define isblank(ch) ((ch) == ' ' || (ch) == '\t')
 #endif
 
 # 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
+#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
 # include <libintl.h>
 # ifdef _LIBC
 #  undef gettext
 #  define gettext(msgid) \
-  INTUSE(__dcgettext) (INTUSE(_libc_intl_domainname), msgid, LC_MESSAGES)
+  __dcgettext (_libc_intl_domainname, msgid, LC_MESSAGES)
 # endif
 #else
+# undef gettext
 # define gettext(msgid) (msgid)
 #endif
 
 # 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
+#if (defined MB_CUR_MAX && HAVE_WCTYPE_H && HAVE_ISWCTYPE) || _LIBC
 # define RE_ENABLE_I18N
 #endif
 
-#if __GNUC__ >= 3
-# define BE(expr, val) __builtin_expect (expr, val)
+#ifdef __GNUC__
+#define BE(expr, val) __builtin_expect (expr, val)
 #else
-# define BE(expr, val) (expr)
-# define inline
+#define BE(expr, val) (expr)
 #endif
 
-/* Number of bits in a byte.  */
-#define BYTE_BITS 8
-/* Number of single byte character.  */
-#define SBC_MAX 256
+/* Number of ASCII characters.  */
+#define ASCII_CHARS 0x80
+
+/* Number of single byte characters.  */
+#define SBC_MAX (UCHAR_MAX + 1)
 
 #define COLL_ELEM_LEN_MAX 8
 
 
 /* Rename to standard API for using out of glibc.  */
 #ifndef _LIBC
+# undef __wctype
+# undef __iswctype
 # define __wctype wctype
+# define __iswalnum iswalnum
 # define __iswctype iswctype
+# define __towlower towlower
+# define __towupper towupper
 # define __btowc btowc
-# define __mempcpy mempcpy
+# define __mbrtowc mbrtowc
 # define __wcrtomb wcrtomb
+# define __regfree regfree
 # 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);
+#if __GNUC__ < 3 + (__GNUC_MINOR__ < 1)
+# define __attribute__(arg)
+#endif
+
+#ifndef SSIZE_MAX
+# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
+#endif
+
+/* The type of indexes into strings.  This is signed, not size_t,
+   since the API requires indexes to fit in regoff_t anyway, and using
+   signed integers makes the code a bit smaller and presumably faster.
+   The traditional GNU regex implementation uses int for indexes.
+   The POSIX-compatible implementation uses a possibly-wider type.
+   The name 'Idx' is three letters to minimize the hassle of
+   reindenting a lot of regex code that formerly used 'int'.  */
+typedef regoff_t Idx;
+#ifdef _REGEX_LARGE_OFFSETS
+# define IDX_MAX SSIZE_MAX
+#else
+# define IDX_MAX INT_MAX
+#endif
+
+/* A hash value, suitable for computing hash tables.  */
+typedef __re_size_t re_hashval_t;
+
+/* An integer used to represent a set of bits.  It must be unsigned,
+   and must be at least as wide as unsigned int.  */
+typedef unsigned long int bitset_word_t;
+/* All bits set in a bitset_word_t.  */
+#define BITSET_WORD_MAX ULONG_MAX
+
+/* Number of bits in a bitset_word_t.  For portability to hosts with
+   padding bits, do not use '(sizeof (bitset_word_t) * CHAR_BIT)';
+   instead, deduce it directly from BITSET_WORD_MAX.  Avoid
+   greater-than-32-bit integers and unconditional shifts by more than
+   31 bits, as they're not portable.  */
+#if BITSET_WORD_MAX == 0xffffffffUL
+# define BITSET_WORD_BITS 32
+#elif BITSET_WORD_MAX >> 31 >> 4 == 1
+# define BITSET_WORD_BITS 36
+#elif BITSET_WORD_MAX >> 31 >> 16 == 1
+# define BITSET_WORD_BITS 48
+#elif BITSET_WORD_MAX >> 31 >> 28 == 1
+# define BITSET_WORD_BITS 60
+#elif BITSET_WORD_MAX >> 31 >> 31 >> 1 == 1
+# define BITSET_WORD_BITS 64
+#elif BITSET_WORD_MAX >> 31 >> 31 >> 9 == 1
+# define BITSET_WORD_BITS 72
+#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 3 == 1
+# define BITSET_WORD_BITS 128
+#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 7 == 1
+# define BITSET_WORD_BITS 256
+#elif BITSET_WORD_MAX >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 31 >> 7 > 1
+# define BITSET_WORD_BITS 257 /* any value > SBC_MAX will do here */
+# if BITSET_WORD_BITS <= SBC_MAX
+#  error "Invalid SBC_MAX"
+# endif
+#else
+# error "Add case for new bitset_word_t size"
+#endif
+
+/* Number of bitset_word_t values in a bitset_t.  */
+#define BITSET_WORDS ((SBC_MAX + BITSET_WORD_BITS - 1) / BITSET_WORD_BITS)
+
+typedef bitset_word_t bitset_t[BITSET_WORDS];
+typedef bitset_word_t *re_bitset_ptr_t;
+typedef const bitset_word_t *re_const_bitset_ptr_t;
 
 #define PREV_WORD_CONSTRAINT 0x0001
 #define PREV_NOTWORD_CONSTRAINT 0x0002
@@ -136,32 +244,61 @@ static inline void bitset_not_merge (bitset dest, const bitset src);
 #define NEXT_NEWLINE_CONSTRAINT 0x0020
 #define PREV_BEGBUF_CONSTRAINT 0x0040
 #define NEXT_ENDBUF_CONSTRAINT 0x0080
-#define DUMMY_CONSTRAINT 0x0100
+#define WORD_DELIM_CONSTRAINT 0x0100
+#define NOT_WORD_DELIM_CONSTRAINT 0x0200
 
 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,
+  INSIDE_NOTWORD = PREV_NOTWORD_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
+  WORD_DELIM = WORD_DELIM_CONSTRAINT,
+  NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT
 } re_context_type;
 
 typedef struct
 {
-  int alloc;
-  int nelem;
-  int *elems;
+  Idx alloc;
+  Idx nelem;
+  Idx *elems;
 } re_node_set;
 
 typedef enum
 {
   NON_TYPE = 0,
 
+  /* Node type, These are used by token, node, tree.  */
+  CHARACTER = 1,
+  END_OF_RE = 2,
+  SIMPLE_BRACKET = 3,
+  OP_BACK_REF = 4,
+  OP_PERIOD = 5,
+#ifdef RE_ENABLE_I18N
+  COMPLEX_BRACKET = 6,
+  OP_UTF8_PERIOD = 7,
+#endif /* RE_ENABLE_I18N */
+
+  /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used
+     when the debugger shows values of this enum type.  */
+#define EPSILON_BIT 8
+  OP_OPEN_SUBEXP = EPSILON_BIT | 0,
+  OP_CLOSE_SUBEXP = EPSILON_BIT | 1,
+  OP_ALT = EPSILON_BIT | 2,
+  OP_DUP_ASTERISK = EPSILON_BIT | 3,
+  ANCHOR = EPSILON_BIT | 4,
+
+  /* Tree type, these are used only by tree. */
+  CONCAT = 16,
+  SUBEXP = 17,
+
   /* Token type, these are used only by token.  */
+  OP_DUP_PLUS = 18,
+  OP_DUP_QUESTION,
   OP_OPEN_BRACKET,
   OP_CLOSE_BRACKET,
   OP_CHARSET_RANGE,
@@ -176,32 +313,10 @@ typedef enum
   OP_CLOSE_CHAR_CLASS,
   OP_WORD,
   OP_NOTWORD,
-  BACK_SLASH,
+  OP_SPACE,
+  OP_NOTSPACE,
+  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
@@ -236,19 +351,19 @@ typedef struct
   unsigned int non_match : 1;
 
   /* # of multibyte characters.  */
-  int nmbchars;
+  Idx nmbchars;
 
   /* # of collating symbols.  */
-  int ncoll_syms;
+  Idx ncoll_syms;
 
   /* # of equivalence classes. */
-  int nequiv_classes;
+  Idx nequiv_classes;
 
   /* # of range expressions. */
-  int nranges;
+  Idx nranges;
 
   /* # of character classes. */
-  int nchar_classes;
+  Idx nchar_classes;
 } re_charset_t;
 #endif /* RE_ENABLE_I18N */
 
@@ -261,28 +376,27 @@ typedef struct
 #ifdef RE_ENABLE_I18N
     re_charset_t *mbcset;      /* for COMPLEX_BRACKET */
 #endif /* RE_ENABLE_I18N */
-    int idx;                   /* for BACK_REF */
+    Idx idx;                   /* for BACK_REF */
     re_context_type ctx_type;  /* for ANCHOR */
   } opr;
-#if __GNUC__ >= 2
+#if __GNUC__ >= 2 && !defined __STRICT_ANSI__
   re_token_type_t type : 8;
 #else
   re_token_type_t type;
 #endif
   unsigned int constraint : 10;        /* context constraint */
   unsigned int duplicated : 1;
+  unsigned int opt_subexp : 1;
 #ifdef RE_ENABLE_I18N
+  unsigned int accept_mb : 1;
+  /* These 2 bits can be moved into the union if needed (e.g. if running out
+     of bits; move opr.c to opr.c.c and move the flags to opr.c.flags).  */
   unsigned int mb_partial : 1;
 #endif
+  unsigned int word_char : 1;
 } 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)
+#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT)
 
 struct re_string_t
 {
@@ -293,30 +407,33 @@ struct re_string_t
      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;
+  Idx *offsets;
   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;
+  Idx 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;
+  Idx valid_len;
+  /* The corresponding number of bytes in raw_mbs array.  */
+  Idx valid_raw_len;
+  /* The length of the buffers MBS and WCS.  */
+  Idx 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;
+  Idx cur_idx;
+  /* length of RAW_MBS array.  */
+  Idx raw_len;
+  /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN.  */
+  Idx 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;
+  Idx raw_stop;
+  /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS.  */
+  Idx 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
@@ -324,53 +441,36 @@ struct re_string_t
   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;
+  /* Copy of re_dfa_t's word_char.  */
+  re_const_bitset_ptr_t word_char;
+  /* true if REG_ICASE.  */
+  unsigned char icase;
+  unsigned char is_utf8;
+  unsigned char map_notascii;
+  unsigned char mbs_allocated;
+  unsigned char offsets_needed;
+  unsigned char newline_anchor;
+  unsigned char word_ops_used;
+  int mb_cur_max;
 };
 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);
+
+
+struct re_dfa_t;
+typedef struct re_dfa_t re_dfa_t;
+
+#ifndef _LIBC
+# define IS_IN(libc) false
+#endif
+
 #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)
+  ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF)
 #define re_string_is_single_byte_char(pstr, idx) \
-  ((pstr)->wcs[idx] != WEOF && ((pstr)->len == (idx) \
+  ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \
                                || (pstr)->wcs[(idx) + 1] != WEOF))
 #define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
 #define re_string_cur_idx(pstr) ((pstr)->cur_idx)
@@ -380,6 +480,38 @@ static unsigned int re_string_context_at (const re_string_t *input, int idx,
 #define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
 #define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
 
+#if defined _LIBC || HAVE_ALLOCA
+# include <alloca.h>
+#endif
+
+#ifndef _LIBC
+# if HAVE_ALLOCA
+/* The OS usually guarantees only one guard page at the bottom of the stack,
+   and a page size can be as small as 4096 bytes.  So we cannot safely
+   allocate anything larger than 4096 bytes.  Also care for the possibility
+   of a few compiler-allocated temporary stack slots.  */
+#  define __libc_use_alloca(n) ((n) < 4032)
+# else
+/* alloca is implemented with malloc, so just use malloc.  */
+#  define __libc_use_alloca(n) 0
+#  undef alloca
+#  define alloca(n) malloc (n)
+# endif
+#endif
+
+#ifdef _LIBC
+# define MALLOC_0_IS_NONNULL 1
+#elif !defined MALLOC_0_IS_NONNULL
+# define MALLOC_0_IS_NONNULL 0
+#endif
+
+#ifndef MAX
+# define MAX(a,b) ((a) < (b) ? (b) : (a))
+#endif
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
 #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)
@@ -389,18 +521,26 @@ struct bin_tree_t
   struct bin_tree_t *parent;
   struct bin_tree_t *left;
   struct bin_tree_t *right;
+  struct bin_tree_t *first;
+  struct bin_tree_t *next;
 
-  /* `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;
+  re_token_t token;
 
-  int first;
-  int next;
-  re_node_set eclosure;
+  /* 'node_idx' is the index in dfa->nodes, if 'type' == 0.
+     Otherwise 'type' indicate the type of this node.  */
+  Idx node_idx;
 };
 typedef struct bin_tree_t bin_tree_t;
 
+#define BIN_TREE_STORAGE_SIZE \
+  ((1024 - sizeof (void *)) / sizeof (bin_tree_t))
+
+struct bin_tree_storage_t
+{
+  struct bin_tree_storage_t *next;
+  bin_tree_t data[BIN_TREE_STORAGE_SIZE];
+};
+typedef struct bin_tree_storage_t bin_tree_storage_t;
 
 #define CONTEXT_WORD 1
 #define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
@@ -415,7 +555,7 @@ typedef struct bin_tree_t bin_tree_t;
 
 #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_WORD_CHAR(ch) (__iswalnum (ch) || (ch) == L'_')
 #define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
 
 #define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
@@ -432,19 +572,17 @@ typedef struct bin_tree_t bin_tree_t;
 
 struct re_dfastate_t
 {
-  unsigned int hash;
+  re_hashval_t hash;
   re_node_set nodes;
+  re_node_set non_eps_nodes;
+  re_node_set inveclosure;
   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;
+  struct re_dfastate_t **trtable, **word_trtable;
+  unsigned int context : 4;
   unsigned int halt : 1;
-  /* If this state can accept `multi byte'.
+  /* If this state can accept "multi byte".
      Note that we refer to multibyte characters, and multi character
-     collating elements as `multi byte'.  */
+     collating elements as "multi byte".  */
   unsigned int accept_mb : 1;
   /* If this state has backreference node(s).  */
   unsigned int has_backref : 1;
@@ -452,17 +590,10 @@ struct re_dfastate_t
 };
 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;
+  Idx num;
+  Idx alloc;
   re_dfastate_t **array;
 };
 
@@ -470,8 +601,8 @@ struct re_state_table_entry
 
 typedef struct
 {
-  int next_idx;
-  int alloc;
+  Idx next_idx;
+  Idx alloc;
   re_dfastate_t **array;
 } state_array_t;
 
@@ -479,8 +610,8 @@ typedef struct
 
 typedef struct
 {
-  int node;
-  int str_idx; /* The position NODE match at.  */
+  Idx node;
+  Idx str_idx; /* The position NODE match at.  */
   state_array_t path;
 } re_sub_match_last_t;
 
@@ -490,147 +621,128 @@ typedef struct
 
 typedef struct
 {
-  int str_idx;
-  int node;
-  int next_last_offset;
+  Idx str_idx;
+  Idx node;
   state_array_t *path;
-  int alasts; /* Allocation size of LASTS.  */
-  int nlasts; /* The number of LASTS.  */
+  Idx alasts; /* Allocation size of LASTS.  */
+  Idx 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;
+  Idx node;
+  Idx str_idx;
+  Idx subexp_from;
+  Idx subexp_to;
+  char more;
+  char unused;
+  unsigned short int eps_reachable_subexps_map;
 };
 
 typedef struct
 {
+  /* The string object corresponding to the input string.  */
+  re_string_t input;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+  const re_dfa_t *const dfa;
+#else
+  const re_dfa_t *dfa;
+#endif
   /* 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;
+  Idx match_last;
+  Idx last_node;
   /* The state log used by the matcher.  */
   re_dfastate_t **state_log;
-  int state_log_top;
+  Idx state_log_top;
   /* Back reference cache.  */
-  int nbkref_ents;
-  int abkref_ents;
+  Idx nbkref_ents;
+  Idx abkref_ents;
   struct re_backref_cache_entry *bkref_ents;
   int max_mb_elem_len;
-  int nsub_tops;
-  int asub_tops;
+  Idx nsub_tops;
+  Idx 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;
-
+  Idx last_node;
+  Idx last_str_idx;
   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;
+  Idx idx;
+  Idx node;
   regmatch_t *regs;
   re_node_set eps_via_nodes;
 };
 
 struct re_fail_stack_t
 {
-  int num;
-  int alloc;
+  Idx num;
+  Idx 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;
+  size_t nodes_alloc;
+  size_t nodes_len;
+  Idx *nexts;
+  Idx *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.  */
+  bin_tree_t *str_tree;
+  bin_tree_storage_t *str_tree_storage;
+  re_bitset_ptr_t sb_char;
+  int str_tree_storage_idx;
+
+  /* number of subexpressions 're_nsub' is in regex_t.  */
+  re_hashval_t state_hash_mask;
+  Idx init_node;
+  Idx 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
+  bitset_word_t used_bkref_map;
+  bitset_word_t completed_bkref_map;
+
   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;
+  unsigned int is_utf8 : 1;
+  unsigned int map_notascii : 1;
+  unsigned int word_ops_used : 1;
+  int mb_cur_max;
+  bitset_t word_char;
+  reg_syntax_t syntax;
+  Idx *subexp_map;
+#ifdef DEBUG
+  char* re_str;
+#endif
+  lock_define (lock)
 };
-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
@@ -654,72 +766,110 @@ typedef struct
 } bracket_elem_t;
 
 
-/* Inline functions for bitset operation.  */
+/* Functions for bitset_t operation.  */
+
+static inline void
+bitset_set (bitset_t set, Idx i)
+{
+  set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS;
+}
+
 static inline void
-bitset_not (set)
-     bitset set;
+bitset_clear (bitset_t set, Idx i)
+{
+  set[i / BITSET_WORD_BITS] &= ~ ((bitset_word_t) 1 << i % BITSET_WORD_BITS);
+}
+
+static inline bool
+bitset_contain (const bitset_t set, Idx i)
+{
+  return (set[i / BITSET_WORD_BITS] >> i % BITSET_WORD_BITS) & 1;
+}
+
+static inline void
+bitset_empty (bitset_t set)
+{
+  memset (set, '\0', sizeof (bitset_t));
+}
+
+static inline void
+bitset_set_all (bitset_t set)
+{
+  memset (set, -1, sizeof (bitset_word_t) * (SBC_MAX / BITSET_WORD_BITS));
+  if (SBC_MAX % BITSET_WORD_BITS != 0)
+    set[BITSET_WORDS - 1] =
+      ((bitset_word_t) 1 << SBC_MAX % BITSET_WORD_BITS) - 1;
+}
+
+static inline void
+bitset_copy (bitset_t dest, const bitset_t src)
+{
+  memcpy (dest, src, sizeof (bitset_t));
+}
+
+static inline void
+bitset_not (bitset_t set)
 {
   int bitset_i;
-  for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
+  for (bitset_i = 0; bitset_i < SBC_MAX / BITSET_WORD_BITS; ++bitset_i)
     set[bitset_i] = ~set[bitset_i];
+  if (SBC_MAX % BITSET_WORD_BITS != 0)
+    set[BITSET_WORDS - 1] =
+      ((((bitset_word_t) 1 << SBC_MAX % BITSET_WORD_BITS) - 1)
+       & ~set[BITSET_WORDS - 1]);
 }
 
 static inline void
-bitset_merge (dest, src)
-     bitset dest;
-     const bitset src;
+bitset_merge (bitset_t dest, const bitset_t src)
 {
   int bitset_i;
-  for (bitset_i = 0; bitset_i < BITSET_UINTS; ++bitset_i)
+  for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
     dest[bitset_i] |= src[bitset_i];
 }
 
 static inline void
-bitset_not_merge (dest, src)
-     bitset dest;
-     const bitset src;
+bitset_mask (bitset_t dest, const bitset_t src)
 {
-  int i;
-  for (i = 0; i < BITSET_UINTS; ++i)
-    dest[i] |= ~src[i];
+  int bitset_i;
+  for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
+    dest[bitset_i] &= src[bitset_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;
+/* Functions for re_string.  */
+static int
+__attribute__ ((pure, unused))
+re_string_char_size_at (const re_string_t *pstr, Idx idx)
 {
   int byte_idx;
-  if (MB_CUR_MAX == 1)
+  if (pstr->mb_cur_max == 1)
     return 1;
-  for (byte_idx = 1; idx + byte_idx < pstr->len; ++byte_idx)
+  for (byte_idx = 1; idx + byte_idx < pstr->valid_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;
+static wint_t
+__attribute__ ((pure, unused))
+re_string_wchar_at (const re_string_t *pstr, Idx idx)
 {
-  if (MB_CUR_MAX == 1)
+  if (pstr->mb_cur_max == 1)
     return (wint_t) pstr->mbs[idx];
   return (wint_t) pstr->wcs[idx];
 }
 
+# ifdef _LIBC
+#  include <locale/weight.h>
+# endif
+
 static int
-re_string_elem_size_at (pstr, idx)
-     const re_string_t *pstr;
-     int idx;
+__attribute__ ((pure, unused))
+re_string_elem_size_at (const re_string_t *pstr, Idx idx)
 {
-#ifdef _LIBC
+# 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)
@@ -730,13 +880,38 @@ re_string_elem_size_at (pstr, idx)
       indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
                                                _NL_COLLATE_INDIRECTMB);
       p = pstr->mbs + idx;
-      tmp = findidx (&p);
+      findidx (table, indirect, extra, &p, pstr->len - idx);
       return p - pstr->mbs - idx;
     }
   else
-#endif /* _LIBC */
+# endif /* _LIBC */
     return 1;
 }
 #endif /* RE_ENABLE_I18N */
 
+#ifndef __GNUC_PREREQ
+# if defined __GNUC__ && defined __GNUC_MINOR__
+#  define __GNUC_PREREQ(maj, min) \
+         ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
+# else
+#  define __GNUC_PREREQ(maj, min) 0
+# endif
+#endif
+
+#if __GNUC_PREREQ (3,4)
+# undef __attribute_warn_unused_result__
+# define __attribute_warn_unused_result__ \
+   __attribute__ ((__warn_unused_result__))
+#else
+# define __attribute_warn_unused_result__ /* empty */
+#endif
+
+#ifndef FALLTHROUGH
+# if __GNUC__ < 7
+#  define FALLTHROUGH ((void) 0)
+# else
+#  define FALLTHROUGH __attribute__ ((__fallthrough__))
+# endif
+#endif
+
 #endif /*  _REGEX_INTERNAL_H */
index 3296028b81a934c579f999aa9aa20fe0eb16b626..73644c2341336e6655980a6eb29bad2110ea73a2 100644 (file)
@@ -1,5 +1,5 @@
 /* Extended regular expression matching and search library.
-   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 2002-2018 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
 
    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., 51 Franklin Street, Fifth Floor, Boston,
-   MA 02110-1301 USA.  */
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
 
 static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
-                                    re_string_t *input, int n);
+                                    Idx 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 reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, Idx node,
+                                         Idx str_idx, Idx from, Idx to);
+static Idx search_cur_bkref_entry (const re_match_context_t *mctx, Idx str_idx);
+static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, Idx node,
+                                          Idx str_idx);
 static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop,
-                                                  int node, int str_idx);
+                                                   Idx node, Idx 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);
+                          re_dfastate_t **limited_sts, Idx last_node,
+                          Idx last_str_idx);
 static reg_errcode_t re_search_internal (const regex_t *preg,
-                                        const char *string, int length,
-                                        int start, int range, int stop,
+                                        const char *string, Idx length,
+                                        Idx start, Idx last_start, Idx 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 regoff_t re_search_2_stub (struct re_pattern_buffer *bufp,
+                                 const char *string1, Idx length1,
+                                 const char *string2, Idx length2,
+                                 Idx start, regoff_t range,
+                                 struct re_registers *regs,
+                                 Idx stop, bool ret_len);
+static regoff_t re_search_stub (struct re_pattern_buffer *bufp,
+                               const char *string, Idx length, Idx start,
+                               regoff_t range, Idx stop,
+                               struct re_registers *regs,
+                               bool 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);
+                              Idx nregs, int regs_allocated);
+static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx);
+static Idx check_matching (re_match_context_t *mctx, bool fl_longest_match,
+                          Idx *p_match_first);
+static Idx check_halt_state_context (const re_match_context_t *mctx,
+                                    const re_dfastate_t *state, Idx idx);
+static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+                        regmatch_t *prev_idx_match, Idx cur_node,
+                        Idx cur_idx, Idx nmatch);
 static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs,
-                                     int str_idx, int *dests, int nregs,
+                                     Idx str_idx, Idx dest_node, Idx 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);
+                              bool 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,
+static int sift_states_iter_mb (const re_match_context_t *mctx,
                                re_sift_context_t *sctx,
-                               int node_idx, int str_idx, int max_str_idx);
+                               Idx node_idx, Idx str_idx, Idx max_str_idx);
 #endif /* RE_ENABLE_I18N */
-static reg_errcode_t sift_states_backward (const regex_t *preg,
-                                          re_match_context_t *mctx,
+static reg_errcode_t sift_states_backward (const 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,
+static reg_errcode_t build_sifted_states (const re_match_context_t *mctx,
+                                         re_sift_context_t *sctx, Idx str_idx,
+                                         re_node_set *cur_dest);
+static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx,
                                              re_sift_context_t *sctx,
-                                             int str_idx,
+                                             Idx str_idx,
                                              re_node_set *dest_nodes);
-static reg_errcode_t add_epsilon_src_nodes (re_dfa_t *dfa,
+static reg_errcode_t add_epsilon_src_nodes (const 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,
+static bool check_dst_limits (const re_match_context_t *mctx,
+                             const re_node_set *limits,
+                             Idx dst_node, Idx dst_idx, Idx src_node,
+                             Idx src_idx);
+static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx,
+                                       int boundaries, Idx subexp_idx,
+                                       Idx from_node, Idx bkref_idx);
+static int check_dst_limits_calc_pos (const re_match_context_t *mctx,
+                                     Idx limit, Idx subexp_idx,
+                                     Idx node, Idx str_idx,
+                                     Idx bkref_idx);
+static reg_errcode_t check_subexp_limits (const 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,
+                                         Idx str_idx);
+static reg_errcode_t sift_states_bkref (const 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,
+                                       Idx str_idx, const re_node_set *candidates);
+static reg_errcode_t merge_state_array (const re_dfa_t *dfa,
+                                       re_dfastate_t **dst,
+                                       re_dfastate_t **src, Idx num);
+static re_dfastate_t *find_recover_state (reg_errcode_t *err,
+                                        re_match_context_t *mctx);
+static re_dfastate_t *transit_state (reg_errcode_t *err,
                                     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_dfastate_t *state);
+static re_dfastate_t *merge_state_with_log (reg_errcode_t *err,
+                                           re_match_context_t *mctx,
+                                           re_dfastate_t *next_state);
+static reg_errcode_t check_subexp_matching_top (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);
+                                               Idx str_idx);
+#if 0
+static re_dfastate_t *transit_state_sb (reg_errcode_t *err,
+                                       re_match_context_t *mctx,
+                                       re_dfastate_t *pstate);
+#endif
 #ifdef RE_ENABLE_I18N
-static reg_errcode_t transit_state_mb (const regex_t *preg,
-                                      re_dfastate_t *pstate,
-                                      re_match_context_t *mctx);
+static reg_errcode_t transit_state_mb (re_match_context_t *mctx,
+                                      re_dfastate_t *pstate);
 #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,
+static reg_errcode_t transit_state_bkref (re_match_context_t *mctx,
+                                         const re_node_set *nodes);
+static reg_errcode_t get_subexp (re_match_context_t *mctx,
+                                Idx bkref_node, Idx bkref_str_idx);
+static reg_errcode_t get_subexp_sub (re_match_context_t *mctx,
+                                    const 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,
+                                    Idx bkref_node, Idx bkref_str);
+static Idx find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+                            Idx subexp_idx, int type);
+static reg_errcode_t check_arrival (re_match_context_t *mctx,
+                                   state_array_t *path, Idx top_node,
+                                   Idx top_str, Idx last_node, Idx last_str,
+                                   int type);
+static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx,
+                                                  Idx str_idx,
                                                   re_node_set *cur_nodes,
                                                   re_node_set *next_nodes);
-static reg_errcode_t check_arrival_expand_ecl (re_dfa_t *dfa,
+static reg_errcode_t check_arrival_expand_ecl (const 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,
+                                              Idx ex_subexp, int type);
+static reg_errcode_t check_arrival_expand_ecl_sub (const 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);
+                                                  Idx target, Idx ex_subexp,
+                                                  int type);
+static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx,
+                                        re_node_set *cur_nodes, Idx cur_str,
+                                        Idx subexp_num, int type);
+static bool build_trtable (const re_dfa_t *dfa, re_dfastate_t *state);
 #ifdef RE_ENABLE_I18N
-static int check_node_accept_bytes (const regex_t *preg, int node_idx,
-                                   const re_string_t *input, int idx);
+static int check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx,
+                                   const re_string_t *input, Idx 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,
+static Idx group_nodes_into_DFAstates (const re_dfa_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);
+                                      bitset_t *states_ch);
+static bool check_node_accept (const re_match_context_t *mctx,
+                              const re_token_t *node, Idx idx);
+static reg_errcode_t extend_buffers (re_match_context_t *mctx, int min_len);
 \f
 /* Entry point for POSIX code.  */
 
@@ -197,36 +178,69 @@ static reg_errcode_t extend_buffers (re_match_context_t *mctx);
    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
+   '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
+   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;
+regexec (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);
+  Idx start, length;
+  re_dfa_t *dfa = preg->buffer;
+
+  if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND))
+    return REG_BADPAT;
+
+  if (eflags & REG_STARTEND)
+    {
+      start = pmatch[0].rm_so;
+      length = pmatch[0].rm_eo;
+    }
+  else
+    {
+      start = 0;
+      length = strlen (string);
+    }
+
+  lock_lock (dfa->lock);
   if (preg->no_sub)
-    err = re_search_internal (preg, string, length, 0, length, length, 0,
-                             NULL, eflags);
+    err = re_search_internal (preg, string, length, start, length,
+                             length, 0, NULL, eflags);
   else
-    err = re_search_internal (preg, string, length, 0, length, length, nmatch,
-                             pmatch, eflags);
+    err = re_search_internal (preg, string, length, start, length,
+                             length, nmatch, pmatch, eflags);
+  lock_unlock (dfa->lock);
   return err != REG_NOERROR;
 }
+
 #ifdef _LIBC
-weak_alias (__regexec, regexec)
+libc_hidden_def (__regexec)
+
+# include <shlib-compat.h>
+versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4);
+
+# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4)
+__typeof__ (__regexec) __compat_regexec;
+
+int
+attribute_compat_text_section
+__compat_regexec (const regex_t *_Restrict_ preg,
+                 const char *_Restrict_ string, size_t nmatch,
+                 regmatch_t pmatch[], int eflags)
+{
+  return regexec (preg, string, nmatch, pmatch,
+                 eflags & (REG_NOTBOL | REG_NOTEOL));
+}
+compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0);
+# endif
 #endif
 
 /* Entry points for GNU code.  */
@@ -250,7 +264,7 @@ weak_alias (__regexec, regexec)
    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
+   and all groups is stored in REGS.  (For the "_2" variants, the offsets are
    computed relative to the concatenation, not relative to the individual
    strings.)
 
@@ -258,88 +272,82 @@ weak_alias (__regexec, regexec)
    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;
+regoff_t
+re_match (struct re_pattern_buffer *bufp, const char *string, Idx length,
+         Idx start, struct re_registers *regs)
 {
-  return re_search_stub (bufp, string, length, start, 0, length, regs, 1);
+  return re_search_stub (bufp, string, length, start, 0, length, regs, true);
 }
 #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;
+regoff_t
+re_search (struct re_pattern_buffer *bufp, const char *string, Idx length,
+          Idx start, regoff_t range, struct re_registers *regs)
 {
-  return re_search_stub (bufp, string, length, start, range, length, regs, 0);
+  return re_search_stub (bufp, string, length, start, range, length, regs,
+                        false);
 }
 #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;
+regoff_t
+re_match_2 (struct re_pattern_buffer *bufp, const char *string1, Idx length1,
+           const char *string2, Idx length2, Idx start,
+           struct re_registers *regs, Idx stop)
 {
   return re_search_2_stub (bufp, string1, length1, string2, length2,
-                          start, 0, regs, stop, 1);
+                          start, 0, regs, stop, true);
 }
 #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;
+regoff_t
+re_search_2 (struct re_pattern_buffer *bufp, const char *string1, Idx length1,
+            const char *string2, Idx length2, Idx start, regoff_t range,
+            struct re_registers *regs, Idx stop)
 {
   return re_search_2_stub (bufp, string1, length1, string2, length2,
-                          start, range, regs, stop, 0);
+                          start, range, regs, stop, false);
 }
 #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;
+static regoff_t
+re_search_2_stub (struct re_pattern_buffer *bufp, const char *string1,
+                 Idx length1, const char *string2, Idx length2, Idx start,
+                 regoff_t range, struct re_registers *regs,
+                 Idx stop, bool ret_len)
 {
   const char *str;
-  int rval;
-  int len = length1 + length2;
-  int free_str = 0;
+  regoff_t rval;
+  Idx len;
+  char *s = NULL;
 
-  if (BE (length1 < 0 || length2 < 0 || stop < 0, 0))
+  if (BE ((length1 < 0 || length2 < 0 || stop < 0
+           || INT_ADD_WRAPV (length1, length2, &len)),
+          0))
     return -2;
 
   /* Concatenate the strings.  */
   if (length2 > 0)
     if (length1 > 0)
       {
-       char *s = re_malloc (char, len);
+       s = re_malloc (char, len);
 
        if (BE (s == NULL, 0))
          return -2;
+#ifdef _LIBC
+       memcpy (__mempcpy (s, string1, length1), string2, length2);
+#else
        memcpy (s, string1, length1);
        memcpy (s + length1, string2, length2);
+#endif
        str = s;
-       free_str = 1;
       }
     else
       str = string2;
@@ -348,41 +356,43 @@ re_search_2_stub (bufp, string1, length1, string2, length2, start, range, regs,
 
   rval = re_search_stub (bufp, str, len, start, range, stop, regs,
                         ret_len);
-  if (free_str)
-    re_free ((char *) str);
+  re_free (s);
   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);
+   If RET_LEN is true 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;
+static regoff_t
+re_search_stub (struct re_pattern_buffer *bufp, const char *string, Idx length,
+               Idx start, regoff_t range, Idx stop, struct re_registers *regs,
+               bool ret_len)
 {
   reg_errcode_t result;
   regmatch_t *pmatch;
-  int nregs, rval;
+  Idx nregs;
+  regoff_t rval;
   int eflags = 0;
+  re_dfa_t *dfa = bufp->buffer;
+  Idx last_start = start + range;
 
   /* 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;
+  if (BE (length < last_start || (0 <= range && last_start < start), 0))
+    last_start = length;
+  else if (BE (last_start < 0 || (range < 0 && start <= last_start), 0))
+    last_start = 0;
+
+  lock_lock (dfa->lock);
 
   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)
+  if (start < last_start && bufp->fastmap != NULL && !bufp->fastmap_accurate)
     re_compile_fastmap (bufp);
 
   if (BE (bufp->no_sub, 0))
@@ -391,8 +401,8 @@ re_search_stub (bufp, string, length, start, range, stop, regs, ret_len)
   /* 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))
+  else if (BE (bufp->regs_allocated == REGS_FIXED
+              && regs->num_regs <= bufp->re_nsub, 0))
     {
       nregs = regs->num_regs;
       if (BE (nregs < 1, 0))
@@ -406,16 +416,19 @@ re_search_stub (bufp, string, length, start, range, stop, regs, ret_len)
     nregs = bufp->re_nsub + 1;
   pmatch = re_malloc (regmatch_t, nregs);
   if (BE (pmatch == NULL, 0))
-    return -2;
+    {
+      rval = -2;
+      goto out;
+    }
 
-  result = re_search_internal (bufp, string, length, start, range, stop,
+  result = re_search_internal (bufp, string, length, start, last_start, stop,
                               nregs, pmatch, eflags);
 
   rval = 0;
 
-  /* I hope we needn't fill ther regs with -1's when no match was found.  */
+  /* I hope we needn't fill their regs with -1's when no match was found.  */
   if (result != REG_NOERROR)
-    rval = -1;
+    rval = result == REG_NOMATCH ? -1 : -2;
   else if (regs != NULL)
     {
       /* If caller wants register contents data back, copy them.  */
@@ -436,19 +449,19 @@ re_search_stub (bufp, string, length, start, range, stop, regs, ret_len)
        rval = pmatch[0].rm_so;
     }
   re_free (pmatch);
+ out:
+  lock_unlock (dfa->lock);
   return rval;
 }
 
 static unsigned
-re_copy_regs (regs, pmatch, nregs, regs_allocated)
-    struct re_registers *regs;
-    regmatch_t *pmatch;
-    int nregs, regs_allocated;
+re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, Idx nregs,
+             int 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
+  Idx i;
+  Idx 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?  */
@@ -469,21 +482,20 @@ re_copy_regs (regs, pmatch, nregs, regs_allocated)
     { /* 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)
+      if (BE (need_regs > regs->num_regs, 0))
        {
-         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))
+         regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs);
+         regoff_t *new_end;
+         if (BE (new_start == NULL, 0))
+           return REGS_UNALLOCATED;
+         new_end = re_realloc (regs->end, regoff_t, need_regs);
+         if (BE (new_end == NULL, 0))
            {
-             re_free (regs->start);
+             re_free (new_start);
              return REGS_UNALLOCATED;
            }
+         regs->start = new_start;
+         regs->end = new_end;
          regs->num_regs = need_regs;
        }
     }
@@ -521,11 +533,8 @@ re_copy_regs (regs, pmatch, nregs, regs_allocated)
    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;
+re_set_registers (struct re_pattern_buffer *bufp, struct re_registers *regs,
+                 __re_size_t num_regs, regoff_t *starts, regoff_t *ends)
 {
   if (num_regs)
     {
@@ -538,7 +547,7 @@ re_set_registers (bufp, regs, num_regs, starts, ends)
     {
       bufp->regs_allocated = REGS_UNALLOCATED;
       regs->num_regs = 0;
-      regs->start = regs->end = (regoff_t *) 0;
+      regs->start = regs->end = NULL;
     }
 }
 #ifdef _LIBC
@@ -553,44 +562,57 @@ int
 # ifdef _LIBC
 weak_function
 # endif
-re_exec (s)
-     const char *s;
+re_exec (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.
+   meaning as with regexec.  LAST_START is START + RANGE, where
+   START and RANGE have the same meaning as 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)  */
+   (0 <= LAST_START && LAST_START <= 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[];
+__attribute_warn_unused_result__
+re_search_internal (const regex_t *preg, const char *string, Idx length,
+                   Idx start, Idx last_start, Idx stop, size_t nmatch,
+                   regmatch_t pmatch[], int eflags)
 {
   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;
+  const re_dfa_t *dfa = preg->buffer;
+  Idx left_lim, right_lim;
+  int incr;
+  bool fl_longest_match;
+  int match_kind;
+  Idx match_first;
+  Idx match_last = -1;
+  Idx extra_nmatch;
+  bool sb;
+  int ch;
+#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
+  re_match_context_t mctx = { .dfa = dfa };
+#else
   re_match_context_t mctx;
+#endif
   char *fastmap = ((preg->fastmap != NULL && preg->fastmap_accurate
-                   && range && !preg->can_be_null) ? preg->fastmap : NULL);
+                   && start != last_start && !preg->can_be_null)
+                  ? preg->fastmap : NULL);
+  RE_TRANSLATE_TYPE t = preg->translate;
+
+#if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L))
+  memset (&mctx, '\0', sizeof (re_match_context_t));
+  mctx.dfa = dfa;
+#endif
+
+  extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0;
+  nmatch -= extra_nmatch;
 
   /* Check if the DFA haven't been compiled.  */
   if (BE (preg->used == 0 || dfa->init_state == NULL
@@ -598,19 +620,37 @@ re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch,
          || dfa->init_state_begbuf == NULL, 0))
     return REG_NOMATCH;
 
-  re_node_set_init_empty (&empty_set);
-  memset (&mctx, '\0', sizeof (re_match_context_t));
+#ifdef DEBUG
+  /* We assume front-end functions already check them.  */
+  assert (0 <= last_start && last_start <= length);
+#endif
+
+  /* If initial states with non-begbuf contexts have no elements,
+     the regex must be anchored.  If preg->newline_anchor is set,
+     we'll never use init_state_nl, so do not check it.  */
+  if (dfa->init_state->nodes.nelem == 0
+      && dfa->init_state_word->nodes.nelem == 0
+      && (dfa->init_state_nl->nodes.nelem == 0
+         || !preg->newline_anchor))
+    {
+      if (start != 0 && last_start != 0)
+        return REG_NOMATCH;
+      start = last_start = 0;
+    }
 
   /* 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);
+  err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1,
+                           preg->translate, (preg->syntax & RE_ICASE) != 0,
+                           dfa);
   if (BE (err != REG_NOERROR, 0))
     goto free_return;
-  input.stop = stop;
+  mctx.input.stop = stop;
+  mctx.input.raw_stop = stop;
+  mctx.input.newline_anchor = preg->newline_anchor;
 
-  err = match_ctx_init (&mctx, eflags, &input, dfa->nbackref * 2);
+  err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2);
   if (BE (err != REG_NOERROR, 0))
     goto free_return;
 
@@ -620,7 +660,15 @@ re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch,
      multi character collating element.  */
   if (nmatch > 1 || dfa->has_mb_node)
     {
-      mctx.state_log = re_malloc (re_dfastate_t *, dfa->nodes_len + 1);
+      /* Avoid overflow.  */
+      if (BE ((MIN (IDX_MAX, SIZE_MAX / sizeof (re_dfastate_t *))
+               <= mctx.input.bufs_len), 0))
+       {
+         err = REG_ESPACE;
+         goto free_return;
+       }
+
+      mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1);
       if (BE (mctx.state_log == NULL, 0))
        {
          err = REG_ESPACE;
@@ -630,167 +678,183 @@ re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch,
   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 (;;)
+  mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF
+                          : CONTEXT_NEWLINE | CONTEXT_BEGBUF;
+
+  /* Check incrementally whether the input string matches.  */
+  incr = (last_start < start) ? -1 : 1;
+  left_lim = (last_start < start) ? last_start : start;
+  right_lim = (last_start < start) ? start : last_start;
+  sb = dfa->mb_cur_max == 1;
+  match_kind =
+    (fastmap
+     ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0)
+       | (start <= last_start ? 2 : 0)
+       | (t != NULL ? 1 : 0))
+     : 8);
+
+  for (;; match_first += incr)
     {
-      /* At first get the current byte from input string.  */
-      if (fastmap)
+      err = REG_NOMATCH;
+      if (match_first < left_lim || right_lim < match_first)
+       goto free_return;
+
+      /* Advance as rapidly as possible through the string, until we
+        find a plausible place to start matching.  This may be done
+        with varying efficiency, so there are various possibilities:
+        only the most common of them are specialized, in order to
+        save on code size.  We use a switch statement for speed.  */
+      switch (match_kind)
        {
-         if (BE (fast_translate, 1))
+       case 8:
+         /* No fastmap.  */
+         break;
+
+       case 7:
+         /* Fastmap with single-byte translation, match forward.  */
+         while (BE (match_first < right_lim, 1)
+                && !fastmap[t[(unsigned char) string[match_first]]])
+           ++match_first;
+         goto forward_match_found_start_or_reached_end;
+
+       case 6:
+         /* Fastmap without translation, match forward.  */
+         while (BE (match_first < right_lim, 1)
+                && !fastmap[(unsigned char) string[match_first]])
+           ++match_first;
+
+       forward_match_found_start_or_reached_end:
+         if (BE (match_first == right_lim, 0))
            {
-             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;
-               }
+             ch = match_first >= length
+                      ? 0 : (unsigned char) string[match_first];
+             if (!fastmap[t ? t[ch] : ch])
+               goto free_return;
            }
-         else
+         break;
+
+       case 4:
+       case 5:
+         /* Fastmap without multi-byte translation, match backwards.  */
+         while (match_first >= left_lim)
            {
-             int ch;
+             ch = match_first >= length
+                      ? 0 : (unsigned char) string[match_first];
+             if (fastmap[t ? t[ch] : ch])
+               break;
+             --match_first;
+           }
+         if (match_first < left_lim)
+           goto free_return;
+         break;
 
-             do
+       default:
+         /* 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.  */
+         for (;;)
+           {
+             /* If MATCH_FIRST is out of the valid range, reconstruct the
+                buffers.  */
+             __re_size_t offset = match_first - mctx.input.raw_mbs_idx;
+             if (BE (offset >= (__re_size_t) mctx.input.valid_raw_len, 0))
                {
-                 /* 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;
+                 err = re_string_reconstruct (&mctx.input, match_first,
+                                              eflags);
+                 if (BE (err != REG_NOERROR, 0))
+                   goto free_return;
+
+                 offset = match_first - mctx.input.raw_mbs_idx;
                }
-             while (match_first >= left_lim && match_first <= right_lim);
-             if (! fastmap[ch])
+             /* 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 (&mctx.input, offset));
+             if (fastmap[ch])
                break;
+             match_first += incr;
+             if (match_first < left_lim || match_first > right_lim)
+               {
+                 err = REG_NOMATCH;
+                 goto free_return;
+               }
            }
+         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);
+        the matching starts from the beginning of the buffer.  */
+      err = re_string_reconstruct (&mctx.input, match_first, eflags);
       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))
+     /* Don't consider this char as a possible match start if it part,
+       yet isn't the head, of a multibyte character.  */
+      if (!sb && !re_string_first_byte (&mctx.input, 0))
+       continue;
 #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 (&mctx, fl_longest_match,
+                                  start <= last_start ? &match_first : NULL);
+      if (match_last != -1)
        {
-         /* 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
            {
-             if (BE (match_last == -2, 0))
+             mctx.match_last = match_last;
+             if ((!preg->no_sub && nmatch > 1) || dfa->nbackref)
                {
-                 err = REG_ESPACE;
-                 goto free_return;
+                 re_dfastate_t *pstate = mctx.state_log[match_last];
+                 mctx.last_node = check_halt_state_context (&mctx, pstate,
+                                                            match_last);
                }
-             else
+             if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match)
+                 || dfa->nbackref)
                {
-                 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.  */
+                 err = prune_impossible_nodes (&mctx);
+                 if (err == REG_NOERROR)
+                   break;
+                 if (BE (err != REG_NOMATCH, 0))
+                   goto free_return;
+                 match_last = -1;
                }
+             else
+               break; /* We found a match.  */
            }
-         match_ctx_clean (&mctx);
        }
-      /* Update counter.  */
-      match_first += incr;
-      if (match_first < left_lim || right_lim < match_first)
-       break;
+
+      match_ctx_clean (&mctx);
     }
 
+#ifdef DEBUG
+  assert (match_last != -1);
+  assert (err == REG_NOERROR);
+#endif
+
   /* Set pmatch[] if we need.  */
-  if (match_last != -1 && nmatch > 0)
+  if (nmatch > 0)
     {
-      int reg_idx;
+      Idx reg_idx;
 
       /* Initialize registers.  */
-      for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
+      for (reg_idx = 1; 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;
+      /* FIXME: This function should fail if mctx.match_last exceeds
+        the maximum possible regoff_t value.  We need a new error
+        code REG_OVERFLOW.  */
 
       if (!preg->no_sub && nmatch > 1)
        {
@@ -800,32 +864,62 @@ re_search_internal (preg, string, length, start, range, stop, nmatch, pmatch,
            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.  */
+      /* At last, add the offset to each register, since we slid
+        the buffers so that we could assume that the matching starts
+        from 0.  */
       for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
        if (pmatch[reg_idx].rm_so != -1)
          {
+#ifdef RE_ENABLE_I18N
+           if (BE (mctx.input.offsets_needed != 0, 0))
+             {
+               pmatch[reg_idx].rm_so =
+                 (pmatch[reg_idx].rm_so == mctx.input.valid_len
+                  ? mctx.input.valid_raw_len
+                  : mctx.input.offsets[pmatch[reg_idx].rm_so]);
+               pmatch[reg_idx].rm_eo =
+                 (pmatch[reg_idx].rm_eo == mctx.input.valid_len
+                  ? mctx.input.valid_raw_len
+                  : mctx.input.offsets[pmatch[reg_idx].rm_eo]);
+             }
+#else
+           assert (mctx.input.offsets_needed == 0);
+#endif
            pmatch[reg_idx].rm_so += match_first;
            pmatch[reg_idx].rm_eo += match_first;
          }
+      for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx)
+       {
+         pmatch[nmatch + reg_idx].rm_so = -1;
+         pmatch[nmatch + reg_idx].rm_eo = -1;
+       }
+
+      if (dfa->subexp_map)
+       for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++)
+         if (dfa->subexp_map[reg_idx] != reg_idx)
+           {
+             pmatch[reg_idx + 1].rm_so
+               = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so;
+             pmatch[reg_idx + 1].rm_eo
+               = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo;
+           }
     }
-  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);
+  re_string_destruct (&mctx.input);
   return err;
 }
 
 static reg_errcode_t
-prune_impossible_nodes (preg, mctx)
-     const regex_t *preg;
-     re_match_context_t *mctx;
+__attribute_warn_unused_result__
+prune_impossible_nodes (re_match_context_t *mctx)
 {
-  int halt_node, match_last;
+  const re_dfa_t *const dfa = mctx->dfa;
+  Idx 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;
@@ -834,6 +928,11 @@ prune_impossible_nodes (preg, mctx)
 #endif
   match_last = mctx->match_last;
   halt_node = mctx->last_node;
+
+  /* Avoid overflow.  */
+  if (BE (MIN (IDX_MAX, SIZE_MAX / sizeof (re_dfastate_t *)) <= match_last, 0))
+    return REG_ESPACE;
+
   sifted_states = re_malloc (re_dfastate_t *, match_last + 1);
   if (BE (sifted_states == NULL, 0))
     {
@@ -852,10 +951,9 @@ prune_impossible_nodes (preg, mctx)
        {
          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);
+                        match_last);
+         ret = sift_states_backward (mctx, &sctx);
          re_node_set_free (&sctx.limits);
          if (BE (ret != REG_NOERROR, 0))
              goto free_return;
@@ -869,10 +967,11 @@ prune_impossible_nodes (preg, mctx)
                  ret = REG_NOMATCH;
                  goto free_return;
                }
-           } while (!mctx->state_log[match_last]->halt);
-         halt_node = check_halt_state_context (preg,
+           } while (mctx->state_log[match_last] == NULL
+                    || !mctx->state_log[match_last]->halt);
+         halt_node = check_halt_state_context (mctx,
                                                mctx->state_log[match_last],
-                                               mctx, match_last);
+                                               match_last);
        }
       ret = merge_state_array (dfa, sifted_states, lim_states,
                               match_last + 1);
@@ -883,12 +982,16 @@ prune_impossible_nodes (preg, mctx)
     }
   else
     {
-      sift_ctx_init (&sctx, sifted_states, lim_states, halt_node,
-                    match_last, 0);
-      ret = sift_states_backward (preg, mctx, &sctx);
+      sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last);
+      ret = sift_states_backward (mctx, &sctx);
       re_node_set_free (&sctx.limits);
       if (BE (ret != REG_NOERROR, 0))
        goto free_return;
+      if (sifted_states[0] == NULL)
+       {
+         ret = REG_NOMATCH;
+         goto free_return;
+       }
     }
   re_free (mctx->state_log);
   mctx->state_log = sifted_states;
@@ -907,20 +1010,15 @@ prune_impossible_nodes (preg, mctx)
    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;
+__attribute__ ((always_inline))
+acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx,
+                           Idx idx)
 {
-  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
-
-  *err = REG_NOERROR;
+  const re_dfa_t *const dfa = mctx->dfa;
   if (dfa->init_state->has_constraint)
     {
       unsigned int context;
-      context =  re_string_context_at (mctx->input, idx - 1, mctx->eflags,
-                                      preg->newline_anchor);
+      context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags);
       if (IS_WORD_CONTEXT (context))
        return dfa->init_state_word;
       else if (IS_ORDINARY_CONTEXT (context))
@@ -932,9 +1030,9 @@ acquire_init_state_context (err, preg, mctx, idx)
       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);
+         return re_acquire_state_context (err, dfa,
+                                          dfa->init_state->entrance_nodes,
+                                          context);
        }
       else
        /* Must not happen?  */
@@ -945,54 +1043,64 @@ acquire_init_state_context (err, preg, mctx, idx)
 }
 
 /* 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,
+   and return the index where the matching end.  Return -1 if
+   there is no match, and return -2 in case of an error.
    FL_LONGEST_MATCH means we want the POSIX longest matching.
-   Note that the matcher assume that the maching starts from the current
+   If P_MATCH_FIRST is not NULL, and the match fails, it is set to the
+   next place where we may want to try matching.
+   Note that the matcher assumes that the matching 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;
+static Idx
+__attribute_warn_unused_result__
+check_matching (re_match_context_t *mctx, bool fl_longest_match,
+               Idx *p_match_first)
 {
-  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  const re_dfa_t *const dfa = mctx->dfa;
   reg_errcode_t err;
-  int match = 0;
-  int match_last = -1;
-  int cur_str_idx = re_string_cur_idx (mctx->input);
+  Idx match = 0;
+  Idx match_last = -1;
+  Idx cur_str_idx = re_string_cur_idx (&mctx->input);
   re_dfastate_t *cur_state;
+  bool at_init_state = p_match_first != NULL;
+  Idx next_start_idx = cur_str_idx;
 
-  cur_state = acquire_init_state_context (&err, preg, mctx, cur_str_idx);
-  /* An initial state must not be NULL(invalid state).  */
+  err = REG_NOERROR;
+  cur_state = acquire_init_state_context (&err, mctx, cur_str_idx);
+  /* An initial state must not be NULL (invalid).  */
   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;
+      assert (err == REG_ESPACE);
+      return -2;
     }
 
-  if (cur_state->has_backref)
+  if (mctx->state_log != NULL)
     {
-      err = transit_state_bkref (preg, &cur_state->nodes, mctx);
-      if (BE (err != REG_NOERROR, 0))
-       return err;
+      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 (BE (dfa->nbackref, 0))
+       {
+         at_init_state = false;
+         err = check_subexp_matching_top (mctx, &cur_state->nodes, 0);
+         if (BE (err != REG_NOERROR, 0))
+           return err;
+
+         if (cur_state->has_backref)
+           {
+             err = transit_state_bkref (mctx, &cur_state->nodes);
+             if (BE (err != REG_NOERROR, 0))
+               return err;
+           }
+       }
     }
 
   /* If the RE accepts NULL string.  */
-  if (cur_state->halt)
+  if (BE (cur_state->halt, 0))
     {
       if (!cur_state->has_constraint
-         || check_halt_state_context (preg, cur_state, mctx, cur_str_idx))
+         || check_halt_state_context (mctx, cur_state, cur_str_idx))
        {
          if (!fl_longest_match)
            return cur_str_idx;
@@ -1004,166 +1112,175 @@ check_matching (preg, mctx, fl_search, fl_longest_match)
        }
     }
 
-  while (!re_string_eoi (mctx->input))
+  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.  */
+      re_dfastate_t *old_state = cur_state;
+      Idx next_char_idx = re_string_cur_idx (&mctx->input) + 1;
+
+      if ((BE (next_char_idx >= mctx->input.bufs_len, 0)
+          && mctx->input.bufs_len < mctx->input.len)
+         || (BE (next_char_idx >= mctx->input.valid_len, 0)
+             && mctx->input.valid_len < mctx->input.len))
        {
-         cur_str_idx = re_string_cur_idx (mctx->input);
+         err = extend_buffers (mctx, next_char_idx + 1);
          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;
+             assert (err == REG_ESPACE);
+             return -2;
            }
-         else if (!fl_longest_match && match)
+       }
+
+      cur_state = transit_state (&err, mctx, cur_state);
+      if (mctx->state_log != NULL)
+       cur_state = merge_state_with_log (&err, mctx, cur_state);
+
+      if (cur_state == NULL)
+       {
+         /* Reached the invalid state or an error.  Try to recover a valid
+            state using the state log, if available and if we have not
+            already found a valid (even if not the longest) match.  */
+         if (BE (err != REG_NOERROR, 0))
+           return -2;
+
+         if (mctx->state_log == NULL
+             || (match && !fl_longest_match)
+             || (cur_state = find_recover_state (&err, mctx)) == NULL)
            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)
+      if (BE (at_init_state, 0))
        {
-         /* Reached at a halt state.
+         if (old_state == cur_state)
+           next_start_idx = next_char_idx;
+         else
+           at_init_state = false;
+       }
+
+      if (cur_state->halt)
+       {
+         /* Reached 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)))
+             || check_halt_state_context (mctx, cur_state,
+                                          re_string_cur_idx (&mctx->input)))
            {
              /* We found an appropriate halt state.  */
-             match_last = re_string_cur_idx (mctx->input);
+             match_last = re_string_cur_idx (&mctx->input);
              match = 1;
+
+             /* We found a match, do not modify match_first below.  */
+             p_match_first = NULL;
              if (!fl_longest_match)
                break;
            }
        }
-   }
+    }
+
+  if (p_match_first)
+    *p_match_first += next_start_idx;
+
   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;
+static bool
+check_halt_node_context (const re_dfa_t *dfa, Idx 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;
+    return false;
   if (!constraint)
-    return 1;
+    return true;
   if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context))
-    return 0;
-  return 1;
+    return false;
+  return true;
 }
 
 /* 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;
+static Idx
+check_halt_state_context (const re_match_context_t *mctx,
+                         const re_dfastate_t *state, Idx idx)
+{
+  Idx i;
   unsigned int context;
 #ifdef DEBUG
   assert (state->halt);
 #endif
-  context = re_string_context_at (mctx->input, idx, mctx->eflags,
-                                 preg->newline_anchor);
+  context = re_string_context_at (&mctx->input, idx, mctx->eflags);
   for (i = 0; i < state->nodes.nelem; ++i)
-    if (check_halt_node_context (dfa, state->nodes.elems[i], context))
+    if (check_halt_node_context (mctx->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.  */
+   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;
+static Idx
+proceed_next_node (const re_match_context_t *mctx, Idx nregs, regmatch_t *regs,
+                  Idx *pidx, Idx node, re_node_set *eps_via_nodes,
+                  struct re_fail_stack_t *fs)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  Idx i;
+  bool ok;
   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];
+      re_node_set *edests = &dfa->edests[node];
+      Idx dest_node;
+      ok = re_node_set_insert (eps_via_nodes, node);
+      if (BE (! ok, 0))
+       return -2;
+      /* Pick up a valid destination, or return -1 if none
+        is found.  */
+      for (dest_node = -1, i = 0; i < edests->nelem; ++i)
+       {
+         Idx candidate = edests->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];
+          if (dest_node == -1)
+           dest_node = candidate;
+
+         else
+           {
+             /* In order to avoid infinite loop like "(a*)*", return the second
+                epsilon-transition if the first was already considered.  */
+             if (re_node_set_contains (eps_via_nodes, dest_node))
+               return candidate;
+
+             /* Otherwise, push the second epsilon-transition on the fail stack.  */
+             else if (fs != NULL
+                      && push_fail_stack (fs, *pidx, candidate, nregs, regs,
+                                          eps_via_nodes))
+               return -2;
+
+             /* We know we are going to exit.  */
+             break;
+           }
+       }
+      return dest_node;
     }
   else
     {
-      int naccepted = 0;
+      Idx 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);
+      if (dfa->nodes[node].accept_mb)
+       naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx);
       else
 #endif /* RE_ENABLE_I18N */
       if (type == OP_BACK_REF)
        {
-         int subexp_idx = dfa->nodes[node].opr.idx;
+         Idx subexp_idx = dfa->nodes[node].opr.idx + 1;
          naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so;
          if (fs != NULL)
            {
@@ -1171,7 +1288,7 @@ proceed_next_node (preg, nregs, regs, mctx, pidx, node, eps_via_nodes, fs)
                return -1;
              else if (naccepted)
                {
-                 char *buf = (char *) re_string_get_buffer (mctx->input);
+                 char *buf = (char *) re_string_get_buffer (&mctx->input);
                  if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx,
                              naccepted) != 0)
                    return -1;
@@ -1180,8 +1297,9 @@ proceed_next_node (preg, nregs, regs, mctx, pidx, node, eps_via_nodes, fs)
 
          if (naccepted == 0)
            {
-             err = re_node_set_insert (eps_via_nodes, node);
-             if (BE (err < 0, 0))
+             Idx dest_node;
+             ok = re_node_set_insert (eps_via_nodes, node);
+             if (BE (! ok, 0))
                return -2;
              dest_node = dfa->edests[node].elems[0];
              if (re_node_set_contains (&mctx->state_log[*pidx]->nodes,
@@ -1191,9 +1309,9 @@ proceed_next_node (preg, nregs, regs, mctx, pidx, node, eps_via_nodes, fs)
        }
 
       if (naccepted != 0
-         || check_node_accept (preg, dfa->nodes + node, mctx, *pidx))
+         || check_node_accept (mctx, dfa->nodes + node, *pidx))
        {
-         dest_node = dfa->nexts[node];
+         Idx 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,
@@ -1207,42 +1325,39 @@ proceed_next_node (preg, nregs, regs, mctx, pidx, node, eps_via_nodes, fs)
 }
 
 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;
+__attribute_warn_unused_result__
+push_fail_stack (struct re_fail_stack_t *fs, Idx str_idx, Idx dest_node,
+                Idx nregs, regmatch_t *regs, re_node_set *eps_via_nodes)
 {
   reg_errcode_t err;
-  int num = fs->num++;
+  Idx 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));
+      new_array = re_realloc (fs->stack, struct re_fail_stack_ent_t,
+                              fs->alloc * 2);
       if (new_array == NULL)
        return REG_ESPACE;
+      fs->alloc *= 2;
       fs->stack = new_array;
     }
   fs->stack[num].idx = str_idx;
-  fs->stack[num].node = dests[1];
+  fs->stack[num].node = dest_node;
   fs->stack[num].regs = re_malloc (regmatch_t, nregs);
+  if (fs->stack[num].regs == NULL)
+    return REG_ESPACE;
   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;
+static Idx
+pop_fail_stack (struct re_fail_stack_t *fs, Idx *pidx, Idx nregs,
+               regmatch_t *regs, re_node_set *eps_via_nodes)
 {
-  int num = --fs->num;
+  Idx num = --fs->num;
   assert (num >= 0);
- *pidx = fs->stack[num].idx;
 *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);
@@ -1253,21 +1368,21 @@ pop_fail_stack (fs, pidx, nregs, regs, eps_via_nodes)
 /* 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).  */
+   pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch.  */
 
 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;
+__attribute_warn_unused_result__
+set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch,
+         regmatch_t *pmatch, bool fl_backtrack)
+{
+  const re_dfa_t *dfa = preg->buffer;
+  Idx idx, cur_node;
   re_node_set eps_via_nodes;
   struct re_fail_stack_t *fs;
-  struct re_fail_stack_t fs_body = {0, 2, NULL};
+  struct re_fail_stack_t fs_body = { 0, 2, NULL };
+  regmatch_t *prev_idx_match;
+  bool prev_idx_match_malloced = false;
+
 #ifdef DEBUG
   assert (nmatch > 1);
   assert (mctx->state_log != NULL);
@@ -1276,18 +1391,36 @@ set_regs (preg, mctx, nmatch, pmatch, fl_backtrack)
     {
       fs = &fs_body;
       fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc);
+      if (fs->stack == NULL)
+       return REG_ESPACE;
     }
   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);
+
+  if (__libc_use_alloca (nmatch * sizeof (regmatch_t)))
+    prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t));
+  else
+    {
+      prev_idx_match = re_malloc (regmatch_t, nmatch);
+      if (prev_idx_match == NULL)
+       {
+         free_fail_stack_return (fs);
+         return REG_ESPACE;
+       }
+      prev_idx_match_malloced = true;
+    }
+  memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+
   for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;)
     {
-      update_regs (dfa, pmatch, cur_node, idx, real_nmatch);
+      update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch);
+
       if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node)
        {
-         int reg_idx;
+         Idx reg_idx;
          if (fs)
            {
              for (reg_idx = 0; reg_idx < nmatch; ++reg_idx)
@@ -1296,6 +1429,8 @@ set_regs (preg, mctx, nmatch, pmatch, fl_backtrack)
              if (reg_idx == nmatch)
                {
                  re_node_set_free (&eps_via_nodes);
+                 if (prev_idx_match_malloced)
+                   re_free (prev_idx_match);
                  return free_fail_stack_return (fs);
                }
              cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch,
@@ -1304,39 +1439,50 @@ set_regs (preg, mctx, nmatch, pmatch, fl_backtrack)
          else
            {
              re_node_set_free (&eps_via_nodes);
+             if (prev_idx_match_malloced)
+               re_free (prev_idx_match);
              return REG_NOERROR;
            }
        }
 
       /* Proceed to next node.  */
-      cur_node = proceed_next_node (preg, nmatch, pmatch, mctx, &idx, cur_node,
+      cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node,
                                    &eps_via_nodes, fs);
 
       if (BE (cur_node < 0, 0))
        {
-         if (cur_node == -2)
-           return REG_ESPACE;
+         if (BE (cur_node == -2, 0))
+           {
+             re_node_set_free (&eps_via_nodes);
+             if (prev_idx_match_malloced)
+               re_free (prev_idx_match);
+             free_fail_stack_return (fs);
+             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);
+             if (prev_idx_match_malloced)
+               re_free (prev_idx_match);
              return REG_NOMATCH;
            }
        }
     }
   re_node_set_free (&eps_via_nodes);
+  if (prev_idx_match_malloced)
+    re_free (prev_idx_match);
   return free_fail_stack_return (fs);
 }
 
 static reg_errcode_t
-free_fail_stack_return (fs)
-     struct re_fail_stack_t *fs;
+free_fail_stack_return (struct re_fail_stack_t *fs)
 {
   if (fs)
     {
-      int fs_idx;
+      Idx fs_idx;
       for (fs_idx = 0; fs_idx < fs->num; ++fs_idx)
        {
          re_node_set_free (&fs->stack[fs_idx].eps_via_nodes);
@@ -1348,85 +1494,100 @@ free_fail_stack_return (fs)
 }
 
 static void
-update_regs (dfa, pmatch, cur_node, cur_idx, nmatch)
-     re_dfa_t *dfa;
-     regmatch_t *pmatch;
-     int cur_node, cur_idx, nmatch;
+update_regs (const re_dfa_t *dfa, regmatch_t *pmatch,
+            regmatch_t *prev_idx_match, Idx cur_node, Idx cur_idx, 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)
     {
+      Idx reg_num = dfa->nodes[cur_node].opr.idx + 1;
+
       /* We are at the first node of this sub expression.  */
-      pmatch[reg_num].rm_so = cur_idx;
-      pmatch[reg_num].rm_eo = -1;
+      if (reg_num < nmatch)
+       {
+         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;
+    {
+      Idx reg_num = dfa->nodes[cur_node].opr.idx + 1;
+      if (reg_num < nmatch)
+       {
+         /* We are at the last node of this sub expression.  */
+         if (pmatch[reg_num].rm_so < cur_idx)
+           {
+             pmatch[reg_num].rm_eo = cur_idx;
+             /* This is a non-empty match or we are not inside an optional
+                subexpression.  Accept this right away.  */
+             memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch);
+           }
+         else
+           {
+             if (dfa->nodes[cur_node].opt_subexp
+                 && prev_idx_match[reg_num].rm_so != -1)
+               /* We transited through an empty match for an optional
+                  subexpression, like (a?)*, and this is not the subexp's
+                  first match.  Copy back the old content of the registers
+                  so that matches of an inner subexpression are undone as
+                  well, like in ((a?))*.  */
+               memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch);
+             else
+               /* We completed a subexpression, but it may be part of
+                  an optional one, so do not update PREV_IDX_MATCH.  */
+               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...
+   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':
+       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'.
+          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':
+           thrown away, we throw away the node 'a'.
+     3. When 0 <= STR_IDX < MATCH_LAST 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'.  */
+          node 'a'.
+       ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown 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;
+sift_states_backward (const 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;
+  Idx 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);
+  err = update_cur_sifted_state (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)
@@ -1438,64 +1599,19 @@ sift_states_backward (preg, mctx, sctx)
        }
       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;
-           }
+      if (mctx->state_log[str_idx])
+       {
+         err = build_sifted_states (mctx, sctx, str_idx, &cur_dest);
+         if (BE (err != REG_NOERROR, 0))
+           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);
+      err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest);
       if (BE (err != REG_NOERROR, 0))
        goto free_return;
     }
@@ -1505,21 +1621,80 @@ sift_states_backward (preg, mctx, sctx)
   return err;
 }
 
+static reg_errcode_t
+__attribute_warn_unused_result__
+build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx,
+                    Idx str_idx, re_node_set *cur_dest)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes;
+  Idx i;
+
+  /* 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]'
+     (with the epsilon nodes pre-filtered out).  */
+  for (i = 0; i < cur_src->nelem; i++)
+    {
+      Idx prev_node = cur_src->elems[i];
+      int naccepted = 0;
+      bool ok;
+
+#ifdef DEBUG
+      re_token_type_t type = dfa->nodes[prev_node].type;
+      assert (!IS_EPSILON_NODE (type));
+#endif
+#ifdef RE_ENABLE_I18N
+      /* If the node may accept "multi byte".  */
+      if (dfa->nodes[prev_node].accept_mb)
+       naccepted = sift_states_iter_mb (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 (mctx, dfa->nodes + prev_node, 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)
+       {
+         Idx to_idx = str_idx + naccepted;
+         if (check_dst_limits (mctx, &sctx->limits,
+                               dfa->nexts[prev_node], to_idx,
+                               prev_node, str_idx))
+           continue;
+       }
+      ok = re_node_set_insert (cur_dest, prev_node);
+      if (BE (! ok, 0))
+       return REG_ESPACE;
+    }
+
+  return REG_NOERROR;
+}
+
 /* 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;
+static reg_errcode_t
+clean_state_log_if_needed (re_match_context_t *mctx, Idx next_state_log_idx)
 {
-  int top = mctx->state_log_top;
+  Idx 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))
+  if ((next_state_log_idx >= mctx->input.bufs_len
+       && mctx->input.bufs_len < mctx->input.len)
+      || (next_state_log_idx >= mctx->input.valid_len
+         && mctx->input.valid_len < mctx->input.len))
     {
       reg_errcode_t err;
-      err = extend_buffers (mctx);
+      err = extend_buffers (mctx, next_state_log_idx + 1);
       if (BE (err != REG_NOERROR, 0))
        return err;
     }
@@ -1534,13 +1709,10 @@ clean_state_log_if_need (mctx, next_state_log_idx)
 }
 
 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;
+merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst,
+                  re_dfastate_t **src, Idx num)
 {
-  int st_idx;
+  Idx st_idx;
   reg_errcode_t err;
   for (st_idx = 0; st_idx < num; ++st_idx)
     {
@@ -1563,46 +1735,46 @@ merge_state_array (dfa, dst, src, num)
 }
 
 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;
+update_cur_sifted_state (const re_match_context_t *mctx,
+                        re_sift_context_t *sctx, Idx str_idx,
+                        re_node_set *dest_nodes)
 {
-  reg_errcode_t err;
-  re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err = REG_NOERROR;
   const re_node_set *candidates;
-  candidates = ((mctx->state_log[str_idx] == NULL) ? &empty_set
+  candidates = ((mctx->state_log[str_idx] == NULL) ? NULL
                : &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)
+  if (dest_nodes->nelem == 0)
+    sctx->sifted_states[str_idx] = NULL;
+  else
     {
-      err = add_epsilon_src_nodes (dfa, dest_nodes, candidates);
-      if (BE (err != REG_NOERROR, 0))
-       return err;
-    }
+      if (candidates)
+       {
+         /* At first, add the nodes which can epsilon transit to a node in
+            DEST_NODE.  */
+         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);
+         /* Then, check the limitations in the current sift_context.  */
+         if (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;
+           }
+       }
+
+      sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes);
       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))
+  if (candidates && mctx->state_log[str_idx]->has_backref)
     {
-      err = sift_states_bkref (preg, mctx, sctx, str_idx, dest_nodes);
+      err = sift_states_bkref (mctx, sctx, str_idx, candidates);
       if (BE (err != REG_NOERROR, 0))
        return err;
     }
@@ -1610,54 +1782,52 @@ update_cur_sifted_state (preg, mctx, sctx, str_idx, dest_nodes)
 }
 
 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;
+__attribute_warn_unused_result__
+add_epsilon_src_nodes (const 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;
+  reg_errcode_t err = REG_NOERROR;
+  Idx i;
 
-  err = re_node_set_init_copy (&src_copy, dest_nodes);
+  re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes);
   if (BE (err != REG_NOERROR, 0))
     return err;
-  for (src_idx = 0; src_idx < src_copy.nelem; ++src_idx)
+
+  if (!state->inveclosure.alloc)
     {
-      err = re_node_set_add_intersect (dest_nodes, candidates,
-                                      dfa->inveclosures
-                                      + src_copy.elems[src_idx]);
+      err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem);
       if (BE (err != REG_NOERROR, 0))
+       return REG_ESPACE;
+      for (i = 0; i < dest_nodes->nelem; i++)
        {
-         re_node_set_free (&src_copy);
-         return err;
+         err = re_node_set_merge (&state->inveclosure,
+                                  dfa->inveclosures + dest_nodes->elems[i]);
+         if (BE (err != REG_NOERROR, 0))
+           return REG_ESPACE;
        }
     }
-  re_node_set_free (&src_copy);
-  return REG_NOERROR;
+  return re_node_set_add_intersect (dest_nodes, candidates,
+                                   &state->inveclosure);
 }
 
 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;
+sub_epsilon_src_nodes (const re_dfa_t *dfa, Idx node, re_node_set *dest_nodes,
+                      const re_node_set *candidates)
 {
-    int ecl_idx;
+    Idx 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];
+       Idx 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)
+           Idx edst1 = dfa->edests[cur_node].elems[0];
+           Idx 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))
@@ -1677,10 +1847,10 @@ sub_epsilon_src_nodes (dfa, node, dest_nodes, candidates)
       }
     for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx)
       {
-       int cur_node = inv_eclosure->elems[ecl_idx];
+       Idx 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;
+           Idx idx = re_node_set_contains (dest_nodes, cur_node) - 1;
            re_node_set_remove_at (dest_nodes, idx);
          }
       }
@@ -1688,28 +1858,28 @@ sub_epsilon_src_nodes (dfa, node, dest_nodes, candidates)
     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;
+static bool
+check_dst_limits (const re_match_context_t *mctx, const re_node_set *limits,
+                 Idx dst_node, Idx dst_idx, Idx src_node, Idx src_idx)
 {
-  int lim_idx, src_pos, dst_pos;
+  const re_dfa_t *const dfa = mctx->dfa;
+  Idx lim_idx, src_pos, dst_pos;
 
+  Idx dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx);
+  Idx src_bkref_idx = search_cur_bkref_entry (mctx, src_idx);
   for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
     {
-      int subexp_idx;
+      Idx 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;
+      subexp_idx = dfa->nodes[ent->node].opr.idx;
 
-      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);
+      dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+                                          subexp_idx, dst_node, dst_idx,
+                                          dst_bkref_idx);
+      src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx],
+                                          subexp_idx, src_node, src_idx,
+                                          src_bkref_idx);
 
       /* In case of:
         <src> <dst> ( <subexp> )
@@ -1718,101 +1888,147 @@ check_dst_limits (dfa, limits, mctx, dst_node, dst_idx, src_node, src_idx)
       if (src_pos == dst_pos)
        continue; /* This is unrelated limitation.  */
       else
-       return 1;
+       return true;
     }
-  return 0;
+  return false;
 }
 
 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;
+check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries,
+                            Idx subexp_idx, Idx from_node, Idx bkref_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))
+  const re_dfa_t *const dfa = mctx->dfa;
+  const re_node_set *eclosures = dfa->eclosures + from_node;
+  Idx node_idx;
+
+  /* Else, we are on the boundary: examine the nodes on the epsilon
+     closure.  */
+  for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
     {
-      int node_idx;
-      for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx)
+      Idx node = eclosures->elems[node_idx];
+      switch (dfa->nodes[node].type)
        {
-         int node = eclosures->elems[node_idx];
-         re_token_type_t type= dfa->nodes[node].type;
-         if (type == OP_BACK_REF)
+       case OP_BACK_REF:
+         if (bkref_idx != -1)
            {
-             int bi = search_cur_bkref_entry (mctx, str_idx);
-             for (; bi < mctx->nbkref_ents; ++bi)
+             struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx;
+             do
                {
-                 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)
+                 Idx dst;
+                 int cpos;
+
+                 if (ent->node != node)
+                   continue;
+
+                 if (subexp_idx < BITSET_WORD_BITS
+                     && !(ent->eps_reachable_subexps_map
+                          & ((bitset_word_t) 1 << subexp_idx)))
+                   continue;
+
+                 /* Recurse trying to reach the OP_OPEN_SUBEXP and
+                    OP_CLOSE_SUBEXP cases below.  But, if the
+                    destination node is the same node as the source
+                    node, don't recurse because it would cause an
+                    infinite loop: a regex that exhibits this behavior
+                    is ()\1*\1*  */
+                 dst = dfa->edests[node].elems[0];
+                 if (dst == from_node)
                    {
-                     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 (boundaries & 1)
+                       return -1;
+                     else /* if (boundaries & 2) */
+                       return 0;
                    }
+
+                 cpos =
+                   check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+                                                dst, bkref_idx);
+                 if (cpos == -1 /* && (boundaries & 1) */)
+                   return -1;
+                 if (cpos == 0 && (boundaries & 2))
+                   return 0;
+
+                 if (subexp_idx < BITSET_WORD_BITS)
+                   ent->eps_reachable_subexps_map
+                     &= ~((bitset_word_t) 1 << subexp_idx);
                }
+             while (ent++->more);
            }
-         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;
+
+       case OP_OPEN_SUBEXP:
+         if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx)
+           return -1;
+         break;
+
+       case OP_CLOSE_SUBEXP:
+         if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx)
+           return 0;
+         break;
+
+       default:
            break;
        }
-      if (node_idx == eclosures->nelem && str_idx == lim->subexp_to)
-       pos = 1;
     }
-  return pos;
+
+  return (boundaries & 2) ? 1 : 0;
+}
+
+static int
+check_dst_limits_calc_pos (const re_match_context_t *mctx, Idx limit,
+                          Idx subexp_idx, Idx from_node, Idx str_idx,
+                          Idx bkref_idx)
+{
+  struct re_backref_cache_entry *lim = mctx->bkref_ents + limit;
+  int boundaries;
+
+  /* If we are outside the range of the subexpression, return -1 or 1.  */
+  if (str_idx < lim->subexp_from)
+    return -1;
+
+  if (lim->subexp_to < str_idx)
+    return 1;
+
+  /* If we are within the subexpression, return 0.  */
+  boundaries = (str_idx == lim->subexp_from);
+  boundaries |= (str_idx == lim->subexp_to) << 1;
+  if (boundaries == 0)
+    return 0;
+
+  /* Else, examine epsilon closure.  */
+  return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx,
+                                     from_node, bkref_idx);
 }
 
 /* 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;
+check_subexp_limits (const 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, Idx str_idx)
 {
   reg_errcode_t err;
-  int node_idx, lim_idx;
+  Idx node_idx, lim_idx;
 
   for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx)
     {
-      int subexp_idx;
+      Idx 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;
+      subexp_idx = dfa->nodes[ent->node].opr.idx;
       if (ent->subexp_to == str_idx)
        {
-         int ops_node = -1;
-         int cls_node = -1;
+         Idx ops_node = -1;
+         Idx 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;
+             Idx 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;
@@ -1825,48 +2041,48 @@ check_subexp_limits (dfa, dest_nodes, candidates, limits, bkref_ents, str_idx)
          /* 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);
+             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;
-               }
-           }
+         if (cls_node >= 0)
+           for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx)
+             {
+               Idx 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;
+             Idx 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;
-                   }
+                 /* 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;
                }
            }
        }
@@ -1875,116 +2091,92 @@ check_subexp_limits (dfa, dest_nodes, candidates, limits, bkref_ents, str_idx)
 }
 
 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;
+__attribute_warn_unused_result__
+sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx,
+                  Idx str_idx, const re_node_set *candidates)
 {
+  const re_dfa_t *const dfa = mctx->dfa;
   reg_errcode_t err;
-  re_dfa_t *dfa = (re_dfa_t *)preg->buffer;
-  int node_idx, node;
+  Idx 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);
+  Idx first_idx = search_cur_bkref_entry (mctx, str_idx);
+
+  if (first_idx == -1)
+    return REG_NOERROR;
+
   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);
+      Idx enabled_idx;
       re_token_type_t type;
+      struct re_backref_cache_entry *entry;
       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)
+      if (type != OP_BACK_REF)
+       continue;
+
+      entry = mctx->bkref_ents + first_idx;
+      enabled_idx = first_idx;
+      do
        {
-         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;
-                   }
+         Idx subexp_len;
+         Idx to_idx;
+         Idx dst_node;
+         bool ok;
+         re_dfastate_t *cur_state;
 
-                 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;
-               }
+         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 (mctx, &sctx->limits, node,
+                                  str_idx, dst_node, to_idx))
+           continue;
+
+         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;
            }
-         enabled_idx = search_cur_bkref_entry (mctx, str_idx);
-         for (; enabled_idx < mctx->nbkref_ents; ++enabled_idx)
+         local_sctx.last_node = node;
+         local_sctx.last_str_idx = str_idx;
+         ok = re_node_set_insert (&local_sctx.limits, enabled_idx);
+         if (BE (! ok, 0))
            {
-             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_ESPACE;
+             goto free_return;
            }
+         cur_state = local_sctx.sifted_states[str_idx];
+         err = sift_states_backward (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);
+
+         /* mctx->bkref_ents may have changed, reload the pointer.  */
+         entry = mctx->bkref_ents + enabled_idx;
        }
+      while (enabled_idx++, entry++->more);
     }
   err = REG_NOERROR;
  free_return:
@@ -1999,25 +2191,22 @@ sift_states_bkref (preg, mctx, sctx, str_idx, dest_nodes)
 
 #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;
+sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx,
+                    Idx node_idx, Idx str_idx, Idx max_str_idx)
 {
-  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
+  const re_dfa_t *const dfa = mctx->dfa;
   int naccepted;
-  /* Check the node can accept `multi byte'.  */
-  naccepted = check_node_accept_bytes (preg, node_idx, mctx->input, str_idx);
+  /* Check the node can accept "multi byte".  */
+  naccepted = check_node_accept_bytes (dfa, 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'.   */
+    /* The node can't accept the "multi byte", or the
+       destination was already thrown 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.  */
+     'naccepted' bytes input.  */
   return naccepted;
 }
 #endif /* RE_ENABLE_I18N */
@@ -2031,140 +2220,162 @@ sift_states_iter_mb (preg, mctx, sctx, node_idx, str_idx, max_str_idx)
    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;
+__attribute_warn_unused_result__
+transit_state (reg_errcode_t *err, re_match_context_t *mctx,
+              re_dfastate_t *state)
+{
+  re_dfastate_t **trtable;
   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))
+#ifdef RE_ENABLE_I18N
+  /* If the current state can accept multibyte.  */
+  if (BE (state->accept_mb, 0))
     {
-      *err = extend_buffers (mctx);
+      *err = transit_state_mb (mctx, state);
       if (BE (*err != REG_NOERROR, 0))
        return NULL;
     }
+#endif /* RE_ENABLE_I18N */
 
-  *err = REG_NOERROR;
-  if (state == NULL)
-    {
-      next_state = state;
-      re_string_skip_bytes (mctx->input, 1);
-    }
-  else
+  /* Then decide the next state with the single byte.  */
+#if 0
+  if (0)
+    /* don't use transition table  */
+    return transit_state_sb (err, mctx, state);
+#endif
+
+  /* Use transition table  */
+  ch = re_string_fetch_byte (&mctx->input);
+  for (;;)
     {
-#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 */
+      trtable = state->trtable;
+      if (BE (trtable != NULL, 1))
+       return trtable[ch];
 
-      /* Then decide the next state with the single byte.  */
-      if (1)
+      trtable = state->word_trtable;
+      if (BE (trtable != NULL, 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];
+         unsigned int context;
+         context
+           = re_string_context_at (&mctx->input,
+                                   re_string_cur_idx (&mctx->input) - 1,
+                                   mctx->eflags);
+         if (IS_WORD_CONTEXT (context))
+           return trtable[ch + SBC_MAX];
+         else
+           return trtable[ch];
        }
-      else
+
+      if (!build_trtable (mctx->dfa, state))
        {
-         /* 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;
+         *err = REG_ESPACE;
+         return NULL;
        }
+
+      /* Retry, we now have a transition table.  */
     }
+}
 
-  cur_idx = re_string_cur_idx (mctx->input);
-  /* Update the state_log if we need.  */
-  if (mctx->state_log != NULL)
+/* Update the state_log if we need */
+static re_dfastate_t *
+merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx,
+                     re_dfastate_t *next_state)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  Idx cur_idx = re_string_cur_idx (&mctx->input);
+
+  if (cur_idx > mctx->state_log_top)
     {
-      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
+      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)
        {
-         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,
+         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);
+         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);
+      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 */)
+  if (BE (dfa->nbackref, 0) && next_state != NULL)
     {
-      *err = check_subexp_matching_top (dfa, mctx, &next_state->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.  */
+      *err = check_subexp_matching_top (mctx, &next_state->nodes,
                                        cur_idx);
       if (BE (*err != REG_NOERROR, 0))
        return NULL;
+
+      /* If the next state has back references.  */
+      if (next_state->has_backref)
+       {
+         *err = transit_state_bkref (mctx, &next_state->nodes);
+         if (BE (*err != REG_NOERROR, 0))
+           return NULL;
+         next_state = mctx->state_log[cur_idx];
+       }
     }
 
-  /* If the next state has back references.  */
-  if (next_state != NULL && next_state->has_backref)
+  return next_state;
+}
+
+/* Skip bytes in the input that correspond to part of a
+   multi-byte match, then look in the log for a state
+   from which to restart matching.  */
+static re_dfastate_t *
+find_recover_state (reg_errcode_t *err, re_match_context_t *mctx)
+{
+  re_dfastate_t *cur_state;
+  do
     {
-      *err = transit_state_bkref (preg, &next_state->nodes, mctx);
-      if (BE (*err != REG_NOERROR, 0))
-       return NULL;
-      next_state = mctx->state_log[cur_idx];
+      Idx max = mctx->state_log_top;
+      Idx cur_str_idx = re_string_cur_idx (&mctx->input);
+
+      do
+       {
+         if (++cur_str_idx > max)
+           return NULL;
+         re_string_skip_bytes (&mctx->input, 1);
+       }
+      while (mctx->state_log[cur_str_idx] == NULL);
+
+      cur_state = merge_state_with_log (err, mctx, NULL);
     }
-  return next_state;
+  while (*err == REG_NOERROR && cur_state == NULL);
+  return cur_state;
 }
 
 /* Helper functions for transit_state.  */
@@ -2172,16 +2383,14 @@ transit_state (err, preg, mctx, state, fl_search)
 /* 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.  */
+   corresponding 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;
+check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes,
+                          Idx str_idx)
 {
-  int node_idx;
+  const re_dfa_t *const dfa = mctx->dfa;
+  Idx node_idx;
   reg_errcode_t err;
 
   /* TODO: This isn't efficient.
@@ -2191,9 +2400,11 @@ check_subexp_matching_top (dfa, mctx, cur_nodes, str_idx)
           E.g. RE: (a){2}  */
   for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx)
     {
-      int node = cur_nodes->elems[node_idx];
+      Idx node = cur_nodes->elems[node_idx];
       if (dfa->nodes[node].type == OP_OPEN_SUBEXP
-         && dfa->used_bkref_map & (1 << dfa->nodes[node].opr.idx))
+         && dfa->nodes[node].opr.idx < BITSET_WORD_BITS
+         && (dfa->used_bkref_map
+             & ((bitset_word_t) 1 << dfa->nodes[node].opr.idx)))
        {
          err = match_ctx_add_subtop (mctx, node, str_idx);
          if (BE (err != REG_NOERROR, 0))
@@ -2203,21 +2414,18 @@ check_subexp_matching_top (dfa, mctx, cur_nodes, str_idx)
   return REG_NOERROR;
 }
 
+#if 0
 /* 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;
+transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx,
+                 re_dfastate_t *state)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
   re_node_set next_nodes;
   re_dfastate_t *next_state;
-  int node_cnt, cur_str_idx = re_string_cur_idx (mctx->input);
+  Idx 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);
@@ -2225,8 +2433,8 @@ transit_state_sb (err, preg, state, fl_search, mctx)
     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))
+      Idx cur_node = state->nodes.elems[node_cnt];
+      if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx))
        {
          *err = re_node_set_merge (&next_nodes,
                                    dfa->eclosures + dfa->nexts[cur_node]);
@@ -2237,89 +2445,64 @@ transit_state_sb (err, preg, state, fl_search, mctx)
            }
        }
     }
-  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);
+  context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags);
   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);
+  re_string_skip_bytes (&mctx->input, 1);
   return next_state;
 }
+#endif
 
 #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;
+transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate)
 {
+  const re_dfa_t *const dfa = mctx->dfa;
   reg_errcode_t err;
-  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
-  int i;
+  Idx 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;
+      Idx cur_node_idx = pstate->nodes.elems[i];
+      int naccepted;
+      Idx dest_idx;
       unsigned int context;
       re_dfastate_t *dest_state;
 
+      if (!dfa->nodes[cur_node_idx].accept_mb)
+       continue;
+
       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);
+         context = re_string_context_at (&mctx->input,
+                                         re_string_cur_idx (&mctx->input),
+                                         mctx->eflags);
          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));
+      /* How many bytes the node can accept?  */
+      naccepted = check_node_accept_bytes (dfa, 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;
+      /* 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);
+      err = clean_state_log_if_needed (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]];
+      new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx];
 
       dest_state = mctx->state_log[dest_idx];
       if (dest_state == NULL)
@@ -2331,8 +2514,8 @@ transit_state_mb (preg, pstate, mctx)
          if (BE (err != REG_NOERROR, 0))
            return err;
        }
-      context = re_string_context_at (mctx->input, dest_idx - 1, mctx->eflags,
-                                     preg->newline_anchor);
+      context = re_string_context_at (&mctx->input, dest_idx - 1,
+                                     mctx->eflags);
       mctx->state_log[dest_idx]
        = re_acquire_state_context (&err, dfa, &dest_nodes, context);
       if (dest_state != NULL)
@@ -2345,51 +2528,48 @@ transit_state_mb (preg, pstate, mctx)
 #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;
+transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes)
 {
+  const re_dfa_t *const dfa = mctx->dfa;
   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);
+  Idx i;
+  Idx 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];
+      Idx dest_str_idx, prev_nelem, bkc_idx;
+      Idx node_idx = nodes->elems[i];
       unsigned int context;
-      re_token_t *node = dfa->nodes + node_idx;
+      const re_token_t *node = dfa->nodes + node_idx;
       re_node_set *new_dest_nodes;
 
-      /* Check whether `node' is a backreference or not.  */
+      /* 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);
+         context = re_string_context_at (&mctx->input, cur_str_idx,
+                                         mctx->eflags);
          if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
            continue;
        }
 
-      /* `node' is a backreference.
+      /* '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);
+      err = get_subexp (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
+      /* 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;
+         Idx subexp_len;
          re_dfastate_t *dest_state;
          struct re_backref_cache_entry *bkref_ent;
          bkref_ent = mctx->bkref_ents + bkc_idx;
@@ -2401,12 +2581,12 @@ transit_state_bkref (preg, nodes, mctx)
                            : 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);
+         context = re_string_context_at (&mctx->input, dest_str_idx - 1,
+                                         mctx->eflags);
          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.  */
+         /* Add 'new_dest_node' to state_log.  */
          if (dest_state == NULL)
            {
              mctx->state_log[dest_str_idx]
@@ -2439,11 +2619,11 @@ transit_state_bkref (preg, nodes, mctx)
          if (subexp_len == 0
              && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem)
            {
-             err = check_subexp_matching_top (dfa, mctx, new_dest_nodes,
+             err = check_subexp_matching_top (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);
+             err = transit_state_bkref (mctx, new_dest_nodes);
              if (BE (err != REG_NOERROR, 0))
                goto free_return;
            }
@@ -2461,25 +2641,25 @@ transit_state_bkref (preg, nodes, mctx)
    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);
+__attribute_warn_unused_result__
+get_subexp (re_match_context_t *mctx, Idx bkref_node, Idx bkref_str_idx)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  Idx subexp_num, sub_top_idx;
+  const char *buf = (const 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)
+  Idx cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx);
+  if (cache_idx != -1)
     {
-      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.  */
+      const struct re_backref_cache_entry *entry
+       = mctx->bkref_ents + cache_idx;
+      do
+       if (entry->node == bkref_node)
+         return REG_NOERROR; /* We already checked it.  */
+      while (entry++->more);
     }
-  subexp_num = dfa->nodes[bkref_node].opr.idx - 1;
+
+  subexp_num = dfa->nodes[bkref_node].opr.idx;
 
   /* For each sub expression  */
   for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx)
@@ -2487,35 +2667,56 @@ get_subexp (preg, mctx, bkref_node, bkref_str_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;
+      Idx sub_last_idx, sl_str, bkref_str_off;
 
       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;
+      bkref_str_off = 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;
+         regoff_t 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;
+         if (sl_str_diff > 0)
+           {
+             if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0))
+               {
+                 /* Not enough chars for a successful match.  */
+                 if (bkref_str_off + sl_str_diff > mctx->input.len)
+                   break;
+
+                 err = clean_state_log_if_needed (mctx,
+                                                  bkref_str_off
+                                                  + sl_str_diff);
+                 if (BE (err != REG_NOERROR, 0))
+                   return err;
+                 buf = (const char *) re_string_get_buffer (&mctx->input);
+               }
+             if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0)
+               /* We don't need to search this sub expression any more.  */
+               break;
+           }
+         bkref_str_off += sl_str_diff;
          sl_str += sl_str_diff;
-         err = get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node,
+         err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
                                bkref_str_idx);
+
+         /* Reload buf, since the preceding call might have reallocated
+            the buffer.  */
+         buf = (const char *) re_string_get_buffer (&mctx->input);
+
          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)
@@ -2523,19 +2724,36 @@ get_subexp (preg, mctx, bkref_node, bkref_str_idx)
       /* 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;
+         Idx cls_node;
+         regoff_t sl_str_off;
+         const 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 (sl_str_off > 0)
+           {
+             if (BE (bkref_str_off >= mctx->input.valid_len, 0))
+               {
+                 /* If we are at the end of the input, we cannot match.  */
+                 if (bkref_str_off >= mctx->input.len)
+                   break;
+
+                 err = extend_buffers (mctx, bkref_str_off + 1);
+                 if (BE (err != REG_NOERROR, 0))
+                   return err;
+
+                 buf = (const char *) re_string_get_buffer (&mctx->input);
+               }
+             if (buf [bkref_str_off++] != buf[sl_str - 1])
+               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);
+         cls_node = find_subexp_node (dfa, nodes, subexp_num,
+                                      OP_CLOSE_SUBEXP);
          if (cls_node == -1)
            continue; /* No.  */
          if (sub_top->path == NULL)
@@ -2547,8 +2765,9 @@ get_subexp (preg, mctx, bkref_node, bkref_str_idx)
            }
          /* 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);
+         err = check_arrival (mctx, sub_top->path, sub_top->node,
+                              sub_top->str_idx, cls_node, sl_str,
+                              OP_CLOSE_SUBEXP);
          if (err == REG_NOMATCH)
              continue;
          if (BE (err != REG_NOERROR, 0))
@@ -2556,7 +2775,7 @@ get_subexp (preg, mctx, bkref_node, bkref_str_idx)
          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,
+         err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node,
                                bkref_str_idx);
          if (err == REG_NOMATCH)
            continue;
@@ -2572,18 +2791,15 @@ get_subexp (preg, mctx, bkref_node, bkref_str_idx)
    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;
+get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top,
+               re_sub_match_last_t *sub_last, Idx bkref_node, Idx bkref_str)
 {
   reg_errcode_t err;
-  int to_idx;
+  Idx 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);
+  err = check_arrival (mctx, &sub_last->path, sub_last->node,
+                      sub_last->str_idx, bkref_node, bkref_str,
+                      OP_OPEN_SUBEXP);
   if (err != REG_NOERROR)
     return err;
   err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx,
@@ -2591,8 +2807,7 @@ get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node, bkref_str)
   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;
+  return clean_state_log_if_needed (mctx, to_idx);
 }
 
 /* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX.
@@ -2603,19 +2818,16 @@ get_subexp_sub (preg, mctx, sub_top, sub_last, bkref_node, bkref_str)
         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;
+static Idx
+find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
+                 Idx subexp_idx, int type)
 {
-  int cls_idx;
+  Idx 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))
+      Idx cls_node = nodes->elems[cls_idx];
+      const re_token_t *node = dfa->nodes + cls_node;
+      if (node->type == type
          && node->opr.idx == subexp_idx)
        return cls_node;
     }
@@ -2628,16 +2840,13 @@ find_subexp_node (dfa, nodes, subexp_idx, fl_open)
    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;
+__attribute_warn_unused_result__
+check_arrival (re_match_context_t *mctx, state_array_t *path, Idx top_node,
+              Idx top_str, Idx last_node, Idx last_str, int type)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  reg_errcode_t err = REG_NOERROR;
+  Idx 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;
@@ -2645,36 +2854,42 @@ check_arrival (preg, mctx, path, top_node, top_str, last_node, last_str,
 
   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)
+  if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0))
     {
       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)
+      Idx old_alloc = path->alloc;
+      Idx incr_alloc = last_str + mctx->max_mb_elem_len + 1;
+      Idx new_alloc;
+      if (BE (IDX_MAX - old_alloc < incr_alloc, 0))
+       return REG_ESPACE;
+      new_alloc = old_alloc + incr_alloc;
+      if (BE (SIZE_MAX / sizeof (re_dfastate_t *) < new_alloc, 0))
+       return REG_ESPACE;
+      new_array = re_realloc (path->array, re_dfastate_t *, new_alloc);
+      if (BE (new_array == NULL, 0))
        return REG_ESPACE;
       path->array = new_array;
+      path->alloc = new_alloc;
       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;
+  str_idx = path->next_idx ? path->next_idx : top_str;
 
   /* Temporary modify MCTX.  */
   backup_state_log = mctx->state_log;
-  backup_cur_idx = mctx->input->cur_idx;
+  backup_cur_idx = mctx->input.cur_idx;
   mctx->state_log = path->array;
-  mctx->input->cur_idx = str_idx;
+  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);
+  context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
   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);
+      err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
       if (BE (err != REG_NOERROR, 0))
        {
          re_node_set_free (&next_nodes);
@@ -2687,7 +2902,7 @@ check_arrival (preg, mctx, path, top_node, top_str, last_node, last_str,
       if (cur_state && cur_state->has_backref)
        {
          err = re_node_set_init_copy (&next_nodes, &cur_state->nodes);
-         if (BE ( err != REG_NOERROR, 0))
+         if (BE (err != REG_NOERROR, 0))
            return err;
        }
       else
@@ -2697,9 +2912,9 @@ check_arrival (preg, mctx, path, top_node, top_str, last_node, last_str,
     {
       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))
+         err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+                                   subexp_num, type);
+         if (BE (err != REG_NOERROR, 0))
            {
              re_node_set_free (&next_nodes);
              return err;
@@ -2729,8 +2944,9 @@ check_arrival (preg, mctx, path, top_node, top_str, last_node, last_str,
        }
       if (cur_state)
        {
-         err = check_arrival_add_next_nodes(preg, dfa, mctx, str_idx,
-                                            &cur_state->nodes, &next_nodes);
+         err = check_arrival_add_next_nodes (mctx, str_idx,
+                                             &cur_state->non_eps_nodes,
+                                             &next_nodes);
          if (BE (err != REG_NOERROR, 0))
            {
              re_node_set_free (&next_nodes);
@@ -2740,23 +2956,21 @@ check_arrival (preg, mctx, path, top_node, top_str, last_node, last_str,
       ++str_idx;
       if (next_nodes.nelem)
        {
-         err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num,
-                                         fl_open);
+         err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type);
          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))
+         err = expand_bkref_cache (mctx, &next_nodes, str_idx,
+                                   subexp_num, type);
+         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);
+      context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags);
       cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context);
       if (BE (cur_state == NULL && err != REG_NOERROR, 0))
        {
@@ -2773,14 +2987,13 @@ check_arrival (preg, mctx, path, top_node, top_str, last_node, last_str,
 
   /* Fix MCTX.  */
   mctx->state_log = backup_state_log;
-  mctx->input->cur_idx = backup_cur_idx;
+  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);
+  if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node))
+    return REG_NOERROR;
+
+  return REG_NOMATCH;
 }
 
 /* Helper functions for check_arrival.  */
@@ -2792,35 +3005,37 @@ check_arrival (preg, mctx, path, top_node, top_str, last_node, last_str,
         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;
+__attribute_warn_unused_result__
+check_arrival_add_next_nodes (re_match_context_t *mctx, Idx str_idx,
+                             re_node_set *cur_nodes, re_node_set *next_nodes)
+{
+  const re_dfa_t *const dfa = mctx->dfa;
+  bool ok;
+  Idx cur_idx;
+#ifdef RE_ENABLE_I18N
+  reg_errcode_t err = REG_NOERROR;
+#endif
   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];
+      Idx cur_node = cur_nodes->elems[cur_idx];
+#ifdef DEBUG
       re_token_type_t type = dfa->nodes[cur_node].type;
-      if (IS_EPSILON_NODE(type))
-       continue;
+      assert (!IS_EPSILON_NODE (type));
+#endif
 #ifdef RE_ENABLE_I18N
-      /* If the node may accept `multi byte'.  */
-      if (ACCEPT_MB_NODE (type))
+      /* If the node may accept "multi byte".  */
+      if (dfa->nodes[cur_node].accept_mb)
        {
-         naccepted = check_node_accept_bytes (preg, cur_node, mctx->input,
+         naccepted = check_node_accept_bytes (dfa, 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;
+             Idx next_node = dfa->nexts[cur_node];
+             Idx next_idx = str_idx + naccepted;
              dest_state = mctx->state_log[next_idx];
              re_node_set_empty (&union_set);
              if (dest_state)
@@ -2831,21 +3046,12 @@ check_arrival_add_next_nodes (preg, dfa, mctx, str_idx, cur_nodes, next_nodes)
                      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
+             ok = re_node_set_insert (&union_set, next_node);
+             if (BE (! ok, 0))
                {
-                 err = re_node_set_insert (&union_set, next_node);
-                 if (BE (err < 0, 0))
-                   {
-                     re_node_set_free (&union_set);
-                     return REG_ESPACE;
-                   }
+                 re_node_set_free (&union_set);
+                 return REG_ESPACE;
                }
              mctx->state_log[next_idx] = re_acquire_state (&err, dfa,
                                                            &union_set);
@@ -2859,11 +3065,10 @@ check_arrival_add_next_nodes (preg, dfa, mctx, str_idx, cur_nodes, next_nodes)
        }
 #endif /* RE_ENABLE_I18N */
       if (naccepted
-         || check_node_accept (preg, dfa->nodes + cur_node, mctx,
-                               str_idx))
+         || check_node_accept (mctx, dfa->nodes + cur_node, str_idx))
        {
-         err = re_node_set_insert (next_nodes, dfa->nexts[cur_node]);
-         if (BE (err < 0, 0))
+         ok = re_node_set_insert (next_nodes, dfa->nexts[cur_node]);
+         if (BE (! ok, 0))
            {
              re_node_set_free (&union_set);
              return REG_ESPACE;
@@ -2881,13 +3086,11 @@ check_arrival_add_next_nodes (preg, dfa, mctx, str_idx, cur_nodes, next_nodes)
 */
 
 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;
+check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes,
+                         Idx ex_subexp, int type)
 {
   reg_errcode_t err;
-  int idx, outside_node;
+  Idx idx, outside_node;
   re_node_set new_nodes;
 #ifdef DEBUG
   assert (cur_nodes->nelem);
@@ -2900,9 +3103,9 @@ check_arrival_expand_ecl (dfa, cur_nodes, ex_subexp, fl_open)
 
   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);
+      Idx cur_node = cur_nodes->elems[idx];
+      const re_node_set *eclosure = dfa->eclosures + cur_node;
+      outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type);
       if (outside_node == -1)
        {
          /* There are no problematic nodes, just merge them.  */
@@ -2917,7 +3120,7 @@ check_arrival_expand_ecl (dfa, cur_nodes, ex_subexp, fl_open)
        {
          /* There are problematic nodes, re-calculate incrementally.  */
          err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node,
-                                             ex_subexp, fl_open);
+                                             ex_subexp, type);
          if (BE (err != REG_NOERROR, 0))
            {
              re_node_set_free (&new_nodes);
@@ -2935,39 +3138,37 @@ check_arrival_expand_ecl (dfa, cur_nodes, ex_subexp, fl_open)
    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;
+__attribute_warn_unused_result__
+check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes,
+                             Idx target, Idx ex_subexp, int type)
 {
-  int cur_node, type;
+  Idx cur_node;
   for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);)
     {
-      int err;
-      type = dfa->nodes[cur_node].type;
+      bool ok;
 
-      if (((type == OP_OPEN_SUBEXP && fl_open)
-          || (type == OP_CLOSE_SUBEXP && !fl_open))
+      if (dfa->nodes[cur_node].type == type
          && dfa->nodes[cur_node].opr.idx == ex_subexp)
        {
-         if (!fl_open)
+         if (type == OP_CLOSE_SUBEXP)
            {
-             err = re_node_set_insert (dst_nodes, cur_node);
-             if (BE (err == -1, 0))
+             ok = re_node_set_insert (dst_nodes, cur_node);
+             if (BE (! ok, 0))
                return REG_ESPACE;
            }
          break;
        }
-      err = re_node_set_insert (dst_nodes, cur_node);
-      if (BE (err == -1, 0))
+      ok = re_node_set_insert (dst_nodes, cur_node);
+      if (BE (! ok, 0))
        return REG_ESPACE;
       if (dfa->edests[cur_node].nelem == 0)
        break;
       if (dfa->edests[cur_node].nelem == 2)
        {
+         reg_errcode_t err;
          err = check_arrival_expand_ecl_sub (dfa, dst_nodes,
                                              dfa->edests[cur_node].elems[1],
-                                             ex_subexp, fl_open);
+                                             ex_subexp, type);
          if (BE (err != REG_NOERROR, 0))
            return err;
        }
@@ -2982,25 +3183,24 @@ check_arrival_expand_ecl_sub (dfa, dst_nodes, target, ex_subexp, fl_open)
    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;
+__attribute_warn_unused_result__
+expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes,
+                   Idx cur_str, Idx subexp_num, int type)
 {
+  const re_dfa_t *const dfa = mctx->dfa;
   reg_errcode_t err;
-  re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
-  int cache_idx, cache_idx_start;
-  /* The current state.  */
+  Idx cache_idx_start = search_cur_bkref_entry (mctx, cur_str);
+  struct re_backref_cache_entry *ent;
+
+  if (cache_idx_start == -1)
+    return REG_NOERROR;
 
-  cache_idx_start = search_cur_bkref_entry (mctx, cur_str);
-  for (cache_idx = cache_idx_start; cache_idx < mctx->nbkref_ents; ++cache_idx)
+ restart:
+  ent = mctx->bkref_ents + cache_idx_start;
+  do
     {
-      int to_idx, next_node;
-      struct re_backref_cache_entry *ent = mctx->bkref_ents + cache_idx;
-      if (ent->str_idx > cur_str)
-       break;
+      Idx to_idx, next_node;
+
       /* Is this entry ENT is appropriate?  */
       if (!re_node_set_contains (cur_nodes, ent->node))
        continue; /* No.  */
@@ -3018,8 +3218,7 @@ expand_bkref_cache (preg, mctx, cur_nodes, cur_str, last_str, subexp_num,
          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);
+         err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type);
          err3 = re_node_set_merge (cur_nodes, &new_dests);
          re_node_set_free (&new_dests);
          if (BE (err != REG_NOERROR || err2 != REG_NOERROR
@@ -3030,8 +3229,7 @@ expand_bkref_cache (preg, mctx, cur_nodes, cur_str, last_str, subexp_num,
              return err;
            }
          /* TODO: It is still inefficient...  */
-         cache_idx = cache_idx_start - 1;
-         continue;
+         goto restart;
        }
       else
        {
@@ -3039,14 +3237,14 @@ expand_bkref_cache (preg, mctx, cur_nodes, cur_str, last_str, subexp_num,
          next_node = dfa->nexts[ent->node];
          if (mctx->state_log[to_idx])
            {
-             int ret;
+             bool ok;
              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))
+             ok = re_node_set_insert (&union_set, next_node);
+             if (BE (err != REG_NOERROR || ! ok, 0))
                {
                  re_node_set_free (&union_set);
                  err = err != REG_NOERROR ? err : REG_ESPACE;
@@ -3066,99 +3264,105 @@ expand_bkref_cache (preg, mctx, cur_nodes, cur_str, last_str, subexp_num,
            return err;
        }
     }
+  while (ent++->more);
   return REG_NOERROR;
 }
 
 /* Build transition table for the state.
-   Return the new table if succeeded, otherwise return NULL.  */
+   Return true if successful.  */
 
-static re_dfastate_t **
-build_trtable (preg, state, fl_search)
-    const regex_t *preg;
-    const re_dfastate_t *state;
-    int fl_search;
+static bool
+build_trtable (const re_dfa_t *dfa, re_dfastate_t *state)
 {
   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'.  */
+  Idx i, j;
+  int ch;
+  bool need_word_trtable = false;
+  bitset_word_t elem, mask;
+  bool dests_node_malloced = false;
+  bool dest_states_malloced = false;
+  Idx 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;
+  bitset_t *dests_ch;
+  bitset_t acceptable;
+
+  struct dests_alloc
+  {
+    re_node_set dests_node[SBC_MAX];
+    bitset_t dests_ch[SBC_MAX];
+  } *dests_alloc;
 
   /* 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
+     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);
+  if (__libc_use_alloca (sizeof (struct dests_alloc)))
+    dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc));
   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_alloc = re_malloc (struct dests_alloc, 1);
+      if (BE (dests_alloc == NULL, 0))
+       return false;
+      dests_node_malloced = true;
     }
-  dests_ch = (bitset *) (dests_node + SBC_MAX);
+  dests_node = dests_alloc->dests_node;
+  dests_ch = dests_alloc->dests_ch;
 
-  /* 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;
-    }
+  /* Initialize transition table.  */
+  state->word_trtable = state->trtable = NULL;
 
-  /* At first, group all nodes belonging to `state' into several
+  /* At first, group all nodes belonging to 'state' into several
      destinations.  */
-  ndests = group_nodes_into_DFAstates (preg, state, dests_node, dests_ch);
+  ndests = group_nodes_into_DFAstates (dfa, 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.  */
+       re_free (dests_alloc);
+      /* Return false in case of an error, true otherwise.  */
       if (ndests == 0)
-       return trtable;
-      free (trtable);
-      return NULL;
+       {
+         state->trtable = (re_dfastate_t **)
+           calloc (sizeof (re_dfastate_t *), SBC_MAX);
+          if (BE (state->trtable == NULL, 0))
+            return false;
+         return true;
+       }
+      return false;
     }
 
   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
+  /* Avoid arithmetic overflow in size calculation.  */
+  if (BE ((((SIZE_MAX - (sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX)
+           / (3 * sizeof (re_dfastate_t *)))
+          < ndests),
+         0))
+    goto out_free;
+
+  if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX
                         + ndests * 3 * sizeof (re_dfastate_t *)))
     dest_states = (re_dfastate_t **)
-                 alloca (ndests * 3 * sizeof (re_dfastate_t *));
+      alloca (ndests * 3 * sizeof (re_dfastate_t *));
   else
-#endif
     {
-      dest_states = (re_dfastate_t **)
-                   malloc (ndests * 3 * sizeof (re_dfastate_t *));
+      dest_states = re_malloc (re_dfastate_t *, ndests * 3);
       if (BE (dest_states == NULL, 0))
        {
 out_free:
          if (dest_states_malloced)
-           free (dest_states);
+           re_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;
+           re_free (dests_alloc);
+         return false;
        }
-      dest_states_malloced = 1;
+      dest_states_malloced = true;
     }
   dest_states_word = dest_states + ndests;
   dest_states_nl = dest_states_word + ndests;
@@ -3167,7 +3371,7 @@ out_free:
   /* Then build the states for all destinations.  */
   for (i = 0; i < ndests; ++i)
     {
-      int next_node;
+      Idx next_node;
       re_node_set_empty (&follows);
       /* Merge the follows of this destination states.  */
       for (j = 0; j < dests_node[i].nelem; ++j)
@@ -3180,26 +3384,6 @@ out_free:
                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;
@@ -3211,6 +3395,10 @@ out_free:
                                                          CONTEXT_WORD);
          if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0))
            goto out_free;
+
+         if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1)
+           need_word_trtable = true;
+
          dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows,
                                                        CONTEXT_NEWLINE);
          if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0))
@@ -3224,47 +3412,77 @@ out_free:
       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))
+  if (!BE (need_word_trtable, 0))
+    {
+      /* We don't care about whether the following character is a word
+        character, or we are in a single-byte character set so we can
+        discern by looking at the character code: allocate a
+        256-entry transition table.  */
+      trtable = state->trtable =
+       (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX);
+      if (BE (trtable == NULL, 0))
+       goto out_free;
+
+      /* For all characters ch...:  */
+      for (i = 0; i < BITSET_WORDS; ++i)
+       for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+            elem;
+            mask <<= 1, elem >>= 1, ++ch)
+         if (BE (elem & 1, 0))
            {
-             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;
-                 }
+             /* There must be exactly one destination which accepts
+                character ch.  See group_nodes_into_DFAstates.  */
+             for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+               ;
+
+             /* j-th destination accepts the word character ch.  */
+             if (dfa->word_char[i] & mask)
+               trtable[ch] = dest_states_word[j];
+             else
+               trtable[ch] = dest_states[j];
            }
-         else /* not WORD_CHAR */
+    }
+  else
+    {
+      /* We care about whether the following character is a word
+        character, and we are in a multi-byte character set: discern
+        by looking at the character code: build two 256-entry
+        transition tables, one starting at trtable[0] and one
+        starting at trtable[SBC_MAX].  */
+      trtable = state->word_trtable =
+       (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX);
+      if (BE (trtable == NULL, 0))
+       goto out_free;
+
+      /* For all characters ch...:  */
+      for (i = 0; i < BITSET_WORDS; ++i)
+       for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1;
+            elem;
+            mask <<= 1, elem >>= 1, ++ch)
+         if (BE (elem & 1, 0))
            {
-             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;
-                 }
+             /* There must be exactly one destination which accepts
+                character ch.  See group_nodes_into_DFAstates.  */
+             for (j = 0; (dests_ch[j][i] & mask) == 0; ++j)
+               ;
+
+             /* j-th destination accepts the word character ch.  */
+             trtable[ch] = dest_states[j];
+             trtable[ch + SBC_MAX] = dest_states_word[j];
            }
-       }
+    }
+
   /* 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))
+      for (j = 0; j < ndests; ++j)
+       if (bitset_contain (dests_ch[j], NEWLINE_CHAR))
          {
            /* k-th destination accepts newline character.  */
-           trtable[NEWLINE_CHAR] = dest_states_nl[k];
+           trtable[NEWLINE_CHAR] = dest_states_nl[j];
+           if (need_word_trtable)
+             trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j];
            /* There must be only one destination which accepts
               newline.  See group_nodes_into_DFAstates.  */
            break;
@@ -3272,16 +3490,16 @@ out_free:
     }
 
   if (dest_states_malloced)
-    free (dest_states);
+    re_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);
+    re_free (dests_alloc);
 
-  return trtable;
+  return true;
 }
 
 /* Group all nodes belonging to STATE into several destinations.
@@ -3289,23 +3507,20 @@ out_free:
    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;
+static Idx
+group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state,
+                           re_node_set *dests_node, bitset_t *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.  */
+  bool ok;
+  Idx i, j, k;
+  Idx ndests; /* Number of the destinations from 'state'.  */
+  bitset_t 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 all the nodes belonging to 'state',  */
   for (i = 0; i < cur_nodes->nelem; ++i)
     {
       re_token_t *node = &dfa->nodes[cur_nodes->elems[i]];
@@ -3321,67 +3536,123 @@ group_nodes_into_DFAstates (preg, state, dests_node, dests_ch)
        }
       else if (type == OP_PERIOD)
        {
-         bitset_set_all (accepts);
-         if (!(preg->syntax & RE_DOT_NEWLINE))
+#ifdef RE_ENABLE_I18N
+         if (dfa->mb_cur_max > 1)
+           bitset_merge (accepts, dfa->sb_char);
+         else
+#endif
+           bitset_set_all (accepts);
+         if (!(dfa->syntax & RE_DOT_NEWLINE))
+           bitset_clear (accepts, '\n');
+         if (dfa->syntax & RE_DOT_NOT_NULL)
+           bitset_clear (accepts, '\0');
+       }
+#ifdef RE_ENABLE_I18N
+      else if (type == OP_UTF8_PERIOD)
+       {
+         if (ASCII_CHARS % BITSET_WORD_BITS == 0)
+           memset (accepts, -1, ASCII_CHARS / CHAR_BIT);
+         else
+           bitset_merge (accepts, utf8_sb_map);
+         if (!(dfa->syntax & RE_DOT_NEWLINE))
            bitset_clear (accepts, '\n');
-         if (preg->syntax & RE_DOT_NOT_NULL)
+         if (dfa->syntax & RE_DOT_NOT_NULL)
            bitset_clear (accepts, '\0');
        }
+#endif
       else
        continue;
 
-      /* Check the `accepts' and sift the characters which are not
+      /* 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);
+             bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR);
              bitset_empty (accepts);
              if (accepts_newline)
                bitset_set (accepts, NEWLINE_CHAR);
              else
                continue;
            }
+         if (constraint & NEXT_ENDBUF_CONSTRAINT)
+           {
+             bitset_empty (accepts);
+             continue;
+           }
+
+         if (constraint & NEXT_WORD_CONSTRAINT)
+           {
+             bitset_word_t any_set = 0;
+             if (type == CHARACTER && !node->word_char)
+               {
+                 bitset_empty (accepts);
+                 continue;
+               }
+#ifdef RE_ENABLE_I18N
+             if (dfa->mb_cur_max > 1)
+               for (j = 0; j < BITSET_WORDS; ++j)
+                 any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j]));
+             else
+#endif
+               for (j = 0; j < BITSET_WORDS; ++j)
+                 any_set |= (accepts[j] &= dfa->word_char[j]);
+             if (!any_set)
+               continue;
+           }
+         if (constraint & NEXT_NOTWORD_CONSTRAINT)
+           {
+             bitset_word_t any_set = 0;
+             if (type == CHARACTER && node->word_char)
+               {
+                 bitset_empty (accepts);
+                 continue;
+               }
+#ifdef RE_ENABLE_I18N
+             if (dfa->mb_cur_max > 1)
+               for (j = 0; j < BITSET_WORDS; ++j)
+                 any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j]));
+             else
+#endif
+               for (j = 0; j < BITSET_WORDS; ++j)
+                 any_set |= (accepts[j] &= ~dfa->word_char[j]);
+             if (!any_set)
+               continue;
+           }
        }
 
-      /* Then divide `accepts' into DFA states, or create a new
-        state.  */
+      /* Then divide 'accepts' into DFA states, or create a new
+        state.  Above, we make sure that accepts is not empty.  */
       for (j = 0; j < ndests; ++j)
        {
-         bitset intersec; /* Intersection sets, see below.  */
-         bitset remains;
+         bitset_t intersec; /* Intersection sets, see below.  */
+         bitset_t remains;
          /* Flags, see below.  */
-         int has_intersec, not_subset, not_consumed;
+         bitset_word_t 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'.  */
+         /* Enumerate the intersection set of this state and 'accepts'.  */
          has_intersec = 0;
-         for (k = 0; k < BITSET_UINTS; ++k)
+         for (k = 0; k < BITSET_WORDS; ++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'.  */
+         /* Then check if this state is a subset of 'accepts'.  */
          not_subset = not_consumed = 0;
-         for (k = 0; k < BITSET_UINTS; ++k)
+         for (k = 0; k < BITSET_WORDS; ++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 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);
@@ -3393,8 +3664,8 @@ group_nodes_into_DFAstates (preg, state, dests_node, dests_ch)
            }
 
          /* Put the position in the current group. */
-         err = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
-         if (BE (err < 0, 0))
+         ok = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]);
+         if (BE (! ok, 0))
            goto error_return;
 
          /* If all characters are consumed, go to next node. */
@@ -3420,7 +3691,7 @@ group_nodes_into_DFAstates (preg, state, dests_node, dests_ch)
 }
 
 #ifdef RE_ENABLE_I18N
-/* Check how many bytes the node `dfa->nodes[node_idx]' accepts.
+/* 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.
 
@@ -3428,39 +3699,97 @@ group_nodes_into_DFAstates (preg, state, dests_node, dests_ch)
    one collating element like '.', '[a-z]', opposite to the other nodes
    can only accept one byte.  */
 
+# ifdef _LIBC
+#  include <locale/weight.h>
+# endif
+
 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;
+check_node_accept_bytes (const re_dfa_t *dfa, Idx node_idx,
+                        const re_string_t *input, Idx str_idx)
 {
-  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;
+  int char_len, elem_len;
+  Idx i;
+
+  if (BE (node->type == OP_UTF8_PERIOD, 0))
+    {
+      unsigned char c = re_string_byte_at (input, str_idx), d;
+      if (BE (c < 0xc2, 1))
+       return 0;
+
+      if (str_idx + 2 > input->len)
+       return 0;
+
+      d = re_string_byte_at (input, str_idx + 1);
+      if (c < 0xe0)
+       return (d < 0x80 || d > 0xbf) ? 0 : 2;
+      else if (c < 0xf0)
+       {
+         char_len = 3;
+         if (c == 0xe0 && d < 0xa0)
+           return 0;
+       }
+      else if (c < 0xf8)
+       {
+         char_len = 4;
+         if (c == 0xf0 && d < 0x90)
+           return 0;
+       }
+      else if (c < 0xfc)
+       {
+         char_len = 5;
+         if (c == 0xf8 && d < 0x88)
+           return 0;
+       }
+      else if (c < 0xfe)
+       {
+         char_len = 6;
+         if (c == 0xfc && d < 0x84)
+           return 0;
+       }
+      else
+       return 0;
+
+      if (str_idx + char_len > input->len)
+       return 0;
+
+      for (i = 1; i < char_len; ++i)
+       {
+         d = re_string_byte_at (input, str_idx + i);
+         if (d < 0x80 || d > 0xbf)
+           return 0;
+       }
+      return char_len;
+    }
+
+  char_len = re_string_char_size_at (input, str_idx);
   if (node->type == OP_PERIOD)
     {
+      if (char_len <= 1)
+       return 0;
+      /* FIXME: I don't think this if is needed, as both '\n'
+        and '\0' are char_len == 1.  */
       /* '.' accepts any one character except the following two cases.  */
-      if ((!(preg->syntax & RE_DOT_NEWLINE) &&
+      if ((!(dfa->syntax & RE_DOT_NEWLINE) &&
           re_string_byte_at (input, str_idx) == '\n') ||
-         ((preg->syntax & RE_DOT_NOT_NULL) &&
+         ((dfa->syntax & RE_DOT_NOT_NULL) &&
           re_string_byte_at (input, str_idx) == '\0'))
        return 0;
       return char_len;
     }
-  else if (node->type == COMPLEX_BRACKET)
+
+  elem_len = re_string_elem_size_at (input, str_idx);
+  if ((elem_len <= 1 && char_len <= 1) || char_len == 0)
+    return 0;
+
+  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);
+      const unsigned char *pin
+       = ((const unsigned char *) re_string_get_buffer (input) + str_idx);
+      Idx j;
+      uint32_t nrules;
 # endif /* _LIBC */
       int match_len = 0;
       wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars)
@@ -3485,15 +3814,13 @@ check_node_accept_bytes (preg, node_idx, input, str_idx)
        }
 
 # ifdef _LIBC
+      nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
       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)
@@ -3523,12 +3850,13 @@ check_node_accept_bytes (preg, node_idx, input, str_idx)
              if (elem_len <= char_len)
                {
                  collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
-                 in_collseq = collseq_table_lookup (collseqwc, wc);
+                 in_collseq = __collseq_table_lookup (collseqwc, wc);
                }
              else
                in_collseq = find_collation_sequence_value (pin, elem_len);
            }
          /* match with range expression?  */
+         /* FIXME: Implement rational ranges here, too.  */
          for (i = 0; i < cset->nranges; ++i)
            if (cset->range_starts[i] <= in_collseq
                && in_collseq <= cset->range_ends[i])
@@ -3549,44 +3877,37 @@ check_node_accept_bytes (preg, node_idx, input, str_idx)
                _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
              indirect = (const int32_t *)
                _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB);
-             idx = findidx (&cp);
+             int32_t idx = findidx (table, indirect, extra, &cp, elem_len);
+             int32_t rule = idx >> 24;
+             idx &= 0xffffff;
              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;
-                         }
-                     }
-                 }
+               {
+                 size_t weight_len = weights[idx];
+                 for (i = 0; i < cset->nequiv_classes; ++i)
+                   {
+                     int32_t equiv_class_idx = cset->equiv_classes[i];
+                     int32_t equiv_class_rule = equiv_class_idx >> 24;
+                     equiv_class_idx &= 0xffffff;
+                     if (weights[equiv_class_idx] == weight_len
+                         && equiv_class_rule == rule
+                         && memcmp (weights + idx + 1,
+                                    weights + equiv_class_idx + 1,
+                                    weight_len) == 0)
+                       {
+                         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)
+             if (cset->range_starts[i] <= wc && wc <= cset->range_ends[i])
                {
                  match_len = char_len;
                  goto check_node_accept_bytes_match;
@@ -3609,9 +3930,7 @@ check_node_accept_bytes (preg, node_idx, input, str_idx)
 
 # ifdef _LIBC
 static unsigned int
-find_collation_sequence_value (mbs, mbs_len)
-    const unsigned char *mbs;
-    size_t mbs_len;
+find_collation_sequence_value (const unsigned char *mbs, size_t mbs_len)
 {
   uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
   if (nrules == 0)
@@ -3630,10 +3949,13 @@ find_collation_sequence_value (mbs, mbs_len)
       int32_t idx;
       const unsigned char *extra = (const unsigned char *)
        _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB);
+      int32_t extrasize = (const unsigned char *)
+       _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra;
 
-      for (idx = 0; ;)
+      for (idx = 0; idx < extrasize;)
        {
-         int mbs_cnt, found = 0;
+         int mbs_cnt;
+         bool found = false;
          int32_t elem_mbs_len;
          /* Skip the name of collating element name.  */
          idx = idx + extra[idx] + 1;
@@ -3645,7 +3967,7 @@ find_collation_sequence_value (mbs, mbs_len)
                  break;
              if (mbs_cnt == elem_mbs_len)
                /* Found the entry.  */
-               found = 1;
+               found = true;
            }
          /* Skip the byte sequence of the collating element.  */
          idx += elem_mbs_len;
@@ -3654,13 +3976,14 @@ find_collation_sequence_value (mbs, mbs_len)
          /* 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);
+         idx = idx + sizeof (uint32_t) * (*(int32_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);
        }
+      return UINT_MAX;
     }
 }
 # endif /* _LIBC */
@@ -3669,56 +3992,82 @@ find_collation_sequence_value (mbs, mbs_len)
 /* 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;
+static bool
+check_node_accept (const re_match_context_t *mctx, const re_token_t *node,
+                  Idx idx)
 {
   unsigned char ch;
+  ch = re_string_byte_at (&mctx->input, idx);
+  switch (node->type)
+    {
+    case CHARACTER:
+      if (node->opr.c != ch)
+        return false;
+      break;
+
+    case SIMPLE_BRACKET:
+      if (!bitset_contain (node->opr.sbcset, ch))
+        return false;
+      break;
+
+#ifdef RE_ENABLE_I18N
+    case OP_UTF8_PERIOD:
+      if (ch >= ASCII_CHARS)
+        return false;
+      FALLTHROUGH;
+#endif
+    case OP_PERIOD:
+      if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE))
+         || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL)))
+       return false;
+      break;
+
+    default:
+      return false;
+    }
+
   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);
+      unsigned int context = re_string_context_at (&mctx->input, idx,
+                                                  mctx->eflags);
       if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context))
-       return 0;
+       return false;
     }
-  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;
+
+  return true;
 }
 
 /* Extend the buffers, if the buffers have run out.  */
 
 static reg_errcode_t
-extend_buffers (mctx)
-     re_match_context_t *mctx;
+__attribute_warn_unused_result__
+extend_buffers (re_match_context_t *mctx, int min_len)
 {
   reg_errcode_t ret;
-  re_string_t *pstr = mctx->input;
+  re_string_t *pstr = &mctx->input;
+
+  /* Avoid overflow.  */
+  if (BE (MIN (IDX_MAX, SIZE_MAX / sizeof (re_dfastate_t *)) / 2
+          <= pstr->bufs_len, 0))
+    return REG_ESPACE;
 
-  /* Double the lengthes of the buffers.  */
-  ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2);
+  /* Double the lengths of the buffers, but allocate at least MIN_LEN.  */
+  ret = re_string_realloc_buffers (pstr,
+                                  MAX (min_len,
+                                       MIN (pstr->len, 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);
+      /* XXX We have no indication of the size of this buffer.  If this
+        allocation fail we have no indication that the state_log array
+        does not have the right size.  */
+      re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *,
+                                             pstr->bufs_len + 1);
       if (BE (new_array == NULL, 0))
        return REG_ESPACE;
       mctx->state_log = new_array;
@@ -3728,8 +4077,12 @@ extend_buffers (mctx)
   if (pstr->icase)
     {
 #ifdef RE_ENABLE_I18N
-      if (MB_CUR_MAX > 1)
-       build_wcs_upper_buffer (pstr);
+      if (pstr->mb_cur_max > 1)
+       {
+         ret = build_wcs_upper_buffer (pstr);
+         if (BE (ret != REG_NOERROR, 0))
+           return ret;
+       }
       else
 #endif /* RE_ENABLE_I18N  */
        build_upper_buffer (pstr);
@@ -3737,15 +4090,13 @@ extend_buffers (mctx)
   else
     {
 #ifdef RE_ENABLE_I18N
-      if (MB_CUR_MAX > 1)
+      if (pstr->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;
@@ -3757,27 +4108,32 @@ extend_buffers (mctx)
 /* 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;
+__attribute_warn_unused_result__
+match_ctx_init (re_match_context_t *mctx, int eflags, Idx n)
 {
   mctx->eflags = eflags;
-  mctx->input = input;
   mctx->match_last = -1;
   if (n > 0)
     {
+      /* Avoid overflow.  */
+      size_t max_object_size =
+       MAX (sizeof (struct re_backref_cache_entry),
+            sizeof (re_sub_match_top_t *));
+      if (BE (MIN (IDX_MAX, SIZE_MAX / max_object_size) < n, 0))
+       return REG_ESPACE;
+
       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;
+  /* Already zero-ed by the caller.
+     else
+       mctx->bkref_ents = NULL;
+     mctx->nbkref_ents = 0;
+     mctx->nsub_tops = 0;  */
   mctx->abkref_ents = n;
   mctx->max_mb_elem_len = 1;
-  mctx->nsub_tops = 0;
   mctx->asub_tops = n;
   return REG_NOERROR;
 }
@@ -3787,35 +4143,12 @@ match_ctx_init (mctx, eflags, input, n)
    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;
+match_ctx_clean (re_match_context_t *mctx)
 {
-  int st_idx;
+  Idx st_idx;
   for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx)
     {
-      int sl_idx;
+      Idx sl_idx;
       re_sub_match_top_t *top = mctx->sub_tops[st_idx];
       for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx)
        {
@@ -3829,8 +4162,22 @@ match_ctx_free_subtops (mctx)
          re_free (top->path->array);
          re_free (top->path);
        }
-      free (top);
+      re_free (top);
     }
+
+  mctx->nsub_tops = 0;
+  mctx->nbkref_ents = 0;
+}
+
+/* Free all the memory associated with MCTX.  */
+
+static void
+match_ctx_free (re_match_context_t *mctx)
+{
+  /* First, free all the memory associated with MCTX->SUB_TOPS.  */
+  match_ctx_clean (mctx);
+  re_free (mctx->sub_tops);
+  re_free (mctx->bkref_ents);
 }
 
 /* Add a new backreference entry to MCTX.
@@ -3839,9 +4186,9 @@ match_ctx_free_subtops (mctx)
 */
 
 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;
+__attribute_warn_unused_result__
+match_ctx_add_entry (re_match_context_t *mctx, Idx node, Idx str_idx, Idx from,
+                    Idx to)
 {
   if (mctx->nbkref_ents >= mctx->abkref_ents)
     {
@@ -3858,26 +4205,40 @@ match_ctx_add_entry (mctx, node, str_idx, from, to)
              sizeof (struct re_backref_cache_entry) * mctx->abkref_ents);
       mctx->abkref_ents *= 2;
     }
+  if (mctx->nbkref_ents > 0
+      && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx)
+    mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1;
+
   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;
+
+  /* This is a cache that saves negative results of check_dst_limits_calc_pos.
+     If bit N is clear, means that this entry won't epsilon-transition to
+     an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression.  If
+     it is set, check_dst_limits_calc_pos_1 will recurse and try to find one
+     such node.
+
+     A backreference does not epsilon-transition unless it is empty, so set
+     to all zeros if FROM != TO.  */
+  mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map
+    = (from == to ? -1 : 0);
+
+  mctx->bkref_ents[mctx->nbkref_ents++].more = 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.  */
+/* Return the first entry with the same str_idx, or -1 if none is
+   found.  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;
+static Idx
+search_cur_bkref_entry (const re_match_context_t *mctx, Idx str_idx)
 {
-  int left, right, mid;
-  right = mctx->nbkref_ents;
+  Idx left, right, mid, last;
+  last = right = mctx->nbkref_ents;
   for (left = 0; left < right;)
     {
       mid = (left + right) / 2;
@@ -3886,44 +4247,36 @@ search_cur_bkref_entry (mctx, str_idx)
       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;
-    }
+  if (left < last && mctx->bkref_ents[left].str_idx == str_idx)
+    return left;
+  else
+    return -1;
 }
 
 /* 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;
+__attribute_warn_unused_result__
+match_ctx_add_subtop (re_match_context_t *mctx, Idx node, Idx str_idx)
 {
 #ifdef DEBUG
   assert (mctx->sub_tops != NULL);
   assert (mctx->asub_tops > 0);
 #endif
-  if (mctx->nsub_tops == mctx->asub_tops)
+  if (BE (mctx->nsub_tops == mctx->asub_tops, 0))
     {
-      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);
+      Idx new_asub_tops = mctx->asub_tops * 2;
+      re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops,
+                                                  re_sub_match_top_t *,
+                                                  new_asub_tops);
       if (BE (new_array == NULL, 0))
        return REG_ESPACE;
       mctx->sub_tops = new_array;
+      mctx->asub_tops = new_asub_tops;
     }
   mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t));
-  if (mctx->sub_tops[mctx->nsub_tops] == NULL)
+  if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0))
     return REG_ESPACE;
   mctx->sub_tops[mctx->nsub_tops]->node = node;
   mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx;
@@ -3934,44 +4287,38 @@ match_ctx_add_subtop (mctx, node, str_idx)
    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;
+match_ctx_add_sublast (re_sub_match_top_t *subtop, Idx node, Idx str_idx)
 {
   re_sub_match_last_t *new_entry;
-  if (subtop->nlasts == subtop->alasts)
+  if (BE (subtop->nlasts == subtop->alasts, 0))
     {
-      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);
+      Idx new_alasts = 2 * subtop->alasts + 1;
+      re_sub_match_last_t **new_array = re_realloc (subtop->lasts,
+                                                   re_sub_match_last_t *,
+                                                   new_alasts);
       if (BE (new_array == NULL, 0))
        return NULL;
       subtop->lasts = new_array;
+      subtop->alasts = new_alasts;
     }
   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;
+  if (BE (new_entry != NULL, 1))
+    {
+      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;
+sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts,
+              re_dfastate_t **limited_sts, Idx last_node, Idx last_str_idx)
 {
   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);
 }
index abc5f02c3a51faea7328298999dff7d3107b7153..94b82a6353fa435e1fdcfd0b95f76c0f9cff726e 100644 (file)
@@ -4,17 +4,9 @@
  * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2006-15 Douglas Gilbert <dgilbert@interlog.com>
- * Copyright (C) 2009-17 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Copyright (C) 2009-18 Christian Franke
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  * The code in this file is based on the SCSI to ATA Translation (SAT)
  * draft found at http://www.t10.org . The original draft used for this
  * code is sat-r08.pdf which is not too far away from becoming a
@@ -43,7 +35,7 @@
  * Note that in the latter case, this code does not solve the
  * addressing issue (i.e. which SATA disk to address behind the logical
  * SCSI (RAID) interface).
- * 
+ *
  */
 
 #include <stdio.h>
@@ -53,7 +45,7 @@
 #include <errno.h>
 
 #include "config.h"
-#include "int64.h"
+
 #include "scsicmds.h"
 #include "atacmds.h" // ataReadHDIdentity()
 #include "knowndrives.h" // lookup_usb_device()
@@ -61,8 +53,9 @@
 #include "dev_interface.h"
 #include "dev_ata_cmd_set.h" // ata_device_with_command_set
 #include "dev_tunnelled.h" // tunnelled_device<>
+#include "sg_unaligned.h"
 
-const char * scsiata_cpp_cvsid = "$Id: scsiata.cpp 4386 2017-01-28 16:35:06Z chrfranke $";
+const char * scsiata_cpp_cvsid = "$Id: scsiata.cpp 4848 2018-12-05 18:30:46Z chrfranke $";
 
 /* This is a slightly stretched SCSI sense "descriptor" format header.
    The addition is to allow the 0x70 and 0x71 response codes. The idea
@@ -112,8 +105,14 @@ class sat_device
   virtual public /*implements*/ scsi_device
 {
 public:
+  enum sat_scsi_mode {
+    sat_always,
+    sat_auto,
+    scsi_always
+  };
+
   sat_device(smart_interface * intf, scsi_device * scsidev,
-    const char * req_type, int passthrulen = 0, bool enable_auto = false);
+    const char * req_type, sat_scsi_mode mode = sat_always, int passthrulen = 0);
 
   virtual ~sat_device() throw();
 
@@ -125,27 +124,28 @@ public:
 
 private:
   int m_passthrulen;
-  bool m_enable_auto;
+  sat_scsi_mode m_mode;
 };
 
 
 sat_device::sat_device(smart_interface * intf, scsi_device * scsidev,
-  const char * req_type, int passthrulen /* = 0 */, bool enable_auto /* = false */)
+  const char * req_type, sat_scsi_mode mode /* = sat_always */,
+  int passthrulen /* = 0 */)
 : smart_device(intf, scsidev->get_dev_name(),
-    (enable_auto ? "sat,auto" : "sat"), req_type),
+    (mode == sat_always ? "sat" : mode == sat_auto ? "sat,auto" : "scsi"), req_type),
   tunnelled_device<ata_device, scsi_device>(scsidev),
   m_passthrulen(passthrulen),
-  m_enable_auto(enable_auto)
+  m_mode(mode)
 {
-  if (enable_auto)
+  if (mode != sat_always)
     hide_ata(); // Start as SCSI, switch to ATA in autodetect_open()
   else
     hide_scsi(); // ATA always
   if (strcmp(scsidev->get_dev_type(), "scsi"))
     set_info().dev_type += strprintf("+%s", scsidev->get_dev_type());
 
-  set_info().info_name = strprintf("%s [%sSAT]", scsidev->get_info_name(),
-                                   (enable_auto ? "SCSI/" : ""));
+  set_info().info_name = strprintf("%s [%s]", scsidev->get_info_name(),
+    (mode == sat_always ? "SAT" : mode == sat_auto ? "SCSI/SAT" : "SCSI"));
 }
 
 sat_device::~sat_device() throw()
@@ -240,7 +240,7 @@ sat_device::~sat_device() throw()
 // RETURN VALUES
 //  -1 if the command failed
 //   0 if the command succeeded,
-//   STATUS_CHECK routine: 
+//   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"
@@ -410,7 +410,7 @@ bool sat_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
                      scsiErrString(status));
                 if (ardp && (scsi_debugmode > 1)) {
                     pout("Values from ATA Return Descriptor are:\n");
-                    dStrHex((const char *)ardp, ard_len, 1);
+                    dStrHex((const uint8_t *)ardp, ard_len, 1);
                 }
             }
             if (t_dir && (t_length > 0) && (in.direction == ata_cmd_in::data_in))
@@ -423,7 +423,7 @@ bool sat_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
             if (ardp) {
                 if (scsi_debugmode > 1) {
                     pout("Values from ATA Return Descriptor are:\n");
-                    dStrHex((const char *)ardp, ard_len, 1);
+                    dStrHex((const uint8_t *)ardp, ard_len, 1);
                 }
                 // Set output registers
                 ata_out_regs & lo = out.out_regs;
@@ -467,7 +467,7 @@ bool sat_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
                          * count_upper_nonzero or lba_upper_nonzero are set
                          * involves fetching the SCSI ATA PASS-THROUGH
                          * Results log page and decoding the descriptor with
-                         * the matching log_index field. Painful. */ 
+                         * the matching log_index field. Painful. */
                     }
                 }
             }
@@ -481,7 +481,7 @@ bool sat_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
                 if (scsi_debugmode > 0) {
                     if (sense_descriptor && ardp) {
                         pout("Values from ATA Return Descriptor are:\n");
-                        dStrHex((const char *)ardp, ard_len, 1);
+                        dStrHex((const uint8_t *)ardp, ard_len, 1);
                     } else if (! sense_descriptor) {
                         pout("Values from ATA fixed format sense are:\n");
                         pout("  Error: 0x%x\n", io_hdr.sensep[3]);
@@ -509,7 +509,7 @@ bool sat_device::scsi_pass_through(scsi_cmnd_io * iop)
 
 smart_device * sat_device::autodetect_open()
 {
-  if (!open() || !m_enable_auto)
+  if (!open() || m_mode != sat_auto)
     return this;
 
   scsi_device * scsidev = get_tunnel_dev();
@@ -547,7 +547,7 @@ smart_device * sat_device::autodetect_open()
 static bool has_sat_pass_through(ata_device * dev, bool packet_interface = false)
 {
     /* Note:  malloc() ensures the read buffer lands on a single
-       page.  This avoids some bugs seen on LSI controlers under
+       page.  This avoids some bugs seen on LSI controllers under
        FreeBSD */
     char *data = (char *)malloc(512);
     ata_cmd_in in;
@@ -596,40 +596,6 @@ static int sg_scsi_normalize_sense(const unsigned char * sensep, int sb_len,
     return 1;
 }
 
-
-// Call scsi_pass_through and check sense.
-// TODO: Provide as member function of class scsi_device (?)
-static bool scsi_pass_through_and_check(scsi_device * scsidev,  scsi_cmnd_io * iop,
-                                        const char * msg = "")
-{
-  // Provide sense buffer
-  unsigned char sense[32] = {0, };
-  iop->sensep = sense;
-  iop->max_sense_len = sizeof(sense);
-  iop->timeout = SCSI_TIMEOUT_DEFAULT;
-
-  // Run cmd
-  if (!scsidev->scsi_pass_through(iop)) {
-    if (scsi_debugmode > 0)
-      pout("%sscsi_pass_through() failed, errno=%d [%s]\n",
-           msg, scsidev->get_errno(), scsidev->get_errmsg());
-    return false;
-  }
-
-  // Check sense
-  scsi_sense_disect sinfo;
-  scsi_do_sense_disect(iop, &sinfo);
-  int err = scsiSimpleSenseFilter(&sinfo);
-  if (err) {
-    if (scsi_debugmode > 0)
-      pout("%sscsi error: %s\n", msg, scsiErrString(err));
-    return scsidev->set_err(EIO, "scsi error %s", scsiErrString(err));
-  }
-
-  return true;
-}
-
-
 /////////////////////////////////////////////////////////////////////////////
 
 namespace sat {
@@ -657,7 +623,7 @@ protected:
 
 usbcypress_device::usbcypress_device(smart_interface * intf, scsi_device * scsidev,
   const char * req_type, unsigned char signature)
-: smart_device(intf, scsidev->get_dev_name(), "sat", req_type),
+: smart_device(intf, scsidev->get_dev_name(), "usbcypress", req_type),
   tunnelled_device<ata_device_with_command_set, scsi_device>(scsidev),
   m_signature(signature)
 {
@@ -831,7 +797,7 @@ int usbcypress_device::ata_command_interface(smart_command_set command, int sele
 
     // if there is a sense the command failed or the
     // device doesn't support usbcypress
-    if (io_hdr.scsi_status == SCSI_STATUS_CHECK_CONDITION && 
+    if (io_hdr.scsi_status == SCSI_STATUS_CHECK_CONDITION &&
             sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len, NULL)) {
         return -1;
     }
@@ -849,7 +815,7 @@ int usbcypress_device::ata_command_interface(smart_command_set command, int sele
         cdb[2] = (1<<0); /* ask read taskfile */
         memset(sense, 0, sizeof(sense));
 
-        /* transfert 8 bytes */
+        /* transfer 8 bytes */
         memset(&io_hdr, 0, sizeof(io_hdr));
         io_hdr.dxfer_dir = DXFER_FROM_DEVICE;
         io_hdr.dxfer_len = ard_len;
@@ -872,7 +838,7 @@ int usbcypress_device::ata_command_interface(smart_command_set command, int sele
         }
         // if there is a sense the command failed or the
         // device doesn't support usbcypress
-        if (io_hdr.scsi_status == SCSI_STATUS_CHECK_CONDITION && 
+        if (io_hdr.scsi_status == SCSI_STATUS_CHECK_CONDITION &&
                 sg_scsi_normalize_sense(io_hdr.sensep, io_hdr.resp_sense_len, NULL)) {
             return -1;
         }
@@ -880,7 +846,7 @@ int usbcypress_device::ata_command_interface(smart_command_set command, int sele
 
         if (scsi_debugmode > 1) {
             pout("Values from ATA Return Descriptor are:\n");
-            dStrHex((const char *)ardp, ard_len, 1);
+            dStrHex((const uint8_t *)ardp, ard_len, 1);
         }
 
         if (ATA_CHECK_POWER_MODE == ata_command)
@@ -897,7 +863,7 @@ int usbcypress_device::ata_command_interface(smart_command_set command, int sele
             pout("Retry without other disc access\n");
             pout("Please get assistance from " PACKAGE_HOMEPAGE "\n");
             pout("Values from ATA Return Descriptor are:\n");
-            dStrHex((const char *)ardp, ard_len, 1);
+            dStrHex((const uint8_t *)ardp, ard_len, 1);
             return -1;
         }
     }
@@ -1079,8 +1045,7 @@ bool usbjmicron_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou
   cdb[ 0] = 0xdf;
   cdb[ 1] = (rwbit ? 0x10 : 0x00);
   cdb[ 2] = 0x00;
-  cdb[ 3] = (unsigned char)(io_hdr.dxfer_len >> 8);
-  cdb[ 4] = (unsigned char)(io_hdr.dxfer_len     );
+  sg_put_unaligned_be16(io_hdr.dxfer_len, cdb + 3);
   cdb[ 5] = in.in_regs.features;
   cdb[ 6] = in.in_regs.sector_count;
   cdb[ 7] = in.in_regs.lba_low;
@@ -1096,7 +1061,7 @@ bool usbjmicron_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou
   io_hdr.cmnd_len = (!m_prolific ? 12 : 14);
 
   scsi_device * scsidev = get_tunnel_dev();
-  if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+  if (!scsidev->scsi_pass_through_and_check(&io_hdr,
          "usbjmicron_device::ata_pass_through: "))
     return set_err(scsidev->get_err());
 
@@ -1151,11 +1116,9 @@ bool usbjmicron_device::get_registers(unsigned short addr,
   cdb[ 0] = 0xdf;
   cdb[ 1] = 0x10;
   cdb[ 2] = 0x00;
-  cdb[ 3] = (unsigned char)(size >> 8);
-  cdb[ 4] = (unsigned char)(size     );
+  sg_put_unaligned_be16(size, cdb + 3);
   cdb[ 5] = 0x00;
-  cdb[ 6] = (unsigned char)(addr >> 8);
-  cdb[ 7] = (unsigned char)(addr     );
+  sg_put_unaligned_be16(addr, cdb + 6);
   cdb[ 8] = 0x00;
   cdb[ 9] = 0x00;
   cdb[10] = 0x00;
@@ -1173,7 +1136,7 @@ bool usbjmicron_device::get_registers(unsigned short addr,
   io_hdr.cmnd_len = (!m_prolific ? 12 : 14);
 
   scsi_device * scsidev = get_tunnel_dev();
-  if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+  if (!scsidev->scsi_pass_through_and_check(&io_hdr,
          "usbjmicron_device::get_registers: "))
     return set_err(scsidev->get_err());
 
@@ -1267,10 +1230,7 @@ bool usbprolific_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & o
   cdb[ 3] = in.in_regs.features;        // Feature register (SMART command)
   cdb[ 4] = 0x06;         // Check Word (VendorID magic, Prolific: 0x067B)
   cdb[ 5] = 0x7B;         // Check Word (VendorID magic, Prolific: 0x067B)
-  cdb[ 6] = (unsigned char)(io_hdr.dxfer_len >> 24);  // Length MSB
-  cdb[ 7] = (unsigned char)(io_hdr.dxfer_len >> 16);  // Length ...
-  cdb[ 8] = (unsigned char)(io_hdr.dxfer_len >>  8);  // Length ...
-  cdb[ 9] = (unsigned char)(io_hdr.dxfer_len      );  // Length LSB
+  sg_put_unaligned_be32(io_hdr.dxfer_len, cdb + 6);
   cdb[10] = in.in_regs.sector_count;    // Sector Count
   cdb[11] = in.in_regs.lba_low;         // LBA Low (7:0)
   cdb[12] = in.in_regs.lba_mid;         // LBA Mid (15:8)
@@ -1283,7 +1243,7 @@ bool usbprolific_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & o
   io_hdr.cmnd_len = 16;
 
   scsi_device * scsidev = get_tunnel_dev();
-  if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+  if (!scsidev->scsi_pass_through_and_check(&io_hdr,
          "usbprolific_device::ata_pass_through: "))
     return set_err(scsidev->get_err());
 
@@ -1302,7 +1262,7 @@ bool usbprolific_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & o
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
 
-    if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+    if (!scsidev->scsi_pass_through_and_check(&io_hdr,
            "usbprolific_device::scsi_pass_through (get registers): "))
       return set_err(scsidev->get_err());
 
@@ -1390,7 +1350,7 @@ bool usbsunplus_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou
     io_hdr.cmnd_len = sizeof(cdb);
 
     scsi_device * scsidev = get_tunnel_dev();
-    if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+    if (!scsidev->scsi_pass_through_and_check(&io_hdr,
            "usbsunplus_device::scsi_pass_through (presetting): "))
       return set_err(scsidev->get_err());
   }
@@ -1437,7 +1397,7 @@ bool usbsunplus_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou
   io_hdr.cmnd_len = sizeof(cdb);
 
   scsi_device * scsidev = get_tunnel_dev();
-  if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+  if (!scsidev->scsi_pass_through_and_check(&io_hdr,
          "usbsunplus_device::scsi_pass_through: "))
     // Returns sense key 0x03 (medium error) on ATA command error
     return set_err(scsidev->get_err());
@@ -1457,7 +1417,7 @@ bool usbsunplus_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
 
-    if (!scsi_pass_through_and_check(scsidev, &io_hdr,
+    if (!scsidev->scsi_pass_through_and_check(&io_hdr,
            "usbsunplus_device::scsi_pass_through (get registers): "))
       return set_err(scsidev->get_err());
 
@@ -1494,10 +1454,10 @@ ata_device * smart_interface::get_sat_device(const char * type, scsi_device * sc
 
   if (!strncmp(type, "sat", 3)) {
     const char * t = type + 3;
-    bool enable_auto = false;
+    sat_device::sat_scsi_mode mode = sat_device::sat_always;
     if (!strncmp(t, ",auto", 5)) {
       t += 5;
-      enable_auto = true;
+      mode = sat_device::sat_auto;
     }
     int ptlen = 0, n = -1;
     if (*t && !(sscanf(t, ",%d%n", &ptlen, &n) == 1 && n == (int)strlen(t)
@@ -1505,7 +1465,11 @@ ata_device * smart_interface::get_sat_device(const char * type, scsi_device * sc
       set_err(EINVAL, "Option '-d sat[,auto][,N]' requires N to be 0, 12 or 16");
       return 0;
     }
-    satdev = new sat_device(this, scsidev, type, ptlen, enable_auto);
+    satdev = new sat_device(this, scsidev, type, mode, ptlen);
+  }
+
+  else if (!strcmp(type, "scsi")) {
+    satdev = new sat_device(this, scsidev, type, sat_device::scsi_always);
   }
 
   else if (!strncmp(type, "usbcypress", 10)) {
@@ -1567,7 +1531,9 @@ ata_device * smart_interface::autodetect_sat_device(scsi_device * scsidev,
     return 0;
 
   // SAT ?
-  if (inqdata && inqsize >= 36 && !memcmp(inqdata + 8, "ATA     ", 8)) { // TODO: Linux-specific?
+  if (inqdata && inqsize >= 36 && !memcmp(inqdata + 8, "ATA     ", 8)) {
+    // TODO: Linux-specific? No, all SAT standards say the 'T10 Vendor
+    // Identification' field shall be 'ATA     '.
     ata_device_auto_ptr atadev( new sat_device(this, scsidev, "") , scsidev);
     if (has_sat_pass_through(atadev.get()))
       return atadev.release(); // Detected SAT
index 9d0439469006fc4826583f72af7bd7102c3c2aa6..066a90f53ed0338adcb5b8eda51a8f12a45be7d0 100644 (file)
@@ -5,20 +5,9 @@
  *
  * Copyright (C) 2002-8 Bruce Allen
  * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
- * Copyright (C) 2003-16 Douglas Gilbert <dgilbert@interlog.com>
+ * Copyright (C) 2003-18 Douglas Gilbert <dgilbert@interlog.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, see <http://www.gnu.org/licenses/>.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
+ * SPDX-License-Identifier: GPL-2.0-or-later
  *
  *
  * In the SCSI world "SMART" is a dead or withdrawn standard. In recent
 #include <ctype.h>
 
 #include "config.h"
-#include "int64.h"
+
 #include "scsicmds.h"
 #include "atacmds.h" // FIXME: for smart_command_set only
 #include "dev_interface.h"
 #include "utility.h"
+#include "sg_unaligned.h"
 
-const char *scsicmds_c_cvsid="$Id: scsicmds.cpp 4414 2017-03-27 21:00:46Z chrfranke $"
+const char *scsicmds_c_cvsid="$Id: scsicmds.cpp 4842 2018-12-02 16:07:26Z chrfranke $"
   SCSICMDS_H_CVSID;
 
+static const char * logSenStr = "Log Sense";
+
 // Print SCSI debug messages?
 unsigned char scsi_debugmode = 0;
 
@@ -62,7 +54,7 @@ supported_vpd_pages::supported_vpd_pages(scsi_device * device) : num_valid(0)
     memset(b, 0, sizeof(b));
     if (device && (0 == scsiInquiryVpd(device, SCSI_VPD_SUPPORTED_VPD_PAGES,
                    b, sizeof(b)))) {
-        num_valid = (b[2] << 8) + b[3];
+        num_valid = sg_get_unaligned_be16(b + 2);
         int n = sizeof(pages);
         if (num_valid > n)
             num_valid = n;
@@ -82,11 +74,11 @@ supported_vpd_pages::is_supported(int vpd_page_num) const
     return false;
 }
 
-/* output binary in hex and optionally ascii */
+/* output binary in ASCII hex and optionally ASCII. Uses pout() for output. */
 void
-dStrHex(const char* str, int len, int no_ascii)
+dStrHex(const uint8_t * up, int len, int no_ascii)
 {
-    const char* p = str;
+    const uint8_t * p = up;
     char buff[82];
     int a = 0;
     const int bpstart = 5;
@@ -105,11 +97,11 @@ dStrHex(const char* str, int len, int no_ascii)
 
     for(i = 0; i < len; i++)
     {
-        unsigned char c = *p++;
+        uint8_t c = *p++;
         bpos += 3;
         if (bpos == (bpstart + (9 * 3)))
             bpos++;
-        snprintf(buff+bpos, sizeof(buff)-bpos, "%.2x", (int)(unsigned char)c);
+        snprintf(buff+bpos, sizeof(buff)-bpos, "%.2x", (unsigned int)c);
         buff[bpos + 2] = ' ';
         if (no_ascii)
             buff[cpos++] = ' ';
@@ -141,8 +133,76 @@ dStrHex(const char* str, int len, int no_ascii)
     }
 }
 
+/* This is a heuristic that takes into account the command bytes and length
+ * to decide whether the presented unstructured sequence of bytes could be
+ * a SCSI command. If so it returns true otherwise false. Vendor specific
+ * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed
+ * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The
+ * only SCSI commands considered above 16 bytes of length are the Variable
+ * Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e).
+ * Both have an inbuilt length field which can be cross checked with clen.
+ * No NVMe commands (64 bytes long plus some extra added by some OSes) have
+ * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS
+ * structures that are sent across the wire. The FIS register structure is
+ * used to move a command from a SATA host to device, but the ATA 'command'
+ * is not the first byte. So it is harder to say what will happen if a
+ * FIS structure is presented as a SCSI command, hopefully there is a low
+ * probability this function will yield true in that case. */
+bool
+is_scsi_cdb(const uint8_t * cdbp, int clen)
+{
+    int ilen, sa;
+    uint8_t opcode;
+    uint8_t top3bits;
+
+    if (clen < 6)
+        return false;
+    opcode = cdbp[0];
+    top3bits = opcode >> 5;
+    if (0x3 == top3bits) {      /* Opcodes 0x60 to 0x7f */
+        if ((clen < 12) || (clen % 4))
+            return false;       /* must be modulo 4 and 12 or more bytes */
+        switch (opcode) {
+        case 0x7e:      /* Extended cdb (XCDB) */
+            ilen = 4 + sg_get_unaligned_be16(cdbp + 2);
+            return (ilen == clen);
+        case 0x7f:      /* Variable Length cdb */
+            ilen = 8 + cdbp[7];
+            sa = sg_get_unaligned_be16(cdbp + 8);
+            /* service action (sa) 0x0 is reserved */
+            return ((ilen == clen) && sa);
+        default:
+            return false;
+        }
+    } else if (clen <= 16) {
+        switch (clen) {
+        case 6:
+            if (top3bits > 0x5)         /* vendor */
+                return true;
+            return (0x0 == top3bits);   /* 6 byte cdb */
+        case 10:
+            if (top3bits > 0x5)         /* vendor */
+                return true;
+            return ((0x1 == top3bits) || (0x2 == top3bits)); /* 10 byte cdb */
+        case 16:
+            if (top3bits > 0x5)         /* vendor */
+                return true;
+            return (0x4 == top3bits);   /* 16 byte cdb */
+        case 12:
+            if (top3bits > 0x5)         /* vendor */
+                return true;
+            return (0x5 == top3bits);   /* 12 byte cdb */
+        default:
+            return false;
+        }
+    }
+    /* NVMe probably falls out here, clen > 16 and (opcode < 0x60 or
+     * opcode > 0x7f). */
+    return false;
+}
+
 struct scsi_opcode_name {
-    UINT8 opcode;
+    uint8_t opcode;
     const char * name;
 };
 
@@ -174,7 +234,7 @@ static const char * vendor_specific = "<vendor specific>";
 /* Need to expand to take service action into account. For commands
  * of interest the service action is in the 2nd command byte */
 const char *
-scsi_get_opcode_name(UINT8 opcode)
+scsi_get_opcode_name(uint8_t opcode)
 {
     int len = sizeof(opcode_name_arr) / sizeof(opcode_name_arr[0]);
 
@@ -439,30 +499,33 @@ scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s, int slen,
 }
 
 /* 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. */
+ * 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. If
+ * known_resp_len < 0 then does single fetch for BufLen bytes. */
 int
-scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
+scsiLogSense(scsi_device * device, int pagenum, int subpagenum, uint8_t *pBuf,
              int bufLen, int known_resp_len)
 {
+    int pageLen;
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[10];
-    UINT8 sense[32];
-    int pageLen;
+    uint8_t cdb[10];
+    uint8_t sense[32];
 
     if (known_resp_len > bufLen)
         return -EIO;
     if (known_resp_len > 0)
         pageLen = known_resp_len;
-    else {
+    else if (known_resp_len < 0)
+        pageLen = bufLen;
+    else {      /* 0 == known_resp_len */
         /* Starting twin fetch strategy: first fetch to find respone length */
         pageLen = 4;
         if (pageLen > bufLen)
@@ -476,10 +539,9 @@ scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
         io_hdr.dxfer_len = pageLen;
         io_hdr.dxferp = pBuf;
         cdb[0] = LOG_SENSE;
-        cdb[2] = 0x40 | (pagenum & 0x3f);  /* Page control (PC)==1 */
-        cdb[3] = subpagenum;
-        cdb[7] = (pageLen >> 8) & 0xff;
-        cdb[8] = pageLen & 0xff;
+        cdb[2] = 0x40 | (pagenum & 0x3f);       /* Page control (PC)==1 */
+        cdb[3] = subpagenum;                    /* 0 for no sub-page */
+        sg_put_unaligned_be16(pageLen, cdb + 7);
         io_hdr.cmnd = cdb;
         io_hdr.cmnd_len = sizeof(cdb);
         io_hdr.sensep = sense;
@@ -495,9 +557,10 @@ scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
         /* sanity check on response */
         if ((SUPPORTED_LPAGES != pagenum) && ((pBuf[0] & 0x3f) != pagenum))
             return SIMPLE_ERR_BAD_RESP;
-        if (0 == ((pBuf[2] << 8) + pBuf[3]))
+        uint16_t u = sg_get_unaligned_be16(pBuf + 2);
+        if (0 == u)
             return SIMPLE_ERR_BAD_RESP;
-        pageLen = (pBuf[2] << 8) + pBuf[3] + 4;
+        pageLen = u + 4;
         if (4 == pageLen)  /* why define a lpage with no payload? */
             pageLen = 252; /* some IBM tape drives don't like double fetch */
         /* some SCSI HBA don't like "odd" length transfers */
@@ -514,8 +577,8 @@ scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
     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;
+    cdb[3] = subpagenum;
+    sg_put_unaligned_be16(pageLen, cdb + 7);
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;
@@ -531,7 +594,7 @@ scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
     /* sanity check on response */
     if ((SUPPORTED_LPAGES != pagenum) && ((pBuf[0] & 0x3f) != pagenum))
         return SIMPLE_ERR_BAD_RESP;
-    if (0 == ((pBuf[2] << 8) + pBuf[3]))
+    if (0 == sg_get_unaligned_be16(pBuf + 2))
         return SIMPLE_ERR_BAD_RESP;
     return 0;
 }
@@ -543,12 +606,12 @@ scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
  * returns negated errno. SPC-4 sections 6.5 and 7.2 (rev 20) */
 int
 scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum,
-              int subpagenum, UINT8 *pBuf, int bufLen)
+              int subpagenum, uint8_t *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[10];
-    UINT8 sense[32];
+    uint8_t cdb[10];
+    uint8_t sense[32];
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -559,8 +622,7 @@ scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum,
     cdb[1] = (pcr ? 2 : 0) | (sp ? 1 : 0);
     cdb[2] = ((pc << 6) & 0xc0) | (pagenum & 0x3f);
     cdb[3] = (subpagenum & 0xff);
-    cdb[7] = ((bufLen >> 8) & 0xff);
-    cdb[8] = (bufLen & 0xff);
+    sg_put_unaligned_be16(bufLen, cdb + 7);
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;
@@ -579,12 +641,12 @@ scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum,
  * SPC-3 sections 6.9 and 7.4 (rev 22a) [mode subpage==0] */
 int
 scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,
-              UINT8 *pBuf, int bufLen)
+              uint8_t *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[6];
-    UINT8 sense[32];
+    uint8_t cdb[6];
+    uint8_t sense[32];
 
     if ((bufLen < 0) || (bufLen > 255))
         return -EINVAL;
@@ -633,12 +695,12 @@ scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,
  * 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(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
+scsiModeSelect(scsi_device * device, int sp, uint8_t *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[6];
-    UINT8 sense[32];
+    uint8_t cdb[6];
+    uint8_t sense[32];
     int pg_offset, pg_len, hdr_plus_1_pg;
 
     pg_offset = 4 + pBuf[3];
@@ -648,7 +710,7 @@ scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
     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[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));
@@ -676,12 +738,12 @@ scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
  * SPC-3 sections 6.10 and 7.4 (rev 22a) [mode subpage==0] */
 int
 scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
-                UINT8 *pBuf, int bufLen)
+                uint8_t *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[10];
-    UINT8 sense[32];
+    uint8_t cdb[10];
+    uint8_t sense[32];
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -691,8 +753,7 @@ scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
     cdb[0] = MODE_SENSE_10;
     cdb[2] = (pc << 6) | (pagenum & 0x3f);
     cdb[3] = subpagenum;
-    cdb[7] = (bufLen >> 8) & 0xff;
-    cdb[8] = bufLen & 0xff;
+    sg_put_unaligned_be16(bufLen, cdb + 7);
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;
@@ -729,15 +790,15 @@ scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
  * 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(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
+scsiModeSelect10(scsi_device * device, int sp, uint8_t *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[10];
-    UINT8 sense[32];
+    uint8_t cdb[10];
+    uint8_t sense[32];
     int pg_offset, pg_len, hdr_plus_1_pg;
 
-    pg_offset = 8 + (pBuf[6] << 8) + pBuf[7];
+    pg_offset = 8 + sg_get_unaligned_be16(pBuf + 6);
     if (pg_offset + 2 >= bufLen)
         return -EINVAL;
     pg_len = pBuf[pg_offset + 1] + 2;
@@ -754,7 +815,8 @@ scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
     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 */
+    /* make sure only one page sent */
+    sg_put_unaligned_be16(hdr_plus_1_pg, cdb + 7);
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;
@@ -771,12 +833,12 @@ scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen)
  * 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(scsi_device * device, UINT8 *pBuf, int bufLen)
+scsiStdInquiry(scsi_device * device, uint8_t *pBuf, int bufLen)
 {
     struct scsi_sense_disect sinfo;
     struct scsi_cmnd_io io_hdr;
-    UINT8 cdb[6];
-    UINT8 sense[32];
+    uint8_t cdb[6];
+    uint8_t sense[32];
 
     if ((bufLen < 0) || (bufLen > 1023))
         return -EINVAL;
@@ -786,8 +848,7 @@ scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen)
     io_hdr.dxfer_len = bufLen;
     io_hdr.dxferp = pBuf;
     cdb[0] = INQUIRY;
-    cdb[3] = (bufLen >> 8) & 0xff;
-    cdb[4] = (bufLen & 0xff);
+    sg_put_unaligned_be16(bufLen, cdb + 3);
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;
@@ -805,12 +866,12 @@ scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen)
  * 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(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen)
+scsiInquiryVpd(scsi_device * device, int vpd_page, uint8_t *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[6];
-    UINT8 sense[32];
+    uint8_t cdb[6];
+    uint8_t sense[32];
     int res;
 
     /* Assume SCSI_VPD_SUPPORTED_VPD_PAGES is first VPD page fetched */
@@ -832,8 +893,7 @@ try_again:
     cdb[0] = INQUIRY;
     cdb[1] = 0x1;       /* set EVPD bit (enable Vital Product Data) */
     cdb[2] = vpd_page;
-    cdb[3] = (bufLen >> 8) & 0xff;
-    cdb[4] = (bufLen & 0xff);
+    sg_put_unaligned_be16(bufLen, cdb + 3);
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;
@@ -870,17 +930,18 @@ int
 scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
 {
     struct scsi_cmnd_io io_hdr;
-    UINT8 cdb[6];
-    UINT8 sense[32];
-    UINT8 buff[18];
+    uint8_t cdb[6];
+    uint8_t sense[32];
+    uint8_t buff[18];
+    int sz_buff = sizeof(buff);
 
     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.dxfer_len = sz_buff;
     io_hdr.dxferp = buff;
     cdb[0] = REQUEST_SENSE;
-    cdb[4] = sizeof(buff);
+    cdb[4] = sz_buff;
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;
@@ -890,7 +951,7 @@ scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
     if (!device->scsi_pass_through(&io_hdr))
       return -device->get_errno();
     if (sense_info) {
-        UINT8 resp_code = buff[0] & 0x7f;
+        uint8_t resp_code = buff[0] & 0x7f;
         sense_info->resp_code = resp_code;
         sense_info->sense_key = buff[2] & 0xf;
         sense_info->asc = 0;
@@ -902,7 +963,7 @@ scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
                 sense_info->ascq = buff[13];
             }
         }
-    // fill progrss indicator, if available
+    // fill progress indicator, if available
     sense_info->progress = -1;
     switch (resp_code) {
       const unsigned char * ucp;
@@ -910,12 +971,11 @@ scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
       case 0x70:
       case 0x71:
           sk = (buff[2] & 0xf);
-          if ((sizeof(buff) < 18) ||
-              ((SCSI_SK_NO_SENSE != sk) && (SCSI_SK_NOT_READY != sk))) {
+          if (! ((SCSI_SK_NO_SENSE == sk) || (SCSI_SK_NOT_READY == sk))) {
               break;
           }
           if (buff[15] & 0x80) {        /* SKSV bit set */
-              sense_info->progress = (buff[16] << 8) + buff[17];
+              sense_info->progress = sg_get_unaligned_be16(buff + 16);
               break;
           } else {
               break;
@@ -925,13 +985,13 @@ scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
           /* sense key specific progress (0x2) or progress descriptor (0xa) */
           sk = (buff[1] & 0xf);
           sk_pr = (SCSI_SK_NO_SENSE == sk) || (SCSI_SK_NOT_READY == sk);
-          if (sk_pr && ((ucp = sg_scsi_sense_desc_find(buff, sizeof(buff), 2))) &&
+          if (sk_pr && ((ucp = sg_scsi_sense_desc_find(buff, sz_buff, 2))) &&
               (0x6 == ucp[1]) && (0x80 & ucp[4])) {
-              sense_info->progress = (ucp[5] << 8) + ucp[6];
+              sense_info->progress = sg_get_unaligned_be16(ucp + 5);
               break;
-          } else if (((ucp = sg_scsi_sense_desc_find(buff, sizeof(buff), 0xa))) &&
+          } else if (((ucp = sg_scsi_sense_desc_find(buff, sz_buff, 0xa))) &&
                      ((0x6 == ucp[1]))) {
-              sense_info->progress = (ucp[6] << 8) + ucp[7];
+              sense_info->progress = sg_get_unaligned_be16(ucp + 6);
               break;
           } else
               break;
@@ -946,13 +1006,13 @@ scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info)
  * not supported, 3 if field in command not supported or returns negated
  * errno. SPC-3 section 6.28 (rev 22a) */
 int
-scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf,
+scsiSendDiagnostic(scsi_device * device, int functioncode, uint8_t *pBuf,
                    int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[6];
-    UINT8 sense[32];
+    uint8_t cdb[6];
+    uint8_t sense[32];
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -966,8 +1026,7 @@ scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf,
         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;
+    sg_put_unaligned_be16(bufLen, cdb + 3);
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;
@@ -986,8 +1045,8 @@ static int
 _testunitready(scsi_device * device, struct scsi_sense_disect * sinfo)
 {
     struct scsi_cmnd_io io_hdr;
-    UINT8 cdb[6];
-    UINT8 sense[32];
+    uint8_t cdb[6];
+    uint8_t sense[32];
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -1035,12 +1094,12 @@ scsiTestUnitReady(scsi_device * device)
  * negated errno. SBC-2 section 5.12 (rev 16) */
 int
 scsiReadDefect10(scsi_device * device, int req_plist, int req_glist,
-                 int dl_format, UINT8 *pBuf, int bufLen)
+                 int dl_format, uint8_t *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[10];
-    UINT8 sense[32];
+    uint8_t cdb[10];
+    uint8_t sense[32];
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -1050,8 +1109,7 @@ scsiReadDefect10(scsi_device * device, int req_plist, int req_glist,
     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;
+    sg_put_unaligned_be16(bufLen, cdb + 7);
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;
@@ -1073,12 +1131,12 @@ scsiReadDefect10(scsi_device * device, int req_plist, int req_glist,
  * negated errno. SBC-3 section 5.18 (rev 35; vale Mark Evans) */
 int
 scsiReadDefect12(scsi_device * device, int req_plist, int req_glist,
-                 int dl_format, int addrDescIndex, UINT8 *pBuf, int bufLen)
+                 int dl_format, int addrDescIndex, uint8_t *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[12];
-    UINT8 sense[32];
+    uint8_t cdb[12];
+    uint8_t sense[32];
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -1088,14 +1146,8 @@ scsiReadDefect12(scsi_device * device, int req_plist, int req_glist,
     cdb[0] = READ_DEFECT_12;
     cdb[1] = (unsigned char)(((req_plist << 4) & 0x10) |
                ((req_glist << 3) & 0x8) | (dl_format & 0x7));
-    cdb[2] = (addrDescIndex >> 24) & 0xff;
-    cdb[3] = (addrDescIndex >> 16) & 0xff;
-    cdb[4] = (addrDescIndex >> 8) & 0xff;
-    cdb[5] = addrDescIndex & 0xff;
-    cdb[6] = (bufLen >> 24) & 0xff;
-    cdb[7] = (bufLen >> 16) & 0xff;
-    cdb[8] = (bufLen >> 8) & 0xff;
-    cdb[9] = bufLen & 0xff;
+    sg_put_unaligned_be32(addrDescIndex, cdb + 2);
+    sg_put_unaligned_be32(bufLen, cdb + 6);
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;
@@ -1121,9 +1173,9 @@ scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap,
     int res;
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[10];
-    UINT8 sense[32];
-    UINT8 resp[8];
+    uint8_t cdb[10];
+    uint8_t sense[32];
+    uint8_t resp[8];
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -1145,11 +1197,9 @@ scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap,
     if (res)
         return res;
     if (last_lbap)
-        *last_lbap = (resp[0] << 24) | (resp[1] << 16) | (resp[2] << 8) |
-                     resp[3];
+        *last_lbap = sg_get_unaligned_be32(resp + 0);
     if (lb_sizep)
-        *lb_sizep = (resp[4] << 24) | (resp[5] << 16) | (resp[6] << 8) |
-                    resp[7];
+        *lb_sizep = sg_get_unaligned_be32(resp + 4);
     return 0;
 }
 
@@ -1157,12 +1207,12 @@ scsiReadCapacity10(scsi_device * device, unsigned int * last_lbap,
  * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command
  * not supported or returns negated errno. SBC-3 section 5.16 (rev 26) */
 int
-scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen)
+scsiReadCapacity16(scsi_device * device, uint8_t *pBuf, int bufLen)
 {
     struct scsi_cmnd_io io_hdr;
     struct scsi_sense_disect sinfo;
-    UINT8 cdb[16];
-    UINT8 sense[32];
+    uint8_t cdb[16];
+    uint8_t sense[32];
 
     memset(&io_hdr, 0, sizeof(io_hdr));
     memset(cdb, 0, sizeof(cdb));
@@ -1171,10 +1221,7 @@ scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen)
     io_hdr.dxferp = pBuf;
     cdb[0] = READ_CAPACITY_16;
     cdb[1] = SAI_READ_CAPACITY_16;
-    cdb[10] = (bufLen >> 24) & 0xff;
-    cdb[11] = (bufLen >> 16) & 0xff;
-    cdb[12] = (bufLen >> 8) & 0xff;
-    cdb[13] = bufLen & 0xff;
+    sg_put_unaligned_be32(bufLen, cdb + 10);
     io_hdr.cmnd = cdb;
     io_hdr.cmnd_len = sizeof(cdb);
     io_hdr.sensep = sense;
@@ -1188,82 +1235,101 @@ scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen)
 }
 
 /* Return number of bytes of storage in 'device' or 0 if error. If
- * successful and lb_sizep is not NULL then the logical block size
- * in bytes is written to the location pointed to by lb_sizep. */
+ * successful and lb_sizep is not NULL then the logical block size in bytes
+ * is written to the location pointed to by lb_sizep. If the 'Logical Blocks
+ * per Physical Block Exponent' pointer (lb_per_pb_expp,) is non-null then
+ * the value is written. If 'Protection information Intervals Exponent'*/
 uint64_t
-scsiGetSize(scsi_device * device, unsigned int * lb_sizep,
-            int * lb_per_pb_expp)
+scsiGetSize(scsi_device * device, bool avoid_rcap16,
+            struct scsi_readcap_resp * srrp)
 {
+    bool try_16 = false;
+    bool try_12 = false;
     unsigned int last_lba = 0, lb_size = 0;
     int res;
     uint64_t ret_val = 0;
-    UINT8 rc16resp[32];
+    uint8_t rc16resp[32];
 
-    res = scsiReadCapacity10(device, &last_lba, &lb_size);
-    if (res) {
-        if (scsi_debugmode)
-            pout("scsiGetSize: READ CAPACITY(10) failed, res=%d\n", res);
-        return 0;
+    if (avoid_rcap16) {
+        res = scsiReadCapacity10(device, &last_lba, &lb_size);
+        if (res) {
+            if (scsi_debugmode)
+                pout("%s: READ CAPACITY(10) failed, res=%d\n", __func__, res);
+            try_16 = true;
+        } else {        /* rcap10 succeeded */
+            if (0xffffffff == last_lba) {
+                /* so number of blocks needs > 32 bits to represent */
+                try_16 = true;
+                device->set_rcap16_first();
+            } else {
+                ret_val = last_lba + 1;
+                if (srrp) {
+                    memset(srrp, 0, sizeof(*srrp));
+                    srrp->num_lblocks = ret_val;
+                    srrp->lb_size = lb_size;
+                }
+            }
+        }
     }
-    if (0xffffffff == last_lba) {
+    if (try_16 || (! avoid_rcap16)) {
         res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp));
         if (res) {
             if (scsi_debugmode)
-                pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res);
-            return 0;
-        }
-        for (int k = 0; k < 8; ++k) {
-            if (k > 0)
-                ret_val <<= 8;
-            ret_val |= rc16resp[k + 0];
+                pout("%s: READ CAPACITY(16) failed, res=%d\n", __func__, res);
+            if (try_16)         /* so already tried rcap10 */
+                return 0;
+            try_12 = true;
+        } else {        /* rcap16 succeeded */
+            bool prot_en;
+            uint8_t  p_type;
+
+            ret_val = sg_get_unaligned_be64(rc16resp + 0) + 1;
+            lb_size = sg_get_unaligned_be32(rc16resp + 8);
+            if (srrp) {         /* writes to all fields */
+                srrp->num_lblocks = ret_val;
+                srrp->lb_size = lb_size;
+                prot_en = !!(0x1 & rc16resp[12]);
+                p_type = ((rc16resp[12] >> 1) & 0x7);
+                srrp->prot_type = prot_en ? (1 + p_type) : 0;
+                srrp->p_i_exp = ((rc16resp[13] >> 4) & 0xf);
+                srrp->lb_p_pb_exp = (rc16resp[13] & 0xf);
+                srrp->lbpme = !!(0x80 & rc16resp[14]);
+                srrp->lbprz = !!(0x40 & rc16resp[14]);
+                srrp->l_a_lba = sg_get_unaligned_be16(rc16resp + 14) & 0x3fff;
+            }
         }
-        if (lb_per_pb_expp)
-            *lb_per_pb_expp = (rc16resp[13] & 0xf);
-    } else {
-        ret_val = last_lba;
-        if (lb_per_pb_expp)
-            *lb_per_pb_expp = 0;
     }
-    if (lb_sizep)
-        *lb_sizep = lb_size;
-    ++ret_val;  /* last_lba is origin 0 so need to bump to get number of */
-    return ret_val * lb_size;
-}
-
-/* Gets drive Protection and Logical/Physical block information. Writes
- * back bytes 12 to 31 from a READ CAPACITY 16 command to the rc16_12_31p
- * pointer. So rc16_12_31p should point to an array of 20 bytes. Returns 0
- * if ok, 1 if NOT READY, 2 if command not supported, 3 if field in command
- * not supported or returns negated errno. */
-int
-scsiGetProtPBInfo(scsi_device * device, unsigned char * rc16_12_31p)
-{
-    int res;
-    UINT8 rc16resp[32];
-
-    res = scsiReadCapacity16(device, rc16resp, sizeof(rc16resp));
-    if (res) {
-        if (scsi_debugmode)
-            pout("scsiGetSize: READ CAPACITY(16) failed, res=%d\n", res);
-        return res;
+    if (try_12) {  /* case where only rcap16 has been tried and failed */
+        res = scsiReadCapacity10(device, &last_lba, &lb_size);
+        if (res) {
+            if (scsi_debugmode)
+                pout("%s: 2nd READ CAPACITY(10) failed, res=%d\n", __func__,
+                     res);
+            return 0;
+        } else {        /* rcap10 succeeded */
+            ret_val = (uint64_t)last_lba + 1;
+            if (srrp) {
+                memset(srrp, 0, sizeof(*srrp));
+                srrp->num_lblocks = ret_val;
+                srrp->lb_size = lb_size;
+            }
+        }
     }
-    if (rc16_12_31p)
-        memcpy(rc16_12_31p, rc16resp + 12, 20);
-    return 0;
+    return (ret_val * lb_size);
 }
 
 /* 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)
+scsiModePageOffset(const uint8_t * resp, int len, int modese_len)
 {
     int offset = -1;
 
     if (resp) {
         int resp_len, bd_len;
         if (10 == modese_len) {
-            resp_len = (resp[0] << 8) + resp[1] + 2;
-            bd_len = (resp[6] << 8) + resp[7];
+            resp_len = sg_get_unaligned_be16(resp + 0) + 2;
+            bd_len = sg_get_unaligned_be16(resp + 6);
             offset = bd_len + 8;
         } else {
             resp_len = resp[0] + 1;
@@ -1393,7 +1459,7 @@ scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
 {
     int offset, resp_len;
     int err = 0;
-    UINT8 rout[SCSI_IECMP_RAW_LEN];
+    uint8_t rout[SCSI_IECMP_RAW_LEN];
 
     if ((! iecp) || (! iecp->gotCurrent))
         return -EINVAL;
@@ -1404,28 +1470,22 @@ scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
     memcpy(rout, iecp->raw_curr, SCSI_IECMP_RAW_LEN);
     /* mask out DPOFUA device specific (disk) parameter bit */
     if (10 == iecp->modese_len) {
-        resp_len = (rout[0] << 8) + rout[1] + 2;
+        resp_len = sg_get_unaligned_be16(rout + 0) + 2;
         rout[3] &= 0xef;
     } else {
         resp_len = rout[0] + 1;
         rout[2] &= 0xef;
     }
-    int sp = (rout[offset] & 0x80) ? 1 : 0; /* PS bit becomes 'SELECT's SP bit */
+    int sp = !! (rout[offset] & 0x80); /* PS bit becomes 'SELECT's SP bit */
     if (enabled) {
         rout[offset + 2] = SCSI_IEC_MP_BYTE2_ENABLED;
         if (scsi_debugmode > 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;
+        sg_put_unaligned_be32(SCSI_IEC_MP_INTERVAL_T, rout + offset + 4);
+        sg_put_unaligned_be32(SCSI_IEC_MP_REPORT_COUNT, rout + offset + 8);
         if (iecp->gotChangeable) {
-            UINT8 chg2 = iecp->raw_chg[offset + 2];
+            uint8_t chg2 = iecp->raw_chg[offset + 2];
 
             rout[offset + 2] = chg2 ? (rout[offset + 2] & chg2) :
                                       iecp->raw_curr[offset + 2];
@@ -1464,9 +1524,9 @@ scsiSetExceptionControlAndWarning(scsi_device * device, int enabled,
 }
 
 int
-scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp)
+scsiGetTemp(scsi_device * device, uint8_t *currenttemp, uint8_t *triptemp)
 {
-    UINT8 tBuf[252];
+    uint8_t tBuf[252];
     int err;
 
     memset(tBuf, 0, sizeof(tBuf));
@@ -1474,7 +1534,8 @@ scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp)
                             sizeof(tBuf), 0))) {
         *currenttemp = 0;
         *triptemp = 0;
-        pout("Log Sense for temperature failed [%s]\n", scsiErrString(err));
+        pout("%s for temperature failed [%s]\n", logSenStr,
+             scsiErrString(err));
         return err;
     }
     *currenttemp = tBuf[9];
@@ -1488,12 +1549,13 @@ scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp)
  * (Celsius) implies that the temperature not available. */
 int
 scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
-            UINT8 *asc, UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp)
+            uint8_t *asc, uint8_t *ascq, uint8_t *currenttemp,
+            uint8_t *triptemp)
 {
-    UINT8 tBuf[252];
+    uint8_t tBuf[252];
     struct scsi_sense_disect sense_info;
     int err;
-    UINT8 currTemp, trTemp;
+    uint8_t currTemp, trTemp;
 
     *asc = 0;
     *ascq = 0;
@@ -1504,13 +1566,14 @@ scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
     if (hasIELogPage) {
         if ((err = scsiLogSense(device, IE_LPAGE, 0, tBuf,
                                 sizeof(tBuf), 0))) {
-            pout("Log Sense failed, IE page [%s]\n", scsiErrString(err));
+            pout("%s failed, IE page [%s]\n", logSenStr, scsiErrString(err));
             return err;
         }
         // pull out page size from response, don't forget to add 4
-        unsigned short pagesize = (unsigned short) ((tBuf[2] << 8) | tBuf[3]) + 4;
+        unsigned short pagesize = sg_get_unaligned_be16(tBuf + 2) + 4;
         if ((pagesize < 4) || tBuf[4] || tBuf[5]) {
-            pout("Log Sense failed, IE page, bad parameter code or length\n");
+            pout("%s failed, IE page, bad parameter code or length\n",
+                 logSenStr);
             return SIMPLE_ERR_BAD_PARAM;
         }
         if (tBuf[7] > 1) {
@@ -1546,13 +1609,16 @@ scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
 static const char * TapeAlertsMessageTable[]= {
     " ",
     /* 0x01 */
-   "W: The tape drive is having problems reading data. No data has been lost,\n"
+   "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"
+   "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"
+   "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"
@@ -1567,17 +1633,20 @@ static const char * TapeAlertsMessageTable[]= {
        "  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"
+   "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"
+   "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"
+   "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.",
@@ -1585,27 +1654,32 @@ static const char * TapeAlertsMessageTable[]= {
    "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"
+   "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"
+   "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.",
+       "  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"
+   "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"
+       "  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"
@@ -1615,15 +1689,19 @@ static const char * TapeAlertsMessageTable[]= {
        "  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"
+       "  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.",
+       "  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.",
+       "  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"
@@ -1643,7 +1721,8 @@ static const char * TapeAlertsMessageTable[]= {
    "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"
+       "  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.",
@@ -1687,10 +1766,12 @@ static const char * TapeAlertsMessageTable[]= {
        "  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"
+       "  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"
+   "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"
@@ -1752,7 +1833,8 @@ static const char * TapeAlertsMessageTable[]= {
         "  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.",
+        "  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"
@@ -1792,19 +1874,23 @@ static const char * ChangerTapeAlertsMessageTable[]= {
     "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"
+        "  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"
+        "  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"
+        "  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"
@@ -1815,7 +1901,8 @@ static const char * ChangerTapeAlertsMessageTable[]= {
         "  supplier help line.",
     /* 0x08 */
     "W: Preventive maintenance of the library is required.\n"
-        "  Check the library users manual for device specific preventative maintenance\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"
@@ -1831,7 +1918,8 @@ static const char * ChangerTapeAlertsMessageTable[]= {
     "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"
+        "  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"
@@ -1862,11 +1950,13 @@ static const char * ChangerTapeAlertsMessageTable[]= {
     "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 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.",
+    "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.",
@@ -1879,7 +1969,8 @@ static const char * ChangerTapeAlertsMessageTable[]= {
     "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"
+        "  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.",
@@ -1889,15 +1980,18 @@ static const char * ChangerTapeAlertsMessageTable[]= {
     "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.",
+        "  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"
+    "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"
+        "  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.",
@@ -1909,7 +2003,8 @@ scsiTapeAlertsChangerDevice(unsigned short code)
     const int num = sizeof(ChangerTapeAlertsMessageTable) /
                         sizeof(ChangerTapeAlertsMessageTable[0]);
 
-    return (code < num) ?  ChangerTapeAlertsMessageTable[code] : "Unknown Alert";
+    return (code < num) ?  ChangerTapeAlertsMessageTable[code] :
+                           "Unknown Alert";
 }
 
 
@@ -2039,7 +2134,7 @@ static const char * strs_for_asc_b[] = {
 static char spare_buff[128];
 
 const char *
-scsiGetIEString(UINT8 asc, UINT8 ascq)
+scsiGetIEString(uint8_t asc, uint8_t ascq)
 {
     const char * rp;
 
@@ -2143,7 +2238,7 @@ scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec,
                               int modese_len)
 {
     int err, offset;
-    UINT8 buff[64];
+    uint8_t buff[64];
 
     memset(buff, 0, sizeof(buff));
     if (modese_len <= 6) {
@@ -2168,7 +2263,7 @@ scsiFetchExtendedSelfTestTime(scsi_device * device, int * durationSec,
     if (offset < 0)
         return -EINVAL;
     if (buff[offset + 1] >= 0xa) {
-        int res = (buff[offset + 10] << 8) | buff[offset + 11];
+        int res = sg_get_unaligned_be16(buff + offset + 10);
         *durationSec = res;
         return 0;
     }
@@ -2180,10 +2275,10 @@ void
 scsiDecodeErrCounterPage(unsigned char * resp, struct scsiErrorCounter *ecp)
 {
     memset(ecp, 0, sizeof(*ecp));
-    int num = (resp[2] << 8) | resp[3];
+    int num = sg_get_unaligned_be16(resp + 2);
     unsigned char * ucp = &resp[0] + 4;
     while (num > 3) {
-        int pc = (ucp[0] << 8) | ucp[1];
+        int pc = sg_get_unaligned_be16(ucp + 0);
         int pl = ucp[3] + 4;
         uint64_t * ullp;
         switch (pc) {
@@ -2208,12 +2303,7 @@ scsiDecodeErrCounterPage(unsigned char * resp, struct scsiErrorCounter *ecp)
             xp += (k - sizeof(*ullp));
             k = sizeof(*ullp);
         }
-        *ullp = 0;
-        for (int j = 0; j < k; ++j) {
-            if (j > 0)
-                *ullp <<= 8;
-            *ullp |= xp[j];
-        }
+        *ullp = sg_get_unaligned_be(k, xp);
         num -= pl;
         ucp += pl;
     }
@@ -2224,11 +2314,11 @@ scsiDecodeNonMediumErrPage(unsigned char *resp,
                            struct scsiNonMediumError *nmep)
 {
     memset(nmep, 0, sizeof(*nmep));
-    int num = (resp[2] << 8) | resp[3];
+    int num = sg_get_unaligned_be16(resp + 2);
     unsigned char * ucp = &resp[0] + 4;
     int szof = sizeof(nmep->counterPC0);
     while (num > 3) {
-        int pc = (ucp[0] << 8) | ucp[1];
+        int pc = sg_get_unaligned_be16(ucp + 0);
         int pl = ucp[3] + 4;
         int k;
         unsigned char * xp;
@@ -2241,12 +2331,7 @@ scsiDecodeNonMediumErrPage(unsigned char *resp,
                     xp += (k - szof);
                     k = szof;
                 }
-                nmep->counterPC0 = 0;
-                for (int j = 0; j < k; ++j) {
-                    if (j > 0)
-                        nmep->counterPC0 <<= 8;
-                    nmep->counterPC0 |= xp[j];
-                }
+                nmep->counterPC0 = sg_get_unaligned_be(k, xp + 0);
                 break;
             case 0x8009:
                 nmep->gotTFE_H = 1;
@@ -2256,12 +2341,7 @@ scsiDecodeNonMediumErrPage(unsigned char *resp,
                     xp += (k - szof);
                     k = szof;
                 }
-                nmep->counterTFE_H = 0;
-                for (int j = 0; j < k; ++j) {
-                    if (j > 0)
-                        nmep->counterTFE_H <<= 8;
-                    nmep->counterTFE_H |= xp[j];
-                }
+                nmep->counterTFE_H = sg_get_unaligned_be(k, xp + 0);
                 break;
             case 0x8015:
                 nmep->gotPE_H = 1;
@@ -2271,12 +2351,7 @@ scsiDecodeNonMediumErrPage(unsigned char *resp,
                     xp += (k - szof);
                     k = szof;
                 }
-                nmep->counterPE_H = 0;
-                for (int j = 0; j < k; ++j) {
-                    if (j > 0)
-                        nmep->counterPE_H <<= 8;
-                    nmep->counterPE_H |= xp[j];
-                }
+                nmep->counterPE_H = sg_get_unaligned_be(k, xp + 0);
                 break;
         default:
                 nmep->gotExtraPC = 1;
@@ -2299,7 +2374,7 @@ int
 scsiCountFailedSelfTests(scsi_device * fd, int noisy)
 {
     int num, k, err, fails, fail_hour;
-    UINT8 * ucp;
+    uint8_t * ucp;
     unsigned char resp[LOG_RESP_SELF_TEST_LEN];
 
     if ((err = scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp,
@@ -2310,15 +2385,16 @@ scsiCountFailedSelfTests(scsi_device * fd, int noisy)
     }
     if ((resp[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
         if (noisy)
-            pout("Self-test Log Sense Failed, page mismatch\n");
+            pout("Self-test %s Failed, page mismatch\n", logSenStr);
         return -1;
     }
     // compute page length
-    num = (resp[2] << 8) + resp[3];
+    num = sg_get_unaligned_be16(resp + 2);
     // 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);
+            pout("Self-test %s length is 0x%x not 0x190 bytes\n", logSenStr,
+                 num);
         return -1;
     }
     fails = 0;
@@ -2327,7 +2403,7 @@ scsiCountFailedSelfTests(scsi_device * fd, int noisy)
     for (k = 0, ucp = resp + 4; k < 20; ++k, ucp += 20 ) {
 
         // timestamp in power-on hours (or zero if test in progress)
-        int n = (ucp[6] << 8) | ucp[7];
+        int n = sg_get_unaligned_be16(ucp + 6);
 
         // The spec says "all 20 bytes will be zero if no test" but
         // DG has found otherwise.  So this is a heuristic.
@@ -2337,7 +2413,7 @@ scsiCountFailedSelfTests(scsi_device * fd, int noisy)
         if ((res > 2) && (res < 8)) {
             fails++;
             if (1 == fails)
-                fail_hour = (ucp[6] << 8) + ucp[7];
+                fail_hour = sg_get_unaligned_be16(ucp + 6);
         }
     }
     return (fail_hour << 8) + fails;
@@ -2349,7 +2425,7 @@ int
 scsiSelfTestInProgress(scsi_device * fd, int * inProgress)
 {
     int num;
-    UINT8 * ucp;
+    uint8_t * ucp;
     unsigned char resp[LOG_RESP_SELF_TEST_LEN];
 
     if (scsiLogSense(fd, SELFTEST_RESULTS_LPAGE, 0, resp,
@@ -2358,7 +2434,7 @@ scsiSelfTestInProgress(scsi_device * fd, int * inProgress)
     if (resp[0] != SELFTEST_RESULTS_LPAGE)
         return -1;
     // compute page length
-    num = (resp[2] << 8) + resp[3];
+    num = sg_get_unaligned_be16(resp + 2);
     // Log sense page length 0x190 bytes
     if (num != 0x190) {
         return -1;
@@ -2369,7 +2445,7 @@ scsiSelfTestInProgress(scsi_device * fd, int * inProgress)
     return 0;
 }
 
-/* Returns a negative value if failed to fetch Contol mode page or it was
+/* Returns a negative value if failed to fetch Control 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. */
@@ -2377,7 +2453,7 @@ int
 scsiFetchControlGLTSD(scsi_device * device, int modese_len, int current)
 {
     int err, offset;
-    UINT8 buff[64];
+    uint8_t buff[64];
     int pc = current ? MPAGE_CONTROL_CURRENT : MPAGE_CONTROL_DEFAULT;
 
     memset(buff, 0, sizeof(buff));
@@ -2413,14 +2489,14 @@ scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp,
            int * haw_zbcp)
 {
     int err, offset;
-    UINT8 buff[64];
+    uint8_t buff[64];
     int pc = MPAGE_CONTROL_DEFAULT;
 
     memset(buff, 0, sizeof(buff));
     if ((0 == scsiInquiryVpd(device, SCSI_VPD_BLOCK_DEVICE_CHARACTERISTICS,
                              buff, sizeof(buff))) &&
-        (((buff[2] << 8) + buff[3]) > 2)) {
-        int speed = (buff[4] << 8) + buff[5];
+        ((sg_get_unaligned_be16(buff + 2)) > 2)) {
+        int speed = sg_get_unaligned_be16(buff + 4);
         if (form_factorp)
             *form_factorp = buff[7] & 0xf;
         if (haw_zbcp)
@@ -2448,7 +2524,7 @@ scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp,
             return -EINVAL;
     }
     offset = scsiModePageOffset(buff, sizeof(buff), modese_len);
-    return (buff[offset + 20] << 8) | buff[offset + 21];
+    return sg_get_unaligned_be16(buff + offset + 20);
 }
 
 /* Returns a non-zero value in case of error, wcep/rcdp == -1 - get value,
@@ -2459,14 +2535,15 @@ scsiGetSetCache(scsi_device * device,  int modese_len, short int * wcep,
                 short int * rcdp)
 {
     int err, offset, resp_len, sp;
-    UINT8 buff[64], ch_buff[64];
+    uint8_t buff[64], ch_buff[64];
     short set_wce = *wcep;
     short set_rcd = *rcdp;
 
     memset(buff, 0, sizeof(buff));
     if (modese_len <= 6) {
-        if ((err = scsiModeSense(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT,
-                                 buff, sizeof(buff)))) {
+        err = scsiModeSense(device, CACHING_PAGE, 0, MPAGE_CONTROL_CURRENT,
+                            buff, sizeof(buff));
+        if (err) {
             if (SIMPLE_ERR_BAD_OPCODE == err)
                 modese_len = 10;
             else {
@@ -2507,14 +2584,14 @@ scsiGetSetCache(scsi_device * device,  int modese_len, short int * wcep,
                               MPAGE_CONTROL_CHANGEABLE,
                               ch_buff, sizeof(ch_buff));
     if (err) {
-        device->set_err(EINVAL, "WCE/RCD bits not changable");
+        device->set_err(EINVAL, "WCE/RCD bits not changeable");
         return err;
     }
 
     // set WCE bit
     if(set_wce >= 0 && *wcep != set_wce) {
        if (0 == (ch_buff[offset + 2] & 0x04)) {
-         device->set_err(EINVAL, "WCE bit not changable");
+         device->set_err(EINVAL, "WCE bit not changeable");
          return 1;
        }
        if(set_wce)
@@ -2525,7 +2602,7 @@ scsiGetSetCache(scsi_device * device,  int modese_len, short int * wcep,
     // set RCD bit
     if(set_rcd >= 0 && *rcdp != set_rcd) {
        if (0 == (ch_buff[offset + 2] & 0x01)) {
-         device->set_err(EINVAL, "RCD bit not changable");
+         device->set_err(EINVAL, "RCD bit not changeable");
          return 1;
        }
        if(set_rcd)
@@ -2536,7 +2613,7 @@ scsiGetSetCache(scsi_device * device,  int modese_len, short int * wcep,
 
     /* mask out DPOFUA device specific (disk) parameter bit */
     if (10 == modese_len) {
-        resp_len = (buff[0] << 8) + buff[1] + 2;
+        resp_len = sg_get_unaligned_be16(buff + 0) + 2;
         buff[3] &= 0xef;
     } else {
         resp_len = buff[0] + 1;
@@ -2561,8 +2638,8 @@ int
 scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len)
 {
     int err, offset, resp_len, sp;
-    UINT8 buff[64];
-    UINT8 ch_buff[64];
+    uint8_t buff[64];
+    uint8_t ch_buff[64];
 
     memset(buff, 0, sizeof(buff));
     if (modese_len <= 6) {
@@ -2607,8 +2684,8 @@ scsiSetControlGLTSD(scsi_device * device, int enabled, int modese_len)
 
     /* mask out DPOFUA device specific (disk) parameter bit */
     if (10 == modese_len) {
-        resp_len = (buff[0] << 8) + buff[1] + 2;
-        buff[3] &= 0xef;    
+        resp_len = sg_get_unaligned_be16(buff + 0) + 2;
+        buff[3] &= 0xef;
     } else {
         resp_len = buff[0] + 1;
         buff[2] &= 0xef;
@@ -2632,7 +2709,7 @@ int
 scsiFetchTransportProtocol(scsi_device * device, int modese_len)
 {
     int err, offset;
-    UINT8 buff[64];
+    uint8_t buff[64];
 
     memset(buff, 0, sizeof(buff));
     if (modese_len <= 6) {
@@ -2690,7 +2767,7 @@ sg_scsi_sense_desc_find(const unsigned char * sensep, int sense_len,
 
 // Convenience function for formatting strings from SCSI identify
 void
-scsi_format_id_string(char * out, const unsigned char * in, int n)
+scsi_format_id_string(char * out, const uint8_t * in, int n)
 {
   char tmp[65];
   n = n > 64 ? 64 : n;
@@ -2707,7 +2784,7 @@ scsi_format_id_string(char * out, const unsigned char * in, int n)
     }
 
   if (first == -1) {
-    // There are no non-space characters.
+    // There are only space characters.
     out[0] = '\0';
     return;
   }
index bbb9f8af19aadfc0e937a140c1c44ed361ff3c45..516f773a655bd86b7e57a19498270d58206e43dc 100644 (file)
@@ -5,21 +5,10 @@
  *
  * Copyright (C) 2002-8 Bruce Allen
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
- * Copyright (C) 2003-15 Douglas Gilbert <dgilbert@interlog.com>
+ * Copyright (C) 2003-18 Douglas Gilbert <dgilbert@interlog.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.
+ * SPDX-License-Identifier: GPL-2.0-or-later
  *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 4557 2017-10-19 19:17:29Z samm2 $\n"
+#define SCSICMDS_H_CVSID "$Id: scsicmds.h 4842 2018-12-02 16:07:26Z chrfranke $\n"
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stdint.h>
 #include <string.h>
 
 /* #define SCSI_DEBUG 1 */ /* Comment out to disable command debugging */
 #define SAT_ATA_PASSTHROUGH_16 0x85
 #endif
 
-typedef unsigned char UINT8;
-typedef signed char INT8;
-typedef unsigned int UINT32;
-typedef int INT32;
 
 #define DXFER_NONE        0
 #define DXFER_FROM_DEVICE 1
@@ -113,97 +99,144 @@ typedef int INT32;
 
 struct scsi_cmnd_io
 {
-    UINT8 * cmnd;       /* [in]: ptr to SCSI command block (cdb) */
+    uint8_t * 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 */
+    uint8_t * 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
+    uint8_t * 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 ... */
+    uint8_t 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 resp_code;
-    UINT8 sense_key;
-    UINT8 asc;
-    UINT8 ascq;
+    uint8_t resp_code;
+    uint8_t sense_key;
+    uint8_t asc;
+    uint8_t ascq;
     int progress; /* -1 -> N/A, 0-65535 -> available */
 };
 
 /* 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];
+    uint8_t requestedCurrent;
+    uint8_t gotCurrent;
+    uint8_t requestedChangeable;
+    uint8_t gotChangeable;
+    uint8_t modese_len;   /* 0 (don't know), 6 or 10 */
+    uint8_t raw_curr[SCSI_IECMP_RAW_LEN];
+    uint8_t raw_chg[SCSI_IECMP_RAW_LEN];
 };
 
 /* Carrier for Error counter log pages (e.g. read, write, verify ...) */
 struct scsiErrorCounter {
-    UINT8 gotPC[7];
-    UINT8 gotExtraPC;
+    uint8_t gotPC[7];
+    uint8_t gotExtraPC;
     uint64_t counter[8];
 };
 
 /* Carrier for Non-medium error log page */
 struct scsiNonMediumError {
-    UINT8 gotPC0;
-    UINT8 gotExtraPC;
+    uint8_t gotPC0;
+    uint8_t gotExtraPC;
     uint64_t counterPC0;
-    UINT8 gotTFE_H;
+    uint8_t gotTFE_H;
     uint64_t counterTFE_H; /* Track following errors [Hitachi] */
-    UINT8 gotPE_H;
+    uint8_t gotPE_H;
     uint64_t counterPE_H;  /* Positioning errors [Hitachi] */
 };
 
+struct scsi_readcap_resp {
+    uint64_t num_lblocks;       /* Number of Logical Blocks on device */
+    uint32_t lb_size;   /* should be available in all non-error cases */
+    /* following fields from READ CAPACITY(16) or set to 0 */
+    uint8_t prot_type;  /* 0, 1, 2 or 3 protection type, deduced from
+                         * READ CAPACITY(16) P_TYPE and PROT_EN fields */
+    uint8_t p_i_exp;    /* Protection information Intervals Exponent */
+    uint8_t lb_p_pb_exp;/* Logical Blocks per Physical Block Exponent */
+    bool lbpme;         /* Logical Block Provisioning Management Enabled */
+    bool lbprz;         /* Logical Block Provisioning Read Zeros */
+    uint16_t l_a_lba;   /* Lowest Aligned Logical Block Address */
+};
+
 /* 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 LB_PROV_LPAGE                           0x0c   /* SBC-3 */
-#define TEMPERATURE_LPAGE                       0x0d
-#define STARTSTOP_CYCLE_COUNTER_LPAGE           0x0e
-#define APPLICATION_CLIENT_LPAGE                0x0f
-#define SELFTEST_RESULTS_LPAGE                  0x10
-#define SS_MEDIA_LPAGE                          0x11   /* SBC-3 */
-#define BACKGROUND_RESULTS_LPAGE                0x15   /* SBC-3 */
-#define NONVOL_CACHE_LPAGE                      0x17   /* SBC-3 */
-#define PROTOCOL_SPECIFIC_LPAGE                 0x18
-#define IE_LPAGE                                0x2f
+#define SCSI_PT_HOST_MANAGED            0x14
+
+/* Transport protocol identifiers or just Protocol identifiers */
+#define SCSI_TPROTO_FCP 0
+#define SCSI_TPROTO_SPI 1
+#define SCSI_TPROTO_SSA 2
+#define SCSI_TPROTO_1394 3
+#define SCSI_TPROTO_SRP 4            /* SCSI over RDMA */
+#define SCSI_TPROTO_ISCSI 5
+#define SCSI_TPROTO_SAS 6
+#define SCSI_TPROTO_ADT 7
+#define SCSI_TPROTO_ATA 8
+#define SCSI_TPROTO_UAS 9            /* USB attached SCSI */
+#define SCSI_TPROTO_SOP 0xa          /* SCSI over PCIe */
+#define SCSI_TPROTO_PCIE 0xb         /* includes NVMe */
+#define SCSI_TPROTO_NONE 0xf
+
+
+/* SCSI Log Pages retrieved by LOG SENSE. 0x0 to 0x3f, 0x30 to 0x3e vendor */
+#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_EVENTS_LPAGE           0x07
+#define FORMAT_STATUS_LPAGE                 0x08
+#define LAST_N_DEFERRED_LPAGE               0x0b   /* or async events */
+#define LB_PROV_LPAGE                       0x0c   /* SBC-3 */
+#define TEMPERATURE_LPAGE                   0x0d
+#define STARTSTOP_CYCLE_COUNTER_LPAGE       0x0e
+#define APPLICATION_CLIENT_LPAGE            0x0f
+#define SELFTEST_RESULTS_LPAGE              0x10
+#define SS_MEDIA_LPAGE                      0x11   /* SBC-3 */
+#define BACKGROUND_RESULTS_LPAGE            0x15   /* SBC-3 */
+#define ATA_PT_RESULTS_LPAGE                0x16   /* SAT */
+#define NONVOL_CACHE_LPAGE                  0x17   /* SBC-3 */
+#define PROTOCOL_SPECIFIC_LPAGE             0x18
+#define GEN_STATS_PERF_LPAGE                0x19
+#define POWER_COND_TRANS_LPAGE              0x1a
+#define IE_LPAGE                            0x2f
+
+/* SCSI Log subpages (8 bits), added spc4r05 2006, standardized SPC-4 2015 */
+#define NO_SUBPAGE_L_SPAGE              0x0     /* 0x0-0x3f,0x0 */
+#define LAST_N_INQ_DAT_L_SPAGE          0x1     /* 0xb,0x1 */
+#define LAST_N_MODE_PG_L_SPAGE          0x2     /* 0xb,0x2 */
+#define ENVIRO_REP_L_SPAGE              0x1     /* 0xd,0x1 */
+#define ENVIRO_LIMITS_L_SPAGE           0x2     /* 0xd,0x2 */
+#define UTILIZATION_L_SPAGE             0x1     /* 0xe,0x1 */
+#define ZB_DEV_STATS_L_SPAGE            0x1     /* 0x14,0x1 */
+#define PEND_DEFECTS_L_SPAGE            0x1     /* 0x15,0x1 */
+#define BACKGROUND_OP_L_SPAGE           0x2     /* 0x15,0x2 */
+#define LPS_MISALIGN_L_SPAGE            0x3     /* 0x15,0x3 */
+#define SUPP_SPAGE_L_SPAGE              0xff    /* 0x0,0xff pages+subpages */
 
 /* Seagate vendor specific log pages. */
-#define SEAGATE_CACHE_LPAGE                     0x37
-#define SEAGATE_FACTORY_LPAGE                   0x3e
+#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
+/* See the SSC-2 document at www.t10.org . Earlier note: From IBM
 Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */
 #define TAPE_ALERTS_LPAGE                        0x2e
 
@@ -290,7 +323,7 @@ Documentation, see http://www.storage.ibm.com/techsup/hddtech/prodspecs.htm */
 #define SIMPLE_ERR_TRY_AGAIN            8       /* some warning, try again */
 #define SIMPLE_ERR_MEDIUM_HARDWARE      9       /* medium or hardware error */
 #define SIMPLE_ERR_UNKNOWN              10      /* unknown sense value */
-#define SIMPLE_ERR_ABORTED_COMMAND      11      /* most likely transport error */
+#define SIMPLE_ERR_ABORTED_COMMAND      11      /* probably transport error */
 
 
 /* defines for functioncode parameter in SENDDIAGNOSTIC function */
@@ -336,6 +369,22 @@ private:
 
 extern supported_vpd_pages * supported_vpd_pages_p;
 
+/* This is a heuristic that takes into account the command bytes and length
+ * to decide whether the presented unstructured sequence of bytes could be
+ * a SCSI command. If so it returns true otherwise false. Vendor specific
+ * SCSI commands (i.e. opcodes from 0xc0 to 0xff), if presented, are assumed
+ * to follow SCSI conventions (i.e. length of 6, 10, 12 or 16 bytes). The
+ * only SCSI commands considered above 16 bytes of length are the Variable
+ * Length Commands (opcode 0x7f) and the XCDB wrapped commands (opcode 0x7e).
+ * Both have an inbuilt length field which can be cross checked with clen.
+ * No NVMe commands (64 bytes long plus some extra added by some OSes) have
+ * opcodes 0x7e or 0x7f yet. ATA is register based but SATA has FIS
+ * structures that are sent across the wire. The FIS register structure is
+ * used to move a command from a SATA host to device, but the ATA 'command'
+ * is not the first byte. So it is harder to say what will happen if a
+ * FIS structure is presented as a SCSI command, hopefully there is a low
+ * probability this function will yield true in that case. */
+bool is_scsi_cdb(const uint8_t * cdbp, int clen);
 
 // Print SCSI debug messages?
 extern unsigned char scsi_debugmode;
@@ -358,46 +407,51 @@ int scsi_decode_lu_dev_id(const unsigned char * b, int blen, char * s,
 /* STANDARD SCSI Commands  */
 int scsiTestUnitReady(scsi_device * device);
 
-int scsiStdInquiry(scsi_device * device, UINT8 *pBuf, int bufLen);
+int scsiStdInquiry(scsi_device * device, uint8_t *pBuf, int bufLen);
 
-int scsiInquiryVpd(scsi_device * device, int vpd_page, UINT8 *pBuf, int bufLen);
+int scsiInquiryVpd(scsi_device * device, int vpd_page, uint8_t *pBuf,
+                   int bufLen);
 
-int scsiLogSense(scsi_device * device, int pagenum, int subpagenum, UINT8 *pBuf,
-                 int bufLen, int known_resp_len);
+int scsiLogSense(scsi_device * device, int pagenum, int subpagenum,
+                 uint8_t *pBuf, int bufLen, int known_resp_len);
 
 int scsiLogSelect(scsi_device * device, int pcr, int sp, int pc, int pagenum,
-                  int subpagenum, UINT8 *pBuf, int bufLen);
+                  int subpagenum, uint8_t *pBuf, int bufLen);
 
 int scsiModeSense(scsi_device * device, int pagenum, int subpagenum, int pc,
-                  UINT8 *pBuf, int bufLen);
+                  uint8_t *pBuf, int bufLen);
 
-int scsiModeSelect(scsi_device * device, int sp, UINT8 *pBuf, int bufLen);
+int scsiModeSelect(scsi_device * device, int sp, uint8_t *pBuf, int bufLen);
 
 int scsiModeSense10(scsi_device * device, int pagenum, int subpagenum, int pc,
-                    UINT8 *pBuf, int bufLen);
+                    uint8_t *pBuf, int bufLen);
 
-int scsiModeSelect10(scsi_device * device, int sp, UINT8 *pBuf, int bufLen);
+int scsiModeSelect10(scsi_device * device, int sp, uint8_t *pBuf, int bufLen);
 
-int scsiModePageOffset(const UINT8 * resp, int len, int modese_len);
+int scsiModePageOffset(const uint8_t * resp, int len, int modese_len);
 
-int scsiRequestSense(scsi_device * device, struct scsi_sense_disect * sense_info);
+int scsiRequestSense(scsi_device * device,
+                     struct scsi_sense_disect * sense_info);
 
-int scsiSendDiagnostic(scsi_device * device, int functioncode, UINT8 *pBuf, int bufLen);
+int scsiSendDiagnostic(scsi_device * device, int functioncode, uint8_t *pBuf,
+                       int bufLen);
 
-int scsiReadDefect10(scsi_device * device, int req_plist, int req_glist, int dl_format,
-                     UINT8 *pBuf, int bufLen);
+int scsiReadDefect10(scsi_device * device, int req_plist, int req_glist,
+                     int dl_format, uint8_t *pBuf, int bufLen);
 
 int scsiReadDefect12(scsi_device * device, int req_plist, int req_glist,
-                     int dl_format, int addrDescIndex, UINT8 *pBuf, int bufLen);
+                     int dl_format, int addrDescIndex, uint8_t *pBuf,
+                     int bufLen);
 
 int scsiReadCapacity10(scsi_device * device, unsigned int * last_lbp,
                        unsigned int * lb_sizep);
 
-int scsiReadCapacity16(scsi_device * device, UINT8 *pBuf, int bufLen);
+int scsiReadCapacity16(scsi_device * device, uint8_t *pBuf, int bufLen);
 
 /* SMART specific commands */
-int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage, UINT8 *asc,
-                UINT8 *ascq, UINT8 *currenttemp, UINT8 *triptemp);
+int scsiCheckIE(scsi_device * device, int hasIELogPage, int hasTempLogPage,
+                uint8_t *asc, uint8_t *ascq, uint8_t *currenttemp,
+                uint8_t *triptemp);
 
 int scsiFetchIECmpage(scsi_device * device, struct scsi_iec_mode_page *iecp,
                       int modese_len);
@@ -420,13 +474,12 @@ int scsiGetRPM(scsi_device * device, int modese_len, int * form_factorp,
                int * haw_zbcp);
 int scsiGetSetCache(scsi_device * device,  int modese_len, short int * wce,
                     short int * rcd);
-uint64_t scsiGetSize(scsi_device * device, unsigned int * lb_sizep,
-                     int * lb_per_pb_expp);
-int scsiGetProtPBInfo(scsi_device * device, unsigned char * rc16_12_31p);
+uint64_t scsiGetSize(scsi_device * device, bool avoid_rcap16,
+                     struct scsi_readcap_resp * srrp);
 
 /* T10 Standard IE Additional Sense Code strings taken from t10.org */
-const char* scsiGetIEString(UINT8 asc, UINT8 ascq);
-int scsiGetTemp(scsi_device * device, UINT8 *currenttemp, UINT8 *triptemp);
+const char* scsiGetIEString(uint8_t asc, uint8_t ascq);
+int scsiGetTemp(scsi_device * device, uint8_t *currenttemp, uint8_t *triptemp);
 
 
 int scsiSmartDefaultSelfTest(scsi_device * device);
@@ -439,18 +492,16 @@ int scsiSmartSelfTestAbort(scsi_device * device);
 const char * scsiTapeAlertsTapeDevice(unsigned short code);
 const char * scsiTapeAlertsChangerDevice(unsigned short code);
 
-const char * scsi_get_opcode_name(UINT8 opcode);
-void scsi_format_id_string(char * out, const unsigned char * in, int n);
+const char * scsi_get_opcode_name(uint8_t opcode);
+void scsi_format_id_string(char * out, const uint8_t * in, int n);
 
-void dStrHex(const char* str, int len, int no_ascii);
-inline void dStrHex(const unsigned char* str, int len, int no_ascii)
-  { dStrHex((const char *)str, len, no_ascii); }
+void dStrHex(const uint8_t * up, int len, int no_ascii);
 
 /* Attempt to find the first SCSI sense data descriptor that matches the
    given 'desc_type'. If found return pointer to start of sense data
    descriptor; otherwise (including fixed format sense data) returns NULL. */
 const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep,
-                                                     int sense_len, int desc_type);
+                                              int sense_len, int desc_type);
 
 
 /* SCSI command transmission interface function declaration. Its
diff --git a/scsinvme.cpp b/scsinvme.cpp
new file mode 100644 (file)
index 0000000..28b8b92
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * scsinvme.cpp
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2018 Harry Mallon <hjmallon@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ */
+
+#include "config.h"
+
+#include "dev_interface.h"
+#include "dev_tunnelled.h"
+#include "scsicmds.h"
+#include "sg_unaligned.h"
+#include "utility.h"
+
+#include <errno.h>
+
+// SNT (SCSI NVMe Translation) namespace and prefix
+namespace snt {
+
+#define SNT_JMICRON_NVME_SIGNATURE 0x454d564eu // 'NVME' reversed (little endian)
+#define SNT_JMICRON_CDB_LEN 12
+#define SNT_JMICRON_NVM_CMD_LEN 512
+
+class sntjmicron_device
+: public tunnelled_device<
+    /*implements*/ nvme_device,
+    /*by tunnelling through a*/ scsi_device
+  >
+{
+public:
+  sntjmicron_device(smart_interface * intf, scsi_device * scsidev,
+                    const char * req_type, unsigned nsid);
+
+  virtual ~sntjmicron_device() throw();
+
+  virtual bool open();
+
+  virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out);
+
+private:
+  enum {
+    proto_nvm_cmd = 0x0, proto_non_data = 0x1, proto_dma_in = 0x2,
+    proto_dma_out = 0x3, proto_response = 0xF
+  };
+};
+
+sntjmicron_device::sntjmicron_device(smart_interface * intf, scsi_device * scsidev,
+                                     const char * req_type, unsigned nsid)
+: smart_device(intf, scsidev->get_dev_name(), "sntjmicron", req_type),
+  tunnelled_device<nvme_device, scsi_device>(scsidev, nsid)
+{
+  set_info().info_name = strprintf("%s [USB NVMe JMicron]", scsidev->get_info_name());
+}
+
+sntjmicron_device::~sntjmicron_device() throw()
+{
+}
+
+bool sntjmicron_device::open()
+{
+  // Open USB first
+  if (!tunnelled_device<nvme_device, scsi_device>::open())
+    return false;
+
+  // No sure how multiple namespaces come up on device so we
+  // cannot detect e.g. /dev/sdX is NSID 2.
+  // Set to broadcast if not available
+  if (!get_nsid()) {
+    set_nsid(0xFFFFFFFF);
+  }
+
+  return true;
+}
+
+// cdb[0]: ATA PASS THROUGH (12) SCSI command opcode byte (0xa1)
+// cdb[1]: [ is admin cmd: 1 ] [ protocol : 7 ]
+// cdb[2]: reserved
+// cdb[3]: parameter list length (23:16)
+// cdb[4]: parameter list length (15:08)
+// cdb[5]: parameter list length (07:00)
+// cdb[6]: reserved
+// cdb[7]: reserved
+// cdb[8]: reserved
+// cdb[9]: reserved
+// cdb[10]: reserved
+// cdb[11]: CONTROL (?)
+bool sntjmicron_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out)
+{
+  /* Only admin commands used */
+  bool admin = true;
+
+  // 1: "NVM Command Set Payload"
+  {
+    unsigned char cdb[SNT_JMICRON_CDB_LEN] = { 0 };
+    cdb[0] = SAT_ATA_PASSTHROUGH_12;
+    cdb[1] = (admin ? 0x80 : 0x00) | proto_nvm_cmd;
+    sg_put_unaligned_be24(SNT_JMICRON_NVM_CMD_LEN, &cdb[3]);
+
+    unsigned nvm_cmd[SNT_JMICRON_NVM_CMD_LEN / sizeof(unsigned)] = { 0 };
+    nvm_cmd[0] = SNT_JMICRON_NVME_SIGNATURE;
+    // nvm_cmd[1]: reserved
+    nvm_cmd[2] = in.opcode; // More of CDW0 may go in here in future
+    nvm_cmd[3] = in.nsid;
+    // nvm_cmd[4-5]: reserved
+    // nvm_cmd[6-7]: metadata pointer
+    // nvm_cmd[8-11]: data ptr (?)
+    nvm_cmd[12] = in.cdw10;
+    nvm_cmd[13] = in.cdw11;
+    nvm_cmd[14] = in.cdw12;
+    nvm_cmd[15] = in.cdw13;
+    nvm_cmd[16] = in.cdw14;
+    nvm_cmd[17] = in.cdw15;
+    // nvm_cmd[18-127]: reserved
+
+    if (isbigendian())
+      for (unsigned i = 0; i < (SNT_JMICRON_NVM_CMD_LEN / sizeof(uint32_t)); i++)
+        swapx(&nvm_cmd[i]);
+
+    scsi_cmnd_io io_nvm;
+    memset(&io_nvm, 0, sizeof(io_nvm));
+
+    io_nvm.cmnd = cdb;
+    io_nvm.cmnd_len = SNT_JMICRON_CDB_LEN;
+    io_nvm.dxfer_dir = DXFER_TO_DEVICE;
+    io_nvm.dxferp = (uint8_t *)nvm_cmd;
+    io_nvm.dxfer_len = SNT_JMICRON_NVM_CMD_LEN;
+
+    scsi_device * scsidev = get_tunnel_dev();
+    if (!scsidev->scsi_pass_through_and_check(&io_nvm,
+         "sntjmicron_device::nvme_pass_through:NVM: "))
+      return set_err(scsidev->get_err());
+  }
+
+  // 2: DMA or Non-Data
+  {
+    unsigned char cdb[SNT_JMICRON_CDB_LEN] = { 0 };
+    cdb[0] = SAT_ATA_PASSTHROUGH_12;
+
+    scsi_cmnd_io io_data;
+    memset(&io_data, 0, sizeof(io_data));
+    io_data.cmnd = cdb;
+    io_data.cmnd_len = SNT_JMICRON_CDB_LEN;
+
+    switch (in.direction()) {
+      case nvme_cmd_in::no_data:
+        cdb[1] = (admin ? 0x80 : 0x00) | proto_non_data;
+        io_data.dxfer_dir = DXFER_NONE;
+        break;
+      case nvme_cmd_in::data_out:
+        cdb[1] = (admin ? 0x80 : 0x00) | proto_dma_out;
+        sg_put_unaligned_be24(in.size, &cdb[3]);
+        io_data.dxfer_dir = DXFER_TO_DEVICE;
+        io_data.dxferp = (uint8_t *)in.buffer;
+        io_data.dxfer_len = in.size;
+        break;
+      case nvme_cmd_in::data_in:
+        cdb[1] = (admin ? 0x80 : 0x00) | proto_dma_in;
+        sg_put_unaligned_be24(in.size, &cdb[3]);
+        io_data.dxfer_dir = DXFER_FROM_DEVICE;
+        io_data.dxferp = (uint8_t *)in.buffer;
+        io_data.dxfer_len = in.size;
+        memset(in.buffer, 0, in.size);
+        break;
+      case nvme_cmd_in::data_io:
+      default:
+        return set_err(EINVAL);
+    }
+
+    scsi_device * scsidev = get_tunnel_dev();
+    if (!scsidev->scsi_pass_through_and_check(&io_data,
+         "sntjmicron_device::nvme_pass_through:Data: "))
+      return set_err(scsidev->get_err());
+  }
+
+  // 3: "Return Response Information"
+  {
+    unsigned char cdb[SNT_JMICRON_CDB_LEN] = { 0 };
+    cdb[0] = SAT_ATA_PASSTHROUGH_12;
+    cdb[1] = (admin ? 0x80 : 0x00) | proto_response;
+    sg_put_unaligned_be24(SNT_JMICRON_NVM_CMD_LEN, &cdb[3]);
+
+    unsigned nvm_reply[SNT_JMICRON_NVM_CMD_LEN / sizeof(unsigned)] = { 0 };
+
+    scsi_cmnd_io io_reply;
+    memset(&io_reply, 0, sizeof(io_reply));
+
+    io_reply.cmnd = cdb;
+    io_reply.cmnd_len = SNT_JMICRON_CDB_LEN;
+    io_reply.dxfer_dir = DXFER_FROM_DEVICE;
+    io_reply.dxferp = (uint8_t *)nvm_reply;
+    io_reply.dxfer_len = SNT_JMICRON_NVM_CMD_LEN;
+
+    scsi_device * scsidev = get_tunnel_dev();
+    if (!scsidev->scsi_pass_through_and_check(&io_reply,
+         "sntjmicron_device::nvme_pass_through:Reply: "))
+      return set_err(scsidev->get_err());
+
+    if (isbigendian())
+      for (unsigned i = 0; i < (SNT_JMICRON_NVM_CMD_LEN / sizeof(uint32_t)); i++)
+        swapx(&nvm_reply[i]);
+
+    if (nvm_reply[0] != SNT_JMICRON_NVME_SIGNATURE)
+      return set_err(EIO, "Out of spec JMicron NVMe reply");
+
+    int status = nvm_reply[5] >> 17;
+
+    if (status > 0)
+      return set_nvme_err(out, status);
+
+    out.result = nvm_reply[2];
+  }
+
+  return true;
+}
+
+} // namespace snt
+
+using namespace snt;
+
+nvme_device * smart_interface::get_snt_device(const char * type, scsi_device * scsidev)
+{
+  if (!scsidev)
+    throw std::logic_error("smart_interface: get_snt_device() called with scsidev=0");
+
+  // Take temporary ownership of 'scsidev' to delete it on error
+  scsi_device_auto_ptr scsidev_holder(scsidev);
+  nvme_device * sntdev = 0;
+
+  // TODO: Remove this and adjust drivedb entry accordingly when no longer EXPERIMENTAL
+  if (!strcmp(type, "sntjmicron#please_try")) {
+    set_err(EINVAL, "USB to NVMe bridge [please try '-d sntjmicron' and report result to: "
+            PACKAGE_BUGREPORT "]");
+    return 0;
+  }
+
+  if (!strncmp(type, "sntjmicron", 10)) {
+    int n1 = -1, n2 = -1, len = strlen(type);
+    unsigned nsid = 0; // invalid namespace id -> use default
+    sscanf(type, "sntjmicron%n,0x%x%n", &n1, &nsid, &n2);
+    if (!(n1 == len || n2 == len)) {
+      set_err(EINVAL, "Invalid NVMe namespace id in '%s'", type);
+      return 0;
+    }
+    sntdev = new sntjmicron_device(this, scsidev, type, nsid);
+  }
+  else {
+    set_err(EINVAL, "Unknown SNT device type '%s'", type);
+    return 0;
+  }
+
+  // 'scsidev' is now owned by 'sntdev'
+  scsidev_holder.release();
+  return sntdev;
+}
index 9416f23ad128bd1eec8a291ed1f24ec5ec883260..f6f6ff8fd9d9b16a029e95f826cac43a554d523c 100644 (file)
  *
  * Copyright (C) 2002-11 Bruce Allen
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
- * Copyright (C) 2003-15 Douglas Gilbert <dgilbert@interlog.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) 2003-18 Douglas Gilbert <dgilbert@interlog.com>
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 
+#include "config.h"
+#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
+
+#include <inttypes.h>
 #include <stdio.h>
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
 
-#include "config.h"
-#include "int64.h"
 #include "scsicmds.h"
 #include "atacmds.h" // smart_command_set
 #include "dev_interface.h"
 #include "scsiprint.h"
 #include "smartctl.h"
 #include "utility.h"
+#include "sg_unaligned.h"
 
-#define GBUF_SIZE 65535
+#define GBUF_SIZE 65532
 
-const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 4415 2017-03-27 21:14:53Z chrfranke $"
+const char * scsiprint_c_cvsid = "$Id: scsiprint.cpp 4870 2018-12-27 17:07:44Z chrfranke $"
                                  SCSIPRINT_H_CVSID;
 
 
-UINT8 gBuf[GBUF_SIZE];
+uint8_t gBuf[GBUF_SIZE];
 #define LOG_RESP_LEN 252
 #define LOG_RESP_LONG_LEN ((62 * 256) + 252)
 #define LOG_RESP_TAPE_ALERT_LEN 0x144
 
 /* Log pages supported */
-static int gSmartLPage = 0;     /* Informational Exceptions log page */
-static int gTempLPage = 0;
-static int gSelfTestLPage = 0;
-static int gStartStopLPage = 0;
-static int gReadECounterLPage = 0;
-static int gWriteECounterLPage = 0;
-static int gVerifyECounterLPage = 0;
-static int gNonMediumELPage = 0;
-static int gLastNErrorLPage = 0;
-static int gBackgroundResultsLPage = 0;
-static int gProtocolSpecificLPage = 0;
-static int gTapeAlertsLPage = 0;
-static int gSSMediaLPage = 0;
+static bool gSmartLPage = false;     /* Informational Exceptions log page */
+static bool gTempLPage = false;
+static bool gSelfTestLPage = false;
+static bool gStartStopLPage = false;
+static bool gReadECounterLPage = false;
+static bool gWriteECounterLPage = false;
+static bool gVerifyECounterLPage = false;
+static bool gNonMediumELPage = false;
+static bool gLastNErrorEvLPage = false;
+static bool gBackgroundResultsLPage = false;
+static bool gProtocolSpecificLPage = false;
+static bool gTapeAlertsLPage = false;
+static bool gSSMediaLPage = false;
+static bool gFormatStatusLPage = false;
+static bool gEnviroReportingLPage = false;
+static bool gEnviroLimitsLPage = false;
+static bool gUtilizationLPage = false;
+static bool gPendDefectsLPage = false;
+static bool gBackgroundOpLPage = false;
+static bool gLPSMisalignLPage = false;
 
 /* Vendor specific log pages */
-static int gSeagateCacheLPage = 0;
-static int gSeagateFactoryLPage = 0;
+static bool gSeagateCacheLPage = false;
+static bool gSeagateFactoryLPage = false;
 
 /* Mode pages supported */
-static int gIecMPage = 1;     /* N.B. assume it until we know otherwise */
+static bool gIecMPage = true;    /* N.B. assume it until we know otherwise */
 
 /* Remember last successful mode sense/select command */
 static int modese_len = 0;
 
+/* Remember this value from the most recent INQUIRY */
+static int scsi_version;
+#define SCSI_VERSION_SPC_4 0x6
+#define SCSI_VERSION_SPC_5 0x7
+#define SCSI_VERSION_HIGHEST SCSI_VERSION_SPC_5
+
+/* T10 vendor identification. Should match entry in last Annex of SPC
+ * drafts and standards (e.g. SPC-4). */
+static char scsi_vendor[8+1];
+#define T10_VENDOR_SEAGATE "SEAGATE"
+#define T10_VENDOR_HITACHI_1 "HITACHI"
+#define T10_VENDOR_HITACHI_2 "HL-DT-ST"
+#define T10_VENDOR_HITACHI_3 "HGST"
+
+static const char * logSenStr = "Log Sense";
+static const char * logSenRspStr = "Log Sense response";
+
+
+static bool
+seagate_or_hitachi(void)
+{
+    return ((0 == memcmp(scsi_vendor, T10_VENDOR_SEAGATE,
+                         strlen(T10_VENDOR_SEAGATE))) ||
+            (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_1,
+                         strlen(T10_VENDOR_HITACHI_1))) ||
+            (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_2,
+                         strlen(T10_VENDOR_HITACHI_2))) ||
+            (0 == memcmp(scsi_vendor, T10_VENDOR_HITACHI_3,
+                         strlen(T10_VENDOR_HITACHI_3))));
+}
+
+static bool
+all_ffs(const uint8_t * bp, int b_len)
+{
+    if ((NULL == bp) || (b_len <= 0))
+        return false;
+    for (--b_len; b_len >= 0; --b_len) {
+        if (0xff != bp[b_len])
+            return false;
+    }
+    return true;
+}
 
 static void
 scsiGetSupportedLogPages(scsi_device * device)
 {
-    int i, err;
+    bool got_subpages = false;
+    int k, bump, err, payload_len, num_unreported, num_unreported_spg;
+    const uint8_t * up;
+    uint8_t sup_lpgs[LOG_RESP_LEN];
 
+    memset(gBuf, 0, LOG_RESP_LEN);
     if ((err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
                             LOG_RESP_LEN, 0))) {
         if (scsi_debugmode > 0)
-            pout("Log Sense for supported pages failed [%s]\n",
+            pout("%s for supported pages failed [%s]\n", logSenStr,
                  scsiErrString(err));
         /* try one more time with defined length, workaround for the bug #678
         found with ST8000NM0075/E001 */
         err = scsiLogSense(device, SUPPORTED_LPAGES, 0, gBuf,
                             LOG_RESP_LEN, 68); /* 64 max pages + 4b header */
         if (scsi_debugmode > 0)
-            pout("Log Sense for supported pages failed (second attempt) [%s]\n",
-                 scsiErrString(err));
+            pout("%s for supported pages failed (second attempt) [%s]\n",
+                 logSenStr, scsiErrString(err));
         if (err)
             return;
+        memcpy(sup_lpgs, gBuf, LOG_RESP_LEN);
+    } else if ((scsi_version >= SCSI_VERSION_SPC_4) &&
+               (scsi_version <= SCSI_VERSION_HIGHEST)) {
+        /* unclear what code T10 will choose for SPC-6 */
+        memcpy(sup_lpgs, gBuf, LOG_RESP_LEN);
+        if ((err = scsiLogSense(device, SUPPORTED_LPAGES, SUPP_SPAGE_L_SPAGE,
+                                gBuf, LOG_RESP_LONG_LEN,
+                                -1 /* just single not double fetch */))) {
+            if (scsi_debugmode > 0)
+                pout("%s for supported pages and subpages failed [%s]\n",
+                     logSenStr, scsiErrString(err));
+        } else {
+            if (0 == memcmp(gBuf, sup_lpgs, LOG_RESP_LEN)) {
+                if (scsi_debugmode > 0)
+                    pout("%s: %s ignored subpage field, bad\n",
+                         __func__, logSenRspStr);
+            } else if (! ((0x40 & gBuf[0]) &&
+                          (SUPP_SPAGE_L_SPAGE == gBuf[1]))) {
+                if (scsi_debugmode > 0)
+                    pout("%s supported subpages is bad SPF=%u SUBPG=%u\n",
+                         logSenRspStr, !! (0x40 & gBuf[0]), gBuf[2]);
+            } else
+                got_subpages = true;
+        }
+    } else
+        memcpy(sup_lpgs, gBuf, LOG_RESP_LEN);
+
+    if (got_subpages) {
+         payload_len = sg_get_unaligned_be16(gBuf + 2);
+         bump = 2;
+         up = gBuf + LOGPAGEHDRSIZE;
+    } else {
+        payload_len = sup_lpgs[3];
+        bump = 1;
+        up = sup_lpgs + LOGPAGEHDRSIZE;
     }
 
-    for (i = 4; i < gBuf[3] + LOGPAGEHDRSIZE; i++) {
-        switch (gBuf[i])
+    num_unreported_spg = 0;
+    for (num_unreported = 0, k = 0; k < payload_len; k += bump, up += bump) {
+        uint8_t pg_num = 0x3f & up[0];
+        uint8_t sub_pg_num = (0x40 & up[0]) ? up[1] : 0;
+
+        switch (pg_num)
         {
+            case SUPPORTED_LPAGES:
+                if (! ((NO_SUBPAGE_L_SPAGE == sub_pg_num) ||
+                       (SUPP_SPAGE_L_SPAGE == sub_pg_num))) {
+                    if (scsi_debugmode > 1)
+                        pout("%s: Strange Log page number: 0x0,0x%x\n",
+                             __func__, sub_pg_num);
+                }
+                break;
             case READ_ERROR_COUNTER_LPAGE:
-                gReadECounterLPage = 1;
+                gReadECounterLPage = true;
                 break;
             case WRITE_ERROR_COUNTER_LPAGE:
-                gWriteECounterLPage = 1;
+                gWriteECounterLPage = true;
                 break;
             case VERIFY_ERROR_COUNTER_LPAGE:
-                gVerifyECounterLPage = 1;
+                gVerifyECounterLPage = true;
                 break;
-            case LAST_N_ERROR_LPAGE:
-                gLastNErrorLPage = 1;
+            case LAST_N_ERROR_EVENTS_LPAGE:
+                gLastNErrorEvLPage = true;
                 break;
             case NON_MEDIUM_ERROR_LPAGE:
-                gNonMediumELPage = 1;
+                gNonMediumELPage = true;
                 break;
             case TEMPERATURE_LPAGE:
-                gTempLPage = 1;
+                if (NO_SUBPAGE_L_SPAGE == sub_pg_num)
+                    gTempLPage = true;
+                else if (ENVIRO_REP_L_SPAGE == sub_pg_num)
+                    gEnviroReportingLPage = true;
+                else if (ENVIRO_LIMITS_L_SPAGE == sub_pg_num)
+                    gEnviroLimitsLPage = true;
+                else {
+                    ++num_unreported;
+                    ++num_unreported_spg;
+                }
                 break;
             case STARTSTOP_CYCLE_COUNTER_LPAGE:
-                gStartStopLPage = 1;
+                if (NO_SUBPAGE_L_SPAGE == sub_pg_num)
+                    gStartStopLPage = true;
+                else if (UTILIZATION_L_SPAGE == sub_pg_num)
+                    gUtilizationLPage = true;
+                else {
+                    ++num_unreported;
+                    ++num_unreported_spg;
+                }
                 break;
             case SELFTEST_RESULTS_LPAGE:
-                gSelfTestLPage = 1;
+                gSelfTestLPage = true;
                 break;
             case IE_LPAGE:
-                gSmartLPage = 1;
+                gSmartLPage = true;
                 break;
             case BACKGROUND_RESULTS_LPAGE:
-                gBackgroundResultsLPage = 1;
+                if (NO_SUBPAGE_L_SPAGE == sub_pg_num)
+                    gBackgroundResultsLPage = true;
+                else if (PEND_DEFECTS_L_SPAGE == sub_pg_num)
+                    gPendDefectsLPage = true;
+                else if (BACKGROUND_OP_L_SPAGE == sub_pg_num)
+                    gBackgroundOpLPage = true;
+                else if (LPS_MISALIGN_L_SPAGE == sub_pg_num)
+                    gLPSMisalignLPage = true;
+                else {
+                    ++num_unreported;
+                    ++num_unreported_spg;
+                }
                 break;
             case PROTOCOL_SPECIFIC_LPAGE:
-                gProtocolSpecificLPage = 1;
+                gProtocolSpecificLPage = true;
                 break;
             case TAPE_ALERTS_LPAGE:
-                gTapeAlertsLPage = 1;
+                gTapeAlertsLPage = true;
                 break;
             case SS_MEDIA_LPAGE:
-                gSSMediaLPage = 1;
+                gSSMediaLPage = true;
+                break;
+            case FORMAT_STATUS_LPAGE:
+                gFormatStatusLPage = true;
                 break;
             case SEAGATE_CACHE_LPAGE:
-                gSeagateCacheLPage = 1;
+                if (failuretest_permissive) {
+                    gSeagateCacheLPage = true;
+                    break;
+                }
+                if (seagate_or_hitachi())
+                    gSeagateCacheLPage = true;
                 break;
             case SEAGATE_FACTORY_LPAGE:
-                gSeagateFactoryLPage = 1;
+                if (failuretest_permissive) {
+                    gSeagateFactoryLPage = true;
+                    break;
+                }
+                if (seagate_or_hitachi())
+                    gSeagateFactoryLPage = true;
                 break;
             default:
+                if (pg_num < 0x30) {     /* don't count VS pages */
+                    ++num_unreported;
+                    if (sub_pg_num > 0)
+                        ++num_unreported_spg;
+                }
                 break;
         }
     }
+    if (scsi_debugmode > 1)
+        pout("%s: number of unreported (standard) log pages: %d (sub-pages: "
+             "%d)\n", __func__, num_unreported, num_unreported_spg);
 }
 
 /* Returns 0 if ok, -1 if can't check IE, -2 if can check and bad
@@ -155,10 +293,10 @@ scsiGetSupportedLogPages(scsi_device * device)
 static int
 scsiGetSmartData(scsi_device * device, bool attribs)
 {
-    UINT8 asc;
-    UINT8 ascq;
-    UINT8 currenttemp = 255;
-    UINT8 triptemp = 255;
+    uint8_t asc;
+    uint8_t ascq;
+    uint8_t currenttemp = 255;
+    uint8_t triptemp = 255;
     const char * cp;
     int err = 0;
     print_on();
@@ -173,20 +311,31 @@ scsiGetSmartData(scsi_device * device, bool attribs)
     if (cp) {
         err = -2;
         print_on();
-        pout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq);
+        jout("SMART Health Status: %s [asc=%x, ascq=%x]\n", cp, asc, ascq);
         print_off();
-    } else if (gIecMPage)
-        pout("SMART Health Status: OK\n");
+        jglb["smart_status"]["passed"] = false;
+        jglb["smart_status"]["scsi"]["asc"] = asc;
+        jglb["smart_status"]["scsi"]["ascq"] = ascq;
+        jglb["smart_status"]["scsi"]["ie_string"] = cp;
+    }
+    else if (gIecMPage) {
+        jout("SMART Health Status: OK\n");
+        jglb["smart_status"]["passed"] = true;
+    }
 
     if (attribs && !gTempLPage) {
         if (255 == currenttemp)
             pout("Current Drive Temperature:     <not available>\n");
-        else
-            pout("Current Drive Temperature:     %d C\n", currenttemp);
+        else {
+            jout("Current Drive Temperature:     %d C\n", currenttemp);
+            jglb["temperature"]["current"] = currenttemp;
+        }
         if (255 == triptemp)
             pout("Drive Trip Temperature:        <not available>\n");
-        else
-            pout("Drive Trip Temperature:        %d C\n", triptemp);
+        else {
+            jout("Drive Trip Temperature:        %d C\n", triptemp);
+            jglb["temperature"]["drive_trip"] = triptemp;
+    }
     }
     pout("\n");
     return err;
@@ -215,15 +364,15 @@ scsiGetTapeAlertsData(scsi_device * device, int peripheral_type)
         return -1;
     }
     if (gBuf[0] != 0x2e) {
-        pout("TapeAlerts Log Sense Failed\n");
+        pout("TapeAlerts %s Failed\n", logSenStr);
         print_off();
         return -1;
     }
-    pagelength = (unsigned short) gBuf[2] << 8 | gBuf[3];
+    pagelength = sg_get_unaligned_be16(gBuf + 2);
 
     for (s=severities; *s; s++) {
         for (i = 4; i < pagelength; i += 5) {
-            parametercode = (unsigned short) gBuf[i] << 8 | gBuf[i+1];
+            parametercode = sg_get_unaligned_be16(gBuf + i);
 
             if (gBuf[i + 4]) {
                 ts = SCSI_PT_MEDIUM_CHANGER == peripheral_type ?
@@ -262,22 +411,23 @@ scsiGetStartStopData(scsi_device * device)
     }
     if ((gBuf[0] & 0x3f) != STARTSTOP_CYCLE_COUNTER_LPAGE) {
         print_on();
-        pout("StartStop Log Sense Failed, page mismatch\n");
+        pout("StartStop %s Failed, page mismatch\n", logSenStr);
         print_off();
         return;
     }
-    len = ((gBuf[2] << 8) | gBuf[3]);
+    len = sg_get_unaligned_be16(gBuf + 2);
     ucp = gBuf + 4;
     for (k = len; k > 0; k -= extra, ucp += extra) {
         if (k < 3) {
             print_on();
-            pout("StartStop Log Sense Failed: short\n");
+            pout("StartStop %s: short\n", logSenRspStr);
             print_off();
             return;
         }
         extra = ucp[3] + 4;
-        int pc = (ucp[0] << 8) + ucp[1];
-        UINT32 u;
+        int pc = sg_get_unaligned_be16(ucp + 0);
+        uint32_t u = (extra > 7) ? sg_get_unaligned_be32(ucp + 4) : 0;
+        bool is_all_ffs = (extra > 7) ? all_ffs(ucp + 4, 4) : false;
         switch (pc) {
         case 1:
             if (10 == extra)
@@ -288,34 +438,21 @@ scsiGetStartStopData(scsi_device * device)
             /* ignore Accounting date */
             break;
         case 3:
-            if (extra > 7) {
-                u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
-                if (0xffffffff != u)
-                    pout("Specified cycle count over device lifetime:  %u\n",
-                         u);
-            }
+            if ((extra > 7) && (! is_all_ffs))
+                pout("Specified cycle count over device lifetime:  %u\n", u);
             break;
         case 4:
-            if (extra > 7) {
-                u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
-                if (0xffffffff != u)
-                    pout("Accumulated start-stop cycles:  %u\n", u);
-            }
+            if ((extra > 7) && (! is_all_ffs))
+                pout("Accumulated start-stop cycles:  %u\n", u);
             break;
         case 5:
-            if (extra > 7) {
-                u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
-                if (0xffffffff != u)
-                    pout("Specified load-unload count over device "
-                         "lifetime:  %u\n", u);
-            }
+            if ((extra > 7) && (! is_all_ffs))
+                pout("Specified load-unload count over device lifetime:  "
+                     "%u\n", u);
             break;
         case 6:
-            if (extra > 7) {
-                u = (ucp[4] << 24) | (ucp[5] << 16) | (ucp[6] << 8) | ucp[7];
-                if (0xffffffff != u)
-                    pout("Accumulated load-unload cycles:  %u\n", u);
-            }
+            if ((extra > 7) && (! is_all_ffs))
+                pout("Accumulated load-unload cycles:  %u\n", u);
             break;
         default:
             /* ignore */
@@ -323,23 +460,101 @@ scsiGetStartStopData(scsi_device * device)
         }
     }
 }
+/* PENDING_DEFECTS_SUBPG [0x15,0x1]  introduced: SBC-4 */
+static void
+scsiPrintPendingDefectsLPage(scsi_device * device)
+{
+    int num, pl, pc, err;
+    uint32_t count;
+    const uint8_t * bp;
+    static const char * pDefStr = "Pending Defects";
+    static const char * jname = "pending_defects";
+
+    if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE,
+                            PEND_DEFECTS_L_SPAGE, gBuf, LOG_RESP_LONG_LEN,
+                            0))) {
+        print_on();
+        pout("%s Failed [%s]\n", __func__, scsiErrString(err));
+        print_off();
+        return;
+    }
+    if (((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) &&
+        (gBuf[1] != PEND_DEFECTS_L_SPAGE)) {
+        print_on();
+        pout("%s %s, page mismatch\n", pDefStr, logSenRspStr);
+        print_off();
+        return;
+    }
+    num = sg_get_unaligned_be16(gBuf + 2);
+    if (num > LOG_RESP_LONG_LEN) {
+        print_on();
+        pout("%s %s too long\n", pDefStr, logSenRspStr);
+        print_off();
+        return;
+    }
+    bp = gBuf + 4;
+    while (num > 3) {
+        pc = sg_get_unaligned_be16(bp + 0);
+        pl = bp[3] + 4;
+        switch (pc) {
+        case 0x0:
+            printf("  Pending defect count:");
+            if ((pl < 8) || (num < 8)) {
+                print_on();
+                pout("%s truncated descriptor\n", pDefStr);
+                print_off();
+                return;
+            }
+            count = sg_get_unaligned_be32(bp + 4);
+            jglb[jname]["count"] = count;
+            if (0 == count)
+                jout("0 %s\n", pDefStr);
+            else if (1 == count)
+                jout("1 Pending Defect, LBA and accumulated_power_on_hours "
+                     "follow\n");
+            else
+                jout("%u %s: index, LBA and accumulated_power_on_hours "
+                     "follow\n", count, pDefStr);
+            break;
+        default:
+            if ((pl < 16) || (num < 16)) {
+                print_on();
+                pout("%s truncated descriptor\n", pDefStr);
+                print_off();
+                return;
+            }
+            jout("  %4d:  0x%-16" PRIx64 ",  %5u\n", pc,
+                 sg_get_unaligned_be64(bp + 8), sg_get_unaligned_be32(bp + 4));
+            jglb[jname][pc]["LBA"] = sg_get_unaligned_be64(bp + 8);
+            jglb[jname][pc]["accum_power_on_hours"] =
+                   sg_get_unaligned_be32(bp + 4);
+            break;
+        }
+        num -= pl;
+        bp += pl;
+    }
+}
 
 static void
 scsiPrintGrownDefectListLen(scsi_device * device)
 {
-    int err, dl_format, got_rd12;
+    bool got_rd12;
+    int err, dl_format;
     unsigned int dl_len, div;
+    static const char * hname = "Read defect list";
 
     memset(gBuf, 0, 8);
     if ((err = scsiReadDefect12(device, 0 /* req_plist */, 1 /* req_glist */,
                                 4 /* format: bytes from index */,
                                 0 /* addr desc index */, gBuf, 8))) {
         if (2 == err) { /* command not supported */
-            if ((err = scsiReadDefect10(device, 0 /* req_plist */, 1 /* req_glist */,
-                                        4 /* format: bytes from index */, gBuf, 4))) {
+            err = scsiReadDefect10(device, 0 /* req_plist */,
+                                   1 /* req_glist */,
+                                   4 /* format: bytes from index */, gBuf, 4);
+            if (err) {
                 if (scsi_debugmode > 0) {
                     print_on();
-                    pout("Read defect list (10) Failed: %s\n", scsiErrString(err));
+                    pout("%s (10) Failed: %s\n", hname, scsiErrString(err));
                     print_off();
                 }
                 return;
@@ -350,28 +565,27 @@ scsiPrintGrownDefectListLen(scsi_device * device)
         else {
             if (scsi_debugmode > 0) {
                 print_on();
-                pout("Read defect list (12) Failed: %s\n", scsiErrString(err));
+                pout("%s (12) Failed: %s\n", hname, scsiErrString(err));
                 print_off();
             }
             return;
         }
     } else
-        got_rd12 = 1;
+        got_rd12 = true;
 
     if (got_rd12) {
-        int generation = (gBuf[2] << 8) + gBuf[3];
+        int generation = sg_get_unaligned_be16(gBuf + 2);
         if ((generation > 1) && (scsi_debugmode > 0)) {
             print_on();
-            pout("Read defect list (12): generation=%d\n", generation);
+            pout("%s (12): generation=%d\n", hname, generation);
             print_off();
         }
-        dl_len = (gBuf[4] << 24) + (gBuf[5] << 16) + (gBuf[6] << 8) + gBuf[7];
-    } else {
-        dl_len = (gBuf[2] << 8) + gBuf[3];
-    }
+        dl_len = sg_get_unaligned_be32(gBuf + 4);
+    } else
+        dl_len = sg_get_unaligned_be16(gBuf + 2);
     if (0x8 != (gBuf[1] & 0x18)) {
         print_on();
-        pout("Read defect list: asked for grown list but didn't get it\n");
+        pout("%s: asked for grown list but didn't get it\n", hname);
         print_off();
         return;
     }
@@ -397,14 +611,18 @@ scsiPrintGrownDefectListLen(scsi_device * device)
             print_off();
             break;
     }
-    if (0 == dl_len)
-        pout("Elements in grown defect list: 0\n\n");
+    if (0 == dl_len) {
+        jout("Elements in grown defect list: 0\n\n");
+        jglb["scsi_grown_defect_list"] = 0;
+    }
     else {
         if (0 == div)
             pout("Grown defect list length=%u bytes [unknown "
                  "number of elements]\n\n", dl_len);
-        else
-            pout("Elements in grown defect list: %u\n\n", dl_len / div);
+        else {
+            jout("Elements in grown defect list: %u\n\n", dl_len / div);
+            jglb["scsi_grown_defect_list"] = dl_len;
+        }
     }
 }
 
@@ -414,25 +632,31 @@ scsiPrintSeagateCacheLPage(scsi_device * device)
     int num, pl, pc, err, len;
     unsigned char * ucp;
     uint64_t ull;
+    static const char * seaCacStr = "Seagate Cache";
 
     if ((err = scsiLogSense(device, SEAGATE_CACHE_LPAGE, 0, gBuf,
                             LOG_RESP_LEN, 0))) {
-        print_on();
-        pout("Seagate Cache Log Sense Failed: %s\n", scsiErrString(err));
-        print_off();
+        if (scsi_debugmode > 0) {
+            print_on();
+            pout("%s %s Failed: %s\n", seaCacStr, logSenStr,
+                 scsiErrString(err));
+            print_off();
+        }
         return;
     }
     if ((gBuf[0] & 0x3f) != SEAGATE_CACHE_LPAGE) {
-        print_on();
-        pout("Seagate Cache Log Sense Failed, page mismatch\n");
-        print_off();
+        if (scsi_debugmode > 0) {
+            print_on();
+            pout("%s %s, page mismatch\n", seaCacStr, logSenRspStr);
+            print_off();
+        }
         return;
     }
-    len = ((gBuf[2] << 8) | gBuf[3]) + 4;
+    len = sg_get_unaligned_be16(gBuf + 2) + 4;
     num = len - 4;
     ucp = &gBuf[0] + 4;
     while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
+        pc = sg_get_unaligned_be16(ucp + 0);
         pl = ucp[3] + 4;
         switch (pc) {
         case 0: case 1: case 2: case 3: case 4:
@@ -440,8 +664,8 @@ scsiPrintSeagateCacheLPage(scsi_device * device)
         default:
             if (scsi_debugmode > 0) {
                 print_on();
-                pout("Vendor (Seagate) cache lpage has unexpected parameter"
-                     ", skip\n");
+                pout("Vendor (%s) lpage has unexpected parameter, skip\n",
+                     seaCacStr);
                 print_off();
             }
             return;
@@ -449,11 +673,11 @@ scsiPrintSeagateCacheLPage(scsi_device * device)
         num -= pl;
         ucp += pl;
     }
-    pout("Vendor (Seagate) cache information\n");
+    pout("Vendor (%s) information\n", seaCacStr);
     num = len - 4;
     ucp = &gBuf[0] + 4;
     while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
+        pc = sg_get_unaligned_be16(ucp + 0);
         pl = ucp[3] + 4;
         switch (pc) {
         case 0: pout("  Blocks sent to initiator"); break;
@@ -466,17 +690,13 @@ scsiPrintSeagateCacheLPage(scsi_device * device)
         default: pout("  Unknown Seagate parameter code [0x%x]", pc); break;
         }
         int k = pl - 4;
+        const int sz_ull = (int)sizeof(ull);
         unsigned char * xp = ucp + 4;
-        if (k > (int)sizeof(ull)) {
-            xp += (k - (int)sizeof(ull));
-            k = (int)sizeof(ull);
-        }
-        ull = 0;
-        for (int j = 0; j < k; ++j) {
-            if (j > 0)
-                ull <<= 8;
-            ull |= xp[j];
+        if (k > sz_ull) {
+            xp += (k - sz_ull);
+            k = sz_ull;
         }
+        ull = sg_get_unaligned_be(k, xp + 0);
         pout(" = %" PRIu64 "\n", ull);
         num -= pl;
         ucp += pl;
@@ -493,24 +713,28 @@ scsiPrintSeagateFactoryLPage(scsi_device * device)
 
     if ((err = scsiLogSense(device, SEAGATE_FACTORY_LPAGE, 0, gBuf,
                             LOG_RESP_LEN, 0))) {
-        print_on();
-        pout("%s Failed [%s]\n", __func__, scsiErrString(err));
-        print_off();
+        if (scsi_debugmode > 0) {
+            print_on();
+            pout("%s Failed [%s]\n", __func__, scsiErrString(err));
+            print_off();
+        }
         return;
     }
     if ((gBuf[0] & 0x3f) != SEAGATE_FACTORY_LPAGE) {
-        print_on();
-        pout("Seagate/Hitachi Factory Log Sense Failed, page mismatch\n");
-        print_off();
+        if (scsi_debugmode > 0) {
+            print_on();
+            pout("Seagate/Hitachi Factory %s, page mismatch\n", logSenRspStr);
+            print_off();
+        }
         return;
     }
-    len = ((gBuf[2] << 8) | gBuf[3]) + 4;
+    len = sg_get_unaligned_be16(gBuf + 2) + 4;
     num = len - 4;
     ucp = &gBuf[0] + 4;
     good = 0;
     bad = 0;
     while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
+        pc = sg_get_unaligned_be16(ucp + 0);
         pl = ucp[3] + 4;
         switch (pc) {
         case 0: case 8:
@@ -536,7 +760,7 @@ scsiPrintSeagateFactoryLPage(scsi_device * device)
     num = len - 4;
     ucp = &gBuf[0] + 4;
     while (num > 3) {
-        pc = (ucp[0] << 8) | ucp[1];
+        pc = sg_get_unaligned_be16(ucp + 0);
         pl = ucp[3] + 4;
         good = 0;
         switch (pc) {
@@ -562,14 +786,12 @@ scsiPrintSeagateFactoryLPage(scsi_device * device)
                 xp += (k - (int)sizeof(ull));
                 k = (int)sizeof(ull);
             }
-            ull = 0;
-            for (int j = 0; j < k; ++j) {
-                if (j > 0)
-                    ull <<= 8;
-                ull |= xp[j];
-            }
-            if (0 == pc)
+            ull = sg_get_unaligned_be(k, xp + 0);
+            if (0 == pc) {
                 pout(" = %.2f\n", ull / 60.0 );
+                jglb["power_on_time"]["hours"] = ull / 60;
+                jglb["power_on_time"]["minutes"] = ull % 60;
+            }
             else
                 pout(" = %" PRIu64 "\n", ull);
         }
@@ -615,16 +837,31 @@ scsiPrintErrorCounterLog(scsi_device * device)
              "algorithm      processed    uncorrected\n");
         pout("           fast | delayed   rewrites  corrected  "
              "invocations   [10^9 bytes]  errors\n");
+
+        json::ref jref = jglb["scsi_error_counter_log"];
         for (int k = 0; k < 3; ++k) {
             if (! found[k])
                 continue;
             ecp = &errCounterArr[k];
-            static const char * const pageNames[3] = {"read:   ", "write:  ", "verify: "};
-            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]);
+            static const char * const pageNames[3] =
+                                 {"read:   ", "write:  ", "verify: "};
+            static const char * jpageNames[3] =
+                                 {"read", "write", "verify"};
+            jout("%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]);
             double processed_gb = ecp->counter[5] / 1000000000.0;
-            pout("   %12.3f    %8" PRIu64 "\n", processed_gb, ecp->counter[6]);
+            jout("   %12.3f    %8" PRIu64 "\n", processed_gb,
+                 ecp->counter[6]);
+            // Error counter log info
+            jref[jpageNames[k]]["errors_corrected_by_eccfast"] = ecp->counter[0];
+            jref[jpageNames[k]]["errors_corrected_by_eccdelayed"] = ecp->counter[1];
+            jref[jpageNames[k]]["errors_corrected_by_rereads_rewrites"] = ecp->counter[2];
+            jref[jpageNames[k]]["total_errors_corrected"] = ecp->counter[3];
+            jref[jpageNames[k]]["correction_algorithm_invocations"] = ecp->counter[4];
+            jref[jpageNames[k]]["gigabytes_processed"] = strprintf("%.3f", processed_gb);
+            jref[jpageNames[k]]["total_uncorrected_errors"] = ecp->counter[6];
         }
     }
     else
@@ -642,9 +879,10 @@ scsiPrintErrorCounterLog(scsi_device * device)
             pout("Positioning error count [Hitachi]: %8" PRIu64 "\n",
                  nme.counterPE_H);
     }
-    if (gLastNErrorLPage && (0 == scsiLogSense(device,
-                LAST_N_ERROR_LPAGE, 0, gBuf, LOG_RESP_LONG_LEN, 0))) {
-        int num = (gBuf[2] << 8) + gBuf[3] + 4;
+    if (gLastNErrorEvLPage &&
+        (0 == scsiLogSense(device, LAST_N_ERROR_EVENTS_LPAGE, 0, gBuf,
+                           LOG_RESP_LONG_LEN, 0))) {
+        int num = sg_get_unaligned_be16(gBuf + 2) + 4;
         int truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
         if (truncated)
             num = LOG_RESP_LONG_LEN;
@@ -660,12 +898,12 @@ scsiPrintErrorCounterLog(scsi_device * device)
                     break;
                 }
                 pl = ucp[3] + 4;
-                int pc = (ucp[0] << 8) + ucp[1];
+                int pc = sg_get_unaligned_be16(ucp + 0);
                 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);
+                        dStrHex((const uint8_t *)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));
@@ -673,7 +911,7 @@ scsiPrintErrorCounterLog(scsi_device * device)
                         if (scsi_debugmode > 0) {
                             pout("  Error event %d:\n", pc);
                             pout("    [data counter??]:\n");
-                            dStrHex((const char *)ucp + 4, pl - 4, 1);
+                            dStrHex((const uint8_t *)ucp + 4, pl - 4, 1);
                         }
                     }
                 }
@@ -726,15 +964,16 @@ scsiPrintSelfTest(scsi_device * device)
     int num, k, err, durationSec;
     int noheader = 1;
     int retval = 0;
-    UINT8 * ucp;
-    uint64_t ull=0;
+    uint8_t * ucp;
+    uint64_t ull;
     struct scsi_sense_disect sense_info;
+    static const char * hname = "Self-test";
 
     // check if test is running
     if (!scsiRequestSense(device, &sense_info) &&
                         (sense_info.asc == 0x04 && sense_info.ascq == 0x09 &&
                         sense_info.progress != -1)) {
-        pout("Self-test execution status:\t\t%d%% of test remaining\n",
+        pout("%s execution status:\t\t%d%% of test remaining\n", hname,
              100 - ((sense_info.progress * 100) / 65535));
     }
 
@@ -747,25 +986,23 @@ scsiPrintSelfTest(scsi_device * device)
     }
     if ((gBuf[0] & 0x3f) != SELFTEST_RESULTS_LPAGE) {
         print_on();
-        pout("Self-test Log Sense Failed, page mismatch\n");
+        pout("%s %s, page mismatch\n", hname, logSenRspStr);
         print_off();
         return FAILSMART;
     }
     // compute page length
-    num = (gBuf[2] << 8) + gBuf[3];
+    num = sg_get_unaligned_be16(gBuf + 2);
     // Log sense page length 0x190 bytes
     if (num != 0x190) {
         print_on();
-        pout("Self-test Log Sense length is 0x%x not 0x190 bytes\n",num);
+        pout("%s %s length is 0x%x not 0x190 bytes\n", hname, logSenStr, num);
         print_off();
         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)
-        int n = (ucp[6] << 8) | ucp[7];
+        int n = sg_get_unaligned_be16(ucp + 6);
 
         // The spec says "all 20 bytes will be zero if no test" but
         // DG has found otherwise.  So this is a heuristic.
@@ -774,7 +1011,7 @@ scsiPrintSelfTest(scsi_device * device)
 
         // only print header if needed
         if (noheader) {
-            pout("SMART Self-test log\n");
+            pout("SMART %s log\n", hname);
             pout("Num  Test              Status                 segment  "
                    "LifeTime  LBA_first_err [SK ASC ASQ]\n");
             pout("     Description                              number   "
@@ -783,7 +1020,7 @@ scsiPrintSelfTest(scsi_device * device)
         }
 
         // print parameter code (test number) & self-test code text
-        pout("#%2d  %s", (ucp[0] << 8) | ucp[1],
+        pout("#%2d  %s", sg_get_unaligned_be16(ucp + 0),
             self_test_code[(ucp[4] >> 5) & 0x7]);
 
         // check the self-test result nibble, using the self-test results
@@ -840,12 +1077,10 @@ scsiPrintSelfTest(scsi_device * device)
             pout("   %5d", n);
 
         // construct 8-byte integer address of first failure
-        for (i = 0; i < 8; i++) {
-            ull <<= 8;
-            ull |= ucp[i+8];
-        }
+        ull = sg_get_unaligned_be64(ucp + 8);
+        bool is_all_ffs = all_ffs(ucp + 8, 8);
         // print Address of First Failure, if sensible
-        if ((~(uint64_t)0 != ull) && (res > 0) && (res < 0xf)) {
+        if ((! is_all_ffs) && (res > 0) && (res < 0xf)) {
             char buff[32];
 
             // was hex but change to decimal to conform with ATA
@@ -865,12 +1100,12 @@ scsiPrintSelfTest(scsi_device * device)
 
     // if header never printed, then there was no output
     if (noheader)
-        pout("No self-tests have been logged\n");
+        pout("No %ss have been logged\n", hname);
     else
     if ((0 == scsiFetchExtendedSelfTestTime(device, &durationSec,
                         modese_len)) && (durationSec > 0)) {
-        pout("\nLong (extended) Self Test duration: %d seconds "
-             "[%.1f minutes]\n", durationSec, durationSec / 60.0);
+        pout("\nLong (extended) %s duration: %d seconds "
+             "[%.1f minutes]\n", hname, durationSec, durationSec / 60.0);
     }
     pout("\n");
     return retval;
@@ -911,7 +1146,8 @@ scsiPrintBackgroundResults(scsi_device * device)
     int noheader = 1;
     int firstresult = 1;
     int retval = 0;
-    UINT8 * ucp;
+    uint8_t * ucp;
+    static const char * hname = "Background scan results";
 
     if ((err = scsiLogSense(device, BACKGROUND_RESULTS_LPAGE, 0, gBuf,
                             LOG_RESP_LONG_LEN, 0))) {
@@ -922,16 +1158,15 @@ scsiPrintBackgroundResults(scsi_device * device)
     }
     if ((gBuf[0] & 0x3f) != BACKGROUND_RESULTS_LPAGE) {
         print_on();
-        pout("Background scan results Log Sense Failed, page mismatch\n");
+        pout("%s %s, page mismatch\n", hname, logSenRspStr);
         print_off();
         return FAILSMART;
     }
     // compute page length
-    num = (gBuf[2] << 8) + gBuf[3] + 4;
+    num = sg_get_unaligned_be16(gBuf + 2) + 4;
     if (num < 20) {
         print_on();
-        pout("Background scan results Log Sense length is %d, no scan "
-             "status\n", num);
+        pout("%s %s length is %d, no scan status\n", hname, logSenStr, num);
         print_off();
         return FAILSMART;
     }
@@ -941,14 +1176,14 @@ scsiPrintBackgroundResults(scsi_device * device)
     ucp = gBuf + 4;
     num -= 4;
     while (num > 3) {
-        int pc = (ucp[0] << 8) | ucp[1];
+        int pc = sg_get_unaligned_be16(ucp + 0);
         // pcb = ucp[2];
         int pl = ucp[3] + 4;
         switch (pc) {
         case 0:
             if (noheader) {
                 noheader = 0;
-                pout("Background scan results log\n");
+                pout("%s log\n", hname);
             }
             pout("  Status: ");
             if ((pl < 16) || (num < 16)) {
@@ -960,20 +1195,22 @@ scsiPrintBackgroundResults(scsi_device * device)
                 pout("%s\n", bms_status[j]);
             else
                 pout("unknown [0x%x] background scan status value\n", j);
-            j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
+            j = sg_get_unaligned_be32(ucp + 4);
             pout("    Accumulated power on time, hours:minutes %d:%02d "
                  "[%d minutes]\n", (j / 60), (j % 60), j);
+            jglb["power_on_time"]["hours"] = j / 60;
+            jglb["power_on_time"]["minutes"] = j % 60;
             pout("    Number of background scans performed: %d,  ",
-                 (ucp[10] << 8) + ucp[11]);
+                 sg_get_unaligned_be16(ucp + 10));
             pout("scan progress: %.2f%%\n",
-                 (double)((ucp[12] << 8) + ucp[13]) * 100.0 / 65536.0);
+                 (double)sg_get_unaligned_be16(ucp + 12) * 100.0 / 65536.0);
             pout("    Number of background medium scans performed: %d\n",
-                 (ucp[14] << 8) + ucp[15]);
+                 sg_get_unaligned_be16(ucp + 14));
             break;
         default:
             if (noheader) {
                 noheader = 0;
-                pout("\nBackground scan results log\n");
+                pout("\n%s log\n", hname);
             }
             if (firstresult) {
                 firstresult = 0;
@@ -986,7 +1223,7 @@ scsiPrintBackgroundResults(scsi_device * device)
                     pout("parameter length >= 24 expected, got %d\n", pl);
                 break;
             }
-            j = (ucp[4] << 24) + (ucp[5] << 16) + (ucp[6] << 8) + ucp[7];
+            j = sg_get_unaligned_be32(ucp + 4);
             pout("%4d:%02d  ", (j / 60), (j % 60));
             for (m = 0; m < 8; ++m)
                 pout("%02x", ucp[16 + m]);
@@ -1018,7 +1255,8 @@ scsiPrintSSMedia(scsi_device * device)
 {
     int num, err, truncated;
     int retval = 0;
-    UINT8 * ucp;
+    uint8_t * ucp;
+    static const char * hname = "Solid state media";
 
     if ((err = scsiLogSense(device, SS_MEDIA_LPAGE, 0, gBuf,
                             LOG_RESP_LONG_LEN, 0))) {
@@ -1029,15 +1267,15 @@ scsiPrintSSMedia(scsi_device * device)
     }
     if ((gBuf[0] & 0x3f) != SS_MEDIA_LPAGE) {
         print_on();
-        pout("Solid state media Log Sense Failed, page mismatch\n");
+        pout("%s %s, page mismatch\n", hname, logSenStr);
         print_off();
         return FAILSMART;
     }
     // compute page length
-    num = (gBuf[2] << 8) + gBuf[3] + 4;
+    num = sg_get_unaligned_be16(gBuf + 2) + 4;
     if (num < 12) {
         print_on();
-        pout("Solid state media Log Sense length is %d, too short\n", num);
+        pout("%s %s length is %d, too short\n", hname, logSenStr, num);
         print_off();
         return FAILSMART;
     }
@@ -1047,19 +1285,20 @@ scsiPrintSSMedia(scsi_device * device)
     ucp = gBuf + 4;
     num -= 4;
     while (num > 3) {
-        int pc = (ucp[0] << 8) | ucp[1];
+        int pc = sg_get_unaligned_be16(ucp + 0);
         // pcb = ucp[2];
         int pl = ucp[3] + 4;
         switch (pc) {
         case 1:
             if (pl < 8) {
                 print_on();
-                pout("SS Media Percentage used endurance indicator parameter "
-                     "too short (pl=%d)\n", pl);
+                pout("%s Percentage used endurance indicator parameter "
+                     "too short (pl=%d)\n", hname, pl);
                 print_off();
                 return FAILSMART;
             }
-            pout("Percentage used endurance indicator: %d%%\n", ucp[7]);
+            jout("Percentage used endurance indicator: %d%%\n", ucp[7]);
+            jglb["scsi_percentage_used_endurance_indicator"] = ucp[7];
         default:        /* ignore other parameter codes */
             break;
         }
@@ -1069,6 +1308,116 @@ scsiPrintSSMedia(scsi_device * device)
     return retval;
 }
 
+static int
+scsiPrintFormatStatus(scsi_device * device)
+{
+    bool is_count;
+    int k, num, err, truncated;
+    int retval = 0;
+    uint64_t ull;
+    uint8_t * ucp;
+    uint8_t * xp;
+    const char * jout_str;
+    const char * jglb_str;
+    static const char * hname = "Format Status";
+    static const char * jname = "format_status";
+
+    if ((err = scsiLogSense(device, FORMAT_STATUS_LPAGE, 0, gBuf,
+                            LOG_RESP_LONG_LEN, 0))) {
+        print_on();
+        jout("%s: Failed [%s]\n", __func__, scsiErrString(err));
+        print_off();
+        return FAILSMART;
+    }
+    if ((gBuf[0] & 0x3f) != FORMAT_STATUS_LPAGE) {
+        print_on();
+        jout("%s %s, page mismatch\n", hname, logSenRspStr);
+        print_off();
+        return FAILSMART;
+    }
+    // compute page length
+    num = sg_get_unaligned_be16(gBuf + 2) + 4;
+    if (num < 12) {
+        print_on();
+        jout("%s %s length is %d, too short\n", hname, logSenStr, num);
+        print_off();
+        return FAILSMART;
+    }
+    truncated = (num > LOG_RESP_LONG_LEN) ? num : 0;
+    if (truncated)
+        num = LOG_RESP_LONG_LEN;
+    ucp = gBuf + 4;
+    num -= 4;
+    while (num > 3) {
+        int pc = sg_get_unaligned_be16(ucp + 0);
+        // pcb = ucp[2];
+        int pl = ucp[3] + 4;
+
+        is_count = true;
+        jout_str = "";
+        jglb_str = "x";
+        switch (pc) {
+        case 0:
+            if (scsi_debugmode > 1) {
+                if (pl < 5)
+                    jout("Format data out: <empty>\n");
+                else {
+                    if (all_ffs(ucp + 4, pl - 4))
+                        jout("Format data out: <not available>\n");
+                    else {
+                        jout("Format data out:\n");
+                        dStrHex((const uint8_t *)ucp + 4, pl - 4, 0);
+                    }
+                }
+            }
+            is_count = false;
+            break;
+        case 1:
+            jout_str = "Grown defects during certification";
+            jglb_str = "grown_defects_during_cert";
+            break;
+        case 2:
+            jout_str = "Total blocks reassigned during format";
+            jglb_str = "blocks_reassigned_during_format";
+            break;
+        case 3:
+            jout_str = "Total new blocks reassigned";
+            jglb_str = "total_new_block_since_format";
+            break;
+        case 4:
+            jout_str = "Power on minutes since format";
+            jglb_str = "power_on_minutes_since_format";
+            break;
+        default:
+            if (scsi_debugmode > 3) {
+                pout("  Unknown Format parameter code = 0x%x\n", pc);
+                dStrHex((const uint8_t *)ucp, pl, 0);
+            }
+            is_count = false;
+            break;
+        }
+        if (is_count) {
+            k = pl - 4;
+            xp = ucp + 4;
+            if (all_ffs(xp, k)) {
+                pout("%s <not available>\n", jout_str);
+            } else {
+                if (k > (int)sizeof(ull)) {
+                    xp += (k - sizeof(ull));
+                    k = sizeof(ull);
+                }
+                ull = sg_get_unaligned_be(k, xp);
+                jout("%s = %" PRIu64 "\n", jout_str, ull);
+                jglb[jname][jglb_str] = ull;
+            }
+        } else
+        num -= pl;
+        ucp += pl;
+    }
+    return retval;
+
+}
+
 static void
 show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val)
 {
@@ -1112,7 +1461,7 @@ show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val)
         pout("     Received retry-class OPEN_REJECT count: %u\n", val);
         break;
     case 0x25:
-        pout("     Received AIP (WATING ON PARTIAL) count: %u\n", val);
+        pout("     Received AIP (WAITING ON PARTIAL) count: %u\n", val);
         break;
     case 0x26:
         pout("     Received AIP (WAITING ON CONNECTION) count: %u\n", val);
@@ -1203,13 +1552,13 @@ show_sas_phy_event_info(int peis, unsigned int val, unsigned thresh_val)
 static void
 show_sas_port_param(unsigned char * ucp, int param_len)
 {
-    int j, m, n, nphys, t, sz, spld_len;
+    int j, m, nphys, t, sz, spld_len;
     unsigned char * vcp;
     char s[64];
 
     sz = sizeof(s);
     // pcb = ucp[2];
-    t = (ucp[0] << 8) | ucp[1];
+    t = sg_get_unaligned_be16(ucp + 0);
     pout("relative target port id = %d\n", t);
     pout("  generation code = %d\n", ucp[6]);
     nphys = ucp[7];
@@ -1293,25 +1642,21 @@ show_sas_port_param(unsigned char * ucp, int param_len)
         pout("    attached target port: ssp=%d stp=%d smp=%d\n",
                !! (vcp[7] & 8), !! (vcp[7] & 4), !! (vcp[7] & 2));
         if (!dont_print_serial_number) {
-            uint64_t ull;
-            for (n = 0, ull = vcp[8]; n < 8; ++n) {
-                ull <<= 8; ull |= vcp[8 + n];
-            }
+            uint64_t ull = sg_get_unaligned_be64(vcp + 8);
+
             pout("    SAS address = 0x%" PRIx64 "\n", ull);
-            for (n = 0, ull = vcp[16]; n < 8; ++n) {
-                ull <<= 8; ull |= vcp[16 + n];
-            }
+            ull = sg_get_unaligned_be64(vcp + 16);
             pout("    attached SAS address = 0x%" PRIx64 "\n", ull);
         }
         pout("    attached phy identifier = %d\n", vcp[24]);
-        unsigned int ui;
-        ui = (vcp[32] << 24) | (vcp[33] << 16) | (vcp[34] << 8) | vcp[35];
+        unsigned int ui = sg_get_unaligned_be32(vcp + 32);
+
         pout("    Invalid DWORD count = %u\n", ui);
-        ui = (vcp[36] << 24) | (vcp[37] << 16) | (vcp[38] << 8) | vcp[39];
+        ui = sg_get_unaligned_be32(vcp + 36);
         pout("    Running disparity error count = %u\n", ui);
-        ui = (vcp[40] << 24) | (vcp[41] << 16) | (vcp[42] << 8) | vcp[43];
+        ui = sg_get_unaligned_be32(vcp + 40);
         pout("    Loss of DWORD synchronization = %u\n", ui);
-        ui = (vcp[44] << 24) | (vcp[45] << 16) | (vcp[46] << 8) | vcp[47];
+        ui = sg_get_unaligned_be32(vcp + 44);
         pout("    Phy reset problem = %u\n", ui);
         if (spld_len > 51) {
             int num_ped;
@@ -1325,10 +1670,8 @@ show_sas_port_param(unsigned char * ucp, int param_len)
                 int peis;
                 unsigned int pvdt;
                 peis = xcp[3];
-                ui = (xcp[4] << 24) | (xcp[5] << 16) | (xcp[6] << 8) |
-                     xcp[7];
-                pvdt = (xcp[8] << 24) | (xcp[9] << 16) | (xcp[10] << 8) |
-                       xcp[11];
+                ui = sg_get_unaligned_be32(xcp + 4);
+                pvdt = sg_get_unaligned_be32(xcp + 8);
                 show_sas_phy_event_info(peis, ui, pvdt);
             }
         }
@@ -1345,7 +1688,7 @@ show_protocol_specific_page(unsigned char * resp, int len)
     num = len - 4;
     for (k = 0, ucp = resp + 4; k < num; ) {
         int param_len = ucp[3] + 4;
-        if (6 != (0xf & ucp[4]))
+        if (SCSI_TPROTO_SAS != (0xf & ucp[4]))
             return 0;   /* only decode SAS log page */
         if (0 == k)
             pout("Protocol Specific port log page for SAS SSP\n");
@@ -1364,25 +1707,27 @@ static int
 scsiPrintSasPhy(scsi_device * device, int reset)
 {
     int num, err;
+    static const char * hname = "Protocol specific port";
 
     if ((err = scsiLogSense(device, PROTOCOL_SPECIFIC_LPAGE, 0, gBuf,
                             LOG_RESP_LONG_LEN, 0))) {
         print_on();
-        pout("%s Log Sense Failed [%s]\n\n", __func__, scsiErrString(err));
+        pout("%s %s Failed [%s]\n\n", __func__, logSenStr,
+             scsiErrString(err));
         print_off();
         return FAILSMART;
     }
     if ((gBuf[0] & 0x3f) != PROTOCOL_SPECIFIC_LPAGE) {
         print_on();
-        pout("Protocol specific Log Sense Failed, page mismatch\n\n");
+        pout("%s %s, page mismatch\n\n", hname, logSenRspStr);
         print_off();
         return FAILSMART;
     }
     // compute page length
-    num = (gBuf[2] << 8) + gBuf[3];
+    num = sg_get_unaligned_be16(gBuf + 2);
     if (1 != show_protocol_specific_page(gBuf, num + 4)) {
         print_on();
-        pout("Only support protocol specific log page on SAS devices\n\n");
+        pout("Only support %s log page on SAS devices\n\n", hname);
         print_off();
         return FAILSMART;
     }
@@ -1435,6 +1780,7 @@ static const char * peripheral_dt_arr[32] = {
         "unknown or no device type",
 };
 
+/* Symbolic indexes to this array SCSI_TPROTO_* in scscmds.h */
 static const char * transport_proto_arr[] = {
         "Fibre channel (FCP-2)",
         "Parallel SCSI (SPI-4)",
@@ -1447,20 +1793,19 @@ static const char * transport_proto_arr[] = {
         "ATA (ACS-2)",
         "UAS",
         "SOP",
-        "0xb",
+        "PCIe",
         "0xc",
         "0xd",
         "0xe",
-        "0xf"
+        "None given [0xf]"
 };
 
 /* Returns 0 on success, 1 on general error and 2 for early, clean exit */
 static int
-scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
+scsiGetDriveInfo(scsi_device * device, uint8_t * peripheral_type, bool all)
 {
-    char timedatetz[DATEANDEPOCHLEN];
     struct scsi_iec_mode_page iec;
-    int err, iec_err, len, req_len, avail_len, scsi_version;
+    int err, iec_err, len, req_len, avail_len;
     bool is_tape = false;
     int peri_dt = 0;
     int transport = -1;
@@ -1475,7 +1820,7 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
         pout("Standard Inquiry (36 bytes) failed [%s]\n", scsiErrString(err));
         pout("Retrying with a 64 byte Standard Inquiry\n");
         print_off();
-        /* Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices */
+        /* Marvell controllers fail with 36 byte StdInquiry, but 64 is ok */
         req_len = 64;
         if ((err = scsiStdInquiry(device, gBuf, req_len))) {
             print_on();
@@ -1504,20 +1849,30 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
     scsi_version = gBuf[2] & 0x7;
 
     if (all && (0 != strncmp((char *)&gBuf[8], "ATA", 3))) {
-        char vendor[8+1], product[16+1], revision[4+1];
-        scsi_format_id_string(vendor, (const unsigned char *)&gBuf[8], 8);
-        scsi_format_id_string(product, (const unsigned char *)&gBuf[16], 16);
-        scsi_format_id_string(revision, (const unsigned char *)&gBuf[32], 4);
+        char product[16+1], revision[4+1];
+        scsi_format_id_string(scsi_vendor, &gBuf[8], 8);
+        scsi_format_id_string(product, &gBuf[16], 16);
+        scsi_format_id_string(revision, &gBuf[32], 4);
 
         pout("=== START OF INFORMATION SECTION ===\n");
-        pout("Vendor:               %.8s\n", vendor);
-        pout("Product:              %.16s\n", product);
-        if (gBuf[32] >= ' ')
-            pout("Revision:             %.4s\n", revision);
-        if (scsi_version == 0x6)
-            pout("Compliance:           SPC-4\n");
-        else if (scsi_version == 0x7)
-            pout("Compliance:           SPC-5\n");
+        jout("Vendor:               %.8s\n", scsi_vendor);
+        jglb["vendor"] = scsi_vendor;
+        jout("Product:              %.16s\n", product);
+        jglb["product"] = product;
+        jglb["model_name"] = strprintf("%s%s%s",
+          scsi_vendor, (*scsi_vendor && *product ? " " : ""), product);
+        if (gBuf[32] >= ' ') {
+            jout("Revision:             %.4s\n", revision);
+            // jglb["firmware_version"] = revision;
+            jglb["revision"] = revision;        /* could be a hardware rev */
+        }
+        if ((scsi_version > 0x3) && (scsi_version < 0x8)) {
+            char sv_arr[8];
+
+            snprintf(sv_arr, sizeof(sv_arr), "SPC-%d", scsi_version - 2);
+            jout("Compliance:           %s\n", sv_arr);
+            jglb["scsi_version"] = sv_arr;
+        }
     }
 
     if (!*device->get_req_type()/*no type requested*/ &&
@@ -1532,62 +1887,61 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
     protect = gBuf[5] & 0x1;    /* from and including SPC-3 */
 
     if (! is_tape) {    /* assume disk if not tape drive (or tape changer) */
-        unsigned int lb_size = 0;
+        struct scsi_readcap_resp srr;
+        int lbpme = -1;
+        int lbprz = -1;
         unsigned char lb_prov_resp[8];
-        char lb_str[16];
-        int lb_per_pb_exp = 0;
-        uint64_t capacity = scsiGetSize(device, &lb_size, &lb_per_pb_exp);
+        uint64_t capacity = scsiGetSize(device, false /*avoid_rcap16 */,
+                                        &srr);
 
         if (capacity) {
             char cap_str[64], si_str[64];
             format_with_thousands_sep(cap_str, sizeof(cap_str), capacity);
             format_capacity(si_str, sizeof(si_str), capacity);
-            pout("User Capacity:        %s bytes [%s]\n", cap_str, si_str);
-            snprintf(lb_str, sizeof(lb_str) - 1, "%u", lb_size);
-            pout("Logical block size:   %s bytes\n", lb_str);
-        }
-        int lbpme = -1;
-        int lbprz = -1;
-        if (protect || lb_per_pb_exp) {
-            unsigned char rc16_12[20] = {0, };
-
-            if (0 == scsiGetProtPBInfo(device, rc16_12)) {
-                lb_per_pb_exp = rc16_12[1] & 0xf;       /* just in case */
-                if (lb_per_pb_exp > 0) {
-                    snprintf(lb_str, sizeof(lb_str) - 1, "%u",
-                             (lb_size * (1 << lb_per_pb_exp)));
-                    pout("Physical block size:  %s bytes\n", lb_str);
-                    int n = ((rc16_12[2] & 0x3f) << 8) + rc16_12[3];
-                    if (n > 0)  // not common so cut the clutter
-                        pout("Lowest aligned LBA:   %d\n", n);
+            jout("User Capacity:        %s bytes [%s]\n", cap_str, si_str);
+            if (srr.lb_size)
+              jglb["user_capacity"]["blocks"].set_unsafe_uint64(capacity /
+                                                                srr.lb_size);
+            jglb["user_capacity"]["bytes"].set_unsafe_uint64(capacity);
+            jout("Logical block size:   %u bytes\n", srr.lb_size);
+            jglb["logical_block_size"] = srr.lb_size;
+            if (protect || srr.lb_p_pb_exp) {
+                if (srr.lb_p_pb_exp > 0) {
+                    unsigned pb_size = srr.lb_size * (1 << srr.lb_p_pb_exp);
+                    jout("Physical block size:  %u bytes\n", pb_size);
+                    jglb["physical_block_size"] = pb_size;
+                    if (srr.l_a_lba > 0)  // not common so cut the clutter
+                        pout("Lowest aligned LBA:   %u\n", srr.l_a_lba);
                 }
-                if (rc16_12[0] & 0x1) { /* PROT_EN set */
-                    int p_type = ((rc16_12[0] >> 1) & 0x7);
-
-                    switch (p_type) {
-                    case 0 :
+                if (srr.prot_type > 0) {
+                    switch (srr.prot_type) {
+                    case 1 :
                         pout("Formatted with type 1 protection\n");
                         break;
-                    case 1 :
+                    case 2 :
                         pout("Formatted with type 2 protection\n");
                         break;
-                    case 2 :
+                    case 3 :
                         pout("Formatted with type 3 protection\n");
                         break;
                     default:
                         pout("Formatted with unknown protection type [%d]\n",
-                             p_type);
+                             srr.prot_type);
                         break;
                     }
-                    int p_i_exp = ((rc16_12[1] >> 4) & 0xf);
+                    unsigned p_i_per_lb = (1 << srr.p_i_exp);
+                    const unsigned pi_sz = 8;   /* ref-tag(4 bytes),
+                                                   app-tag(2), tag-mask(2) */
 
-                    if (p_i_exp > 0)
+                    if (p_i_per_lb > 1)
                         pout("%d protection information intervals per "
-                             "logical block\n", (1 << p_i_exp));
+                             "logical block\n", p_i_per_lb);
+                    pout("%d bytes of protection information per logical "
+                         "block\n", pi_sz * p_i_per_lb);
                 }
                 /* Pick up some LB provisioning info since its available */
-                lbpme = !! (rc16_12[2] & 0x80);
-                lbprz = !! (rc16_12[2] & 0x40);
+                lbpme = (int)srr.lbpme;
+                lbprz = (int)srr.lbprz;
             }
         }
         /* Thin Provisioning VPD page renamed Logical Block Provisioning VPD
@@ -1638,11 +1992,12 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
             if (0 == rpm)
                 ;       // Not reported
             else if (1 == rpm)
-                pout("Rotation Rate:        Solid State Device\n");
+                jout("Rotation Rate:        Solid State Device\n");
             else if ((rpm <= 0x400) || (0xffff == rpm))
                 ;       // Reserved
             else
-                pout("Rotation Rate:        %d rpm\n", rpm);
+                jout("Rotation Rate:        %d rpm\n", rpm);
+            jglb["rotation_rate"] = (rpm == 1 ? 0 : rpm);
         }
         if (form_factor > 0) {
             const char * cp = NULL;
@@ -1664,8 +2019,11 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
                 cp = "< 1.8";
                 break;
             }
-            if (cp)
-                pout("Form Factor:          %s inches\n", cp);
+            jglb["form_factor"]["scsi_value"] = form_factor;
+            if (cp) {
+                jout("Form Factor:          %s inches\n", cp);
+                jglb["form_factor"]["name"] = strprintf("%s inches", cp);
+            }
         }
         if (haw_zbc > 0)
             pout("Host aware zoned block capable\n");
@@ -1708,7 +2066,8 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
 
             gBuf[4 + len] = '\0';
             scsi_format_id_string(serial, &gBuf[4], len);
-            pout("Serial number:        %s\n", serial);
+            jout("Serial number:        %s\n", serial);
+            jglb["serial_number"] = serial;
         } else if (scsi_debugmode > 0) {
             print_on();
             if (SIMPLE_ERR_BAD_RESP == err)
@@ -1720,11 +2079,14 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
     }
 
     // print SCSI peripheral device type
+    jglb["device_type"]["scsi_value"] = peri_dt;
     if (peri_dt < (int)(sizeof(peripheral_dt_arr) /
-                        sizeof(peripheral_dt_arr[0])))
-        pout("Device type:          %s\n", peripheral_dt_arr[peri_dt]);
+                        sizeof(peripheral_dt_arr[0]))) {
+        jout("Device type:          %s\n", peripheral_dt_arr[peri_dt]);
+        jglb["device_type"]["name"] = peripheral_dt_arr[peri_dt];
+    }
     else
-        pout("Device type:          <%d>\n", peri_dt);
+        jout("Device type:          <%d>\n", peri_dt);
 
     // See if transport protocol is known
     if (transport < 0)
@@ -1733,10 +2095,13 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
         pout("Transport protocol:   %s\n", transport_proto_arr[transport]);
 
     // print current time and date and timezone
-    dateandtimezone(timedatetz);
-    pout("Local Time is:        %s\n", timedatetz);
+    time_t now = time(0);
+    char timedatetz[DATEANDEPOCHLEN]; dateandtimezoneepoch(timedatetz, now);
+    jout("Local Time is:        %s\n", timedatetz);
+    jglb["local_time"]["time_t"] = now;
+    jglb["local_time"]["asctime"] = timedatetz;
 
-    // See if unit accepts SCSI commmands from us
+    // See if unit accepts SCSI commands from us
     if ((err = scsiTestUnitReady(device))) {
         if (SIMPLE_ERR_NOT_READY == err) {
             print_on();
@@ -1771,7 +2136,8 @@ scsiGetDriveInfo(scsi_device * device, UINT8 * peripheral_type, bool all)
     if (iec_err) {
         if (!is_tape) {
             print_on();
-            pout("SMART support is:     Unavailable - device lacks SMART capability.\n");
+            pout("SMART support is:     Unavailable - device lacks SMART "
+                 "capability.\n");
             if (scsi_debugmode > 0)
                 pout(" [%s]\n", scsiErrString(iec_err));
             print_off();
@@ -1867,20 +2233,24 @@ scsiSmartDisable(scsi_device * device)
 static void
 scsiPrintTemp(scsi_device * device)
 {
-    UINT8 temp = 255;
-    UINT8 trip = 255;
+    uint8_t temp = 255;
+    uint8_t trip = 255;
 
     if (scsiGetTemp(device, &temp, &trip))
         return;
 
     if (255 == temp)
         pout("Current Drive Temperature:     <not available>\n");
-    else
-        pout("Current Drive Temperature:     %d C\n", temp);
+    else {
+        jout("Current Drive Temperature:     %d C\n", temp);
+        jglb["temperature"]["current"] = temp;
+    }
     if (255 == trip)
         pout("Drive Trip Temperature:        <not available>\n");
-    else
-        pout("Drive Trip Temperature:        %d C\n", trip);
+    else {
+        jout("Drive Trip Temperature:        %d C\n", trip);
+        jglb["temperature"]["drive_trip"] = trip;
+    }
     pout("\n");
 }
 
@@ -1889,7 +2259,7 @@ int
 scsiPrintMain(scsi_device * device, const scsi_print_options & options)
 {
     int checkedSupportedLogPages = 0;
-    UINT8 peripheral_type = 0;
+    uint8_t peripheral_type = 0;
     int returnval = 0;
     int res, durationSec;
     struct scsi_sense_disect sense_info;
@@ -1912,7 +2282,8 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
             failuretest(MANDATORY_CMD, returnval |= FAILID);
         any_output = true;
     }
-    is_disk = (SCSI_PT_DIRECT_ACCESS == peripheral_type);
+    is_disk = ((SCSI_PT_DIRECT_ACCESS == peripheral_type) ||
+               (SCSI_PT_HOST_MANAGED == peripheral_type));
     is_tape = ((SCSI_PT_SEQUENTIAL_ACCESS == peripheral_type) ||
                (SCSI_PT_MEDIUM_CHANGER == peripheral_type));
 
@@ -2023,7 +2394,8 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
             else
                 pout("TapeAlert Not Supported\n");
         } else { /* disk, cd/dvd, enclosure, etc */
-            if ((res = scsiGetSmartData(device, options.smart_vendor_attrib))) {
+            if ((res = scsiGetSmartData(device,
+                                        options.smart_vendor_attrib))) {
                 if (-2 == res)
                     returnval |= FAILSTATUS;
                 else
@@ -2041,6 +2413,10 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
             res = scsiPrintSSMedia(device);
         if (0 != res)
             failuretest(OPTIONAL_CMD, returnval|=res);
+        if (gFormatStatusLPage)
+            res = scsiPrintFormatStatus(device);
+        if (0 != res)
+            failuretest(OPTIONAL_CMD, returnval|=res);
         any_output = true;
     }
     if (options.smart_vendor_attrib) {
@@ -2063,6 +2439,8 @@ scsiPrintMain(scsi_device * device, const scsi_print_options & options)
         if (! checkedSupportedLogPages)
             scsiGetSupportedLogPages(device);
         scsiPrintErrorCounterLog(device);
+        if (gPendDefectsLPage)
+            scsiPrintPendingDefectsLPage(device);
         if (1 == scsiFetchControlGLTSD(device, modese_len, 1))
             pout("\n[GLTSD (Global Logging Target Save Disable) set. "
                  "Enable Save with '-S on']\n");
index cae657c3cbb272b584aa8a291a15d95ea62b03ae..616df0537c46d98d27ca568ea2dff60157fab5d5 100644 (file)
@@ -7,29 +7,16 @@
  * Copyright (C) 2000 Michael Cornwell <cornwell@acm.org>
  *
  * Additional SCSI work:
- * Copyright (C) 2003-13 Douglas Gilbert <dgilbert@interlog.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) 2003-18 Douglas Gilbert <dgilbert@interlog.com>
  *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 
 #ifndef SCSI_PRINT_H_
 #define SCSI_PRINT_H_
 
-#define SCSIPRINT_H_CVSID "$Id: scsiprint.h 4431 2017-08-08 19:38:15Z chrfranke $\n"
+#define SCSIPRINT_H_CVSID "$Id: scsiprint.h 4760 2018-08-19 18:45:53Z chrfranke $\n"
 
 // Options for scsiPrintMain
 struct scsi_print_options
diff --git a/sg_unaligned.h b/sg_unaligned.h
new file mode 100644 (file)
index 0000000..ca702e8
--- /dev/null
@@ -0,0 +1,489 @@
+#ifndef SG_UNALIGNED_H
+#define SG_UNALIGNED_H
+
+/*
+ * Copyright (c) 2014-2018 Douglas Gilbert.
+ * All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the BSD_LICENSE file.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>     /* for uint8_t and friends */
+#include <string.h>     /* for memcpy */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* These inline functions convert integers (always unsigned) to byte streams
+ * and vice versa. They have two goals:
+ *   - change the byte ordering of integers between host order and big
+ *     endian ("_be") or little endian ("_le")
+ *   - copy the big or little endian byte stream so it complies with any
+ *     alignment that host integers require
+ *
+ * Host integer to given endian byte stream is a "_put_" function taking
+ * two arguments (integer and pointer to byte stream) returning void.
+ * Given endian byte stream to host integer is a "_get_" function that takes
+ * one argument and returns an integer of appropriate size (uint32_t for 24
+ * bit operations, uint64_t for 48 bit operations).
+ *
+ * Big endian byte format "on the wire" is the default used by SCSI
+ * standards (www.t10.org). Big endian is also the network byte order.
+ * Little endian is used by ATA, PCI and NVMe.
+ */
+
+/* The generic form of these routines was borrowed from the Linux kernel,
+ * via mhvtl. There is a specialised version of the main functions for
+ * little endian or big endian provided that not-quite-standard defines for
+ * endianness are available from the compiler and the <byteswap.h> header
+ * (a GNU extension) has been detected by ./configure . To force the
+ * generic version, use './configure --disable-fast-lebe ' . */
+
+/* Note: Assumes that the source and destination locations do not overlap.
+ * An example of overlapping source and destination:
+ *     sg_put_unaligned_le64(j, ((uint8_t *)&j) + 1);
+ * Best not to do things like that.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"     /* need this to see if HAVE_BYTESWAP_H */
+#endif
+
+#undef GOT_UNALIGNED_SPECIALS   /* just in case */
+
+#if defined(__BYTE_ORDER__) && defined(HAVE_BYTESWAP_H) && \
+    ! defined(IGNORE_FAST_LEBE)
+
+#if defined(__LITTLE_ENDIAN__) || (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+
+#define GOT_UNALIGNED_SPECIALS 1
+
+#include <byteswap.h>           /* for bswap_16(), bswap_32() and bswap_64() */
+
+// #warning ">>>>>> Doing Little endian special unaligneds"
+
+static inline uint16_t sg_get_unaligned_be16(const void *p)
+{
+        uint16_t u;
+
+        memcpy(&u, p, 2);
+        return bswap_16(u);
+}
+
+static inline uint32_t sg_get_unaligned_be32(const void *p)
+{
+        uint32_t u;
+
+        memcpy(&u, p, 4);
+        return bswap_32(u);
+}
+
+static inline uint64_t sg_get_unaligned_be64(const void *p)
+{
+        uint64_t u;
+
+        memcpy(&u, p, 8);
+        return bswap_64(u);
+}
+
+static inline void sg_put_unaligned_be16(uint16_t val, void *p)
+{
+        uint16_t u = bswap_16(val);
+
+        memcpy(p, &u, 2);
+}
+
+static inline void sg_put_unaligned_be32(uint32_t val, void *p)
+{
+        uint32_t u = bswap_32(val);
+
+        memcpy(p, &u, 4);
+}
+
+static inline void sg_put_unaligned_be64(uint64_t val, void *p)
+{
+        uint64_t u = bswap_64(val);
+
+        memcpy(p, &u, 8);
+}
+
+static inline uint16_t sg_get_unaligned_le16(const void *p)
+{
+        uint16_t u;
+
+        memcpy(&u, p, 2);
+        return u;
+}
+
+static inline uint32_t sg_get_unaligned_le32(const void *p)
+{
+        uint32_t u;
+
+        memcpy(&u, p, 4);
+        return u;
+}
+
+static inline uint64_t sg_get_unaligned_le64(const void *p)
+{
+        uint64_t u;
+
+        memcpy(&u, p, 8);
+        return u;
+}
+
+static inline void sg_put_unaligned_le16(uint16_t val, void *p)
+{
+        memcpy(p, &val, 2);
+}
+
+static inline void sg_put_unaligned_le32(uint32_t val, void *p)
+{
+        memcpy(p, &val, 4);
+}
+
+static inline void sg_put_unaligned_le64(uint64_t val, void *p)
+{
+        memcpy(p, &val, 8);
+}
+
+#elif defined(__BIG_ENDIAN__) || (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+
+#define GOT_UNALIGNED_SPECIALS 1
+
+#include <byteswap.h>
+
+// #warning ">>>>>> Doing BIG endian special unaligneds"
+
+static inline uint16_t sg_get_unaligned_le16(const void *p)
+{
+        uint16_t u;
+
+        memcpy(&u, p, 2);
+        return bswap_16(u);
+}
+
+static inline uint32_t sg_get_unaligned_le32(const void *p)
+{
+        uint32_t u;
+
+        memcpy(&u, p, 4);
+        return bswap_32(u);
+}
+
+static inline uint64_t sg_get_unaligned_le64(const void *p)
+{
+        uint64_t u;
+
+        memcpy(&u, p, 8);
+        return bswap_64(u);
+}
+
+static inline void sg_put_unaligned_le16(uint16_t val, void *p)
+{
+        uint16_t u = bswap_16(val);
+
+        memcpy(p, &u, 2);
+}
+
+static inline void sg_put_unaligned_le32(uint32_t val, void *p)
+{
+        uint32_t u = bswap_32(val);
+
+        memcpy(p, &u, 4);
+}
+
+static inline void sg_put_unaligned_le64(uint64_t val, void *p)
+{
+        uint64_t u = bswap_64(val);
+
+        memcpy(p, &u, 8);
+}
+
+static inline uint16_t sg_get_unaligned_be16(const void *p)
+{
+        uint16_t u;
+
+        memcpy(&u, p, 2);
+        return u;
+}
+
+static inline uint32_t sg_get_unaligned_be32(const void *p)
+{
+        uint32_t u;
+
+        memcpy(&u, p, 4);
+        return u;
+}
+
+static inline uint64_t sg_get_unaligned_be64(const void *p)
+{
+        uint64_t u;
+
+        memcpy(&u, p, 8);
+        return u;
+}
+
+static inline void sg_put_unaligned_be16(uint16_t val, void *p)
+{
+        memcpy(p, &val, 2);
+}
+
+static inline void sg_put_unaligned_be32(uint32_t val, void *p)
+{
+        memcpy(p, &val, 4);
+}
+
+static inline void sg_put_unaligned_be64(uint64_t val, void *p)
+{
+        memcpy(p, &val, 8);
+}
+
+#endif          /* __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__  */
+#endif          /* #if defined __BYTE_ORDER__ && defined <byteswap.h> &&
+                 *     ! defined IGNORE_FAST_LEBE */
+
+
+#ifndef GOT_UNALIGNED_SPECIALS
+
+/* Now we have no tricks left, so use the only way this can be done
+ * correctly in C safely: lots of shifts. */
+
+// #warning ">>>>>> Doing GENERIC unaligneds"
+
+static inline uint16_t sg_get_unaligned_be16(const void *p)
+{
+        return ((const uint8_t *)p)[0] << 8 | ((const uint8_t *)p)[1];
+}
+
+static inline uint32_t sg_get_unaligned_be32(const void *p)
+{
+        return ((const uint8_t *)p)[0] << 24 | ((const uint8_t *)p)[1] << 16 |
+                ((const uint8_t *)p)[2] << 8 | ((const uint8_t *)p)[3];
+}
+
+static inline uint64_t sg_get_unaligned_be64(const void *p)
+{
+        return (uint64_t)sg_get_unaligned_be32(p) << 32 |
+               sg_get_unaligned_be32((const uint8_t *)p + 4);
+}
+
+static inline void sg_put_unaligned_be16(uint16_t val, void *p)
+{
+        ((uint8_t *)p)[0] = (uint8_t)(val >> 8);
+        ((uint8_t *)p)[1] = (uint8_t)val;
+}
+
+static inline void sg_put_unaligned_be32(uint32_t val, void *p)
+{
+        sg_put_unaligned_be16(val >> 16, p);
+        sg_put_unaligned_be16(val, (uint8_t *)p + 2);
+}
+
+static inline void sg_put_unaligned_be64(uint64_t val, void *p)
+{
+        sg_put_unaligned_be32(val >> 32, p);
+        sg_put_unaligned_be32(val, (uint8_t *)p + 4);
+}
+
+
+static inline uint16_t sg_get_unaligned_le16(const void *p)
+{
+        return ((const uint8_t *)p)[1] << 8 | ((const uint8_t *)p)[0];
+}
+
+static inline uint32_t sg_get_unaligned_le32(const void *p)
+{
+        return ((const uint8_t *)p)[3] << 24 | ((const uint8_t *)p)[2] << 16 |
+                ((const uint8_t *)p)[1] << 8 | ((const uint8_t *)p)[0];
+}
+
+static inline uint64_t sg_get_unaligned_le64(const void *p)
+{
+        return (uint64_t)sg_get_unaligned_le32((const uint8_t *)p + 4) << 32 |
+               sg_get_unaligned_le32(p);
+}
+
+static inline void sg_put_unaligned_le16(uint16_t val, void *p)
+{
+        ((uint8_t *)p)[0] = val & 0xff;
+        ((uint8_t *)p)[1] = val >> 8;
+}
+
+static inline void sg_put_unaligned_le32(uint32_t val, void *p)
+{
+        sg_put_unaligned_le16(val >> 16, (uint8_t *)p + 2);
+        sg_put_unaligned_le16(val, p);
+}
+
+static inline void sg_put_unaligned_le64(uint64_t val, void *p)
+{
+        sg_put_unaligned_le32(val >> 32, (uint8_t *)p + 4);
+        sg_put_unaligned_le32(val, p);
+}
+
+#endif          /* #ifndef GOT_UNALIGNED_SPECIALS */
+
+/* Following are lesser used conversions that don't have specializations
+ * for endianness; big endian first. In summary these are the 24, 48 bit and
+ * given-length conversions plus the "nz" conditional put conversions. */
+
+/* Now big endian, get 24+48 then put 24+48 */
+static inline uint32_t sg_get_unaligned_be24(const void *p)
+{
+        return ((const uint8_t *)p)[0] << 16 | ((const uint8_t *)p)[1] << 8 |
+               ((const uint8_t *)p)[2];
+}
+
+/* Assume 48 bit value placed in uint64_t */
+static inline uint64_t sg_get_unaligned_be48(const void *p)
+{
+        return (uint64_t)sg_get_unaligned_be16(p) << 32 |
+               sg_get_unaligned_be32((const uint8_t *)p + 2);
+}
+
+/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than
+ * 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is
+ * an 8 byte unsigned integer. */
+static inline uint64_t sg_get_unaligned_be(int num_bytes, const void *p)
+{
+        if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t)))
+                return 0;
+        else {
+                const uint8_t * xp = (const uint8_t *)p;
+                uint64_t res = *xp;
+
+                for (++xp; num_bytes > 1; ++xp, --num_bytes)
+                        res = (res << 8) | *xp;
+                return res;
+        }
+}
+
+static inline void sg_put_unaligned_be24(uint32_t val, void *p)
+{
+        ((uint8_t *)p)[0] = (val >> 16) & 0xff;
+        ((uint8_t *)p)[1] = (val >> 8) & 0xff;
+        ((uint8_t *)p)[2] = val & 0xff;
+}
+
+/* Assume 48 bit value placed in uint64_t */
+static inline void sg_put_unaligned_be48(uint64_t val, void *p)
+{
+        sg_put_unaligned_be16(val >> 32, p);
+        sg_put_unaligned_be32(val, (uint8_t *)p + 2);
+}
+
+/* Now little endian, get 24+48 then put 24+48 */
+static inline uint32_t sg_get_unaligned_le24(const void *p)
+{
+        return (uint32_t)sg_get_unaligned_le16(p) |
+               ((const uint8_t *)p)[2] << 16;
+}
+
+/* Assume 48 bit value placed in uint64_t */
+static inline uint64_t sg_get_unaligned_le48(const void *p)
+{
+        return (uint64_t)sg_get_unaligned_le16((const uint8_t *)p + 4) << 32 |
+               sg_get_unaligned_le32(p);
+}
+
+static inline void sg_put_unaligned_le24(uint32_t val, void *p)
+{
+        ((uint8_t *)p)[2] = (val >> 16) & 0xff;
+        ((uint8_t *)p)[1] = (val >> 8) & 0xff;
+        ((uint8_t *)p)[0] = val & 0xff;
+}
+
+/* Assume 48 bit value placed in uint64_t */
+static inline void sg_put_unaligned_le48(uint64_t val, void *p)
+{
+        ((uint8_t *)p)[5] = (val >> 40) & 0xff;
+        ((uint8_t *)p)[4] = (val >> 32) & 0xff;
+        ((uint8_t *)p)[3] = (val >> 24) & 0xff;
+        ((uint8_t *)p)[2] = (val >> 16) & 0xff;
+        ((uint8_t *)p)[1] = (val >> 8) & 0xff;
+        ((uint8_t *)p)[0] = val & 0xff;
+}
+
+/* Returns 0 if 'num_bytes' is less than or equal to 0 or greater than
+ * 8 (i.e. sizeof(uint64_t)). Else returns result in uint64_t which is
+ * an 8 byte unsigned integer. */
+static inline uint64_t sg_get_unaligned_le(int num_bytes, const void *p)
+{
+        if ((num_bytes <= 0) || (num_bytes > (int)sizeof(uint64_t)))
+                return 0;
+        else {
+                const uint8_t * xp = (const uint8_t *)p + (num_bytes - 1);
+                uint64_t res = *xp;
+
+                for (--xp; num_bytes > 1; --xp, --num_bytes)
+                        res = (res << 8) | *xp;
+                return res;
+        }
+}
+
+/* Since cdb and parameter blocks are often memset to zero before these
+ * unaligned function partially fill them, then check for a val of zero
+ * and ignore if it is with these variants. First big endian, then little */
+static inline void sg_nz_put_unaligned_be16(uint16_t val, void *p)
+{
+        if (val)
+                sg_put_unaligned_be16(val, p);
+}
+
+static inline void sg_nz_put_unaligned_be24(uint32_t val, void *p)
+{
+        if (val) {
+                ((uint8_t *)p)[0] = (val >> 16) & 0xff;
+                ((uint8_t *)p)[1] = (val >> 8) & 0xff;
+                ((uint8_t *)p)[2] = val & 0xff;
+        }
+}
+
+static inline void sg_nz_put_unaligned_be32(uint32_t val, void *p)
+{
+        if (val)
+                sg_put_unaligned_be32(val, p);
+}
+
+static inline void sg_nz_put_unaligned_be64(uint64_t val, void *p)
+{
+        if (val)
+            sg_put_unaligned_be64(val, p);
+}
+
+static inline void sg_nz_put_unaligned_le16(uint16_t val, void *p)
+{
+        if (val)
+                sg_put_unaligned_le16(val, p);
+}
+
+static inline void sg_nz_put_unaligned_le24(uint32_t val, void *p)
+{
+        if (val) {
+                ((uint8_t *)p)[2] = (val >> 16) & 0xff;
+                ((uint8_t *)p)[1] = (val >> 8) & 0xff;
+                ((uint8_t *)p)[0] = val & 0xff;
+        }
+}
+
+static inline void sg_nz_put_unaligned_le32(uint32_t val, void *p)
+{
+        if (val)
+                sg_put_unaligned_le32(val, p);
+}
+
+static inline void sg_nz_put_unaligned_le64(uint64_t val, void *p)
+{
+        if (val)
+            sg_put_unaligned_le64(val, p);
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SG_UNALIGNED_H */
index b6cd2946a797fcdfee01ae3d8aaf4dedea819a20..5c5d873a3c84025f5eada3932310411bfdb120c4 100644 (file)
@@ -1,21 +1,10 @@
 .ig
 Copyright (C) 2002-10 Bruce Allen
-Copyright (C) 2004-17 Christian Franke
+Copyright (C) 2004-18 Christian Franke
 
-$Id: smartctl.8.in 4588 2017-11-04 15:15:32Z chrfranke $
+SPDX-License-Identifier: GPL-2.0-or-later
 
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-You should have received a copy of the GNU General Public License
-(for example COPYING); If not, see <http://www.gnu.org/licenses/>.
-
-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/
+$Id: smartctl.8.in 4882 2018-12-29 21:26:45Z chrfranke $
 
 ..
 .\" Macros borrowed from pages generated with Pod::Man
@@ -165,7 +154,6 @@ scsi controller "\\\\.\\Scsi[0\-9]:".
 For SATA or SAS disks behind an Areca controller use
 \fB"/dev/arcmsr[0\-9]"\fP, see \*(Aq\-d areca,N[/E]\*(Aq below.
 .Sp
-[NEW EXPERIMENTAL SMARTCTL FEATURE]
 Use the forms \fB"/dev/nvme[0\-9]"\fP (broadcast namespace) or
 \fB"/dev/nvme[0\-9]n[1\-9]"\fP (specific namespace 1\-9) for first,
 second, ..., NVMe device.
@@ -228,10 +216,8 @@ If \*(Aq\-n\*(Aq (see below) is specified, the power mode of the drive is
 printed.
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .Sp
-[NVMe]
-[NEW EXPERIMENTAL SMARTCTL FEATURE]
-For NVMe devices the information is obtained from the Identify Controller
-and the Identify Namespace data structure.
+[NVMe] For NVMe devices the information is obtained from the Identify
+Controller and the Identify Namespace data structure.
 .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .TP
 .B \-\-identify[=[w][nvb]]
@@ -273,7 +259,7 @@ For ATA devices this is equivalent to
 .br
 \*(Aq\-H \-i \-g all \-g wcreorder \-c \-A \-f brief \-l xerror,error
 \-l xselftest,selftest \-l selective \-l directory \-l scttemp \-l scterc
-\-l devstat \-l sataphy\*(Aq.
+\-l devstat \-l defects \-l sataphy\*(Aq.
 .br
 and for SCSI, this is equivalent to
 .br
@@ -304,7 +290,6 @@ For example:
 smartctl \-\-scan\-open \-\- \-a \-W 4,45,50 \-m admin@work > smartd.conf
 .Ve
 .Sp
-[NEW EXPERIMENTAL SMARTCTL FEATURE]
 Multiple \*(Aq\-d TYPE\*(Aq options may be specified with
 \*(Aq\-\-scan[\-open]\*(Aq to combine the scan results of more than one TYPE.
 .TP
@@ -315,6 +300,48 @@ info.
 .TP
 .B RUN-TIME BEHAVIOR OPTIONS:
 .TP
+.B \-j, \-\-json[=cgiosuv]
+[NEW EXPERIMENTAL SMARTCTL FEATURE]
+Enables JSON output mode.
+.Sp
+The output could be modified or enhanced by the optional argument which
+consists of one or more characters from the set \*(Aqcgiosuv\*(Aq:
+.br
+\*(Aqc\*(Aq: Outputs \fBc\fRompact format without extra spaces and newlines.
+By default, output is pretty-printed.
+.br
+\*(Aqg\*(Aq: Outputs JSON structure as single assignments to allow the usage
+of \fBg\fRrep.
+Each assignment reflects the absolute path of a value.
+The syntax is compatible with \fBgron\fR:
+.br
+\*(Aqjson.KEY1[INDEX2].KEY3 = VALUE;\*(Aq.
+.br
+\*(Aqo\*(Aq: Includes the full \fBo\fRriginal plaintext \fBo\fRutput of
+\fBsmartctl\fR as a JSON array \*(Aqsmartctl.output[]\*(Aq.
+.br
+\*(Aqs\*(Aq: Outputs JSON object elements \fBs\fRorted by key.
+By default, object elements are ordered as generated internally.
+.br
+\*(Aqv\*(Aq: Enables \fBv\fRerbose output of possible unsafe integers.
+If specified, values which may exceed JSON safe integer (53-bit) range are
+always output as a number (with some \*(AqKEY\*(Aq) and a string
+(\*(AqKEY_s\*(Aq), regardless of the actual value.
+Values which may exceed 64-bit range are also output as a little endian
+byte array (\*(AqKEY_le\*(Aq).
+By default, the additional elements are only output if the value actually
+exceeds the range.
+.Sp
+The following two arguments are primarily indented for development:
+.br
+\*(Aqi\*(Aq: Includes lines from the plaintext output which print info already
+\fBi\fRmplemented for JSON output.
+The lines appear as objects with key \*(Aqsmartctl_NNNN_i\*(Aq.
+.br
+\*(Aqu\*(Aq: Includes lines from the plaintext output which print info still
+\fBu\fRnimplemented for JSON output.
+The lines appear as objects with key \*(Aqsmartctl_NNNN_u\*(Aq.
+.TP
 .B \-q TYPE, \-\-quietmode=TYPE
 Specifies that \fBsmartctl\fP should run in one of the quiet modes
 described here.  The valid arguments to this option are:
@@ -347,7 +374,7 @@ This is the default.
 .Sp
 .I test
 \- prints the guessed TYPE, then opens the device and prints the
-(possibly changed) TYPE name and then exists without performing
+(possibly changed) TYPE name and then exits without performing
 any further commands.
 .Sp
 .I ata
@@ -364,8 +391,7 @@ from issuing ATA commands to a SCSI device.
 .\" %ENDIF NOT OS Darwin
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .I nvme[,NSID]
-\- [NEW EXPERIMENTAL SMARTCTL FEATURE]
-the device type is NVM Express (NVMe).
+\- the device type is NVM Express (NVMe).
 The optional parameter NSID specifies the namespace id (in hex) passed
 to the driver.
 Use 0xffffffff for the broadcast namespace id.
@@ -422,6 +448,14 @@ PL2571/2771/2773/2775 USB to SATA bridge.
 \- this device type is for SATA disks that are behind a SunplusIT USB to SATA
 bridge.
 .Sp
+.I sntjmicron[,NSID]
+\- [NEW EXPERIMENTAL SMARTCTL FEATURE]
+this device type is for NVMe disks that are behind a JMicron USB to NVMe
+bridge.
+The optional parameter NSID specifies the namespace id (in hex) passed
+to the driver.
+The default namespace id is the broadcast namespace id (0xffffffff).
+.Sp
 .\" %ENDIF NOT OS Darwin
 .\" %IF OS Linux
 .I marvell
@@ -454,7 +488,7 @@ For PERC5/6 controllers: \fBmegaraid_sas_ioctlN\fP
 .\" %IF OS Linux Windows Cygwin
 .I aacraid,H,L,ID
 \- [Linux, Windows and Cygwin only] the device consists of one or more
-SCSI/SAS disks connected to an AacRaid controller.
+SCSI/SAS or SATA disks connected to an AacRaid controller.
 The non-negative integers H,L,ID (Host number, Lun, ID) denote which disk
 on the controller is monitored.
 Use syntax such as:
@@ -463,6 +497,9 @@ Use syntax such as:
 .br
 \fBsmartctl \-a \-d aacraid,1,0,4 /dev/sdb\fP
 .Sp
+Option \*(Aq\-d sat,auto+...\*(Aq is implicitly enabled to detect SATA disks.
+Use \*(Aq\-d scsi+aacraid,H,L,ID\*(Aq to disable it.
+.Sp
 .\" %ENDIF OS Linux Windows Cygwin
 .\" %IF OS Linux
 On Linux, the following entry in /proc/devices must exist: \fBaac\fP.
@@ -574,6 +611,9 @@ SATA disks connected to a cciss RAID controller.
 The non-negative integer N (in the range from 0 to 15 inclusive) denotes
 which disk on the controller is monitored.
 .Sp
+Option \*(Aq\-d sat,auto+...\*(Aq is implicitly enabled to detect SATA disks.
+Use \*(Aq\-d scsi+cciss,N\*(Aq to disable it.
+.Sp
 To look at disks behind HP Smart Array controllers, use syntax
 such as:
 .\" %ENDIF OS FreeBSD Linux
@@ -717,8 +757,7 @@ send to, or received from the device.
 .Sp
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .I nvmeioctl
-\- [NEW EXPERIMENTAL SMARTCTL FEATURE]
-report only ioctl() transactions with NVMe devices.
+\- report only ioctl() transactions with NVMe devices.
 .Sp
 .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 Any argument may include a positive integer to specify the level of detail
@@ -1038,9 +1077,7 @@ This means that each alert condition is reported only once by \fBsmartctl\fP
 for each initiator for each activation of the condition.
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .Sp
-[NVMe]
-[NEW EXPERIMENTAL SMARTCTL FEATURE]
-NVMe status is obtained by reading the "Critical Warning" byte from
+[NVMe] NVMe status is obtained by reading the "Critical Warning" byte from
 the SMART/Health Information log.
 .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .TP
@@ -1053,10 +1090,8 @@ scanning, and so on.  If the device can carry out self-tests, this
 option also shows the estimated time required to run those tests.
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .Sp
-[NVMe]
-[NEW EXPERIMENTAL SMARTCTL FEATURE]
-Prints various NVMe device capabilities obtained from the Identify Controller
-and the Identify Namespace data structure.
+[NVMe] Prints various NVMe device capabilities obtained from the Identify
+Controller and the Identify Namespace data structure.
 .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .TP
 .B \-A, \-\-attributes
@@ -1156,9 +1191,7 @@ The attributes are output in a relatively free format (compared with ATA
 disk attributes).
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .Sp
-[NVMe]
-[NEW EXPERIMENTAL SMARTCTL FEATURE]
-For NVMe devices the attributes are obtained from the SMART/Health
+[NVMe] For NVMe devices the attributes are obtained from the SMART/Health
 Information log.
 .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .TP
@@ -1170,9 +1203,9 @@ Information log.
 This is the default unless the \*(Aq\-x\*(Aq option is specified.
 .Sp
 .I brief
-\- New format which fits into 80 colums (except in some rare cases).
+\- New format which fits into 80 columns (except in some rare cases).
 This format also decodes four additional attribute flags.
-This is the default if the \*(Aq'\-x\*(Aq option is specified.
+This is the default if the \*(Aq\-x\*(Aq option is specified.
 .Sp
 .I hex,id
 \- Print all attribute IDs as hexadecimal numbers.
@@ -1267,9 +1300,7 @@ The verify row is only output if it has an element other than zero.
 .Sp
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .I error[,NUM]
-\- [NVMe]
-[NEW EXPERIMENTAL SMARTCTL FEATURE]
-prints the NVMe Error Information log.
+\- [NVMe] prints the NVMe Error Information log.
 Only the 16 most recent log entries are printed by default.
 This number can be changed by the optional parameter NUM.
 The maximum number of log entries is vendor specific
@@ -1475,10 +1506,8 @@ writes a binary representation of the one sector log 0x11
 .Sp
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .I nvmelog,PAGE,SIZE
-\- [NVMe only]
-[NEW EXPERIMENTAL SMARTCTL FEATURE]
-prints a hex dump of the first SIZE bytes from the NVMe log with
-identifier PAGE.
+\- [NVMe only] prints a hex dump of the first SIZE bytes from the NVMe
+log with identifier PAGE.
 PAGE is a hexadecimal number in the range from 0x1 to 0xff.
 SIZE is a hexadecimal number in the range from 0x4 to 0x4000 (16 KiB).
 \fBWARNING: Do not specify the identifier of an unknown log page.
@@ -1734,7 +1763,7 @@ accordingly.
 .I xerrorlba
 \- Fixes LBA byte ordering in Extended Comprehensive SMART error log.
 Some disks use little endian byte ordering instead of ATA register
-ordering to specifiy the LBA addresses in the log entries.
+ordering to specify the LBA addresses in the log entries.
 .Sp
 .I swapid
 \- Fixes byte swapped ATA identify strings (device name, serial number,
@@ -2379,4 +2408,4 @@ Links to these and other documents may be found on the Links page of the
 .SH PACKAGE VERSION
 CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
 .br
-$Id: smartctl.8.in 4588 2017-11-04 15:15:32Z chrfranke $
+$Id: smartctl.8.in 4882 2018-12-29 21:26:45Z chrfranke $
index 4bf5c5c92c4a7e260f0f78912138897b0509889b..7aaf5b50b05c56f5faf099d5ab3fc6d632ce8a5d 100644 (file)
@@ -4,25 +4,17 @@
  * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-11 Bruce Allen
- * Copyright (C) 2008-17 Christian Franke
+ * Copyright (C) 2008-18 Christian Franke
  * 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, see <http://www.gnu.org/licenses/>.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
+#include "config.h"
+#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
+
 #include <errno.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <string.h>
@@ -31,8 +23,6 @@
 #include <stdexcept>
 #include <getopt.h>
 
-#include "config.h"
-
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -41,7 +31,6 @@
 #include <sys/param.h>
 #endif
 
-#include "int64.h"
 #include "atacmds.h"
 #include "dev_interface.h"
 #include "ataprint.h"
 #include "nvmeprint.h"
 #include "smartctl.h"
 #include "utility.h"
+#include "svnversion.h"
 
-const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 4585 2017-11-04 13:41:03Z chrfranke $"
+const char * smartctl_cpp_cvsid = "$Id: smartctl.cpp 4871 2018-12-27 20:03:12Z chrfranke $"
   CONFIG_H_CVSID SMARTCTL_H_CVSID;
 
 // Globals to control printing
 bool printing_is_switchable = false;
 bool printing_is_off = false;
 
+// Control JSON output
+json jglb;
+static bool print_as_json = false;
+static json::print_options print_as_json_options;
+static bool print_as_json_output = false;
+static bool print_as_json_impl = false;
+static bool print_as_json_unimpl = false;
+
 static void printslogan()
 {
-  pout("%s\n", format_version_info("smartctl").c_str());
+  jout("%s\n", format_version_info("smartctl").c_str());
 }
 
 static void UsageSummary()
@@ -70,13 +68,47 @@ static void UsageSummary()
   return;
 }
 
+static void js_initialize(int argc, char **argv, bool verbose)
+{
+  if (jglb.is_enabled())
+    return;
+  jglb.enable();
+  if (verbose)
+    jglb.set_verbose();
+
+  // Major.minor version of JSON format
+  jglb["json_format_version"][0] = 1;
+  jglb["json_format_version"][1] = 0;
+
+  // Smartctl version info
+  json::ref jref = jglb["smartctl"];
+  int ver[3] = { 0, 0, 0 };
+  sscanf(PACKAGE_VERSION, "%d.%d.%d", ver, ver+1, ver+2);
+  jref["version"][0] = ver[0];
+  jref["version"][1] = ver[1];
+  if (ver[2] > 0)
+    jref["version"][2] = ver[2];
+
+#ifdef SMARTMONTOOLS_SVN_REV
+  jref["svn_revision"] = SMARTMONTOOLS_SVN_REV;
+#endif
+  jref["platform_info"] = smi()->get_os_version_str();
+#ifdef BUILD_INFO
+  jref["build_info"] = BUILD_INFO;
+#endif
+
+  jref["argv"][0] = "smartctl";
+  for (int i = 1; i < argc; i++)
+    jref["argv"][i] = argv[i];
+}
+
 static std::string getvalidarglist(int opt);
 
 /*  void prints help information for command syntax */
 static void Usage()
 {
-  printf("Usage: smartctl [options] device\n\n");
-  printf(
+  pout("Usage: smartctl [options] device\n\n");
+  pout(
 "============================================ SHOW INFORMATION OPTIONS =====\n\n"
 "  -h, --help, --usage\n"
 "         Display this help and exit\n\n"
@@ -98,8 +130,10 @@ static void Usage()
 "  --scan-open\n"
 "         Scan for devices and try to open each device\n\n"
   );
-  printf(
+  pout(
 "================================== SMARTCTL RUN-TIME BEHAVIOR OPTIONS =====\n\n"
+"  -j, --json[=[cgiosuv]]\n"
+"         Print output in JSON format\n\n"
 "  -q TYPE, --quietmode=TYPE                                           (ATA)\n"
 "         Set smartctl quiet mode to one of: errorsonly, silent, noserial\n\n"
 "  -d TYPE, --device=TYPE\n"
@@ -114,7 +148,7 @@ static void Usage()
 "  -n MODE[,STATUS], --nocheck=MODE[,STATUS]                           (ATA)\n"
 "         No check if: never, sleep, standby, idle (see man page)\n\n",
   getvalidarglist('d').c_str()); // TODO: Use this function also for other options ?
-  printf(
+  pout(
 "============================== DEVICE FEATURE ENABLE/DISABLE COMMANDS =====\n\n"
 "  -s VALUE, --smart=VALUE\n"
 "        Enable/disable SMART on device (on/off)\n\n"
@@ -128,7 +162,7 @@ static void Usage()
 "        standby,[N|off|now], wcache,[on|off], rcache,[on|off],\n"
 "        wcreorder,[on|off[,p]], wcache-sct,[ata|on|off[,p]]\n\n"
   );
-  printf(
+  pout(
 "======================================= READ AND DISPLAY DATA OPTIONS =====\n\n"
 "  -H, --health\n"
 "        Show device SMART health status\n\n"
@@ -158,13 +192,13 @@ static void Usage()
     get_drivedb_path_add()
   );
 #ifdef SMARTMONTOOLS_DRIVEDBDIR
-  printf(
+  pout(
                       "\n"
 "         and then    %s",
     get_drivedb_path_default()
   );
 #endif
-  printf(
+  pout(
          "]\n\n"
 "============================================ DEVICE SELF-TEST OPTIONS =====\n\n"
 "  -t TEST, --test=TEST\n"
@@ -177,7 +211,7 @@ static void Usage()
 );
   std::string examples = smi()->get_app_examples("smartctl");
   if (!examples.empty())
-    printf("%s\n", examples.c_str());
+    pout("%s\n", examples.c_str());
 }
 
 // Values for  --long only options, see parse_options()
@@ -231,6 +265,8 @@ static std::string getvalidarglist(int opt)
            "wcache-sct,[ata|on|off[,p]]";
   case 's':
     return getvalidarglist(opt_smart)+", "+getvalidarglist(opt_set);
+  case 'j':
+    return "c, g, i, o, s, u, v";
   case opt_identify:
     return "n, wn, w, v, wv, wb";
   case 'v':
@@ -244,7 +280,7 @@ static std::string getvalidarglist(int opt)
 static void printvalidarglistmessage(int opt)
 {
   if (opt=='v'){
-    pout("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n",
+    jerr("=======> VALID ARGUMENTS ARE:\n\thelp\n%s\n<=======\n",
          create_vendor_attribute_arg_list().c_str());
   }
   else {
@@ -252,7 +288,7 @@ static void printvalidarglistmessage(int opt)
   // need to figure out which to get the formatting right.
     std::string s = getvalidarglist(opt);
     char separator = strchr(s.c_str(), '\n') ? '\n' : ' ';
-    pout("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, s.c_str(), separator);
+    jerr("=======> VALID ARGUMENTS ARE:%c%s%c<=======\n", separator, s.c_str(), separator);
   }
 
   return;
@@ -269,12 +305,12 @@ static void scan_devices(const smart_devtype_list & types, bool with_open, char
 
 
 /*      Takes command options and sets features to be run */    
-static const char * parse_options(int argc, char** argv,
+static int parse_options(int argc, char** argv, const char * & type,
   ata_print_options & ataopts, scsi_print_options & scsiopts,
   nvme_print_options & nvmeopts, bool & print_type_only)
 {
   // Please update getvalidarglist() if you edit shortopts
-  const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iaxv:P:t:CXF:n:B:f:g:";
+  const char *shortopts = "h?Vq:d:T:b:r:s:o:S:HcAl:iaxv:P:t:CXF:n:B:f:g:j";
   // Please update getvalidarglist() if you edit longopts
   struct option longopts[] = {
     { "help",            no_argument,       0, 'h' },
@@ -307,6 +343,7 @@ static const char * parse_options(int argc, char** argv,
     { "drivedb",         required_argument, 0, 'B' },
     { "format",          required_argument, 0, 'f' },
     { "get",             required_argument, 0, 'g' },
+    { "json",            optional_argument, 0, 'j' },
     { "identify",        optional_argument, 0, opt_identify },
     { "set",             required_argument, 0, opt_set },
     { "scan",            no_argument,       0, opt_scan      },
@@ -318,7 +355,6 @@ static const char * parse_options(int argc, char** argv,
   memset(extraerror, 0, sizeof(extraerror));
   opterr=optopt=0;
 
-  const char * type = 0; // set to -d optarg
   smart_devtype_list scan_types; // multiple -d TYPE options for --scan
   bool use_default_db = true; // set false on '-B FILE'
   bool output_format_set = false; // set true on '-f FORMAT'
@@ -330,12 +366,19 @@ static const char * parse_options(int argc, char** argv,
   char *arg;
 
   while ((optchar = getopt_long(argc, argv, shortopts, longopts, 0)) != -1) {
+
+    // Clang analyzer: Workaround for false positive messages
+    // 'Dereference of null pointer' and 'Null pointer argument'
+    bool optarg_is_set = !!optarg;
+    #ifdef __clang_analyzer__
+    if (!optarg_is_set) optarg = (char *)"";
+    #endif
+
     switch (optchar){
     case 'V':
       printing_is_off = false;
       pout("%s", format_version_info("smartctl", true /*full*/).c_str());
-      EXIT(0);
-      break;
+      return 0;
     case 'q':
       if (!strcmp(optarg,"errorsonly")) {
         printing_is_switchable = true;
@@ -635,7 +678,7 @@ static const char * parse_options(int argc, char** argv,
 
     case opt_identify:
       ataopts.identify_word_level = ataopts.identify_bit_level = 0;
-      if (optarg) {
+      if (optarg_is_set) {
         for (int i = 0; optarg[i]; i++) {
           switch (optarg[i]) {
             case 'w': ataopts.identify_word_level = 1; break;
@@ -677,8 +720,7 @@ static const char * parse_options(int argc, char** argv,
       ataopts.sct_erc_get = true;
       ataopts.sct_wcache_reorder_get = true;
       ataopts.devstat_all_pages = true;
-      // ataopts.pending_defects_log = 31; // TODO: Add if no longer EXPERIMENTAL
-      ataopts.pending_defects_info = true; // TODO: Remove then
+      ataopts.pending_defects_log = 31;
       ataopts.sataphy = true;
       ataopts.get_set_used = true;
       ataopts.get_aam = ataopts.get_apm = true;
@@ -699,7 +741,7 @@ static const char * parse_options(int argc, char** argv,
         printslogan();
         pout("The valid arguments to -v are:\n\thelp\n%s\n",
              create_vendor_attribute_arg_list().c_str());
-        EXIT(0);
+        return 0;
       }
       if (!parse_attribute_def(optarg, ataopts.attribute_defs, PRIOR_USER))
         badarg = true;
@@ -713,14 +755,14 @@ static const char * parse_options(int argc, char** argv,
         ataopts.show_presets = true;
       } else if (!strcmp(optarg, "showall")) {
         if (!init_drive_database(use_default_db))
-          EXIT(FAILCMD);
+          return FAILCMD;
         if (optind < argc) { // -P showall MODEL [FIRMWARE]
           int cnt = showmatchingpresets(argv[optind], (optind+1<argc ? argv[optind+1] : NULL));
-          EXIT(cnt); // report #matches
+          return (cnt >= 0 ? cnt : 0);
         }
         if (showallpresets())
-          EXIT(FAILCMD); // report regexp syntax error
-        EXIT(0);
+          return FAILCMD; // report regexp syntax error
+        return 0;
       } else {
         badarg = true;
       }
@@ -863,15 +905,14 @@ static const char * parse_options(int argc, char** argv,
         else
           use_default_db = false;
         if (!read_drive_database(path))
-          EXIT(FAILCMD);
+          return FAILCMD;
       }
       break;
     case 'h':
       printing_is_off = false;
       printslogan();
       Usage();
-      EXIT(0);  
-      break;
+      return 0;
 
     case 'g':
     case_s_continued: // -s, see above
@@ -1047,6 +1088,33 @@ static const char * parse_options(int argc, char** argv,
       scan = optchar;
       break;
 
+    case 'j':
+      {
+        print_as_json = true;
+        print_as_json_options.pretty = true;
+        print_as_json_options.sorted = false;
+        print_as_json_options.flat = false;
+        print_as_json_output = false;
+        print_as_json_impl = print_as_json_unimpl = false;
+        bool json_verbose = false;
+        if (optarg_is_set) {
+          for (int i = 0; optarg[i]; i++) {
+            switch (optarg[i]) {
+              case 'c': print_as_json_options.pretty = false; break;
+              case 'g': print_as_json_options.flat = true; break;
+              case 'i': print_as_json_impl = true; break;
+              case 'o': print_as_json_output = true; break;
+              case 's': print_as_json_options.sorted = true; break;
+              case 'u': print_as_json_unimpl = true; break;
+              case 'v': json_verbose = true; break;
+              default: badarg = true;
+            }
+          }
+        }
+        js_initialize(argc, argv, json_verbose);
+      }
+      break;
+
     case '?':
     default:
       printing_is_off = false;
@@ -1057,31 +1125,31 @@ static const char * parse_options(int argc, char** argv,
       if (arg[1] == '-' && optchar != 'h') {
         // Iff optopt holds a valid option then argument must be missing.
         if (optopt && (optopt >= opt_scan || strchr(shortopts, optopt))) {
-          pout("=======> ARGUMENT REQUIRED FOR OPTION: %s\n", arg+2);
+          jerr("=======> ARGUMENT REQUIRED FOR OPTION: %s\n", arg+2);
           printvalidarglistmessage(optopt);
         } else
-          pout("=======> UNRECOGNIZED OPTION: %s\n",arg+2);
+          jerr("=======> UNRECOGNIZED OPTION: %s\n",arg+2);
        if (extraerror[0])
          pout("=======> %s", extraerror);
         UsageSummary();
-        EXIT(FAILCMD);
+        return FAILCMD;
       }
       if (0 < optopt && 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);
+          jerr("=======> ARGUMENT REQUIRED FOR OPTION: %c\n", optopt);
           printvalidarglistmessage(optopt);
         } else
-          pout("=======> UNRECOGNIZED OPTION: %c\n",optopt);
+          jerr("=======> UNRECOGNIZED OPTION: %c\n",optopt);
        if (extraerror[0])
          pout("=======> %s", extraerror);
         UsageSummary();
-        EXIT(FAILCMD);
+        return FAILCMD;
       }
       Usage();
-      EXIT(0);  
+      return 0;
     } // closes switch statement to process command-line options
     
     // Check to see if option had an unrecognized or incorrect argument.
@@ -1091,15 +1159,16 @@ static const char * parse_options(int argc, char** argv,
       // here, but we just print the short form.  Please fix this if you know
       // a clean way to do it.
       char optstr[] = { (char)optchar, 0 };
-      pout("=======> INVALID ARGUMENT TO -%s: %s\n",
+      jerr("=======> INVALID ARGUMENT TO -%s: %s\n",
         (optchar == opt_identify ? "-identify" :
          optchar == opt_set ? "-set" :
-         optchar == opt_smart ? "-smart" : optstr), optarg);
+         optchar == opt_smart ? "-smart" :
+         optchar == 'j' ? "-json" : optstr), optarg);
       printvalidarglistmessage(optchar);
       if (extraerror[0])
        pout("=======> %s", extraerror);
       UsageSummary();
-      EXIT(FAILCMD);
+      return FAILCMD;
     }
   }
 
@@ -1107,9 +1176,9 @@ static const char * parse_options(int argc, char** argv,
   if (scan) {
     // Read or init drive database to allow USB ID check.
     if (!init_drive_database(use_default_db))
-      EXIT(FAILCMD);
+      return FAILCMD;
     scan_devices(scan_types, (scan == opt_scan_open), argv + optind);
-    EXIT(0);
+    return 0;
   }
 
   // At this point we have processed all command-line options.  If the
@@ -1122,18 +1191,18 @@ static const char * parse_options(int argc, char** argv,
   if (scan_types.size() > 1) {
     printing_is_off = false;
     printslogan();
-    pout("ERROR: multiple -d TYPE options are only allowed with --scan\n");
+    jerr("ERROR: multiple -d TYPE options are only allowed with --scan\n");
     UsageSummary();
-    EXIT(FAILCMD);
+    return FAILCMD;
   }
 
   // error message if user has asked for more than one test
   if (testcnt > 1) {
     printing_is_off = false;
     printslogan();
-    pout("\nERROR: smartctl can only run a single test type (or abort) at a time.\n");
+    jerr("\nERROR: smartctl can only run a single test type (or abort) at a time.\n");
     UsageSummary();
-    EXIT(FAILCMD);
+    return FAILCMD;
   }
 
   // error message if user has set selective self-test options without
@@ -1143,11 +1212,11 @@ static const char * parse_options(int argc, char** argv,
     printing_is_off = false;
     printslogan();
     if (ataopts.smart_selective_args.pending_time)
-      pout("\nERROR: smartctl -t pending,N must be used with -t select,N-M.\n");
+      jerr("\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");
+      jerr("\nERROR: smartctl -t afterselect,(on|off) must be used with -t select,N-M.\n");
     UsageSummary();
-    EXIT(FAILCMD);
+    return FAILCMD;
   }
 
   // If captive option was used, change test type if appropriate.
@@ -1176,51 +1245,154 @@ static const char * parse_options(int argc, char** argv,
   
   // 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");
+    jerr("ERROR: smartctl requires a device name as the final command-line argument.\n\n");
     UsageSummary();
-    EXIT(FAILCMD);
+    return 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");
+    jerr("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);
+    return FAILCMD;
   }
 
   // Read or init drive database
   if (!init_drive_database(use_default_db))
-    EXIT(FAILCMD);
+    return FAILCMD;
+
+  // No error, continue in main_worker()
+  return -1;
+}
+
+// Printing functions
+
+__attribute_format_printf(3, 0)
+static void vjpout(bool is_js_impl, const char * msg_severity,
+                   const char *fmt, va_list ap)
+{
+  if (!print_as_json) {
+    // Print out directly
+    vprintf(fmt, ap);
+    fflush(stdout);
+  }
+  else {
+    // Add lines to JSON output
+    static char buf[1024];
+    static char * bufnext = buf;
+    vsnprintf(bufnext, sizeof(buf) - (bufnext - buf), fmt, ap);
+    for (char * p = buf, *q; ; p = q) {
+      if (!(q = strchr(p, '\n'))) {
+        // Keep remaining line for next call
+        for (bufnext = buf; *p; bufnext++, p++)
+          *bufnext = *p;
+        break;
+      }
+      *q++ = 0; // '\n' -> '\0'
+
+      static int lineno = 0;
+      lineno++;
+      if (print_as_json_output) {
+        // Collect full output in array
+        static int outindex = 0;
+        jglb["smartctl"]["output"][outindex++] = p;
+      }
+      if (!*p)
+        continue; // Skip empty line
+
+      if (msg_severity) {
+        // Collect non-empty messages in array
+        static int errindex = 0;
+        json::ref jref = jglb["smartctl"]["messages"][errindex++];
+        jref["string"] = p;
+        jref["severity"] = msg_severity;
+      }
+
+      if (   ( is_js_impl && print_as_json_impl  )
+          || (!is_js_impl && print_as_json_unimpl)) {
+        // Add (un)implemented non-empty lines to global object
+        jglb[strprintf("smartctl_%04d_%c", lineno,
+                     (is_js_impl ? 'i' : 'u')).c_str()] = p;
+      }
+    }
+  }
+}
+
+// Default: print to stdout
+// --json: ignore
+// --json=o: append to "output" array
+// --json=u: add "smartctl_NNNN_u" element(s)
+void pout(const char *fmt, ...)
+{
+  if (printing_is_off)
+    return;
+  if (print_as_json && !(print_as_json_output
+      || print_as_json_impl || print_as_json_unimpl))
+    return;
 
-  return type;
+  va_list ap;
+  va_start(ap, fmt);
+  vjpout(false, 0, fmt, ap);
+  va_end(ap);
 }
 
-// Printing function (controlled by global printing_is_off)
-// [From GLIBC Manual: Since the prototype doesn't specify types for
-// optional arguments, in a call to a variadic function the default
-// argument promotions are performed on the optional argument
-// values. This means the objects of type char or short int (whether
-// signed or not) are promoted to either int or unsigned int, as
-// appropriate.]
-void pout(const char *fmt, ...){
+// Default: Print to stdout
+// --json: ignore
+// --json=o: append to "output" array
+// --json=i: add "smartctl_NNNN_i" element(s)
+void jout(const char *fmt, ...)
+{
+  if (printing_is_off)
+    return;
+  if (print_as_json && !(print_as_json_output
+      || print_as_json_impl || print_as_json_unimpl))
+    return;
+
   va_list ap;
-  
-  // initialize variable argument list 
-  va_start(ap,fmt);
-  if (printing_is_off) {
-    va_end(ap);
+  va_start(ap, fmt);
+  vjpout(true, 0, fmt, ap);
+  va_end(ap);
+}
+
+// Default: print to stdout
+// --json: append to "messages"
+// --json=o: append to "output" array
+// --json=i: add "smartctl_NNNN_i" element(s)
+void jinf(const char *fmt, ...)
+{
+  if (printing_is_off)
     return;
-  }
 
-  // print out
-  vprintf(fmt,ap);
+  va_list ap;
+  va_start(ap, fmt);
+  vjpout(true, "information", fmt, ap);
+  va_end(ap);
+}
+
+void jwrn(const char *fmt, ...)
+{
+  if (printing_is_off)
+    return;
+
+  va_list ap;
+  va_start(ap, fmt);
+  vjpout(true, "warning", fmt, ap);
+  va_end(ap);
+}
+
+void jerr(const char *fmt, ...)
+{
+  if (printing_is_off)
+    return;
+
+  va_list ap;
+  va_start(ap, fmt);
+  vjpout(true, "error", fmt, ap);
   va_end(ap);
-  fflush(stdout);
-  return;
 }
 
 // Globals to set failuretest() policy
@@ -1237,7 +1409,7 @@ void failuretest(failure_type type, int returnvalue)
     if (!failuretest_conservative)
       return;
     pout("An optional SMART command failed: exiting. Remove '-T conservative' option to continue.\n");
-    EXIT(returnvalue);
+    throw int(returnvalue);
   }
 
   // If this is an error in a "mandatory" SMART command
@@ -1245,7 +1417,7 @@ void failuretest(failure_type type, int returnvalue)
     if (failuretest_permissive--)
       return;
     pout("A mandatory SMART command failed: exiting. To continue, add one or more '-T permissive' options.\n");
-    EXIT(returnvalue);
+    throw int(returnvalue);
   }
 
   throw std::logic_error("failuretest: Unknown type");
@@ -1263,7 +1435,7 @@ void checksumwarning(const char * string)
 
   // user has asked us to fail on checksum errors
   if (checksum_err_mode == CHECKSUM_ERR_EXIT)
-    EXIT(FAILSMART);
+    throw int(FAILSMART);
 }
 
 // Return info string about device protocol
@@ -1280,6 +1452,15 @@ static const char * get_protocol_info(const smart_device * dev)
   }
 }
 
+// Add JSON device info
+static void js_device_info(const json::ref & jref, const smart_device * dev)
+{
+  jref["name"] = dev->get_dev_name();
+  jref["info_name"] = dev->get_info_name();
+  jref["type"] = dev->get_dev_type();
+  jref["protocol"] = get_protocol_info(dev);
+}
+
 // Device scan
 // smartctl [-d type] --scan[-open] -- [PATTERN] [smartd directive ...]
 void scan_devices(const smart_devtype_list & types, bool with_open, char ** argv)
@@ -1303,27 +1484,31 @@ void scan_devices(const smart_devtype_list & types, bool with_open, char ** argv
 
   for (unsigned i = 0; i < devlist.size(); i++) {
     smart_device_auto_ptr dev( devlist.release(i) );
+    json::ref jref = jglb["devices"][i];
 
     if (with_open) {
       printing_is_off = dont_print;
       dev.replace ( dev->autodetect_open() );
       printing_is_off = false;
+    }
 
-      if (!dev->is_open()) {
-        pout("# %s -d %s # %s, %s device open failed: %s\n", dev->get_dev_name(),
-          dev->get_dev_type(), dev->get_info_name(),
-          get_protocol_info(dev.get()), dev->get_errmsg());
-        continue;
-      }
+    js_device_info(jref, dev.get());
+
+    if (with_open && !dev->is_open()) {
+      jout("# %s -d %s # %s, %s device open failed: %s\n", dev->get_dev_name(),
+           dev->get_dev_type(), dev->get_info_name(),
+           get_protocol_info(dev.get()), dev->get_errmsg());
+      jref["open_error"] = dev->get_errmsg();
+      continue;
     }
 
-    pout("%s -d %s", dev->get_dev_name(), dev->get_dev_type());
+    jout("%s -d %s", dev->get_dev_name(), dev->get_dev_type());
     if (!argv[ai])
-      pout(" # %s, %s device\n", dev->get_info_name(), get_protocol_info(dev.get()));
+      jout(" # %s, %s device\n", dev->get_info_name(), get_protocol_info(dev.get()));
     else {
       for (int j = ai; argv[j]; j++)
-        pout(" %s", argv[j]);
-      pout("\n");
+        jout(" %s", argv[j]);
+      jout("\n");
     }
 
     if (dev->is_open())
@@ -1343,11 +1528,16 @@ static int main_worker(int argc, char **argv)
     return 1;
 
   // Parse input arguments
+  const char * type = 0;
   ata_print_options ataopts;
   scsi_print_options scsiopts;
   nvme_print_options nvmeopts;
   bool print_type_only = false;
-  const char * type = parse_options(argc, argv, ataopts, scsiopts, nvmeopts, print_type_only);
+  {
+    int status = parse_options(argc, argv, type, ataopts, scsiopts, nvmeopts, print_type_only);
+    if (status >= 0)
+      return status;
+  }
 
   const char * name = argv[argc-1];
 
@@ -1366,7 +1556,7 @@ static int main_worker(int argc, char **argv)
     dev = smi()->get_smart_device(name, type);
 
   if (!dev) {
-    pout("%s: %s\n", name, smi()->get_errmsg());
+    jerr("%s: %s\n", name, smi()->get_errmsg());
     if (type)
       printvalidarglistmessage('d');
     else
@@ -1381,7 +1571,7 @@ static int main_worker(int argc, char **argv)
          dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev.get()));
 
   if (dev->is_ata() && ataopts.powermode>=2 && dev->is_powered_down()) {
-    pout("Device is in STANDBY (OS) mode, exit(%d)\n", ataopts.powerexit);
+    jinf("Device is in STANDBY (OS) mode, exit(%d)\n", ataopts.powerexit);
     return ataopts.powerexit;
   }
 
@@ -1400,14 +1590,17 @@ static int main_worker(int argc, char **argv)
         dev->get_info_name(), oldinfo.dev_type.c_str(), dev->get_dev_type());
   }
   if (!dev->is_open()) {
-    pout("Smartctl open device: %s failed: %s\n", dev->get_info_name(), dev->get_errmsg());
+    jerr("Smartctl open device: %s failed: %s\n", dev->get_info_name(), dev->get_errmsg());
     return FAILDEV;
   }
 
+  // Add JSON info similar to --scan output
+  js_device_info(jglb["device"], dev.get());
+
   // now call appropriate ATA or SCSI routine
   int retval = 0;
   if (print_type_only)
-    pout("%s: Device of type '%s' [%s] opened\n",
+    jout("%s: Device of type '%s' [%s] opened\n",
          dev->get_info_name(), dev->get_dev_type(), get_protocol_info(dev.get()));
   else if (dev->is_ata())
     retval = ataPrintMain(dev->to_ata(), ataopts);
@@ -1431,12 +1624,19 @@ int main(int argc, char **argv)
   bool badcode = false;
 
   try {
-    // Do the real work ...
-    status = main_worker(argc, argv);
-  }
-  catch (int ex) {
-    // EXIT(status) arrives here
-    status = ex;
+    try {
+      // Do the real work ...
+      status = main_worker(argc, argv);
+    }
+    catch (int ex) {
+      // Exit status from checksumwarning() and failuretest() arrives here
+      status = ex;
+    }
+    // Print JSON if enabled
+    if (jglb.has_uint128_output())
+      jglb["smartctl"]["uint128_precision_bits"] = uint128_to_str_precision_bits();
+    jglb["smartctl"]["exit_status"] = status;
+    jglb.print(stdout, print_as_json_options);
   }
   catch (const std::bad_alloc & /*ex*/) {
     // Memory allocation failed (also thrown by std::operator new)
index e379c7e8b15de177256fe07698979f9aebfe64cd..305f625ec7134e3394a989367283daef40488532 100644 (file)
@@ -4,33 +4,20 @@
  * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-10 Bruce Allen
- * Copyright (C) 2008-10 Christian Franke
+ * Copyright (C) 2008-17 Christian Franke
  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef SMARTCTL_H_
 #define SMARTCTL_H_
 
-#define SMARTCTL_H_CVSID "$Id: smartctl.h 4431 2017-08-08 19:38:15Z chrfranke $\n"
+#define SMARTCTL_H_CVSID "$Id: smartctl.h 4842 2018-12-02 16:07:26Z chrfranke $\n"
 
 // Return codes (bitmask)
 
-// command line did not parse, or internal error occured in smartctl
+// command line did not parse, or internal error occurred in smartctl
 #define FAILCMD   (0x01<<0)
 
 // device open failed
@@ -95,4 +82,22 @@ inline void print_off()
     printing_is_off = true;
 }
 
+// The singleton global JSON object
+#include "json.h"
+extern json jglb;
+
+#include "utility.h" // __attribute_format_printf()
+// TODO: move this to a new include file?
+
+// Version of pout() for items already included in JSON output
+void jout(const char *fmt, ...)
+  __attribute_format_printf(1, 2);
+// Version of pout() for info/warning/error messages
+void jinf(const char *fmt, ...)
+  __attribute_format_printf(1, 2);
+void jwrn(const char *fmt, ...)
+__attribute_format_printf(1, 2);
+void jerr(const char *fmt, ...)
+__attribute_format_printf(1, 2);
+
 #endif
index 5d5521478a7fa04979cea486b13614cde107821a..80cb4e8263f6a323cd055b42b89518a656417cc8 100644 (file)
@@ -1,21 +1,10 @@
 .ig
 Copyright (C) 2002-10 Bruce Allen
-Copyright (C) 2004-17 Christian Franke
+Copyright (C) 2004-18 Christian Franke
 
-$Id: smartd.8.in 4576 2017-10-29 16:41:44Z chrfranke $
+SPDX-License-Identifier: GPL-2.0-or-later
 
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-You should have received a copy of the GNU General Public License
-(for example COPYING); If not, see <http://www.gnu.org/licenses/>.
-
-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/
+$Id: smartd.8.in 4861 2018-12-16 18:24:57Z chrfranke $
 
 ..
 .\" Macros borrowed from pages generated with Pod::Man
@@ -38,10 +27,10 @@ California, Santa Cruz. http://ssrc.soe.ucsc.edu/
 .TH SMARTD 8 "CURRENT_SVN_DATE" "CURRENT_SVN_VERSION" "SMART Monitoring Tools"
 .SH NAME
 \fBsmartd\fP \- SMART Disk Monitoring Daemon
-
+.Sp
 .SH SYNOPSIS
 .B smartd [options]
-
+.Sp
 .SH DESCRIPTION
 .\" %IF NOT OS ALL
 .\"! [This man page is generated for the OS_MAN_FILTER version of smartmontools.
@@ -108,6 +97,21 @@ To exit \fBsmartd\fP use CONTROL-\e.
 .\" %IF OS Windows
 (Windows: CONTROL-Break).
 .\" %ENDIF OS Windows
+.\" %IF ENABLE_SYSTEMD_NOTIFY
+.PP
+[Linux only]
+[NEW EXPERMIMENTAL SMARTD FEATURE]
+If \fBsmartd\fP is started as a \fBsystemd\fP(1) service and
+\*(AqType=Notify\*(Aq is specified in the service file, the service manager
+is notified after successful startup.
+Other state changes are reported via systemd notify STATUS messages.
+Notification of successful reloads (after \fBHUP\fP signal) is not supported.
+To detect this process start-up type, \fBsmartd\fP checks whether the
+environment variable \*(AqNOTIFY_SOCKET\*(Aq is set.
+Note that it is required to set the \*(Aq\-n\*(Aq (\*(Aq\-\-nofork\*(Aq)
+option in the \*(AqExecStart=/usr/local/sbin/smartd\*(Aq command line
+if \*(AqType=Notify\*(Aq is used.
+.\" %ENDIF ENABLE_SYSTEMD_NOTIFY
 .PP
 On startup, in the absence of the configuration file
 \fB/usr/local/etc/smartd.conf\fP, the \fBsmartd\fP daemon first scans for all
@@ -119,7 +123,6 @@ devices, and \fB"/dev/sd[a\-z]"\fP, \fB"/dev/sd[a\-c][a\-z]"\fP
 for ATA/SATA or SCSI/SAS devices.
 Disks behind RAID controllers are not included.
 .Sp
-[NEW EXPERIMENTAL SMARTD FEATURE]
 If directive \*(Aq\-d nvme\*(Aq
 .\" %IF ENABLE_NVME_DEVICESCAN
 or no \*(Aq\-d\*(Aq directive
@@ -164,7 +167,6 @@ ICHxR controller with RST driver.
 .Sp
 Disks behind Areca RAID controllers are not included.
 .Sp
-[NEW EXPERIMENTAL SMARTD FEATURE]
 If directive \*(Aq\-d nvme\*(Aq
 .\" %IF ENABLE_NVME_DEVICESCAN
 or no \*(Aq\-d\*(Aq directive
@@ -421,8 +423,7 @@ this option are:
 .Sp
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .I nvmeioctl
-\- [NEW EXPERIMENTAL SMARTD FEATURE]
-report only ioctl() transactions with NVMe devices.
+\- report only ioctl() transactions with NVMe devices.
 .Sp
 .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 Any argument may include a positive integer to specify the level of
@@ -808,6 +809,10 @@ Alternatively send the info to the smartmontools support mailing list:
 .br
 \fBupdate-smart-drivedb\fP(8).
 .\" %ENDIF ENABLE_UPDATE_SMART_DRIVEDB
+.\" %IF ENABLE_SYSTEMD_NOTIFY
+.br
+\fBsystemd.exec\fP(5).
+.\" %ENDIF ENABLE_SYSTEMD_NOTIFY
 .Sp
 .SH REFERENCES
 Please see the following web site for more info:
@@ -834,4 +839,4 @@ Links to these and other documents may be found on the Links page of the
 .SH PACKAGE VERSION
 CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
 .br
-$Id: smartd.8.in 4576 2017-10-29 16:41:44Z chrfranke $
+$Id: smartd.8.in 4861 2018-12-16 18:24:57Z chrfranke $
index 0afba1ba46b12b7aac07b260908df6e57e1e80ae..a3da1fc6597b8abf8536d3ff7ea9090d23aa2e88 100644 (file)
@@ -1,21 +1,10 @@
 .ig
 Copyright (C) 2002-10 Bruce Allen
-Copyright (C) 2004-17 Christian Franke
+Copyright (C) 2004-18 Christian Franke
 
-$Id: smartd.conf.5.in 4589 2017-11-04 15:30:54Z chrfranke $
+SPDX-License-Identifier: GPL-2.0-or-later
 
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-You should have received a copy of the GNU General Public License
-(for example COPYING); If not, see <http://www.gnu.org/licenses/>.
-
-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/
+$Id: smartd.conf.5.in 4856 2018-12-11 21:42:16Z chrfranke $
 
 ..
 .\" Macros borrowed from pages generated with Pod::Man
@@ -38,7 +27,7 @@ California, Santa Cruz. http://ssrc.soe.ucsc.edu/
 .TH SMARTD.CONF 5 "CURRENT_SVN_DATE" "CURRENT_SVN_VERSION" "SMART Monitoring Tools"
 .SH NAME
 \fBsmartd.conf\fP \- SMART Disk Monitoring Daemon Configuration File\fP
-
+.Sp
 .SH DESCRIPTION
 .\" %IF NOT OS ALL
 .\"! [This man page is generated for the OS_MAN_FILTER version of smartmontools.
@@ -237,18 +226,66 @@ Section below!
 ################################################
 .Ve
 .Sp
-.SH CONFIGURATION FILE DIRECTIVES
+.SH DEVICESCAN
 If a 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.
+If
+.B DEVICESCAN
+is not followed by any Directives, then \*(Aq\-a\*(Aq will apply to all
+devices.
+.PP
 .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.
+devices that are found in the scan.
+For example
+.PP
+.Vb
+\ \ DEVICESCAN \-m root@example.com
+.Ve
+.PP
+will scan for all devices, and then monitor them.
+It will send one email warning per device for any problems that are found.
+.PP
+.Vb
+\ \ DEVICESCAN \-H \-m root@example.com
+.Ve
+.PP
+will do the same, but only monitors the SMART health status of the
+devices, rather than the default \*(Aq\-a\*(Aq.
+.PP
+Multiple \*(Aq\-d TYPE\*(Aq options may be specified with DEVICESCAN
+to combine the scan results of more than one TYPE.
+.PP
+Configuration entries for specific devices may precede the \fBDEVICESCAN\fP
+entry.
+For example
 .PP
+.Vb 4
+\ \ DEFAULT \-m root@example.com
+\ \ /dev/sda \-s S/../.././02
+\ \ /dev/sdc \-d ignore
+\ \ DEVICESCAN \-s L/../.././02
+.Ve
+.PP
+will scan for all devices except /dev/sda and /dev/sdc, monitor them, and
+run a long test between 2\(en3 am every morning.
+Device /dev/sda will also be monitored, but only a short test will be run.
+Device /dev/sdc will be ignored.
+Warning emails will be sent for all monitored devices.
+.PP
+A device is ignored by DEVICESCAN if a configuration line with the same
+device name exists.
+.br
+[NEW EXPERIMENTAL SMARTD FEATURE]
+A device name is also ignored if another device with same identify
+information (vendor, model, firmware version, serial number, WWN) already
+exists.
+.Sp
+.SH DEFAULT SETTINGS
 If an entry in the configuration file starts with
 .B DEFAULT
 instead of a device name, then all directives in this entry are set
@@ -275,7 +312,8 @@ has the same effect as:
 \ \ /dev/sdd \-H \-m admin@example.com
 \ \ /dev/sde \-d removable \-H \-m admin@example.com
 .Ve
-.PP
+.Sp
+.SH CONFIGURATION FILE DIRECTIVES
 The following are the Directives that may appear following the device
 name or
 .B DEVICESCAN
@@ -346,8 +384,7 @@ from issuing ATA commands to a SCSI device.
 .\" %ENDIF NOT OS Darwin
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .I nvme[,NSID]
-\- [NEW EXPERIMENTAL SMARTCTL FEATURE]
-the device type is NVM Express (NVMe).
+\- the device type is NVM Express (NVMe).
 The optional parameter NSID specifies the namespace id (in hex) passed
 to the driver.
 Use 0xffffffff for the broadcast namespace id.
@@ -404,6 +441,14 @@ PL2571/2771/2773/2775 USB to SATA bridge.
 \- this device type is for SATA disks that are behind a SunplusIT USB to SATA
 bridge.
 .Sp
+.I sntjmicron[,NSID]
+\- [NEW EXPERIMENTAL SMARTD FEATURE]
+this device type is for NVMe disks that are behind a JMicron USB to NVMe
+bridge.
+The optional parameter NSID specifies the namespace id (in hex) passed
+to the driver.
+The default namespace id is the broadcast namespace id (0xffffffff).
+.Sp
 .\" %ENDIF NOT OS Darwin
 .\" %IF OS Linux
 .I marvell
@@ -425,7 +470,7 @@ Please see the \fBsmartctl\fP(8) man page for further details.
 .\" %IF OS Linux Windows Cygwin
 .I aacraid,H,L,ID
 \- [Linux, Windows and Cygwin only] the device consists of one or more
-SCSI/SAS disks connected to an AacRaid controller.
+SCSI/SAS or SATA disks connected to an AacRaid controller.
 The non-negative integers H,L,ID (Host number, Lun, ID) denote which disk
 on the controller is monitored.
 In log files and email messages this disk will be identified as
@@ -627,9 +672,8 @@ will be logged to syslog.  [Please see the
 command-line option.]
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .Sp
-[NVMe]
-[NEW EXPERIMENTAL SMARTD FEATURE]
-Checks the "Critical Warning" byte from the SMART/Health Information log.
+[NVMe] Checks the "Critical Warning" byte from the SMART/Health
+Information log.
 If any warning bit is set, a message at loglevel \fB\*(AqLOG_CRIT\*(Aq\fP
 will be logged to syslog.
 .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
@@ -644,9 +688,7 @@ error log has increased since the last check.
 .Sp
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .I error
-\- [NVMe]
-[NEW EXPERIMENTAL SMARTD FEATURE]
-report if the "Number of Error Information Log Entries" from the
+\- [NVMe] report if the "Number of Error Information Log Entries" from the
 SMART/Health Information log has increased since the last check.
 .Sp
 .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
@@ -661,9 +703,7 @@ checks the maximum of both values.
 .Sp
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .I xerror
-\- [NVMe]
-[NEW EXPERIMENTAL SMARTD FEATURE]
-same as \*(Aq\-l error\*(Aq.
+\- [NVMe] same as \*(Aq\-l error\*(Aq.
 .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .Sp
 .I selftest
@@ -1425,7 +1465,6 @@ by default.  This can be changed to Attribute 9 or 220 by the drive
 database or by the \*(Aq\-v 9,temp\*(Aq or \*(Aq\-v 220,temp\*(Aq directive.
 .\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
 .Sp
-[NEW EXPERIMENTAL SMARTD FEATURE]
 For NVMe devices, smartd checks the maximum of the Composite Temperature value
 and all Temperature Sensor values reported by SMART/Health Information log.
 .\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin
@@ -1553,66 +1592,6 @@ configuration file Directives might be:
 If you want more frequent information, use: \fB\-a\fP.
 .Sp
 .TP
-.B ADDITIONAL DETAILS ABOUT DEVICESCAN
-If a 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 (see also \fBsmartd\fP(8) man page).
-.Sp
-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.
-.Sp
-\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
-.br
-.B DEVICESCAN \-m root@example.com
-.br
-will scan for all devices, and then monitor them.  It will send one
-email warning per device for any problems that are found.
-.br
-.B  DEVICESCAN \-d ata \-m root@example.com
-.br
-will do the same, but restricts the scan to ATA devices only.
-.br
-.B  DEVICESCAN \-H \-d ata \-m root@example.com
-.br
-will do the same, but only monitors the SMART health status of the
-devices, (rather than the default \-a, which monitors all SMART
-properties).
-.br
-[NEW EXPERIMENTAL SMARTD FEATURE]
-Multiple \*(Aq\-d TYPE\*(Aq options may be specified with DEVICESCAN
-to combine the scan results of more than one TYPE.
-.Sp
-Configuration entries for specific devices may precede the \fBDEVICESCAN\fP
-entry.
-For example
-.br
-.B  DEFAULT \-m root@example.com
-.br
-.B  /dev/sda \-s S/../.././02
-.br
-.B  /dev/sdc \-d ignore
-.br
-.B  DEVICESCAN \-s L/../.././02
-.br
-will scan for all devices except /dev/sda and /dev/sdc, monitor them, and
-run a long test between 2\(en3 am every morning.
-Device /dev/sda will also be monitored, but only a short test will be run.
-Device /dev/sdc will be ignored.
-Warning emails will be sent for all monitored devices.
-.Sp
-A device is ignored by DEVICESCAN if a configuration line with the same
-device name exists.
-.br
-[NEW EXPERIMENTAL SMARTD FEATURE]
-A device name is also ignored if another device with same identify
-information (vendor, model, firmware version, serial number, WWN) already
-exists.
-.Sp
-.TP
 .B EXAMPLES OF SHELL SCRIPTS FOR \*(Aq\-M exec\*(Aq
 These are two examples of shell scripts that can be used with the \*(Aq\-M
 exec PATH\*(Aq Directive described previously.  The paths to these scripts
@@ -1687,4 +1666,4 @@ full path of this file.
 .SH PACKAGE VERSION
 CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
 .br
-$Id: smartd.conf.5.in 4589 2017-11-04 15:30:54Z chrfranke $
+$Id: smartd.conf.5.in 4856 2018-12-11 21:42:16Z chrfranke $
index 2f1cdc86bd97ea3b2ecb61d91b22b52b7efc0f40..14163dc1fd24a240e43c65b8927b5d6f346d5c45 100644 (file)
@@ -2,29 +2,18 @@
  * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-11 Bruce Allen
- * Copyright (C) 2008-17 Christian Franke
+ * Copyright (C) 2008-18 Christian Franke
  * Copyright (C) 2000    Michael Cornwell <cornwell@acm.org>
  * Copyright (C) 2008    Oliver Bock <brevilo@users.sourceforge.net>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * You should have received a copy of the GNU General Public License
- * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
- *
- * 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/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #include "config.h"
-#include "int64.h"
+#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
 
 // unconditionally included files
+#include <inttypes.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>   // umask
@@ -53,6 +42,7 @@
 #endif
 
 #ifdef _WIN32
+#include "os_win32/popen.h" // popen/pclose()
 #ifdef _MSC_VER
 #pragma warning(disable:4761) // "conversion supplied"
 typedef unsigned short mode_t;
@@ -70,6 +60,10 @@ typedef int pid_t;
 #include <cap-ng.h>
 #endif // LIBCAP_NG
 
+#ifdef HAVE_LIBSYSTEMD
+#include <systemd/sd-daemon.h>
+#endif // HAVE_LIBSYSTEMD
+
 // locally included files
 #include "atacmds.h"
 #include "dev_interface.h"
@@ -78,19 +72,9 @@ typedef int pid_t;
 #include "nvmecmds.h"
 #include "utility.h"
 
-// This is for solaris, where signal() resets the handler to SIG_DFL
-// after the first signal is caught.
-#ifdef HAVE_SIGSET
-#define SIGNALFN sigset
-#else
-#define SIGNALFN signal
-#endif
-
 #ifdef _WIN32
 // fork()/signal()/initd simulation for native Windows
-#include "daemon_win32.h" // daemon_main/detach/signal()
-#undef SIGNALFN
-#define SIGNALFN  daemon_signal
+#include "os_win32/daemon_win32.h" // daemon_main/detach/signal()
 #define strsignal daemon_strsignal
 #define sleep     daemon_sleep
 // SIGQUIT does not exist, CONTROL-Break signals SIGBREAK.
@@ -100,9 +84,46 @@ typedef int pid_t;
 #define SIGQUIT_KEYNAME "CONTROL-\\"
 #endif // _WIN32
 
-const char * smartd_cpp_cvsid = "$Id: smartd.cpp 4556 2017-10-15 17:31:30Z chrfranke $"
+const char * smartd_cpp_cvsid = "$Id: smartd.cpp 4864 2018-12-20 13:02:39Z chrfranke $"
   CONFIG_H_CVSID;
 
+extern "C" {
+  typedef void (*signal_handler_type)(int);
+}
+
+static void set_signal_if_not_ignored(int sig, signal_handler_type handler)
+{
+#if defined(_WIN32)
+  // signal() emulation
+  daemon_signal(sig, handler);
+
+#elif defined(HAVE_SIGACTION)
+  // SVr4, POSIX.1-2001, POSIX.1-2008
+  struct sigaction sa;
+  sa.sa_handler = SIG_DFL;
+  sigaction(sig, (struct sigaction *)0, &sa);
+  if (sa.sa_handler == SIG_IGN)
+    return;
+
+  memset(&sa, 0, sizeof(sa));
+  sa.sa_handler = handler;
+  sa.sa_flags = SA_RESTART; // BSD signal() semantics
+  sigaction(sig, &sa, (struct sigaction *)0);
+
+#elif defined(HAVE_SIGSET)
+  // SVr4, POSIX.1-2001, obsoleted in POSIX.1-2008
+  if (sigset(sig, handler) == SIG_IGN)
+    sigset(sig, SIG_IGN);
+
+#else
+  // POSIX.1-2001, POSIX.1-2008, C89, C99, undefined semantics.
+  // Important: BSD semantics is required.  Traditional signal()
+  // resets the handler to SIG_DFL after the first signal is caught.
+  if (signal(sig, handler) == SIG_IGN)
+    signal(sig, SIG_IGN);
+#endif
+}
+
 using namespace smartmontools;
 
 // smartd exit codes
@@ -171,12 +192,7 @@ static int facility=LOG_DAEMON;
 static bool do_fork=true;
 #endif
 
-#ifdef HAVE_LIBCAP_NG
-// command-line: enable capabilities?
-static bool enable_capabilities = false;
-#endif
-
-// TODO: This smartctl only variable is also used in os_win32.cpp
+// TODO: This smartctl only variable is also used in some os_*.cpp
 unsigned char failuretest_permissive = 0;
 
 // set to one if we catch a USR1 (check devices now)
@@ -198,6 +214,102 @@ static volatile int caughtsigEXIT=0;
 static void PrintOut(int priority, const char *fmt, ...)
                      __attribute_format_printf(2, 3);
 
+#ifdef HAVE_LIBSYSTEMD
+// systemd notify support
+
+static bool notify_enabled = false;
+
+static inline void notify_init()
+{
+  if (!getenv("NOTIFY_SOCKET"))
+    return;
+  notify_enabled = true;
+}
+
+static inline bool notify_post_init()
+{
+  if (!notify_enabled)
+    return true;
+  if (do_fork) {
+    PrintOut(LOG_CRIT, "Option -n (--no-fork) is required if 'Type=notify' is set.\n");
+    return false;
+  }
+  return true;
+}
+
+static void notify_msg(const char * msg, bool ready = false)
+{
+  if (!notify_enabled)
+    return;
+  if (debugmode) {
+    pout("sd_notify(0, \"%sSTATUS=%s\")\n", (ready ? "READY=1\\n" : ""), msg);
+    return;
+  }
+  sd_notifyf(0, "%sSTATUS=%s", (ready ? "READY=1\n" : ""), msg);
+}
+
+static void notify_check(int numdev)
+{
+  if (!notify_enabled)
+    return;
+  char msg[32];
+  snprintf(msg, sizeof(msg), "Checking %d device%s ...",
+           numdev, (numdev != 1 ? "s" : ""));
+  notify_msg(msg);
+}
+
+static void notify_wait(time_t wakeuptime, int numdev)
+{
+  if (!notify_enabled)
+    return;
+  char ts[16], msg[64];
+  strftime(ts, sizeof(ts), "%H:%M:%S", localtime(&wakeuptime));
+  snprintf(msg, sizeof(msg), "Next check of %d device%s will start at %s",
+           numdev, (numdev != 1 ? "s" : ""), ts);
+  static bool ready = true; // first call notifies READY=1
+  notify_msg(msg, ready);
+  ready = false;
+}
+
+static void notify_exit(int status)
+{
+  if (!notify_enabled)
+    return;
+  const char * msg;
+  switch (status) {
+    case 0:             msg = "Exiting ..."; break;
+    case EXIT_BADCMD:   msg = "Error in command line (see SYSLOG)"; break;
+    case EXIT_BADCONF: case EXIT_NOCONF:
+    case EXIT_READCONF: msg = "Error in config file (see SYSLOG)"; break;
+    case EXIT_BADDEV:   msg = "Unable to register a device (see SYSLOG)"; break;
+    case EXIT_NODEV:    msg = "No devices to monitor"; break;
+    default:            msg = "Error (see SYSLOG)"; break;
+  }
+  notify_msg(msg);
+}
+
+#else // HAVE_LIBSYSTEMD
+// No systemd notify support
+
+static inline bool notify_post_init()
+{
+#ifdef __linux__
+  if (getenv("NOTIFY_SOCKET")) {
+    PrintOut(LOG_CRIT, "This version of smartd was build without 'Type=notify' support.\n");
+    return false;
+  }
+#endif
+  return true;
+}
+
+static inline void notify_init() { }
+static inline void notify_msg(const char *) { }
+static inline void notify_check(int) { }
+static inline void notify_wait(time_t, int) { }
+static inline void notify_exit(int) { }
+
+#endif // HAVE_LIBSYSTEMD
+
 // Attribute monitoring flags.
 // See monitor_attr_flags below.
 enum {
@@ -582,12 +694,11 @@ static bool parse_dev_state_line(const char * line, persistent_dev_state & state
       ")" // 16)
      "|(nvme-err-log-entries)" // (24)
      ")" // 1)
-     " *= *([0-9]+)[ \n]*$", // (25)
-    REG_EXTENDED
+     " *= *([0-9]+)[ \n]*$" // (25)
   );
 
   const int nmatch = 1+25;
-  regmatch_t match[nmatch];
+  regular_expression::match_range match[nmatch];
   if (!regex.execute(line, nmatch, match))
     return false;
   if (match[nmatch-1].rm_so < 0)
@@ -846,18 +957,6 @@ static void write_all_dev_attrlogs(const dev_config_vector & configs,
   }
 }
 
-// remove the PID file
-static void RemovePidFile()
-{
-  if (!pid_file.empty()) {
-    if (unlink(pid_file.c_str()))
-      PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n", 
-               pid_file.c_str(), strerror(errno));
-    pid_file.clear();
-  }
-  return;
-}
-
 extern "C" { // signal handlers require C-linkage
 
 //  Note if we catch a SIGUSR1
@@ -898,18 +997,43 @@ static void sighandler(int sig)
 
 } // extern "C"
 
-// Cleanup, print Goodbye message and remove pidfile
-static int Goodbye(int status)
-{
-  // delete PID file, if one was created
-  RemovePidFile();
+#ifdef HAVE_LIBCAP_NG
+// capabilities(7) support
 
-  // and this should be the final output from smartd before it exits
-  PrintOut(status?LOG_CRIT:LOG_INFO, "smartd is exiting (exit status %d)\n", status);
+static bool capabilities_enabled = false;
 
-  return status;
+static void capabilities_drop_now()
+{
+  if (!capabilities_enabled)
+    return;
+  capng_clear(CAPNG_SELECT_BOTH);
+  capng_updatev(CAPNG_ADD, (capng_type_t)(CAPNG_EFFECTIVE|CAPNG_PERMITTED),
+    CAP_SYS_ADMIN, CAP_MKNOD, CAP_SYS_RAWIO, -1);
+  capng_apply(CAPNG_SELECT_BOTH);
+}
+
+static void capabilities_check_config(dev_config_vector & configs)
+{
+  if (!capabilities_enabled)
+    return;
+  for (unsigned i = 0; i < configs.size(); i++) {
+    dev_config & cfg = configs[i];
+    if (!cfg.emailaddress.empty() || !cfg.emailcmdline.empty()) {
+      PrintOut(LOG_INFO, "Device: %s, --capabilites is set, mail will be suppressed.\n",
+               cfg.name.c_str());
+      cfg.emailaddress.clear(); cfg.emailcmdline.clear();
+    }
+  }
 }
 
+#else // HAVE_LIBCAP_NG
+// No capabilities(7) support
+
+static inline void capabilities_drop_now() { }
+static inline void capabilities_check_config(dev_config_vector &) { }
+
+#endif // HAVE_LIBCAP_NG
+
 // 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
@@ -1019,14 +1143,6 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co
       return;
   }
 
-#ifdef HAVE_LIBCAP_NG
-  if (enable_capabilities) {
-    PrintOut(LOG_ERR, "Sending a mail was supressed. "
-             "Mails can't be send when capabilites are enabled\n");
-    return;
-  }
-#endif
-
   // record the time of this mail message, and the first mail message
   if (!mail->logged)
     mail->firstsent=epoch;
@@ -1077,10 +1193,14 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co
   const char * newadd = (!address.empty()? address.c_str() : "<nomailer>");
   const char * newwarn = (which? "Warning via" : "Test of");
 
-#ifndef _WIN32
-  char command[2048];
+  char command[256];
+#ifdef _WIN32
+  // Path may contain spaces
+  snprintf(command, sizeof(command), "\"%s\" 2>&1", warning_script.c_str());
+#else
   snprintf(command, sizeof(command), "%s 2>&1", warning_script.c_str());
-  
+#endif
+
   // tell SYSLOG what we are about to do...
   PrintOut(LOG_INFO,"%s %s to %s ...\n",
            which?"Sending warning via":"Executing test of", executable, newadd);
@@ -1093,7 +1213,7 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co
     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!
+    // pipe succeeded!
     int len, status;
     char buffer[EBUFLEN];
 
@@ -1150,29 +1270,6 @@ static void MailWarning(const dev_config & cfg, dev_state & state, int which, co
       
     }
   }
-  
-#else // _WIN32
-  {
-    char command[2048];
-    snprintf(command, sizeof(command), "cmd /c \"%s\"", warning_script.c_str());
-
-    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, "", 0, 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, (int)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++;
@@ -1317,11 +1414,11 @@ static bool WaitForPidFile()
 
 #endif // _WIN32
 
-// Forks new process, closes ALL file descriptors, redirects stdin,
-// stdout, and stderr.  Not quite daemon().  See
-// http://www.linuxjournal.com/article/2335
+// Forks new process if needed, closes ALL file descriptors,
+// redirects stdin, stdout, and stderr.  Not quite daemon().
+// See https://www.linuxjournal.com/article/2335
 // for a good description of why we do things this way.
-static void DaemonInit()
+static int daemon_init()
 {
 #ifndef _WIN32
 
@@ -1334,15 +1431,15 @@ static void DaemonInit()
     if ((pid=fork()) < 0) {
       // unable to fork!
       PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
-      EXIT(EXIT_STARTUP);
+      return EXIT_STARTUP;
     }
-    else if (pid) {
+    if (pid) {
       // we are the parent process, wait for pid file, then exit cleanly
       if(!WaitForPidFile()) {
         PrintOut(LOG_CRIT,"PID file %s didn't show up!\n", pid_file.c_str());
-        EXIT(EXIT_STARTUP);
-      } else
-        EXIT(0);
+        return EXIT_STARTUP;
+      }
+      return 0;
     }
   
     // from here on, we are the child process.
@@ -1352,11 +1449,11 @@ static void DaemonInit()
     if ((pid=fork()) < 0) {
       // unable to fork!
       PrintOut(LOG_CRIT,"smartd unable to fork daemon process!\n");
-      EXIT(EXIT_STARTUP);
+      return EXIT_STARTUP;
     }
-    else if (pid)
+    if (pid)
       // we are the parent process -- exit cleanly
-      EXIT(0);
+      return 0;
 
     // Now we are the child's child...
   }
@@ -1369,7 +1466,7 @@ static void DaemonInit()
   int fd = open("/dev/null", O_RDWR);
   if (!(fd == 0 && dup(fd) == 1 && dup(fd) == 2 && !chdir("/"))) {
     PrintOut(LOG_CRIT, "smartd unable to redirect to /dev/null or to chdir to root!\n");
-    EXIT(EXIT_STARTUP);
+    return EXIT_STARTUP;
   }
   umask(0022);
 
@@ -1383,16 +1480,18 @@ static void DaemonInit()
   fflush(NULL);
   if (daemon_detach("smartd")) {
     PrintOut(LOG_CRIT,"smartd unable to detach from console!\n");
-    EXIT(EXIT_STARTUP);
+    return EXIT_STARTUP;
   }
   // stdin/out/err now closed if not redirected
 
 #endif // _WIN32
-  return;
+
+  // No error, continue in main_worker()
+  return -1;
 }
 
 // create a PID file containing the current process id
-static void WritePidFile()
+static bool write_pid_file()
 {
   if (!pid_file.empty()) {
     pid_t pid = getpid();
@@ -1408,10 +1507,11 @@ static void WritePidFile()
     umask(old_umask);
     if (!(f && fprintf(f, "%d\n", (int)pid) > 0 && f.close())) {
       PrintOut(LOG_CRIT, "unable to write PID file %s - exiting.\n", pid_file.c_str());
-      EXIT(EXIT_PID);
+      return false;
     }
     PrintOut(LOG_INFO, "file %s written containing PID %d\n", pid_file.c_str(), (int)pid);
   }
+  return true;
 }
 
 // Prints header identifying version of code and home
@@ -1536,8 +1636,12 @@ static void Usage()
 #endif
 #ifndef _WIN32
   PrintOut(LOG_INFO,"  -n, --no-fork\n");
-  PrintOut(LOG_INFO,"        Do not fork into background\n\n");
-#endif  // _WIN32
+  PrintOut(LOG_INFO,"        Do not fork into background\n");
+#ifdef HAVE_LIBSYSTEMD
+  PrintOut(LOG_INFO,"        (systemd 'Type=notify' is assumed if $NOTIFY_SOCKET is set)\n");
+#endif // HAVE_LIBSYSTEMD
+  PrintOut(LOG_INFO,"\n");
+#endif // WIN32
   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");
@@ -1574,7 +1678,7 @@ static int CloseDevice(smart_device * device, const char * name)
     PrintOut(LOG_INFO,"Device: %s, %s, close() failed\n", name, device->get_errmsg());
     return 1;
   }
-  // device sucessfully closed
+  // device successfully closed
   return 0;
 }
 
@@ -1621,9 +1725,34 @@ static int SelfTestErrorCount(ata_device * device, const char * name,
     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, false, firmwarebugs);
+
+  if (!log.mostrecenttest)
+    // No tests logged
+    return 0;
+
+  // Count failed self-tests
+  int errcnt = 0, hours = 0;
+  for (int i = 20; i >= 0; i--) {
+    int j = (i + log.mostrecenttest) % 21;
+    const ata_smart_selftestlog_struct & entry = log.selftest_struct[j];
+    if (!nonempty(&entry, sizeof(entry)))
+      continue;
+
+    int status = entry.selfteststatus >> 4;
+    if (status == 0x0 && (entry.selftestnumber & 0x7f) == 0x02)
+      // First successful extended self-test, stop count
+      break;
+
+    if (0x3 <= status && status <= 0x8) {
+      // Self-test showed an error
+      errcnt++;
+      // Keep track of time of most recent error
+      if (!hours)
+        hours = entry.timestamp;
+    }
+  }
+
+  return ((hours << 8) | errcnt);
 }
 
 #define SELFTEST_ERRORCOUNT(x) (x & 0xff)
@@ -1759,7 +1888,7 @@ static bool is_duplicate_dev_idinfo(const dev_config & cfg, const dev_config_vec
   for (unsigned i = 0; i < prev_cfgs.size(); i++) {
     if (!prev_cfgs[i].id_is_unique)
       continue;
-    if (cfg.dev_idinfo != prev_cfgs[i].dev_idinfo.c_str())
+    if (cfg.dev_idinfo != prev_cfgs[i].dev_idinfo)
       continue;
 
     PrintOut(LOG_INFO, "Device: %s, same identity as %s, ignored\n",
@@ -1894,10 +2023,16 @@ static int ATADeviceScan(dev_config & cfg, dev_state & state, ata_device * atade
     PrintOut(LOG_INFO,"Device: %s, could not enable SMART capability\n",name);
 
     if (ataIsSmartEnabled(&drive) <= 0) {
-      CloseDevice(atadev, name);
-      return 2;
+      if (!cfg.permissive) {
+        PrintOut(LOG_INFO, "Device: %s, to proceed anyway, use '-T permissive' Directive.\n", name);
+        CloseDevice(atadev, name);
+        return 2;
+      }
+      PrintOut(LOG_INFO, "Device: %s, proceeding since '-T permissive' Directive given.\n", name);
+    }
+    else {
+      PrintOut(LOG_INFO, "Device: %s, proceeding since SMART is already enabled\n", name);
     }
-    PrintOut(LOG_INFO, "Device: %s, proceeding since SMART is already enabled\n", name);
   }
   
   // disable device attribute autosave...
@@ -2217,9 +2352,9 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs
   int err, req_len, avail_len, version, len;
   const char *device = cfg.name.c_str();
   struct scsi_iec_mode_page iec;
-  UINT8  tBuf[64];
-  UINT8  inqBuf[96];
-  UINT8  vpdBuf[252];
+  uint8_t  tBuf[64];
+  uint8_t  inqBuf[96];
+  uint8_t  vpdBuf[252];
   char lu_id[64], serial[256], vendor[40], model[40];
 
   // Device must be open
@@ -2273,12 +2408,12 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs
                           vpdBuf, sizeof(vpdBuf))) {
           len = vpdBuf[3];
           vpdBuf[4 + len] = '\0';
-          scsi_format_id_string(serial, (const unsigned char *)&vpdBuf[4], len);
+          scsi_format_id_string(serial, &vpdBuf[4], len);
   }
 
-  unsigned int lb_size;
   char si_str[64];
-  uint64_t capacity = scsiGetSize(scsidev, &lb_size, NULL);
+  struct scsi_readcap_resp srr;
+  uint64_t capacity = scsiGetSize(scsidev, scsidev->use_rcap16(), &srr);
 
   if (capacity)
     format_capacity(si_str, sizeof(si_str), capacity, ".");
@@ -2294,8 +2429,8 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs
   cfg.id_is_unique = (lu_id[0] || serial[0]);
 
   // format "model" string
-  scsi_format_id_string(vendor, (const unsigned char *)&inqBuf[8], 8);
-  scsi_format_id_string(model, (const unsigned char *)&inqBuf[16], 16);
+  scsi_format_id_string(vendor, &inqBuf[8], 8);
+  scsi_format_id_string(model, &inqBuf[16], 16);
   PrintOut(LOG_INFO, "Device: %s, %s\n", device, cfg.dev_idinfo.c_str());
 
   // Check for duplicates
@@ -2381,10 +2516,10 @@ static int SCSIDeviceScan(dev_config & cfg, dev_state & state, scsi_device * scs
   
   // Check if scsiCheckIE() is going to work
   {
-    UINT8 asc = 0;
-    UINT8 ascq = 0;
-    UINT8 currenttemp = 0;
-    UINT8 triptemp = 0;
+    uint8_t asc = 0;
+    uint8_t ascq = 0;
+    uint8_t currenttemp = 0;
+    uint8_t triptemp = 0;
     
     if (scsiCheckIE(scsidev, state.SmartPageSupported, state.TempPageSupported,
                     &asc, &ascq, &currenttemp, &triptemp)) {
@@ -3422,7 +3557,7 @@ static int ATACheckDevice(const dev_config & cfg, dev_state & state, ata_device
       if (cfg.selfteststs) {
         if (   curval.self_test_exec_status != state.smartval.self_test_exec_status
             || state.selftest_started // test was started in previous call
-            || (firstpass && (debugmode || curval.self_test_exec_status != 0x00)))
+            || (firstpass && (debugmode || (curval.self_test_exec_status & 0xf0))))
           log_self_test_exec_status(name, curval.self_test_exec_status);
       }
 
@@ -3492,8 +3627,8 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic
 
   const char * name = cfg.name.c_str();
 
-  UINT8 asc = 0, ascq = 0;
-  UINT8 currenttemp = 0, triptemp = 0;
+  uint8_t asc = 0, ascq = 0;
+  uint8_t currenttemp = 0, triptemp = 0;
   if (!state.SuppressReport) {
     if (scsiCheckIE(scsidev, state.SmartPageSupported, state.TempPageSupported,
                     &asc, &ascq, &currenttemp, &triptemp)) {
@@ -3531,7 +3666,7 @@ static int SCSICheckDevice(const dev_config & cfg, dev_state & state, scsi_devic
   }
   if (!cfg.attrlog_file.empty()){
     // saving error counters to state
-    UINT8 tBuf[252];
+    uint8_t tBuf[252];
     if (state.ReadECounterPageSupported && (0 == scsiLogSense(scsidev,
       READ_ERROR_COUNTER_LPAGE, 0, tBuf, sizeof(tBuf), 0))) {
       scsiDecodeErrCounterPage(tBuf, &state.scsi_error_counters[0].errCounter);
@@ -3729,47 +3864,22 @@ static void CheckDevicesOnce(const dev_config_vector & configs, dev_state_vector
   do_disable_standby_check(configs, states);
 }
 
-// Set if Initialize() was called
-static bool is_initialized = false;
-
-// Does initialization right after fork to daemon mode
-static void Initialize(time_t *wakeuptime)
+// Install all signal handlers
+static void install_signal_handlers()
 {
-  // Call Goodbye() on exit
-  is_initialized = true;
-  
-  // write PID file
-  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);
+  set_signal_if_not_ignored(SIGTERM, sighandler);
+  set_signal_if_not_ignored(SIGQUIT, sighandler);
   
   // in debug mode, <CONTROL-C> ==> HUP
-  if (SIGNALFN(SIGINT, debugmode?HUPhandler:sighandler)==SIG_IGN)
-    SIGNALFN(SIGINT, SIG_IGN);
+  set_signal_if_not_ignored(SIGINT, (debugmode ? HUPhandler : sighandler));
   
   // Catch HUP and USR1
-  if (SIGNALFN(SIGHUP, HUPhandler)==SIG_IGN)
-    SIGNALFN(SIGHUP, SIG_IGN);
-  if (SIGNALFN(SIGUSR1, USR1handler)==SIG_IGN)
-    SIGNALFN(SIGUSR1, SIG_IGN);
+  set_signal_if_not_ignored(SIGHUP, HUPhandler);
+  set_signal_if_not_ignored(SIGUSR1, USR1handler);
 #ifdef _WIN32
-  if (SIGNALFN(SIGUSR2, USR2handler)==SIG_IGN)
-    SIGNALFN(SIGUSR2, SIG_IGN);
+  set_signal_if_not_ignored(SIGUSR2, USR2handler);
 #endif
-
-  // initialize wakeup time to CURRENT time
-  *wakeuptime=time(NULL);
-  
-  return;
 }
 
 #ifdef _WIN32
@@ -3798,7 +3908,7 @@ static void ToggleDebugMode()
 }
 #endif
 
-static time_t dosleep(time_t wakeuptime, bool & sigwakeup)
+static time_t dosleep(time_t wakeuptime, bool & sigwakeup, int numdev)
 {
   // If past wake-up-time, compute next wake-up-time
   time_t timenow=time(NULL);
@@ -3806,7 +3916,9 @@ static time_t dosleep(time_t wakeuptime, bool & sigwakeup)
     int intervals=1+(timenow-wakeuptime)/checktime;
     wakeuptime+=intervals*checktime;
   }
-  
+
+  notify_wait(wakeuptime, numdev);
+
   // sleep until we catch SIGUSR1 or have completed sleeping
   int addtime = 0;
   while (timenow < wakeuptime+addtime && !caughtsigUSR1 && !caughtsigHUP && !caughtsigEXIT) {
@@ -4225,7 +4337,7 @@ static int ParseToken(char * token, dev_config & cfg, smart_devtype_list & scan_
     }
     // Compile regex
     else {
-      if (!cfg.test_regex.compile(arg, REG_EXTENDED)) {
+      if (!cfg.test_regex.compile(arg)) {
         // not a valid regular expression!
         PrintOut(LOG_CRIT, "File %s line %d (drive %s): -s argument \"%s\" is INVALID extended regular expression. %s.\n",
                  configfile, lineno, name, arg, cfg.test_regex.get_errmsg());
@@ -4247,14 +4359,6 @@ static int ParseToken(char * token, dev_config & cfg, smart_devtype_list & scan_
       if (!cfg.emailaddress.empty())
         PrintOut(LOG_INFO, "File %s line %d (drive %s): ignoring previous Address Directive -m %s\n",
                  configfile, lineno, name, cfg.emailaddress.c_str());
-#ifdef _WIN32 // TODO: Remove after smartmontools 6.5
-      if (   !strcmp(arg, "msgbox")          || !strcmp(arg, "sysmsgbox")
-          || str_starts_with(arg, "msgbox,") || str_starts_with(arg, "sysmsgbox,")) {
-        PrintOut(LOG_CRIT, "File %s line %d (drive %s): -m %s is no longer supported, use -m console[,...] instead\n",
-                 configfile, lineno, name, arg);
-        return -1;
-      }
-#endif
       cfg.emailaddress = arg;
     }
     break;
@@ -4568,7 +4672,7 @@ static int ParseConfigLine(dev_config_vector & conf_entries, dev_config & defaul
 // -3:    config file exists but cannot be read
 //
 // In the case where the return value is 0, there are three
-// possiblities:
+// possibilities:
 // Empty configuration file ==> conf_entries.empty()
 // No configuration file    ==> conf_entries[0].lineno == 0
 // SCANDIRECTIVE found      ==> conf_entries.back().lineno != 0 (size >= 1)
@@ -4720,23 +4824,23 @@ static void PrintValidArgs(char opt)
 }
 
 #ifndef _WIN32
-// Report error and exit if specified path is not absolute.
-static void check_abs_path(char option, const std::string & path)
+// Report error and return false if specified path is not absolute.
+static bool check_abs_path(char option, const std::string & path)
 {
   if (path.empty() || path[0] == '/')
-    return;
+    return true;
 
   debugmode = 1;
   PrintHead();
   PrintOut(LOG_CRIT, "=======> INVALID ARGUMENT TO -%c: %s <=======\n\n", option, path.c_str());
   PrintOut(LOG_CRIT, "Error: relative path names are not allowed\n\n");
-  EXIT(EXIT_BADCMD);
+  return false;
 }
 #endif // !_WIN32
 
 // Parses input line, prints usage message and
 // version/license/copyright messages
-static void ParseOpts(int argc, char **argv)
+static int parse_options(int argc, char **argv)
 {
   // Init default path names
 #ifndef _WIN32
@@ -4855,8 +4959,7 @@ static void ParseOpts(int argc, char **argv)
       // print summary of all valid directives
       debugmode = 1;
       Directives();
-      EXIT(0);
-      break;
+      return 0;
     case 'i':
       // Period (time interval) for checking
       // strtol will set errno in the event of overflow, so we'll check it.
@@ -4868,7 +4971,7 @@ static void ParseOpts(int argc, char **argv)
         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);
+        return EXIT_BADCMD;
       }
       checktime = (int)lchecktime;
       break;
@@ -4921,7 +5024,7 @@ static void ParseOpts(int argc, char **argv)
           use_default_db = false;
         unsigned char savedebug = debugmode; debugmode = 1;
         if (!read_drive_database(path))
-          EXIT(EXIT_BADCMD);
+          return EXIT_BADCMD;
         debugmode = savedebug;
       }
       break;
@@ -4932,12 +5035,11 @@ static void ParseOpts(int argc, char **argv)
       // print version and CVS info
       debugmode = 1;
       PrintOut(LOG_INFO, "%s", format_version_info("smartd", true /*full*/).c_str());
-      EXIT(0);
-      break;
+      return 0;
 #ifdef HAVE_LIBCAP_NG
     case 'C':
       // enable capabilities
-      enable_capabilities = true;
+      capabilities_enabled = true;
       break;
 #endif
     case 'h':
@@ -4945,8 +5047,7 @@ static void ParseOpts(int argc, char **argv)
       debugmode=1;
       PrintHead();
       Usage();
-      EXIT(0);
-      break;
+      return 0;
     case '?':
     default:
       // unrecognized option
@@ -4964,7 +5065,7 @@ static void ParseOpts(int argc, char **argv)
           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);
+        return EXIT_BADCMD;
       }
       if (optopt) {
         // Iff optopt holds a valid option then argument must be missing.
@@ -4975,10 +5076,10 @@ static void ParseOpts(int argc, char **argv)
           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);
+        return EXIT_BADCMD;
       }
       Usage();
-      EXIT(0);
+      return 0;
     }
 
     // Check to see if option had an unrecognized or incorrect argument.
@@ -4991,7 +5092,7 @@ static void ParseOpts(int argc, char **argv)
       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);
+      return EXIT_BADCMD;
     }
   }
 
@@ -5001,7 +5102,7 @@ static void ParseOpts(int argc, char **argv)
     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);
+    return EXIT_BADCMD;
   }
 
   // no pidfile in debug mode
@@ -5010,15 +5111,16 @@ static void ParseOpts(int argc, char **argv)
     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.c_str());
-    EXIT(EXIT_BADCMD);
+    return EXIT_BADCMD;
   }
 
 #ifndef _WIN32
   if (!debugmode) {
-    // absolute path names are required due to chdir('/') after fork().
-    check_abs_path('p', pid_file);
-    check_abs_path('s', state_path_prefix);
-    check_abs_path('A', attrlog_path_prefix);
+    // absolute path names are required due to chdir('/') in daemon_init()
+    if (!(   check_abs_path('p', pid_file)
+          && check_abs_path('s', state_path_prefix)
+          && check_abs_path('A', attrlog_path_prefix)))
+      return EXIT_BADCMD;
   }
 #endif
 
@@ -5026,12 +5128,19 @@ static void ParseOpts(int argc, char **argv)
   {
     unsigned char savedebug = debugmode; debugmode = 1;
     if (!init_drive_database(use_default_db))
-        EXIT(EXIT_BADCMD);
+      return EXIT_BADCMD;
     debugmode = savedebug;
   }
 
+  // Check option compatibility of notify support
+  if (!notify_post_init())
+    return EXIT_BADCMD;
+
   // print header
   PrintHead();
+
+  // No error, continue in main_worker()
+  return -1;
 }
 
 // Function we call if no configuration file was found or if the
@@ -5243,8 +5352,8 @@ static bool register_device(dev_config & cfg, dev_state & state, smart_device_au
 // This function tries devices from conf_entries.  Each one that can be
 // registered is moved onto the [ata|scsi]devices lists and removed
 // from the conf_entries list.
-static void RegisterDevices(const dev_config_vector & conf_entries, smart_device_list & scanned_devs,
-                            dev_config_vector & configs, dev_state_vector & states, smart_device_list & devices)
+static bool register_devices(const dev_config_vector & conf_entries, smart_device_list & scanned_devs,
+                             dev_config_vector & configs, dev_state_vector & states, smart_device_list & devices)
 {
   // start by clearing lists/memory of ALL existing devices
   configs.clear();
@@ -5289,12 +5398,12 @@ static void RegisterDevices(const dev_config_vector & conf_entries, smart_device
     // If scanning, pass dev_idinfo of previous devices for duplicate check
     dev_state state;
     if (!register_device(cfg, state, dev, (scanning ? &configs : 0))) {
-      // if device is explictly listed and we can't register it, then
+      // if device is explicitly listed and we can't register it, then
       // exit unless the user has specified that the device is removable
       if (!scanning) {
         if (!(cfg.removable || quit == QUIT_NEVER)) {
           PrintOut(LOG_CRIT, "Unable to register device %s (no Directive -d removable). Exiting.\n", cfg.name.c_str());
-          EXIT(EXIT_BADDEV);
+          return false;
         }
         PrintOut(LOG_INFO, "Device: %s, not available\n", cfg.name.c_str());
         // Prevent retry of registration
@@ -5312,6 +5421,7 @@ static void RegisterDevices(const dev_config_vector & conf_entries, smart_device
   }
 
   init_disable_standby_check(configs);
+  return true;
 }
 
 
@@ -5323,14 +5433,13 @@ static int main_worker(int argc, char **argv)
   if (!smi())
     return 1;
 
-  // is it our first pass through?
-  bool firstpass = true;
-
-  // next time to wake up
-  time_t wakeuptime = 0;
+  // Check whether systemd notify is supported and enabled
+  notify_init();
 
   // parse input and print header and usage info if needed
-  ParseOpts(argc,argv);
+  int status = parse_options(argc,argv);
+  if (status >= 0)
+    return status;
   
   // Configuration for each device
   dev_config_vector configs;
@@ -5339,41 +5448,16 @@ static int main_worker(int argc, char **argv)
   // Devices to monitor
   smart_device_list devices;
 
-  bool write_states_always = true;
+  // Drop capabilities if supported and enabled
+  capabilities_drop_now();
 
-#ifdef HAVE_LIBCAP_NG
-  // Drop capabilities
-  if (enable_capabilities) {
-    capng_clear(CAPNG_SELECT_BOTH);
-    capng_updatev(CAPNG_ADD, (capng_type_t)(CAPNG_EFFECTIVE|CAPNG_PERMITTED),
-                  CAP_SYS_ADMIN, CAP_MKNOD, CAP_SYS_RAWIO, -1);
-    capng_apply(CAPNG_SELECT_BOTH);
-  }
-#endif
+  notify_msg("Initializing ...");
 
   // the main loop of the code
-  for (;;) {
-
-    // 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));
-
-      if (!isok)
-        return EXIT_SIGNAL;
-
-      // Write state files
-      if (!state_path_prefix.empty())
-        write_all_dev_states(configs, states);
-
-      return 0;
-    }
-
+  bool firstpass = true, write_states_always = true;
+  time_t wakeuptime = 0;
+  // assert(status < 0);
+  do {
     // Should we (re)read the config file?
     if (firstpass || caughtsigHUP){
       if (!firstpass) {
@@ -5386,6 +5470,7 @@ static int main_worker(int argc, char **argv)
                  "Signal HUP - rereading configuration file %s\n":
                  "\a\nSignal INT - rereading configuration file %s (" SIGQUIT_KEYNAME " quits)\n\n",
                  configfile);
+        notify_msg("Reloading ...");
       }
 
       {
@@ -5396,9 +5481,14 @@ static int main_worker(int argc, char **argv)
 
         if (entries>=0) {
           // checks devices, then moves onto ata/scsi list or deallocates.
-          RegisterDevices(conf_entries, scanned_devs, configs, states, devices);
+          if (!register_devices(conf_entries, scanned_devs, configs, states, devices)) {
+            status = EXIT_BADDEV;
+            break;
+          }
           if (!(configs.size() == devices.size() && configs.size() == states.size()))
             throw std::logic_error("Invalid result from RegisterDevices");
+          // Handle limitations if capabilities are dropped
+          capabilities_check_config(configs);
         }
         else if (   quit == QUIT_NEVER
                  || ((quit == QUIT_NODEV || quit == QUIT_NODEVSTARTUP) && !firstpass)) {
@@ -5408,45 +5498,38 @@ static int main_worker(int argc, char **argv)
         }
         else {
           // exit with configuration file error status
-          return (entries==-3 ? EXIT_READCONF : entries==-2 ? EXIT_NOCONF : EXIT_BADCONF);
+          status = (entries == -3 ? EXIT_READCONF : entries == -2 ? EXIT_NOCONF : EXIT_BADCONF);
+          break;
         }
       }
 
-      // Log number of devices we are monitoring...
-      if (devices.size() > 0 || quit == QUIT_NEVER || (quit == QUIT_NODEVSTARTUP && !firstpass)) {
-        int numata = 0, numscsi = 0;
-        for (unsigned i = 0; i < devices.size(); i++) {
-          const smart_device * dev = devices.at(i);
-          if (dev->is_ata())
-            numata++;
-          else if (dev->is_scsi())
-            numscsi++;
-        }
-        PrintOut(LOG_INFO,"Monitoring %d ATA/SATA, %d SCSI/SAS and %d NVMe devices\n",
-                 numata, numscsi, (int)devices.size() - numata - numscsi);
+      if (!(   devices.size() > 0 || quit == QUIT_NEVER
+            || (quit == QUIT_NODEVSTARTUP && !firstpass))) {
+        PrintOut(LOG_INFO, "Unable to monitor any SMART enabled devices. %sExiting...\n",
+                 (!debugmode ? "Try debug (-d) option. " : ""));
+        status = EXIT_NODEV;
+        break;
       }
-      else {
-        PrintOut(LOG_INFO,"Unable to monitor any SMART enabled devices. Try debug (-d) option. Exiting...\n");
-        return EXIT_NODEV;
+
+      // Log number of devices we are monitoring...
+      int numata = 0, numscsi = 0;
+      for (unsigned i = 0; i < devices.size(); i++) {
+        const smart_device * dev = devices.at(i);
+        if (dev->is_ata())
+          numata++;
+        else if (dev->is_scsi())
+          numscsi++;
       }
+      PrintOut(LOG_INFO, "Monitoring %d ATA/SATA, %d SCSI/SAS and %d NVMe devices\n",
+               numata, numscsi, (int)devices.size() - numata - numscsi);
 
       if (quit == QUIT_SHOWTESTS) {
         // user has asked to print test schedule
         PrintTestSchedule(configs, states, devices);
+        // assert(firstpass);
         return 0;
       }
 
-#ifdef HAVE_LIBCAP_NG
-      if (enable_capabilities) {
-        for (unsigned i = 0; i < configs.size(); i++) {
-          if (!configs[i].emailaddress.empty() || !configs[i].emailcmdline.empty()) {
-            PrintOut(LOG_WARNING, "Mail can't be enabled together with --capabilities. All mail will be suppressed.\n");
-            break;
-          }
-        }
-      }
-#endif
-
       // reset signal
       caughtsigHUP=0;
 
@@ -5456,6 +5539,7 @@ static int main_worker(int argc, char **argv)
 
     // check all devices once,
     // self tests are not started in first pass unless '-q onecheck' is specified
+    notify_check((int)devices.size());
     CheckDevicesOnce(configs, states, devices, firstpass, (!firstpass || quit == QUIT_ONECHECK));
 
      // Write state files
@@ -5469,25 +5553,75 @@ static int main_worker(int argc, char **argv)
 
     // user has asked us to exit after first check
     if (quit == QUIT_ONECHECK) {
-      PrintOut(LOG_INFO,"Started with '-q onecheck' option. All devices sucessfully checked once.\n"
+      PrintOut(LOG_INFO,"Started with '-q onecheck' option. All devices successfully checked once.\n"
                "smartd is exiting (exit status 0)\n");
+      // assert(firstpass);
       return 0;
     }
     
-    // fork into background if needed
-    if (firstpass && !debugmode) {
-      DaemonInit();
-    }
+    if (firstpass) {
+      if (!debugmode) {
+        // fork() into background if needed, close ALL file descriptors,
+        // redirect stdin, stdout, and stderr, chdir to "/".
+        status = daemon_init();
+        if (status >= 0)
+          return status;
+
+        // Write PID file if configured
+        if (!write_pid_file())
+          return EXIT_PID;
+      }
+
+      // Set exit and signal handlers
+      install_signal_handlers();
+
+      // Initialize wakeup time to CURRENT time
+      wakeuptime = time(0);
 
-    // set exit and signal handlers, write PID file, set wake-up time
-    if (firstpass){
-      Initialize(&wakeuptime);
       firstpass = false;
     }
-    
+
     // sleep until next check time, or a signal arrives
-    wakeuptime = dosleep(wakeuptime, write_states_always);
+    wakeuptime = dosleep(wakeuptime, write_states_always, (int)devices.size());
+
+  } while (!caughtsigEXIT);
+
+  if (caughtsigEXIT && status < 0) {
+    // Loop exited on signal
+    if (caughtsigEXIT == SIGTERM || (debugmode && caughtsigEXIT == SIGQUIT)) {
+      PrintOut(LOG_INFO, "smartd received signal %d: %s\n",
+               caughtsigEXIT, strsignal(caughtsigEXIT));
+    }
+    else {
+      // Unexpected SIGINT or SIGQUIT
+      PrintOut(LOG_CRIT, "smartd received unexpected signal %d: %s\n",
+               caughtsigEXIT, strsignal(caughtsigEXIT));
+      status = EXIT_SIGNAL;
+    }
   }
+
+  // Status unset above implies success
+  if (status < 0)
+    status = 0;
+
+  if (!firstpass) {
+    // Loop exited after daemon_init() and write_pid_file()
+
+    // Write state files only on normal exit
+    if (!status && !state_path_prefix.empty())
+      write_all_dev_states(configs, states);
+
+    // Delete PID file, if one was created
+    if (!pid_file.empty() && unlink(pid_file.c_str()))
+        PrintOut(LOG_CRIT,"Can't unlink PID file %s (%s).\n",
+                 pid_file.c_str(), strerror(errno));
+
+    // and this should be the final output from smartd before it exits
+    PrintOut((status ? LOG_CRIT : LOG_INFO), "smartd is exiting (exit status %d)\n",
+             status);
+  }
+
+  return status;
 }
 
 
@@ -5504,10 +5638,6 @@ static int smartd_main(int argc, char **argv)
     // Do the real work ...
     status = main_worker(argc, argv);
   }
-  catch (int ex) {
-    // EXIT(status) arrives here
-    status = ex;
-  }
   catch (const std::bad_alloc & /*ex*/) {
     // Memory allocation failed (also thrown by std::operator new)
     PrintOut(LOG_CRIT, "Smartd: Out of memory\n");
@@ -5529,9 +5659,7 @@ static int smartd_main(int argc, char **argv)
   if (status == EXIT_BADCODE)
     PrintOut(LOG_CRIT, "Please inform " PACKAGE_BUGREPORT ", including output of smartd -V.\n");
 
-  if (is_initialized)
-    status = Goodbye(status);
-
+  notify_exit(status);
 #ifdef _WIN32
   daemon_winsvc_exitcode = status;
 #endif
diff --git a/smartd.cygwin.initd.in b/smartd.cygwin.initd.in
new file mode 100644 (file)
index 0000000..8879b74
--- /dev/null
@@ -0,0 +1,131 @@
+#! /bin/sh
+#
+# smartmontools initd file for Cygwin smartd
+#
+# Home page of code is: http://www.smartmontools.org
+#
+# Copyright (C) 2004-17 Christian Franke
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# $Id: smartd.cygwin.initd.in 4760 2018-08-19 18:45:53Z chrfranke $
+#
+
+SMARTD_BIN=/usr/local/sbin/smartd
+
+# 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 (SMART) built into \
+ATA/SATA and SCSI/SAS hard drives and solid-state drives. \
+www.smartmontools.org"
+
+# Source configuration file.
+[ -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=; dep2=
+    if cygrunsrv -L 2>/dev/null | grep "^syslogd$" >/dev/null 2>&1; then
+      dep="syslogd"
+    fi
+    if cygrunsrv -L 2>/dev/null | grep "^syslog-ng" >/dev/null 2>&1; then
+      dep2="syslog-ng"
+    fi
+    if [ -z "$dep" ]; then
+      if [ -z "$dep2" ]; then
+        echo "Warning: no syslog service installed, smartd will write to windows event log.";
+      else
+        dep="$dep2"
+      fi
+    else
+      if [ -z "$dep2" ]; then
+        :
+      else
+        dep=
+        echo "Warning: both syslogd and syslog-ng installed, dependency not set."
+      fi
+    fi
+    echo "Installing service ${smartd_svcname}${dep:+ (depending on '$dep')}${smartd_opts:+ with options '$smartd_opts'}:"
+    cygrunsrv -I "$smartd_svcname" -d "$smartd_svcdisp"  -f "$smartd_svcdesc" ${dep:+-y} $dep \
+              -e CYGWIN="$CYGWIN" -p $SMARTD_BIN -a "-n -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
index dd5aedae72df428393efdc8db9766628f302cec0..81fe224b0101cf11dbb00b5e6b62e4758da40731 100644 (file)
@@ -2,7 +2,8 @@
 
 # smartmontools init file for smartd
 # Copyright (C) 2002-8 Bruce Allen
-# $Id: smartd.initd.in 4431 2017-08-08 19:38:15Z chrfranke $
+# SPDX-License-Identifier: GPL-2.0-or-later
+# $Id: smartd.initd.in 4760 2018-08-19 18:45:53Z chrfranke $
 
 # For RedHat and cousins:
 # chkconfig: 2345 40 40
 # 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.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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
@@ -436,61 +425,6 @@ elif [ -f /etc/environment.corel ] ; then
 
 # 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
-               ;;
-           reload)
-               kill -s HUP `cat $PID_FILE`
-               ;;
-           report)
-               kill -s USR1 `cat $PID_FILE`
-               ;;
-           *)
-               echo "Usage: $0 {start|stop|restart|reload|report}"
-               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.
@@ -527,129 +461,6 @@ elif uname -a | grep SunOS > /dev/null 2>&1 ; then
     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://www.smartmontools.org/"
-
-    # Source configuration file.  This should define the shell variable smartd_opts.
-    # Email smartmontools-support mailing list 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=; dep2=
-            if cygrunsrv -L 2>/dev/null | grep "^syslogd$" >/dev/null 2>&1; then
-                dep="syslogd"
-            fi
-            if cygrunsrv -L 2>/dev/null | grep "^syslog-ng" >/dev/null 2>&1; then
-                dep2="syslog-ng"
-            fi
-            if [ -z "$dep" ]; then
-                if [ -z "$dep2" ]; then
-                    echo "Warning: no syslog service installed, smartd will write to windows event log.";
-                else
-                    dep="$dep2"
-                fi
-            else
-                if [ -z "$dep2" ]; then
-                    :
-                else
-                    dep=
-                    echo "Warning: both syslogd and syslog-ng installed, dependency not set."
-                fi
-            fi
-            echo "Installing service ${smartd_svcname}${dep:+ (depending on '$dep')}${smartd_opts:+ with options '$smartd_opts'}:"
-            cygrunsrv -I "$smartd_svcname" -d "$smartd_svcdisp"  -f "$smartd_svcdesc" ${dep:+-y} $dep \
-              -e CYGWIN="$CYGWIN" -p $SMARTD_BIN -a "-n -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"
index dc8b14374a51904a0a47f30b798d42ee7d407047..d7e05c44ba94c2a3d16d6b98ad30c55a7e304f38 100644 (file)
@@ -1,9 +1,9 @@
 [Unit]
 Description=Self Monitoring and Reporting Technology (SMART) Daemon
 Documentation=man:smartd(8) man:smartd.conf(5)
-After=syslog.target
 
 [Service]
+Type=notify
 EnvironmentFile=-/usr/local/etc/sysconfig/smartmontools
 ExecStart=/usr/local/sbin/smartd -n $smartd_opts
 ExecReload=/bin/kill -HUP $MAINPID
index 4f9142519641ee1197ca7fba1d1e11da350ced75..1cc268357caf0b291f0f0cff1eefe1b870e31095 100644 (file)
@@ -6,15 +6,9 @@
 #
 # Copyright (C) 2012-16 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.
+# SPDX-License-Identifier: GPL-2.0-or-later
 #
-# You should have received a copy of the GNU General Public License
-# (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
-#
-# $Id: smartd_warning.sh.in 4351 2016-10-17 18:53:40Z chrfranke $
+# $Id: smartd_warning.sh.in 4839 2018-11-27 18:26:08Z chrfranke $
 #
 
 set -e
@@ -45,7 +39,7 @@ smartd $VERSION warning message script
 
 Usage:
   export SMARTD_MAILER='Path to external script, empty for "$os_mailer"'
-  export SMARTD_ADDRESS='Space separated mail adresses, empty if none'
+  export SMARTD_ADDRESS='Space separated mail addresses, empty if none'
   export SMARTD_MESSAGE='Error Message'
   export SMARTD_FAILTYPE='Type of failure, "EMailTest" for tests'
   export SMARTD_TFIRST='Date of first message sent, empty if none'
index 0ba5b90afb5abdf6745117edd0e521dd558640e0..3ed66eaf6a30da727348c79e82dc043be303e54d 100644 (file)
@@ -1,16 +1,10 @@
 .ig
 Copyright (C) 2013 Hannes von Haugwitz <hannes@vonhaugwitz.com>
-Copyright (C) 2014-17 Christian Franke
+Copyright (C) 2014-18 Christian Franke
 
-$Id: update-smart-drivedb.8.in 4584 2017-11-03 22:43:32Z chrfranke $
+SPDX-License-Identifier: GPL-2.0-or-later
 
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-You should have received a copy of the GNU General Public License
-(for example COPYING); If not, see <http://www.gnu.org/licenses/>.
+$Id: update-smart-drivedb.8.in 4879 2018-12-28 22:05:12Z chrfranke $
 
 ..
 .\" Macros borrowed from pages generated with Pod::Man
@@ -64,7 +58,7 @@ or
 .BR svn (1).
 .PP
 [NEW EXPERIMENTAL UPDATE-SMART-DRIVEDB FEATURE]
-The downloaded file is verified with OpenPGP/GPG key ID DFD22559.
+The downloaded file is verified with OpenPGP/GPG key ID 721042C5.
 The public key block is included in the script.
 .PP
 The old file is kept if the downloaded file is identical (ignoring
@@ -98,17 +92,20 @@ The default is the first one found in PATH.
 Use URL of LOCATION for download.
 LOCATION is one of:
 .br
+.I github
+(GitHub mirror of SVN repository),
+.br
 .I sf
-(Sourceforge code browser via HTTPS),
+(Sourceforge code browser),
 .br
 .I svn
-(SVN repository via HTTPS),
+(SVN repository),
 .br
 .I svni
-(SVN repository via HTTP),
+(SVN repository via HTTP instead of HTTPS),
 .br
 .I trac
-(Trac code browser via HTTPS).
+(Trac code browser).
 .br
 The default is
 .IR svn .
@@ -200,4 +197,4 @@ Alternatively send the info to the smartmontools support mailing list:
 .SH PACKAGE VERSION
 CURRENT_SVN_VERSION CURRENT_SVN_DATE CURRENT_SVN_REV
 .br
-$Id: update-smart-drivedb.8.in 4584 2017-11-03 22:43:32Z chrfranke $
+$Id: update-smart-drivedb.8.in 4879 2018-12-28 22:05:12Z chrfranke $
index d98f6f7397ed99215860976437daf119af75c141..4b23c7f05bbfe8df938cdcb9acd5cb7fd21999f4 100644 (file)
@@ -4,17 +4,11 @@
 #
 # Home page of code is: http://www.smartmontools.org
 #
-# Copyright (C) 2010-17 Christian Franke
+# Copyright (C) 2010-18 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.
+# SPDX-License-Identifier: GPL-2.0-or-later
 #
-# You should have received a copy of the GNU General Public License
-# (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
-#
-# $Id: update-smart-drivedb.in 4584 2017-11-03 22:43:32Z chrfranke $
+# $Id: update-smart-drivedb.in 4879 2018-12-28 22:05:12Z chrfranke $
 #
 
 set -e
@@ -45,10 +39,16 @@ GPG="@gnupg@"
 # Smartctl used for syntax check
 SMARTCTL="$sbindir/smartctl"
 
+# PATH information for help and error messages
+@ENABLE_SCRIPTPATH_FALSE@pathinfo='$PATH'
+@ENABLE_SCRIPTPATH_TRUE@pathinfo="'$PATH'"
+
 myname=$0
 
 usage()
 {
+@ENABLE_SCRIPTPATH_TRUE@  pathinfo="
+@ENABLE_SCRIPTPATH_TRUE@                   $pathinfo"
   cat <<EOF
 smartmontools $VERSION drive database update script
 
@@ -57,12 +57,13 @@ Usage: $myname [OPTIONS] [DESTFILE]
   -s SMARTCTL     Use SMARTCTL for syntax check ('-s -' to disable)
                   [default: $SMARTCTL]
   -t TOOL         Use TOOL for download: $os_dltools
-                  [default: first one found in PATH]
+                  [default: first one found in $pathinfo]
   -u LOCATION     Use URL of LOCATION for download:
-                    sf (Sourceforge code browser via HTTPS)
-                    svn (SVN repository via HTTPS) [default]
-                    svni (SVN repository via HTTP)
-                    trac (Trac code browser via HTTPS)
+                    github (GitHub mirror of SVN repository)
+                    sf (Sourceforge code browser)
+                    svn (SVN repository) [default]
+                    svni (SVN repository via HTTP instead of HTTPS)
+                    trac (Trac code browser)
   --trunk         Download from SVN trunk (may require '--no-verify')
   --cacert FILE   Use CA certificates from FILE to verify the peer
   --capath DIR    Use CA certificate files from DIR to verify the peer
@@ -85,6 +86,14 @@ error()
   exit 1
 }
 
+err_notfound()
+{
+  case $1 in
+    */*) error "$1: not found $2" ;;
+    *)   error "$1: not found in $pathinfo $2" ;;
+  esac
+}
+
 warning()
 {
   echo "$myname: (Warning) $*" >&2
@@ -93,6 +102,10 @@ warning()
 selecturl()
 {
   case $1 in
+    github)  # https://github.com/smartmontools/smartmontools/raw/origin/$BRANCH/smartmontools/drivedb.h
+             # https://github.com/smartmontools/smartmontools/raw/master/smartmontools/drivedb.h
+          # redirected to:
+          url='https://raw.githubusercontent.com/smartmontools/smartmontools/master/smartmontools/drivedb.h' ;;
     sf)   url='https://sourceforge.net/p/smartmontools/code/HEAD/tree/trunk/smartmontools/drivedb.h?format=raw' ;;
     svn)  url='https://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools/drivedb.h' ;;
     svni) url='http://svn.code.sf.net/p/smartmontools/code/trunk/smartmontools/drivedb.h' ;;
@@ -245,57 +258,59 @@ check_file()
 # unexpand_svn_id < INFILE > OUTFILE
 unexpand_svn_id()
 {
-  sed 's,\$''Id'': drivedb\.h [0-9][0-9]* 2[-0-9]* [012][:0-9]*Z [a-z][a-z]* \$,$''Id''$,'
+  sed 's,\$''Id'': drivedb\.h [0-9][0-9]* 2[-0-9]* [012][:0-9]*Z [a-z][a-z0-9]* \$,$''Id''$,'
 }
 
-# Smartmontools Signing Key (through 2018)
+# Smartmontools Signing Key (through 2020)
 # <smartmontools-database@listi.jpberlin.de>
-# Key ID DFD22559
+# Key ID 721042C5
 public_key="\
 -----BEGIN PGP PUBLIC KEY BLOCK-----
 
-mQENBFgOYoEBCAC93841SlFmpp6640hKUvZ8PbZR6OGnZnMXD6QRVzpibXGZXUDB
-f6unujun5Ql4ObAWt6QuRqz5Gk2gF8tcOfN6edR/uK5gyX2rlWVLoZKOV91a3aDI
-iIDh018tLWOpHg3VxgHL6f0iMcFogUYnD5zhC5Z2GVhFb/cVpj+ocZWcxQLQGPVv
-uZPUQWrvdpFEzcnxPMtJJDqXEChzhrdFTXGm69ERxULOro7yDmG1Y5xWmhdGnPPM
-cuCXVVlADz/Gh1w+ay7RqFnzPjqjQmHAuggns467TJEcS0yiX4LJnEoKLyPGen9L
-FH6z38xHCNt4Da05/OeRgXwVLH9M95lu8d6TABEBAAG0U1NtYXJ0bW9udG9vbHMg
-U2lnbmluZyBLZXkgKHRocm91Z2ggMjAxOCkgPHNtYXJ0bW9udG9vbHMtZGF0YWJh
-c2VAbGlzdGkuanBiZXJsaW4uZGU+iQFBBBMBAgArAhsDBQkEHA0ABgsJCAcDAgYV
-CAIJCgsEFgIDAQIeAQIXgAUCWe5KCQIZAQAKCRDzh2PO39IlWbM5CAC6GNFXkJEu
-Beu1TV2e3N47IwZDsQXypn8DGBVh5VmhFGVHPO5dgBBGXEHBcpiFk6RGXOqyLQar
-bZd0qmGaTCuakUU5MipCB/fPEpOm15CSPzJIAAHz0HiDgJc8YW+JfGUA6P4EHa+r
-OyYcfCu66NNYTjBQJ/wHcwcuIY1xNzEMhb4TCEcM/Nex9zZ7d0+WTWsK4U8m3ui3
-IDESRssCzTTjc5gH/tMz8KuEwY3v91mHc0/vNYVQZx9atWOuuj3JJKqdr8oll/qo
-/33gIadY66dgArQhqybdPFCEKckhoHvlPxoqg7XPKSw6oyBxM/0wf/gM5MGsUImD
-qu1djwVlKH7xiQEcBBMBAgAGBQJZ7kylAAoJEC/N7AvTrxqroQQH/jrZAGT5t8uy
-zRTzJCf3Bco8FqwKcfw8hhpF1Uaypa+quxkpYz9PtP+3e9lGxl0XSEzOwHjfgGWX
-ISUOM1ufVxo2hSLG87yO7naFAtylL8l0Zny8Fb6kmT9f3vMktbHdXHUTDNrCUkoE
-lEwwDK3qaur8IPUaIKeSTC3C8E/DVnasLs9cpOs2LPIKr3ishbqbHNeWOgGyHbA4
-KCtvQzBhun9drmtQJW6OyCC9FcIoqPSFM/bs2KHf7qATNu9kSMg/YWw7WLAD4GPq
-H9us1GigQ0h6Y4KG5EgmkFvuQFPLHvT4rtqv51zzs1iwFh4+GIagFp+HJ2jnlp+G
-cZcySlwfnem0V1NtYXJ0bW9udG9vbHMgU2lnbmluZyBLZXkgKHRocm91Z2ggMjAx
-OCkgPHNtYXJ0bW9udG9vbHMtZGF0YWJhc2VAbGlzdHMuc291cmNlZm9yZ2UubmV0
-PokBPgQTAQIAKAUCWA5igQIbAwUJBBwNAAYLCQgHAwIGFQgCCQoLBBYCAwECHgEC
-F4AACgkQ84djzt/SJVldMQf+MxE3PM70mnIr/Dcrubt8AA3JeMkThNV2xFe9Rdkl
-4tJ1ogU8T5PCgMqJ4Gei9mUmbiQu1CKLSf9k/oxBRcLZK8Fav+BMj0+4YERfZx7J
-Qzou3f0RX8u3pc/+pRXLE6lH/Luk453NzqM3tCFyWgw89dEzFlwYpWx7AfxiFwvH
-ShEPNKZdEp+aBAu8oW9lSKiwLNFsembSGVh+5+3yMiKK02oOdC/NPKTnxxDgvzl4
-Ulm3dNjI3uuBtFNFvs6qKk8CXV9AfM2993QxaCtRMph/ymqX4zXcecvJYpn3vulF
-bAzDTzge7TVhkmaupzDNLeE8IS5sgUjSOM1x3+2SkBiVSYkBHAQTAQIABgUCWA5k
-YwAKCRDfDxpJxKSQOp+/CADTlsgisoXI6b+0oohRaD4ZVl5eBtkvTrxNQf6EF7Z1
-uPkVOqi1OLWFGyAmbeLcRmN6c4/DVcaa6GAG7GA+KQwVPRCyC+9Ibsn/+uG6ZFXA
-ez+0eG9NxOfkCnYH8ZP8o2VH+9uKJlGGujh9o5r1SNGVifoLGTc8NkWCW+MAKj8d
-w8WW+wDc80YrdCRrSyLrRU9NLTSE4pIJWKcHLwG63xkXHQPPR1lsJgzdAalfEv1T
-QdIF3sM+GXp4lZ6buahFDiILBh1vj+5C9TdpWZAlqHDYFICa7Rv/MvQa4O9UUl3S
-lN3sed8zwAmL3HeoXE5tBu8iatMaS9e3BmSsVYlhd/q+iQEcBBMBAgAGBQJYDmSW
-AAoJEC/N7AvTrxqr8HsH+QGQuhHYt9Syccd8AF36psyT03mqgbGLMZL8H9ngoa9Z
-qVMq7O8Aqz23SGTtuNuw6EyrcHo7Dy1311GftshI6arsFNJxE2ZNGIfGocRxu9m3
-Ez+AysWT9sxz/haHE+d58NTg+/7R8YWS1q+Tk6m8dA0Xyf3tMBsIJfj0zJvuGMbC
-Lmd93Yw4nk76qtSn9UHbnf76UJN5SctAd8+gK3uO6O4XDcZqC06xkWKl193lzcC8
-sZJBdI15NszC3y/epnILDDMBUNQMBm/XlCYQUetyrJnAVzFGXurtjEXQ/DDnbfy2
-Z8efoG8rtq7v3fxS1TC5jSVOIEqOE4TwzRz1Y/dfqSU=
-=CNK4
+mQINBFwmhpUBEADRoOZaXq13MrqyAmbGe6FlHi6P9ujsT/SJGhTiAoN3W1X56Dbm
+KP21nO9ZAjdXnvA2OmzppfCUX7v5Q3/TG3vN3WwfyQIO/dgSaTrGa1E8odbHEGc7
+rhzYA8ekAn3TmxhOrEUTcRIogumW0zlQewHOlTe0OYsxat6/N8l3Cqn28HwZUpRH
+MrJW3RgefFihQGEhXlnfzo+Tltl14IriURbwBZIDeZOk2AWLGweI0+zqTgYSbF5A
+tI5rXO1QDeoyBYZhSX3MtnncwPdCnxoRasizU5w3KoZWYyKAc5bxJBJgUUp9HDOu
+ATgNqekc8j28x/cUAWerXe183SBYQp0QkzMPbmE9TCGW3GjtW+Kk/NDbNe8ufj6O
+hk0r7EbGyBO0qvgzHLzSsQiSsgaMCkLc5Xt4NzB4g2DvnReFU2WwgRh031lHOVLm
+mvFqRtHzJb20dKufyjOmSMzNKRzURVmobECKARaBlGNP0wHYhq97n4OxM1o0eq7a
+4ugaSp2q+6BSaAQhbZN8ULCF/oGA/376Sz7RNuoOmQwl9aFqnfl3YgopBIqKvnSP
+h4j0QynN45rUFOe/VywTmpWKj+DonGCupxe9VvyZ87NKRgKiHprXGDrhdB0GcNXM
+wV66WbjKBV7qlpSh/GH3oiHwlcYT8LNyZbxTJXcVF5ODtlZfc9zqRtUBWQARAQAB
+tFNTbWFydG1vbnRvb2xzIFNpZ25pbmcgS2V5ICh0aHJvdWdoIDIwMjApIDxzbWFy
+dG1vbnRvb2xzLWRhdGFiYXNlQGxpc3RpLmpwYmVybGluLmRlPokCPgQTAQIAKAUC
+XCaGlQIbAwUJA8etAAYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ6nSrJXIQ
+QsWXYQ/+IVHGQxDOg7lMX9iDbg/UDj/zrQfsJR2HQ2j0iI8TmsQLSK4pphwN0r9D
+g0BuKhQBe3wPphLjwD40HueKatIacE91PgLse/KWmEe4OoQCDxshiIGad3YoIF3X
+yrJg6pcMLOAnfT55Tg04EmWpT1LzWTJmH8RL2iftTM217Q2JnfQGKicTiD/GiYV1
+oyFUvn+H5/u5O7UYhvWKBcccJtal2uhc6h8U2HugMV0SpNM5p83oGDZkV0YYSJ0C
+044im1+axbz06Aeq7Uh3JFScCcbjl+SQ7gK0NJF39uI8HbwC7fcfySCj5JDuVeaq
+KjahWctKa/D6nauKA8+LIGOckkf2oN0sJBrES7Zn8ImHYN/1wLCff9oIDAlux6Jk
+BZ6+MqIJKHit4SSYPd3QnkdI1ehn+2EdxK9VSBU0W2ZPlZmoUSamWboloumhwYyN
+86ohFVJWnN4YWlZiJNJlxj/F6d4GTEJBFqoK9yStdz8Dsg16sAwuNYFVFtCKaesA
+keuhcS3SfoFXwLsz+8cLfHVdsBHmm9/OCfNtOm3EPJqaD57lL5ocTWQeLaAqUCse
+rOCDoIUZul5e6kRytjjNIHFNufWTbuw4YlYM3+FU1nkgckmhw4M9kI/xGtVj7bvs
+tJKKN976kOoRZRIAL+9SlC+3Tqd9a4y4RRjYongvFzqpqRlQfS+JARwEEwECAAYF
+AlwmhpwACgkQL83sC9OvGqsVOggAqLB5eQrUv8E9ikD6kJCito827bzDWF29yD7P
+vfhjXaz5in54jOVpwg3o9CsqIjjRW0/1bBVswC8ZL0sAdZ+GDSDMw5F2IpkD77gj
+nFY79M/e6C9xYyxYzHC7emDPSz9IroOvdkkEgrB+OABKkaOCcS18P4Lk3WNHaPw5
+c7aI0z1iJP52EmSfvB8r86mtUFJB+f15eD/4vaRfkZLFjF9FQ3kgEK1U+rV4s1O2
+bCFfP3WPDcc83NgwRUvtXmcSOSOIoXnemJzyJr+JnqCWVET4XWF6i20mRFXVEpWt
+f5AkJYgR3z/jW0djELbBWA/35bAnpXy5pDHv9NbZsTkBZxK/kokBHAQTAQIABgUC
+XCaGnQAKCRAY7NpGy/a6xn4lB/90tXTnZsgmoftol9uivfQrPdR88WmOZLYmUeQA
+d1rqSFMxe+KzO/qLuU8s6OF4nznwL2cPfbGZxezM4PiYmAmbbEU/3gTONwjVBBA0
+Gfimy/fITEezFtCigo1thkaJ195g/dqY+zE3Vt4rzC03j1vx8mUHRPU6kkvKj8cP
+0j+XHX2xQDsTXTstfnom29wBmGnvSZ9HgcdL71e1VXJXwikmnO3P4J/1C2LeCOlW
+rGqWZ2c0WBLKdJnsYUx7Dm/OvkkB4lF+zWp98zS8jS/5h+1apVgEzrdTMvT8ydTk
+Ur7ObKGkIhK+L+Xo5BD+V9Qf6xKGYPwhhdj/E5/kyjULrm10iQEcBBMBAgAGBQJc
+JoadAAoJEPOHY87f0iVZfiUH/3yKS5wGvTeRInse8+W1WzKuto3XzqXLngb9QXWw
+7nCwqmNS7PbzDnufQi2ThKrMfcK14WgNYABNZPU75I+6bcb0oCB5tlooIUEV/2Ut
+/5Hl/83zFFoNA/kQKVz8kIDqgRcxC+zY2VJ4eTKHyQDvXygVk8wnKTBae3gX+CIZ
+qJHPXiiygHlbl31Mi3G1Iaxu57dP6ocV0vX1dytKSwd4Rbviwwb4L76o/tVT9t3G
+wFM15uK1SqtnAaiaktEdMi3XI4d01H3VUVz/iR0XQbf13RZoEM6CJWmsQ/qvYlwk
+bKOdlahjoHrFlkhADSBaO9N1OZp3OYDjziIujMdt2IPKnmM=
+=0uFV
 -----END PGP PUBLIC KEY BLOCK-----
 "
 
@@ -410,7 +425,7 @@ if [ -z "$tool" ]; then
       break
     fi
   done
-  test -n "$tool" || error "found none of: $os_dltools"
+  test -n "$tool" || error "found none of '$os_dltools' in $pathinfo"
 fi
 
 test -n "$url" || selecturl "svn"
@@ -435,7 +450,7 @@ esac
 # Check for smartctl
 if [ "$smtctl" != "-" ]; then
   "$smtctl" -V >/dev/null 2>&1 \
-  || error "$smtctl: not found ('-s -' to ignore)"
+  || err_notfound "$smtctl" "('-s -' to ignore)"
 fi
 
 # Check for GnuPG
@@ -443,14 +458,15 @@ if [ -z "$no_verify" ]; then
   test -n "$GPG" \
   || error "GnuPG is not available ('--no-verify' to ignore)"
   "$GPG" --version >/dev/null 2>&1 \
-  || error "$GPG: not found ('--no-verify' to ignore)"
+  || err_notfound "$GPG" "('--no-verify' to ignore)"
 fi
 
 # Use destination directory as temp directory for gpg
 tmpdir=`dirname "$DEST"`
 
 # Adjust URLs
-src=`echo "$url" | sed "s,/trunk/,/branches/$BRANCH/,"`
+src=`echo "$url" | sed -e "s,/trunk/,/branches/$BRANCH/," \
+                       -e "s,/master/,/origin/$BRANCH/,"`
 src_asc=`echo "$src" | sed "s,/drivedb\.h,/drivedb.h.raw.asc,"`
 test -z "$trunk" || src=$url
 
index 5aabd26d8671aa801c850fdd9dbbbfeab25be443..e83f6304d23ec74f09e838e0e6f74ca2fdaa8327 100644 (file)
@@ -4,22 +4,10 @@
  * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-12 Bruce Allen
- * Copyright (C) 2008-17 Christian Franke
+ * Copyright (C) 2008-18 Christian Franke
  * 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, see <http://www.gnu.org/licenses/>.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 // THIS FILE IS INTENDED FOR UTILITY ROUTINES THAT ARE APPLICABLE TO
@@ -27,7 +15,9 @@
 // SMARTCTL, OR BOTH.
 
 #include "config.h"
+#define __STDC_FORMAT_MACROS 1 // enable PRI* for C++
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
 #include <stdexcept>
 
 #include "svnversion.h"
-#include "int64.h"
 #include "utility.h"
 
 #include "atacmds.h"
 #include "dev_interface.h"
+#include "sg_unaligned.h"
 
-const char * utility_cpp_cvsid = "$Id: utility.cpp 4583 2017-11-03 21:10:09Z chrfranke $"
-                                 UTILITY_H_CVSID INT64_H_CVSID;
+const char * utility_cpp_cvsid = "$Id: utility.cpp 4842 2018-12-02 16:07:26Z chrfranke $"
+  UTILITY_H_CVSID;
 
 const char * packet_types[] = {
         "Direct-access (disk)",
@@ -90,7 +80,7 @@ std::string format_version_info(const char * prog_name, bool full /*= false*/)
       "(build date " __DATE__ ")" // checkout without expansion of Id keywords
 #endif
       " [%s] " BUILD_INFO "\n"
-    "Copyright (C) 2002-17, Bruce Allen, Christian Franke, www.smartmontools.org\n",
+    "Copyright (C) 2002-18, Bruce Allen, Christian Franke, www.smartmontools.org\n",
     prog_name, smi()->get_os_version_str().c_str()
   );
   if (!full)
@@ -114,15 +104,31 @@ std::string format_version_info(const char * prog_name, bool full /*= false*/)
 #endif
     "smartmontools build host: " SMARTMONTOOLS_BUILD_HOST "\n"
     "smartmontools build with: "
-#if   __cplusplus > 201402
+
+#define N2S_(s) #s
+#define N2S(s) "(" N2S_(s) ")"
+#if   __cplusplus >  201703
+                               "C++2x" N2S(__cplusplus)
+#elif __cplusplus == 201703
                                "C++17"
-#elif __cplusplus > 201103
+#elif __cplusplus >  201402
+                               "C++14" N2S(__cplusplus)
+#elif __cplusplus == 201402
                                "C++14"
-#elif __cplusplus > 199711
+#elif __cplusplus >  201103
+                               "C++11" N2S(__cplusplus)
+#elif __cplusplus == 201103
                                "C++11"
-#else
+#elif __cplusplus >  199711
+                               "C++98" N2S(__cplusplus)
+#elif __cplusplus == 199711
                                "C++98"
+#else
+                               "C++"   N2S(__cplusplus)
 #endif
+#undef N2S
+#undef N2S_
+
 #if defined(__GNUC__) && defined(__VERSION__) // works also with CLang
                                      ", GCC " __VERSION__
 #endif
@@ -163,7 +169,7 @@ static char *ReadSiteDefaultTimezone(){
 #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
+// time-zone since the last time we polled devices. The canonical
 // 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
@@ -272,29 +278,11 @@ const char *packetdevicetype(int type){
   return "Unknown";
 }
 
-// Runtime check of byte ordering, throws if different from isbigendian().
-static void check_endianness()
-{
-  union {
-    // Force compile error if int type is not 32bit.
-    unsigned char c[sizeof(unsigned) == 4 ? 4 : -1];
-    unsigned i;
-  } x = {{1,2,3,4}};
-
-  int big = -1;
-  switch (x.i) {
-    case 0x01020304: big = 1; break;
-    case 0x04030201: big = 0; break;
-  }
-
-  if (big != (isbigendian() ? 1 : 0))
-    throw std::logic_error("CPU endianness does not match compile time test");
-}
-
 // Utility function prints date and time and timezone into a character
-// buffer of length>=64.  All the fuss is needed to get the right
+// buffer of length 64.  All the fuss is needed to get the right
 // timezone info (sigh).
-void dateandtimezoneepoch(char *buffer, time_t tval){
+void dateandtimezoneepoch(char (& buffer)[DATEANDEPOCHLEN], time_t tval)
+{
   struct tm *tmval;
   const char *timezonename;
   char datebuffer[DATEANDEPOCHLEN];
@@ -345,16 +333,6 @@ void dateandtimezoneepoch(char *buffer, time_t tval){
   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;
-}
-
 // 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){
@@ -434,22 +412,13 @@ static const char * check_regex(const char * pattern)
   return (const char *)0;
 }
 
-// Wrapper class for regex(3)
+// Wrapper class for POSIX regex(3) or std::regex
 
-regular_expression::regular_expression()
-: m_flags(0)
-{
-  memset(&m_regex_buf, 0, sizeof(m_regex_buf));
-}
+#ifndef WITH_CXX11_REGEX
 
-regular_expression::regular_expression(const char * pattern, int flags,
-                                       bool throw_on_error /*= true*/)
+regular_expression::regular_expression()
 {
   memset(&m_regex_buf, 0, sizeof(m_regex_buf));
-  if (!compile(pattern, flags) && throw_on_error)
-    throw std::runtime_error(strprintf(
-      "error in regular expression \"%s\": %s",
-      m_pattern.c_str(), m_errmsg.c_str()));
 }
 
 regular_expression::~regular_expression()
@@ -458,15 +427,19 @@ regular_expression::~regular_expression()
 }
 
 regular_expression::regular_expression(const regular_expression & x)
+: m_pattern(x.m_pattern),
+  m_errmsg(x.m_errmsg)
 {
   memset(&m_regex_buf, 0, sizeof(m_regex_buf));
-  copy(x);
+  copy_buf(x);
 }
 
 regular_expression & regular_expression::operator=(const regular_expression & x)
 {
+  m_pattern = x.m_pattern;
+  m_errmsg = x.m_errmsg;
   free_buf();
-  copy(x);
+  copy_buf(x);
   return *this;
 }
 
@@ -478,13 +451,9 @@ void regular_expression::free_buf()
   }
 }
 
-void regular_expression::copy(const regular_expression & x)
+void regular_expression::copy_buf(const regular_expression & x)
 {
-  m_pattern = x.m_pattern;
-  m_flags = x.m_flags;
-  m_errmsg = x.m_errmsg;
-
-  if (!m_pattern.empty() && m_errmsg.empty()) {
+  if (nonempty(&x.m_regex_buf, sizeof(x.m_regex_buf))) {
     // There is no POSIX compiled-regex-copy command.
     if (!compile())
       throw std::runtime_error(strprintf(
@@ -493,17 +462,39 @@ void regular_expression::copy(const regular_expression & x)
   }
 }
 
-bool regular_expression::compile(const char * pattern, int flags)
+#endif // !WITH_CXX11_REGEX
+
+regular_expression::regular_expression(const char * pattern)
+: m_pattern(pattern)
 {
+  if (!compile())
+    throw std::runtime_error(strprintf(
+      "error in regular expression \"%s\": %s",
+      m_pattern.c_str(), m_errmsg.c_str()));
+}
+
+bool regular_expression::compile(const char * pattern)
+{
+#ifndef WITH_CXX11_REGEX
   free_buf();
+#endif
   m_pattern = pattern;
-  m_flags = flags;
   return compile();
 }
 
 bool regular_expression::compile()
 {
-  int errcode = regcomp(&m_regex_buf, m_pattern.c_str(), m_flags);
+#ifdef WITH_CXX11_REGEX
+  try {
+    m_regex.assign(m_pattern, std::regex_constants::extended);
+  }
+  catch (std::regex_error & ex) {
+    m_errmsg = ex.what();
+    return false;
+  }
+
+#else
+  int errcode = regcomp(&m_regex_buf, m_pattern.c_str(), REG_EXTENDED);
   if (errcode) {
     char errmsg[512];
     regerror(errcode, &m_regex_buf, errmsg, sizeof(errmsg));
@@ -511,11 +502,16 @@ bool regular_expression::compile()
     free_buf();
     return false;
   }
+#endif
 
   const char * errmsg = check_regex(m_pattern.c_str());
   if (errmsg) {
     m_errmsg = errmsg;
+#ifdef WITH_CXX11_REGEX
+    m_regex = std::regex();
+#else
     free_buf();
+#endif
     return false;
   }
 
@@ -523,56 +519,38 @@ bool regular_expression::compile()
   return true;
 }
 
-#ifndef HAVE_STRTOULL
-// Replacement for missing strtoull() (Linux with libc < 6, MSVC)
-// Functionality reduced to requirements of smartd and split_selective_arg().
+bool regular_expression::full_match(const char * str) const
+{
+#ifdef WITH_CXX11_REGEX
+  return std::regex_match(str, m_regex);
+#else
+  match_range range;
+  return (   !regexec(&m_regex_buf, str, 1, &range, 0)
+          && range.rm_so == 0 && range.rm_eo == (int)strlen(str));
+#endif
+}
 
-uint64_t strtoull(const char * p, char * * endp, int base)
+bool regular_expression::execute(const char * str, unsigned nmatch, match_range * pmatch) const
 {
-  uint64_t result, maxres;
-  int i = 0;
-  char c = p[i++];
-
-  if (!base) {
-    if (c == '0') {
-      if (p[i] == 'x' || p[i] == 'X') {
-        base = 16; i++;
-      }
-      else
-        base = 8;
-      c = p[i++];
+#ifdef WITH_CXX11_REGEX
+  std::cmatch m;
+  if (!std::regex_search(str, m, m_regex))
+    return false;
+  unsigned sz = m.size();
+  for (unsigned i = 0; i < nmatch; i++) {
+    if (i < sz && *m[i].first) {
+      pmatch[i].rm_so = m[i].first  - str;
+      pmatch[i].rm_eo = m[i].second - str;
     }
     else
-      base = 10;
+      pmatch[i].rm_so = pmatch[i].rm_eo = -1;
   }
+  return true;
 
-  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++];
-  }
-  if (endp)
-    *endp = (char *)p + i - 1;
-  return result;
+#else
+  return !regexec(&m_regex_buf, str, nmatch, pmatch, 0);
+#endif
 }
-#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)
@@ -755,6 +733,83 @@ std::string strprintf(const char * fmt, ...)
   return str;
 }
 
+#if defined(HAVE___INT128)
+// Compiler supports '__int128'.
+
+// Recursive 128-bit to string conversion function
+static int snprint_uint128(char * str, int strsize, unsigned __int128 value)
+{
+  if (strsize <= 0)
+    return -1;
+
+  if (value <= 0xffffffffffffffffULL) {
+    // Print leading digits as 64-bit value
+    return snprintf(str, (size_t)strsize, "%" PRIu64, (uint64_t)value);
+  }
+  else {
+    // Recurse to print leading digits
+    const uint64_t e19 = 10000000000000000000ULL; // 2^63 < 10^19 < 2^64
+    int len1 = snprint_uint128(str, strsize, value / e19);
+    if (len1 < 0)
+      return -1;
+
+    // Print 19 digits remainder as 64-bit value
+    int len2 = snprintf(str + (len1 < strsize ? len1 : strsize - 1),
+                        (size_t)(len1 < strsize ? strsize - len1 : 1),
+                        "%019" PRIu64, (uint64_t)(value % e19)        );
+    if (len2 < 0)
+      return -1;
+    return len1 + len2;
+  }
+}
+
+// Convert 128-bit unsigned integer provided as two 64-bit halves to a string.
+const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo)
+{
+  snprint_uint128(str, strsize, ((unsigned __int128)value_hi << 64) | value_lo);
+  return str;
+}
+
+#elif defined(HAVE_LONG_DOUBLE_WIDER_PRINTF)
+// Compiler and *printf() support 'long double' which is wider than 'double'.
+
+const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo)
+{
+  snprintf(str, strsize, "%.0Lf", value_hi * (0xffffffffffffffffULL + 1.0L) + value_lo);
+  return str;
+}
+
+#else // !HAVE_LONG_DOUBLE_WIDER_PRINTF
+// No '__int128' or 'long double' support, use 'double'.
+
+const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo)
+{
+  snprintf(str, strsize, "%.0f", value_hi * (0xffffffffffffffffULL + 1.0) + value_lo);
+  return str;
+}
+
+#endif // HAVE___INT128
+
+// Runtime check of byte ordering, throws on error.
+static void check_endianness()
+{
+  const union {
+    // Force compile error if int type is not 32bit.
+    unsigned char c[sizeof(int) == 4 ? 8 : -1];
+    uint64_t i;
+  } x = {{1, 2, 3, 4, 5, 6, 7, 8}};
+  const uint64_t le = 0x0807060504030201ULL;
+  const uint64_t be = 0x0102030405060708ULL;
+
+  if (!(   x.i == (isbigendian() ? be : le)
+        && sg_get_unaligned_le16(x.c)   == (uint16_t)le
+        && sg_get_unaligned_be16(x.c+6) == (uint16_t)be
+        && sg_get_unaligned_le32(x.c)   == (uint32_t)le
+        && sg_get_unaligned_be32(x.c+4) == (uint32_t)be
+        && sg_get_unaligned_le64(x.c)   == le
+        && sg_get_unaligned_be64(x.c)   == be          ))
+    throw std::logic_error("CPU endianness does not match compile time test");
+}
 
 #ifndef HAVE_WORKING_SNPRINTF
 // Some versions of (v)snprintf() don't append null char (MSVCRT.DLL),
index bd56a24933aae4efd55ca2bdf522714d0dbcd874..d309128213679046227d625c7957dbe3f9ee6d40 100644 (file)
--- a/utility.h
+++ b/utility.h
@@ -4,37 +4,32 @@
  * Home page of code is: http://www.smartmontools.org
  *
  * Copyright (C) 2002-11 Bruce Allen
- * Copyright (C) 2008-16 Christian Franke
+ * Copyright (C) 2008-18 Christian Franke
  * 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, see <http://www.gnu.org/licenses/>.
- *
- * This code was originally developed as a Senior Thesis by Michael Cornwell
- * at the Concurrent Systems Laboratory (now part of the Storage Systems
- * Research Center), Jack Baskin School of Engineering, University of
- * California, Santa Cruz. http://ssrc.soe.ucsc.edu/
- *
+ * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
 #ifndef UTILITY_H_
 #define UTILITY_H_
 
-#define UTILITY_H_CVSID "$Id: utility.h 4309 2016-04-24 14:59:15Z chrfranke $"
+#define UTILITY_H_CVSID "$Id: utility.h 4848 2018-12-05 18:30:46Z chrfranke $"
 
+#include <float.h> // *DBL_MANT_DIG
 #include <time.h>
-#include <sys/types.h> // for regex.h (according to POSIX)
-#include <regex.h>
 #include <stdarg.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 #include <string>
 
+#include <sys/types.h> // for regex.h (according to POSIX)
+#ifdef WITH_CXX11_REGEX
+#include <regex>
+#else
+#include <regex.h>
+#endif
+
 #ifndef __GNUC__
 #define __attribute_format_printf(x, y)  /**/
 #elif defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO
@@ -48,7 +43,7 @@
 // Make version information string
 std::string format_version_info(const char * prog_name, bool full = false);
 
-// return (v)sprintf() formated std::string
+// return (v)sprintf() formatted std::string
 std::string strprintf(const char * fmt, ...)
     __attribute_format_printf(1, 2);
 std::string vstrprintf(const char * fmt, va_list ap);
@@ -69,18 +64,11 @@ int safe_vsnprintf(char *buf, int size, const char *fmt, va_list ap);
 #define vsnprintf safe_vsnprintf
 #endif
 
-#ifndef HAVE_STRTOULL
-// Replacement for missing strtoull() (Linux with libc < 6, MSVC)
-uint64_t strtoull(const char * p, char * * endp, int base);
-#endif
-
-// Utility function prints current date and time and timezone into a
-// character buffer of length>=64.  All the fuss is needed to get the
+// 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).
 #define DATEANDEPOCHLEN 64
-void dateandtimezone(char *buffer);
-// Same, but for time defined by epoch tval
-void dateandtimezoneepoch(char *buffer, time_t tval);
+void dateandtimezoneepoch(char (& buffer)[DATEANDEPOCHLEN], time_t tval);
 
 // like printf() except that we can control it better. Note --
 // although the prototype is given here in utility.h, the function
@@ -95,10 +83,6 @@ void syserror(const char *message);
 // Function for processing -t selective... option in smartctl
 int split_selective_arg(char *s, uint64_t *start, uint64_t *stop, int *mode);
 
-// Replacement for exit(status)
-// (exit is not compatible with C++ destructors)
-#define EXIT(status) { throw (int)(status); }
-
 // Compile time check of byte ordering
 // (inline const function allows compiler to remove dead code)
 inline bool isbigendian()
@@ -110,6 +94,17 @@ inline bool isbigendian()
 #endif
 }
 
+void swap2(char *location);
+void swap4(char *location);
+void swap8(char *location);
+// Typesafe variants using overloading
+inline void swapx(unsigned short * p)
+  { swap2((char*)p); }
+inline void swapx(unsigned int * p)
+  { swap4((char*)p); }
+inline void swapx(uint64_t * p)
+  { swap8((char*)p); }
+
 // Runtime check of ./configure result, throws on error.
 void check_config();
 
@@ -227,25 +222,30 @@ private:
   void operator=(const stdio_file &);
 };
 
-/// Wrapper class for regex(3).
+/// Wrapper class for POSIX regex(3) or std::regex
 /// Supports copy & assignment and is compatible with STL containers.
 class regular_expression
 {
 public:
   // Construction & assignment
-  regular_expression();
+#ifdef WITH_CXX11_REGEX
+  regular_expression() = default;
 
-  regular_expression(const char * pattern, int flags,
-                     bool throw_on_error = true);
+#else
+  regular_expression();
 
   ~regular_expression();
 
   regular_expression(const regular_expression & x);
 
   regular_expression & operator=(const regular_expression & x);
+#endif
+
+  /// Construct with pattern, throw on error.
+  explicit regular_expression(const char * pattern);
 
   /// Set and compile new pattern, return false on error.
-  bool compile(const char * pattern, int flags);
+  bool compile(const char * pattern);
 
   // Get pattern from last compile().
   const char * get_pattern() const
@@ -259,33 +259,65 @@ public:
   bool empty() const
     { return (m_pattern.empty() || !m_errmsg.empty()); }
 
-  /// Return true if substring matches pattern
-  bool match(const char * str, int flags = 0) const
-    { return !regexec(&m_regex_buf, str, 0, (regmatch_t*)0, flags); }
-
   /// Return true if full string matches pattern
-  bool full_match(const char * str, int flags = 0) const
-    {
-      regmatch_t range;
-      return (   !regexec(&m_regex_buf, str, 1, &range, flags)
-              && range.rm_so == 0 && range.rm_eo == (int)strlen(str));
-    }
+  bool full_match(const char * str) const;
 
-  /// Return true if substring matches pattern, fill regmatch_t array.
-  bool execute(const char * str, unsigned nmatch, regmatch_t * pmatch, int flags = 0) const
-    { return !regexec(&m_regex_buf, str, nmatch, pmatch, flags); }
+#ifdef WITH_CXX11_REGEX
+  struct match_range { int rm_so, rm_eo; };
+#else
+  typedef regmatch_t match_range;
+#endif
+
+  /// Return true if substring matches pattern, fill match_range array.
+  bool execute(const char * str, unsigned nmatch, match_range * pmatch) const;
 
 private:
   std::string m_pattern;
-  int m_flags;
-  regex_t m_regex_buf;
   std::string m_errmsg;
 
+#ifdef WITH_CXX11_REGEX
+  std::regex m_regex;
+#else
+  regex_t m_regex_buf;
   void free_buf();
-  void copy(const regular_expression & x);
+  void copy_buf(const regular_expression & x);
+#endif
+
   bool compile();
 };
 
+// 128-bit unsigned integer to string conversion.
+// Provides full integer precision if compiler supports '__int128'.
+// Otherwise precision depends on supported floating point data types.
+
+#if defined(HAVE_LONG_DOUBLE_WIDER) && \
+    (!defined(__MINGW32__) || defined(__USE_MINGW_ANSI_STDIO))
+    // MinGW 'long double' type does not work with MSVCRT *printf()
+#define HAVE_LONG_DOUBLE_WIDER_PRINTF 1
+#else
+#undef HAVE_LONG_DOUBLE_WIDER_PRINTF
+#endif
+
+// Return #bits precision provided by uint128_hilo_to_str().
+inline int uint128_to_str_precision_bits()
+{
+#if defined(HAVE___INT128)
+  return 128;
+#elif defined(HAVE_LONG_DOUBLE_WIDER_PRINTF)
+  return LDBL_MANT_DIG;
+#else
+  return DBL_MANT_DIG;
+#endif
+}
+
+// Convert 128-bit unsigned integer provided as two 64-bit halves to a string.
+const char * uint128_hilo_to_str(char * str, int strsize, uint64_t value_hi, uint64_t value_lo);
+
+// Version for fixed size buffers.
+template <size_t SIZE>
+inline const char * uint128_hilo_to_str(char (& str)[SIZE], uint64_t value_hi, uint64_t value_lo)
+  { return uint128_hilo_to_str(str, (int)SIZE, value_hi, value_lo); }
+
 #ifdef _WIN32
 // Get exe directory
 //(implemented in os_win32.cpp)
@@ -301,4 +333,3 @@ std::string get_exe_dir();
 #endif
 
 #endif
-